summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils
diff options
context:
space:
mode:
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java20
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java325
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java65
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java4
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/package-info.java7
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java3
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java45
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java56
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java120
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java48
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java7
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java26
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java97
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java17
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java44
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java8
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java12
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java15
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java5
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java8
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java84
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java59
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java17
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java22
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java47
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java4
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java56
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java2
28 files changed, 1030 insertions, 193 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java b/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java
index 755d7e0..713e1e0 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java
@@ -20,6 +20,9 @@ public class PropertyDB {
private static SimpleProperties formats;
+ /*
+ * Whether or not to log during the loading.
+ */
private static final boolean LOGLOAD = false;
/*
@@ -38,22 +41,32 @@ public class PropertyDB {
* being loaded will block, to prevent reads from partial states.
*/
public static void reloadProperties() {
+ /*
+ * Do the load with the write lock taken.
+ */
loadLock.write(() -> {
if (LOGLOAD) {
System.out.println("Reading regex properties:");
}
+
+ /*
+ * Load regexes.
+ */
regexes = new SimpleProperties();
regexes.loadFrom(PropertyDB.class.getResourceAsStream("/regexes.sprop"), false);
if (LOGLOAD) {
regexes.outputProperties();
System.out.println();
}
-
compiledRegexes = new HashMap<>();
if (LOGLOAD) {
System.out.println("Reading format properties:");
}
+
+ /*
+ * Load formats.
+ */
formats = new SimpleProperties();
formats.loadFrom(PropertyDB.class.getResourceAsStream("/formats.sprop"), false);
if (LOGLOAD) {
@@ -100,6 +113,9 @@ public class PropertyDB {
throw new NoSuchElementException(msg);
}
+ /*
+ * Get the regex, and cache a compiled version.
+ */
return compiledRegexes.computeIfAbsent(key, strang -> {
return Pattern.compile(regexes.get(strang));
});
@@ -141,4 +157,4 @@ public class PropertyDB {
public static String applyFormat(final String key, final Object... objects) {
return String.format(getFormat(key), objects);
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java
new file mode 100644
index 0000000..115f782
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java
@@ -0,0 +1,325 @@
+package bjc.utils.cli.objects;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import bjc.utils.ioutils.Prompter;
+import bjc.utils.ioutils.blocks.*;
+
+public class BlockReaderCLI {
+ /*
+ * All of the block readers.
+ */
+ private Map<String, BlockReader> readers;
+
+ /*
+ * All of the I/O sources.
+ */
+ private Map<String, Reader> sources;
+
+ /**
+ * Create a new CLI for configuring BlockReaders.
+ *
+ * @param srcs
+ * The container of initial I/O sources.
+ */
+ public BlockReaderCLI(Map<String, Reader> srcs) {
+ readers = new HashMap();
+
+ sources = srcs;
+ }
+
+ public static void main(String[] args) {
+ /*
+ * Create/configure I/O sources.
+ */
+ Map<String, Reader> sources = new HashMap<>();
+ sources.put("stdio", new InputStreamReader(System.in));
+
+ BlockReaderCLI reader = new BlockReaderCLI(sources);
+
+ reader.run(new Scanner(System.in), "console");
+ }
+
+ /**
+ * Run the CLI on an input source.
+ *
+ * @param input
+ * The place to read input from.
+ * @param ioSource
+ * The name of the place to read input from.
+ */
+ public void run(Scanner input, String ioSource) {
+ int lno = 0;
+ while(input.hasNextLine()) {
+ System.out.printf("reader-conf(%d)>", lno);
+ String ln = input.nextLine();
+
+ lno += 1;
+
+ Command com = Command.fromString(ln, lno, ioSource);
+ if(com == null) continue;
+
+ handleCommand(com);
+ }
+
+ input.close();
+ }
+
+ /*
+ * Handle a command.
+ */
+ public void handleCommand(Command com) {
+ switch(com.nameCommand) {
+ case "def-filtered":
+ defFiltered(com);
+ break;
+ case "def-layered":
+ defLayered(com);
+ break;
+ case "def-pushback":
+ defPushback(com);
+ break;
+ case "def-simple":
+ defSimple(com);
+ break;
+ case "def-serial":
+ defSerial(com);
+ break;
+ case "exit":
+ case "quit":
+ System.out.printf("Exiting reader-conf, %d readers configured in %d commands\n",
+ readers.size(), com.lineNo);
+ break;
+ default:
+ System.err.print(com.error("Unknown command '%s'\n", com.nameCommand));
+ break;
+ }
+ }
+
+ private void defFiltered(Command com) {
+ String remn = com.remnCommand;
+
+ /*
+ * Get the block name.
+ */
+ int idx = remn.indexOf(' ');
+ if(idx == -1) {
+ System.err.print(com.error("No name argument for def-filtered.\n"));
+ }
+ String blockName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if(readers.containsKey(blockName)) {
+ System.err.print(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get the reader name.
+ */
+ idx = remn.indexOf(' ');
+ if(idx == -1) {
+ System.err.print("No reader-name argument for def-filtered.\n");
+ }
+ String readerName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there is a reader bound to that name.
+ */
+ if(!readers.containsKey(readerName)) {
+ System.err.print(com.error("No source named %s\n", readerName));
+ return;
+ }
+
+ /*
+ * Get the pattern.
+ */
+ if(remn.equals("")) {
+ System.err.print("No filter argument for def-filtered\n");
+ }
+
+ String filter = remn;
+
+ try {
+ Pattern pat = Pattern.compile(filter);
+
+ Predicate<Block> pred = (block) -> {
+ Matcher mat = pat.matcher(block.contents);
+
+ return mat.matches();
+ };
+
+ BlockReader reader = new FilteredBlockReader(readers.get(readerName), pred);
+
+ readers.put(blockName, reader);
+ } catch (PatternSyntaxException psex) {
+ System.err.print(com.error("Invalid regular expression '%s' for filter. (%s)\n", filter, psex.getMessage()));
+ }
+ }
+
+ private void defPushback(Command com) {
+ String[] parts = com.remnCommand.split(" ");
+
+ if(parts.length != 2) {
+ System.err.print(com.error("Incorrect number of arguments to def-pushback. Requires a block name and a reader name\n"));
+ return;
+ }
+
+ String blockName = parts[0];
+ if(readers.containsKey(blockName)) {
+ System.err.print(com.warn("Shadowing existing reader %s\n", blockName));
+ }
+
+ String readerName = parts[1];
+ if(!readers.containsKey(readerName)) {
+ System.err.print(com.error("No reader named %s\n", readerName));
+ return;
+ }
+
+ BlockReader reader = new PushbackBlockReader(readers.get(readerName));
+ readers.put(blockName, reader);
+ }
+
+ private void defLayered(Command com) {
+ String[] parts = com.remnCommand.split(" ");
+
+ if(parts.length != 3) {
+ System.err.print(com.error("Incorrect number of arguments to def-layered. Requires a block name and two reader names\n"));
+ return;
+ }
+
+ /*
+ * Get the block name.
+ */
+ String blockName = parts[0];
+ if(readers.containsKey(blockName)) {
+ System.err.print(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Make sure the component readers exist.
+ */
+ if(!readers.containsKey(parts[1])) {
+ System.err.print(com.error("No reader named %s\n", parts[1]));
+ return;
+ }
+
+ if(!readers.containsKey(parts[2])) {
+ System.err.print(com.error("No reader named %s\n", parts[2]));
+ return;
+ }
+
+ BlockReader reader = new LayeredBlockReader(readers.get(parts[1]), readers.get(parts[2]));
+ readers.put(blockName, reader);
+ }
+
+ private void defSerial(Command com) {
+ String[] parts = com.remnCommand.split(" ");
+
+ if(parts.length < 2) {
+ System.err.print(com.error("Not enough arguments to def-serial. Requires at least a block name and at least one reader name\n"));
+ return;
+ }
+
+ /*
+ * Get the name for this BlockReader.
+ */
+ String blockName = parts[0];
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if(readers.containsKey(blockName)) {
+ System.err.print(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get all of the component readers.
+ */
+ BlockReader[] readerArr = new BlockReader[parts.length - 1];
+ for(int i = 1; i < parts.length; i++) {
+ String readerName = parts[i];
+
+ /*
+ * Check there is a source bound to that name.
+ */
+ if(!readers.containsKey(readerName)) {
+ System.err.print(com.error("No reader named %s\n", readerName));
+ return;
+ }
+
+ readerArr[i] = readers.get(readerName);
+ }
+
+ BlockReader reader = new SerialBlockReader(readerArr);
+
+ readers.put(blockName, reader);
+ }
+
+ private void defSimple(Command com) {
+ String remn = com.remnCommand;
+
+ /*
+ * Get the block name.
+ */
+ int idx = remn.indexOf(' ');
+ if(idx == -1) {
+ System.err.print(com.error("No name argument for def-simple.\n"));
+ }
+ String blockName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if(readers.containsKey(blockName)) {
+ System.err.print(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get the source name.
+ */
+ idx = remn.indexOf(' ');
+ if(idx == -1) {
+ System.err.print("No source-name argument for def-simple.\n");
+ }
+ String sourceName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there is a source bound to that name.
+ */
+ if(!sources.containsKey(sourceName)) {
+ System.err.print(com.error("No source named %s\n", sourceName));
+ return;
+ }
+
+ /*
+ * Get the pattern.
+ */
+ if(remn.equals("")) {
+ System.err.print("No delimiter argument for def-simple\n");
+ }
+
+ String delim = remn;
+
+ try {
+ BlockReader reader = new SimpleBlockReader(delim, sources.get(sourceName));
+
+ readers.put(blockName, reader);
+ } catch (PatternSyntaxException psex) {
+ System.err.print(com.error("Invalid regular expression '%s' for delimiter. (%s)\n", delim, psex.getMessage()));
+ }
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java
new file mode 100644
index 0000000..a0d4493
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java
@@ -0,0 +1,65 @@
+package bjc.utils.cli.objects;
+
+public class Command {
+ public final int lineNo;
+
+ public final String fullCommand;
+ public final String remnCommand;
+ public final String nameCommand;
+
+ public final String ioSource;
+
+ /**
+ * Create a new command.
+ *
+ * @param ln
+ * The line to get the command from.
+ * @param lno
+ * The number of the line the command came from.
+ * @param ioSrc
+ * The name of where the I/O came from.
+ */
+ public Command(String ln, int lno, String ioSrc) {
+ int idx = ln.indexOf(' ');
+
+ if(idx == -1) idx = ln.length();
+
+ fullCommand = ln;
+ nameCommand = ln.substring(0, idx).trim();
+ remnCommand = ln.substring(idx).trim();
+
+ lineNo = lno;
+
+ ioSource = ioSrc;
+ }
+
+ public static Command fromString(String ln, int lno, String ioSource) {
+ /*
+ * Ignore blank lines and comments.
+ */
+ if(ln.equals("")) return null;
+ if(ln.startsWith("#")) return null;
+
+ /*
+ * Trim off comments part-way through the line.
+ */
+ int idxHash = ln.indexOf('#');
+ if(idxHash != -1) {
+ ln = ln.substring(0, idxHash).trim();
+ }
+
+ return new Command(ln, lno, ioSource);
+ }
+
+ public String warn(String warning, Object... parms) {
+ String msg = String.format(warning, parms);
+
+ return String.format("WARNING (%s:%d): %s", ioSource, lineNo, msg);
+ }
+
+ public String error(String err, Object... parms) {
+ String msg = String.format(err, parms);
+
+ return String.format("ERROR (%s:%d): %s", ioSource, lineNo, msg);
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java
index 75c3c1b..0d54471 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java
@@ -130,8 +130,8 @@ public class AbbrevMap {
/**
* Removes words from the abbreviation map.
*
- * NOTE: There may be inconsistent behavior. Use
- * {@link AbbrevMap#recalculate()} to fix it if it occurs.
+ * NOTE: There may be inconsistent behavior after removing a word from
+ * the map. Use {@link AbbrevMap#recalculate()} to fix it if it occurs.
*
* @param words
* The words to remove.
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/package-info.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/package-info.java
deleted file mode 100644
index 713eb9f..0000000
--- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/package-info.java
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * Random functional type things that don't belong elsewhere
- *
- * @author ben
- *
- */
-package bjc.utils.funcdata.theory; \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java
index 4310416..db6c43b 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java
@@ -7,6 +7,9 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.BiPredicate;
+/*
+ * Functional implementation of a file visitor.
+ */
final class FunctionalFileVisitor extends SimpleFileVisitor<Path> {
private final BiPredicate<Path, BasicFileAttributes> predicate;
private final BiPredicate<Path, BasicFileAttributes> action;
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java
index 52a2437..c0daa1e 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java
@@ -95,13 +95,19 @@ public class ListUtils {
E element = null;
for (final int index = 0; itr.hasNext(); element = itr.next()) {
- // n - m
+ /*
+ * n - m
+ */
final int winningChance = number - selected.getSize();
- // N - t
+ /*
+ * N - t
+ */
final int totalChance = total - (index - 1);
- // Probability of selecting the t+1'th element
+ /*
+ * Probability of selecting the t+1'th element
+ */
if (NumberUtils.isProbable(winningChance, totalChance, rng)) {
selected.add(element);
}
@@ -185,17 +191,20 @@ public class ListUtils {
&& !rejected.isEmpty(); numberOfIterations++) {
input.forEach(it);
- if (rejected.isEmpty()) // Nothing was rejected, so
- // we're
- // done
+ if (rejected.isEmpty()) {
+ /*
+ * Nothing was rejected, so we're done
+ */
return returned;
+ }
}
- throw new IllegalArgumentException("Heuristic (more than " + MAX_NTRIESPART
- + " iterations of partitioning) detected unpartitionable list " + input.toString()
- + "\nThe following elements were not partitioned: " + rejected.toString()
- + "\nCurrent group in formation: " + it.currentPartition.toString()
- + "\nPreviously formed groups: " + returned.toString());
+
+ final String fmt = "Heuristic (more than %d iterations of partitioning) detected an unpartitionable list. (%s)\nThe following elements were not partitioned: %s\nCurrent group in formation: %s\nPreviously formed groups: %s\n";
+
+ final String msg = String.format(fmt, MAX_NTRIESPART, input.toString(), rejected.toString(), it.currentPartition.toString(), returned.toString());
+
+ throw new IllegalArgumentException(msg);
}
/**
@@ -250,7 +259,9 @@ public class ListUtils {
}
if (count % size != 0) {
- // We need to pad
+ /*
+ * We need to pad
+ */
int needed = count % size;
int threshold = 0;
@@ -269,9 +280,13 @@ public class ListUtils {
}
}
- if (threshold > MAX_NTRIESPART)
- throw new IllegalArgumentException("Heuristic (more than " + MAX_NTRIESPART
- + " iterations of attempting to pad) detected unpaddable list ");
+ if (threshold > MAX_NTRIESPART) {
+ final String fmt = "Heuristic (more than %d iterations of attempting to pad) detected an unpaddable list. (%s)\nPartially padded list: %S";
+
+ final String msg = String.format(fmt, MAX_NTRIESPART, list.toString(), returned.toString());
+
+ throw new IllegalArgumentException(msg);
+ }
}
return returned;
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java
new file mode 100644
index 0000000..dcd5738
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java
@@ -0,0 +1,56 @@
+package bjc.utils.funcutils;
+
+import java.util.LinkedList;
+import java.util.function.Predicate;
+
+import bjc.utils.data.ITree;
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.IList;
+
+/**
+ * Implements various utilities for trees.
+ *
+ * @author Benjamin Culkin
+ */
+public class TreeUtils {
+ /*
+ * Convert a tree into a list of outline nodes that match a certain
+ * path.
+ */
+ public static <T> IList<IList<T>> outlineTree(ITree<T> tre, Predicate<T> leafMarker) {
+ IList<IList<T>> paths = new FunctionalList<>();
+
+ LinkedList<T> path = new LinkedList<>();
+ path.add(tre.getHead());
+
+ tre.doForChildren((child) -> findPath(child, path, leafMarker, paths));
+
+ return paths;
+ }
+
+ private static <T> void findPath(ITree<T> subtree, LinkedList<T> path, Predicate<T> leafMarker, IList<IList<T>> paths) {
+ if(subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) {
+ /*
+ * We're at a matching leaf node. Add it.
+ */
+ IList<T> finalPath = new FunctionalList<>();
+
+ for(T ePath : path) {
+ finalPath.add(ePath);
+ }
+
+ finalPath.add(subtree.getHead());
+
+ paths.add(finalPath);
+ } else {
+ /*
+ * Check the children of this node.
+ */
+ path.add(subtree.getHead());
+
+ subtree.doForChildren((child) -> findPath(child, path, leafMarker, paths));
+
+ path.removeLast();
+ }
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java
index 4216544..7c5205b 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java
@@ -26,21 +26,27 @@ import bjc.utils.funcdata.IMap;
*
*/
public class RuleBasedConfigReader<E> {
- // Function to execute when starting a rule
- // Takes the tokenizer, and a pair of the read token and application
- // state
+ /* Function to execute when starting a rule.
+ * Takes the tokenizer, and a pair of the read token and application state
+ */
private BiConsumer<FunctionalStringTokenizer, IPair<String, E>> start;
- // Function to use when continuing a rule
- // Takes a tokenizer and application state
+ /*
+ * Function to use when continuing a rule
+ * Takes a tokenizer and application state
+ */
private BiConsumer<FunctionalStringTokenizer, E> continueRule;
- // Function to use when ending a rule
- // Takes an application state
+ /*
+ * Function to use when ending a rule
+ * Takes an application state
+ */
private Consumer<E> end;
- // Map of pragma names to pragma actions
- // Pragma actions are functions taking a tokenizer and application state
+ /*
+ * Map of pragma names to pragma actions
+ * Pragma actions are functions taking a tokenizer and application state
+ */
private final IMap<String, BiConsumer<FunctionalStringTokenizer, E>> pragmas;
/**
@@ -71,8 +77,7 @@ public class RuleBasedConfigReader<E> {
* The function to execute when this pragma is read
*/
public void addPragma(final String name, final BiConsumer<FunctionalStringTokenizer, E> action) {
- if (name == null)
- throw new NullPointerException("Pragma name must not be null");
+ if (name == null) throw new NullPointerException("Pragma name must not be null");
else if (action == null) throw new NullPointerException("Pragma action must not be null");
pragmas.put(name, action);
@@ -85,23 +90,35 @@ public class RuleBasedConfigReader<E> {
else if (continueRule == null)
throw new InputMismatchException("Rule continuation not supported for current grammar");
- // Accept the rule
+ /*
+ * Accept the rule
+ */
continueRule.accept(new FunctionalStringTokenizer(line.substring(1), " "), state);
}
private boolean endRule(final E state, final boolean isRuleOpen) {
- // Ignore blank line without an open rule
+ /*
+ * Ignore blank line without an open rule
+ */
if (isRuleOpen == false)
- // Do nothing
+ /*
+ * Do nothing
+ */
return false;
else {
- // Nothing happens on rule end
+ /*
+ * Nothing happens on rule end
+ */
if (end != null) {
- // Process the rule ending
+ /*
+ * Process the rule ending
+ */
end.accept(state);
}
- // Return a closed rule
+ /*
+ * Return a closed rule
+ */
return false;
}
}
@@ -118,37 +135,56 @@ public class RuleBasedConfigReader<E> {
public E fromStream(final InputStream input, final E initialState) {
if (input == null) throw new NullPointerException("Input stream must not be null");
- // Application state: We're giving this back later
+ /*
+ * Application state: We're giving this back later
+ */
final E state = initialState;
- // Prepare our input source
+ /*
+ * Prepare our input source
+ */
try (Scanner source = new Scanner(input)) {
source.useDelimiter("\n");
- // This is true when a rule's open
+ /*
+ * This is true when a rule's open
+ */
final IHolder<Boolean> isRuleOpen = new Identity<>(false);
- // Do something for every line of the file
+ /*
+ * Do something for every line of the file
+ */
source.forEachRemaining((line) -> {
- // Skip comment lines
+ /*
+ * Skip comment lines
+ */
if (line.startsWith("#") || line.startsWith("//"))
- // It's a comment
+ /*
+ * It's a comment
+ */
return;
else if (line.equals("")) {
- // End the rule
+ /*
+ * End the rule
+ */
isRuleOpen.replace(endRule(state, isRuleOpen.getValue()));
} else if (line.startsWith("\t")) {
- // Continue the rule
+ /*
+ * Continue the rule
+ */
continueRule(state, isRuleOpen.getValue(), line);
} else {
- // Open a rule
+ /*
+ * Open a rule
+ */
isRuleOpen.replace(startRule(state, isRuleOpen.getValue(), line));
}
});
}
- // Return the state that the user has created
+ /*
+ * Return the state that the user has created
+ */
return state;
-
}
/**
@@ -184,27 +220,41 @@ public class RuleBasedConfigReader<E> {
}
private boolean startRule(final E state, boolean isRuleOpen, final String line) {
- // Create the line tokenizer
+ /*
+ * Create the line tokenizer
+ */
final FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer(line, " ");
- // Get the initial token
+ /*
+ * Get the initial token
+ */
final String nextToken = tokenizer.nextToken();
- // Handle pragmas
+ /*
+ * Handle pragmas
+ */
if (nextToken.equals("pragma")) {
- // Get the pragma name
+ /*
+ * Get the pragma name
+ */
final String token = tokenizer.nextToken();
- // Handle pragmas
+ /*
+ * Handle pragmas
+ */
pragmas.getOrDefault(token, (tokenzer, stat) -> {
throw new UnknownPragmaException("Unknown pragma " + token);
}).accept(tokenizer, state);
} else {
- // Make sure input is correct
+ /*
+ * Make sure input is correct
+ */
if (isRuleOpen == true)
throw new InputMismatchException("Nested rules are currently not supported");
- // Start a rule
+ /*
+ * Start a rule
+ */
start.accept(tokenizer, new Pair<>(nextToken, state));
isRuleOpen = true;
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java
index ccb73a4..e26a7ee 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java
@@ -28,21 +28,32 @@ public class RuleBasedReaderPragmas {
public static <StateType> BiConsumer<FunctionalStringTokenizer, StateType> buildInteger(final String name,
final BiConsumer<Integer, StateType> consumer) {
return (tokenizer, state) -> {
- // Check our input is correct
- if (!tokenizer.hasMoreTokens())
- throw new PragmaFormatException("Pragma " + name + " requires one integer argument");
+ /*
+ * Check our input is correct
+ */
+ if (!tokenizer.hasMoreTokens()) {
+ String fmt = "Pragma %s requires one integer argument";
- // Read the argument
+ throw new PragmaFormatException(String.format(fmt, name));
+ }
+
+ /*
+ * Read the argument
+ */
final String token = tokenizer.nextToken();
try {
- // Run the pragma
+ /*
+ * Run the pragma
+ */
consumer.accept(Integer.parseInt(token), state);
} catch (final NumberFormatException nfex) {
- // Tell the user their argument isn't correct
- final PragmaFormatException pfex = new PragmaFormatException(
- "Argument " + token + " to " + name + " pragma isn't a valid integer. "
- + "This pragma requires a integer argument");
+ /*
+ * Tell the user their argument isn't correct
+ */
+ String fmt = "Argument %s to %s pragma isn't a valid integer, and this pragma requires an integer argument.";
+
+ final PragmaFormatException pfex = new PragmaFormatException(String.format(fmt, token, name));
pfex.initCause(nfex);
@@ -66,14 +77,23 @@ public class RuleBasedReaderPragmas {
public static <StateType> BiConsumer<FunctionalStringTokenizer, StateType> buildStringCollapser(
final String name, final BiConsumer<String, StateType> consumer) {
return (tokenizer, state) -> {
- // Check our input
- if (!tokenizer.hasMoreTokens()) throw new PragmaFormatException(
- "Pragma " + name + " requires one or more string arguments");
+ /*
+ * Check our input
+ */
+ if (!tokenizer.hasMoreTokens()) {
+ String fmt = "Pragma %s requires one or more string arguments.";
+
+ throw new PragmaFormatException(String.format(fmt, name));
+ }
- // Build our argument
+ /*
+ * Build our argument
+ */
final String collapsed = ListUtils.collapseTokens(tokenizer.toList());
- // Run the pragma
+ /*
+ * Run the pragma
+ */
consumer.accept(collapsed, state);
};
}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java
index b514d17..15f3510 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java
@@ -81,7 +81,8 @@ public class Block {
@Override
public String toString() {
- return String.format("Block #%d (from lines %d to %d), length: %d characters", blockNo, startLine,
- endLine, contents.length());
+ String fmt = "Block #%d (from lines %d to %d), length: %d characters";
+
+ return String.format(fmt, blockNo, startLine, endLine, contents.length());
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java
index dac535e..3c695c6 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java
@@ -34,6 +34,20 @@ public interface BlockReader extends AutoCloseable, Iterator<Block> {
boolean nextBlock();
/**
+ * Retrieve the number of blocks that have been read so far.
+ *
+ * @return The number of blocks read so far.
+ */
+ int getBlockCount();
+
+ @Override
+ void close() throws IOException;
+
+ /*
+ * Methods with default impls.
+ */
+
+ /**
* Execute an action for each remaining block.
*
* @param action
@@ -45,16 +59,6 @@ public interface BlockReader extends AutoCloseable, Iterator<Block> {
}
}
- /**
- * Retrieve the number of blocks that have been read so far.
- *
- * @return The number of blocks read so far.
- */
- int getBlockCount();
-
- @Override
- void close() throws IOException;
-
@Override
default boolean hasNext() {
return hasNextBlock();
@@ -66,4 +70,4 @@ public interface BlockReader extends AutoCloseable, Iterator<Block> {
return getBlock();
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java
new file mode 100644
index 0000000..a9e5923
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java
@@ -0,0 +1,97 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.IOException;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class FilteredBlockReader implements BlockReader {
+ /*
+ * The source of blocks.
+ */
+ private BlockReader source;
+
+ /*
+ * The current and next block.
+ *
+ * Both have already been checked for the predicate.
+ */
+ private Block current;
+ private Block pending;
+
+ /*
+ * Number of blocks that passed the predicate.
+ */
+ private int passed;
+
+ /*
+ * The predicate blocks must pass.
+ */
+ private Predicate<Block> pred;
+
+ /*
+ * The action to call on failure, if there is one.
+ */
+ private Consumer<Block> failAction;
+
+ public FilteredBlockReader(BlockReader src, Predicate predic) {
+ this(src, predic, null);
+ }
+
+ public FilteredBlockReader(BlockReader src, Predicate predic, Consumer<Block> failAct) {
+ source = src;
+ pred = predic;
+ failAction = failAct;
+
+ passed = 0;
+ }
+
+ @Override
+ public boolean hasNextBlock() {
+ if(pending != null) return true;
+
+ while(source.hasNextBlock()) {
+ /*
+ * Only say we have a next block if the next block would
+ * pass the predicate.
+ */
+ pending = source.next();
+
+ if(pred.test(pending)) {
+ passed += 1;
+ return true;
+ } else {
+ failAction.accept(pending);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public Block getBlock() {
+ return current;
+ }
+
+ @Override
+ public boolean nextBlock() {
+ if(pending != null || hasNextBlock()) {
+ current = pending;
+ pending = null;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getBlockCount() {
+ return passed;
+ }
+
+ @Override
+ public void close() throws IOException {
+ source.close();
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java
index 54010fe..967a1f2 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java
@@ -14,9 +14,15 @@ import java.io.IOException;
*
*/
public class LayeredBlockReader implements BlockReader {
- private final BlockReader first;
- private final BlockReader second;
+ /*
+ * The readers to drain from.
+ */
+ private final BlockReader first;
+ private final BlockReader second;
+ /*
+ * The current block number.
+ */
private int blockNo;
/**
@@ -42,13 +48,16 @@ public class LayeredBlockReader implements BlockReader {
public Block getBlock() {
final Block firstBlock = first.getBlock();
+ /*
+ * Only drain a block from the second reader if none are
+ * available in the first reader.
+ */
return firstBlock == null ? second.getBlock() : firstBlock;
}
@Override
public boolean nextBlock() {
final boolean gotFirst = first.nextBlock();
-
final boolean succ = gotFirst ? gotFirst : second.nextBlock();
if (succ) {
@@ -69,4 +78,4 @@ public class LayeredBlockReader implements BlockReader {
first.close();
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java
new file mode 100644
index 0000000..1996421
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java
@@ -0,0 +1,44 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.IOException;
+
+import java.util.function.UnaryOperator;
+
+public class MappedBlockReader implements BlockReader {
+ private BlockReader reader;
+
+ private Block current;
+
+ private UnaryOperator<Block> transform;
+
+ public MappedBlockReader(BlockReader source, UnaryOperator<Block> trans) {
+ reader = source;
+ transform = trans;
+ }
+
+ @Override
+ public boolean hasNextBlock() {
+ return reader.hasNextBlock();
+ }
+
+ @Override
+ public Block getBlock() {
+ return current;
+ }
+
+ @Override
+ public boolean nextBlock() {
+ if(hasNextBlock()) {
+ current = trans.apply(reader.next());
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getBlockCount() {
+ return reader.getBlockCount();
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java
index d7ba247..0cc9dea 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java
@@ -14,6 +14,9 @@ import java.util.LinkedList;
public class PushbackBlockReader implements BlockReader {
private final BlockReader source;
+ /*
+ * The queue of pushed-back blocks.
+ */
private final Deque<Block> waiting;
private Block curBlock;
@@ -44,6 +47,9 @@ public class PushbackBlockReader implements BlockReader {
@Override
public boolean nextBlock() {
+ /*
+ * Drain pushed-back blocks first.
+ */
if (!waiting.isEmpty()) {
curBlock = waiting.pop();
@@ -97,4 +103,4 @@ public class PushbackBlockReader implements BlockReader {
return String.format("PushbackBlockReader [waiting=%s, curBlock=%s, blockNo=%s]", waiting, curBlock,
blockNo);
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java
index 7735981..c229da1 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java
@@ -31,10 +31,15 @@ public class SerialBlockReader implements BlockReader {
public boolean hasNextBlock() {
if (readerQueue.isEmpty()) return false;
+ /*
+ * Attempt to get a block from the first reader.
+ */
boolean hasBlock = readerQueue.peek().hasNextBlock();
-
boolean cont = hasBlock || readerQueue.isEmpty();
+ /*
+ * Close/dispose of readers until we get an open one.
+ */
while (!cont) {
try {
readerQueue.pop().close();
@@ -43,7 +48,6 @@ public class SerialBlockReader implements BlockReader {
}
hasBlock = readerQueue.peek().hasNextBlock();
-
cont = hasBlock || readerQueue.isEmpty();
}
@@ -62,7 +66,6 @@ public class SerialBlockReader implements BlockReader {
if (readerQueue.isEmpty()) return false;
boolean gotBlock = readerQueue.peek().nextBlock();
-
boolean cont = gotBlock || readerQueue.isEmpty();
while (!cont) {
@@ -73,7 +76,6 @@ public class SerialBlockReader implements BlockReader {
}
gotBlock = readerQueue.peek().nextBlock();
-
cont = gotBlock || readerQueue.isEmpty();
}
@@ -97,4 +99,4 @@ public class SerialBlockReader implements BlockReader {
reader.close();
}
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java
index ca04d8f..734bde8 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java
@@ -21,14 +21,18 @@ public class SimpleBlockReader implements BlockReader {
/*
* I/O source for blocks.
*/
- private final Scanner blockReader;
+ private final Scanner blockReader;
/*
* The current block.
*/
- private Block currBlock;
- private int blockNo;
- private int lineNo;
+ private Block currBlock;
+
+ /*
+ * Info about the current block.
+ */
+ private int blockNo;
+ private int lineNo;
/**
* Create a new block reader.
@@ -64,6 +68,9 @@ public class SimpleBlockReader implements BlockReader {
@Override
public boolean nextBlock() {
try {
+ /*
+ * Read in a new block, and keep the line numbers sane.
+ */
final int blockStartLine = lineNo;
final String blockContents = blockReader.next();
final int blockEndLine = lineNo + StringUtils.countMatches(blockContents, "\\R");
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java
index 0e50ad6..74076bb 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java
@@ -11,6 +11,9 @@ import java.io.IOException;
public class TriggeredBlockReader implements BlockReader {
private final BlockReader source;
+ /*
+ * The action to fire.
+ */
private final Runnable action;
/**
@@ -59,4 +62,4 @@ public class TriggeredBlockReader implements BlockReader {
public String toString() {
return String.format("TriggeredBlockReader [source=%s]", source);
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
index 888ea7a..a885808 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java
@@ -36,9 +36,11 @@ class DoubleMatcher {
* Floating point components.
*/
private static final String rFPLeader = getRegex("fpLeader");
- private static final String rFPNum = applyFormat("fpNumber", rSimpleIntDec, rSimpleDec,
- rHexString);
+ private static final String rFPNum = applyFormat("fpNumber", rSimpleIntDec, rSimpleDec, rHexString);
+ /*
+ * Full double.
+ */
private static final String rDouble = applyFormat("fpDouble", rFPLeader, rFPNum);
public static final Pattern doubleLiteral = Pattern.compile("\\A" + rDouble + "\\Z");
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
index 44744f5..a1b5feb 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java
@@ -57,6 +57,9 @@ public class ShuntingYard<TokenType> {
}
}
+ /*
+ * Function that shunts tokens.
+ */
private final class TokenShunter implements Consumer<String> {
private final IList<TokenType> output;
private final Deque<String> stack;
@@ -71,34 +74,47 @@ public class ShuntingYard<TokenType> {
@Override
public void accept(final String token) {
- // Handle operators
+ /*
+ * Handle operators
+ */
if (operators.containsKey(token)) {
- // Pop operators while there isn't a higher
- // precedence one
+ /*
+ * Pop operators while there isn't a higher precedence one
+ */
while (!stack.isEmpty() && isHigherPrec(token, stack.peek())) {
output.add(transformer.apply(stack.pop()));
}
- // Put this operator onto the stack
+ /*
+ * Put this operator onto the stack
+ */
stack.push(token);
} else if (StringUtils.containsOnly(token, "\\(")) {
- // Handle groups of parenthesis for multiple
- // nesting levels
+ /*
+ * Handle groups of parenthesis for multiple nesting levels
+ */
stack.push(token);
} else if (StringUtils.containsOnly(token, "\\)")) {
- // Handle groups of parenthesis for multiple
- // nesting levels
+ /*
+ * Handle groups of parenthesis for multiple nesting levels
+ */
final String swappedToken = token.replace(')', '(');
- // Remove tokens up to a matching parenthesis
+ /*
+ * Remove tokens up to a matching parenthesis
+ */
while (!stack.peek().equals(swappedToken)) {
output.add(transformer.apply(stack.pop()));
}
- // Remove the parenthesis
+ /*
+ * Remove the parenthesis
+ */
stack.pop();
} else {
- // Just add the transformed token
+ /*
+ * Just add the transformed token
+ */
output.add(transformer.apply(token));
}
}
@@ -119,7 +135,9 @@ public class ShuntingYard<TokenType> {
public ShuntingYard(final boolean configureBasics) {
operators = new FunctionalMap<>();
- // Add basic operators if we're configured to do so
+ /*
+ * Add basic operators if we're configured to do so
+ */
if (configureBasics) {
operators.put("+", Operator.ADD);
operators.put("-", Operator.SUBTRACT);
@@ -170,17 +188,25 @@ public class ShuntingYard<TokenType> {
}
private boolean isHigherPrec(final String left, final String right) {
- // Check if the right operator exists
+ /*
+ * Check if the right operator exists
+ */
final boolean exists = operators.containsKey(right);
- // If it doesn't, the left is higher precedence.
+ /*
+ * If it doesn't, the left is higher precedence.
+ */
if (!exists) return false;
- // Get the precedence of operators
+ /*
+ * Get the precedence of operators
+ */
final int rightPrecedence = operators.get(right).getPrecedence();
final int leftPrecedence = operators.get(left).getPrecedence();
- // Evaluate what we were asked
+ /*
+ * Evaluate what we were asked
+ */
return rightPrecedence >= leftPrecedence;
}
@@ -196,21 +222,31 @@ public class ShuntingYard<TokenType> {
* @return A list of tokens in postfix notation.
*/
public IList<TokenType> postfix(final IList<String> input, final Function<String, TokenType> transformer) {
- // Check our input
+ /*
+ * Check our input
+ */
if (input == null)
throw new NullPointerException("Input must not be null");
else if (transformer == null) throw new NullPointerException("Transformer must not be null");
- // Here's what we're handing back
+ /*
+ * Here's what we're handing back
+ */
final IList<TokenType> output = new FunctionalList<>();
- // The stack to put operators on
+ /*
+ * The stack to put operators on
+ */
final Deque<String> stack = new LinkedList<>();
- // Shunt the tokens
+ /*
+ * Shunt the tokens
+ */
input.forEach(new TokenShunter(output, stack, transformer));
- // Transform any resulting tokens
+ /*
+ * Transform any resulting tokens
+ */
stack.forEach(token -> {
output.add(transformer.apply(token));
});
@@ -226,11 +262,13 @@ public class ShuntingYard<TokenType> {
* all operators.
*/
public void removeOp(final String operator) {
- // Check if we want to remove all operators
+ /*
+ * Check if we want to remove all operators
+ */
if (operator == null) {
operators = new FunctionalMap<>();
} else {
operators.remove(operator);
}
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
index 89dc35f..30ccc5a 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
@@ -13,8 +13,13 @@ import bjc.utils.data.Tree;
import bjc.utils.parserutils.TreeConstructor.ConstructorState;
import bjc.utils.parserutils.TreeConstructor.QueueFlattener;
+/*
+ * Handle creating ASTs from tokens.
+ */
final class TokenTransformer<TokenType> implements Consumer<TokenType> {
- // Handle operators
+ /*
+ * Handle operators
+ */
private final class OperatorHandler implements UnaryOperator<ConstructorState<TokenType>> {
private final TokenType element;
@@ -24,23 +29,29 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
@Override
public ConstructorState<TokenType> apply(final ConstructorState<TokenType> pair) {
- // Replace the current AST with the result of handling
- // an operator
+ /*
+ * Replace the current AST with the result of handling an operator
+ */
return new ConstructorState<>(pair.bindLeft(queuedASTs -> {
return handleOperator(queuedASTs);
}));
}
private ConstructorState<TokenType> handleOperator(final Deque<ITree<TokenType>> queuedASTs) {
- // The AST we're going to hand back
+ /*
+ * The AST we're going to hand back
+ */
ITree<TokenType> newAST;
- // Handle special operators
+ /*
+ * Handle special operators
+ */
if (isSpecialOperator.test(element)) {
newAST = handleSpecialOperator.apply(element).apply(queuedASTs);
} else {
- // Error if we don't have enough for a binary
- // operator
+ /*
+ * Error if we don't have enough for a binary operator
+ */
if (queuedASTs.size() < 2) {
final String msg = String.format(
"Attempted to parse binary operator without enough operands\n\tProblem operator is: %s\n\tPossible operand is: %s",
@@ -49,18 +60,26 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
throw new IllegalStateException(msg);
}
- // Grab the two operands
+ /*
+ * Grab the two operands
+ */
final ITree<TokenType> right = queuedASTs.pop();
final ITree<TokenType> left = queuedASTs.pop();
- // Create a new AST
+ /*
+ * Create a new AST
+ */
newAST = new Tree<>(element, left, right);
}
- // Stick it onto the stack
+ /*
+ * Stick it onto the stack
+ */
queuedASTs.push(newAST);
- // Hand back the state
+ /*
+ * Hand back the state
+ */
return new ConstructorState<>(queuedASTs, newAST);
}
}
@@ -72,7 +91,9 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
private final Predicate<TokenType> isSpecialOperator;
private final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator;
- // Create a new transformer
+ /*
+ * Create a new transformer
+ */
public TokenTransformer(final IHolder<ConstructorState<TokenType>> initialState,
final Predicate<TokenType> operatorPredicate, final Predicate<TokenType> isSpecialOperator,
final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
@@ -84,17 +105,21 @@ final class TokenTransformer<TokenType> implements Consumer<TokenType> {
@Override
public void accept(final TokenType element) {
- // Handle operators
+ /*
+ * Handle operators
+ */
if (operatorPredicate.test(element)) {
initialState.transform(new OperatorHandler(element));
} else {
final ITree<TokenType> newAST = new Tree<>(element);
- // Insert the new tree into the AST
+ /*
+ * Insert the new tree into the AST
+ */
initialState.transform(pair -> {
- // Transform the pair, ignoring the current AST
- // in favor of the
- // one consisting of the current element
+ /*
+ * Transform the pair, ignoring the current AST in favor of the one consisting of the current element
+ */
return new ConstructorState<>(pair.bindLeft(queue -> {
queue.push(newAST);
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
index a8f3db2..33dd1a2 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java
@@ -155,13 +155,19 @@ public class TokenUtils {
public static String descapeString(final String inp) {
if (inp == null) throw new NullPointerException("inp must not be null");
+ /*
+ * Prepare the buffer and escape finder.
+ */
final StringBuffer work = new StringBuffer();
-
final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp);
final Matcher escapeFinder = escapePatt.matcher(inp);
while (possibleEscapeFinder.find()) {
if (!escapeFinder.find()) {
+ /*
+ * Found a possible escape that isn't actually an
+ * escape.
+ */
final String msg = String.format("Illegal escape sequence '%s' at position %d",
possibleEscapeFinder.group(), possibleEscapeFinder.start());
@@ -170,6 +176,9 @@ public class TokenUtils {
final String escapeSeq = escapeFinder.group();
+ /*
+ * Convert the escape to a string.
+ */
String escapeRep = "";
switch (escapeSeq) {
case "\\b":
@@ -216,6 +225,9 @@ public class TokenUtils {
return work.toString();
}
+ /*
+ * Handle a unicode codepoint.
+ */
private static String handleUnicodeEscape(final String seq) {
try {
final int codepoint = Integer.parseInt(seq, 16);
@@ -232,6 +244,9 @@ public class TokenUtils {
}
}
+ /*
+ * Handle a octal codepoint.
+ */
private static String handleOctalEscape(final String seq) {
try {
final int codepoint = Integer.parseInt(seq, 8);
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
index d7ed5b0..90141ef 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
@@ -58,7 +58,9 @@ public class TreeConstructor {
*/
public static <TokenType> ITree<TokenType> constructTree(final IList<TokenType> tokens,
final Predicate<TokenType> isOperator) {
- // Construct a tree with no special operators
+ /*
+ * Construct a tree with no special operators
+ */
return constructTree(tokens, isOperator, op -> false, null);
}
@@ -91,7 +93,9 @@ public class TreeConstructor {
public static <TokenType> ITree<TokenType> constructTree(final IList<TokenType> tokens,
final Predicate<TokenType> isOperator, final Predicate<TokenType> isSpecialOperator,
final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
- // Make sure our parameters are valid
+ /*
+ * Make sure our parameters are valid
+ */
if (tokens == null)
throw new NullPointerException("Tokens must not be null");
else if (isOperator == null)
@@ -99,17 +103,23 @@ public class TreeConstructor {
else if (isSpecialOperator == null)
throw new NullPointerException("Special operator determiner must not be null");
- // Here is the state for the tree construction
+ /*
+ * Here is the state for the tree construction
+ */
final IHolder<ConstructorState<TokenType>> initialState = new Identity<>(
new ConstructorState<>(new LinkedList<>(), null));
- // Transform each of the tokens
+ /*
+ * Transform each of the tokens
+ */
tokens.forEach(new TokenTransformer<>(initialState, isOperator, isSpecialOperator,
handleSpecialOperator));
- // Grab the tree from the state
+ /*
+ * Grab the tree from the state
+ */
return initialState.unwrap(pair -> {
return pair.getRight();
});
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java
index 85d4038..b1d8597 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java
@@ -34,12 +34,20 @@ public class DelimiterGroup<T> {
*
*/
public class OpenGroup {
+ /*
+ * The contents of this group.
+ */
private final Deque<ITree<T>> contents;
+ /*
+ * The contents of the current subgroup.
+ */
private IList<ITree<T>> currentGroup;
+ /*
+ * The token that opened the group, and any opening parameters.
+ */
private final T opener;
-
private final T[] params;
/**
@@ -80,14 +88,23 @@ public class DelimiterGroup<T> {
* The characteristics for building the tree.
*/
public void markSubgroup(final T marker, final SequenceCharacteristics<T> chars) {
+ /*
+ * Add all of the contents to the subgroup.
+ */
final ITree<T> subgroupContents = new Tree<>(chars.contents);
for (final ITree<T> itm : currentGroup) {
subgroupContents.addChild(itm);
}
+ /*
+ * Handle subordinate sub-groups.
+ */
while (!contents.isEmpty()) {
final ITree<T> possibleSubordinate = contents.peek();
+ /*
+ * Subordinate lower priority subgroups.
+ */
if (possibleSubordinate.getHead().equals(chars.subgroup)) {
final T otherMarker = possibleSubordinate.getChild(1).getHead();
@@ -103,7 +120,6 @@ public class DelimiterGroup<T> {
final Tree<T> subgroup = new Tree<>(chars.subgroup, subgroupContents, new Tree<>(marker));
- //System.out.println("\tTRACE: generated subgroup\n" + subgroup + "\n\n");
contents.push(subgroup);
currentGroup = new FunctionalList<>();
@@ -121,12 +137,19 @@ public class DelimiterGroup<T> {
* @return This group as a tree.
*/
public ITree<T> toTree(final T closer, final SequenceCharacteristics<T> chars) {
+ /*
+ * Mark any implied subgroups.
+ */
if (impliedSubgroups.containsKey(closer)) {
markSubgroup(impliedSubgroups.get(closer), chars);
}
final ITree<T> res = new Tree<>(chars.contents);
+ /*
+ * Add either the contents of the current group,
+ * or subgroups if they're their.
+ */
if (contents.isEmpty()) {
currentGroup.forEach(res::addChild);
} else {
@@ -440,8 +463,7 @@ public class DelimiterGroup<T> {
* The group opened by the marker.
*/
public void addOpener(final T opener, final T group) {
- if (opener == null)
- throw new NullPointerException("Opener must not be null");
+ if (opener == null) throw new NullPointerException("Opener must not be null");
else if (group == null) throw new NullPointerException("Group to open must not be null");
openDelimiters.put(opener, group);
@@ -457,8 +479,7 @@ public class DelimiterGroup<T> {
* The group opened by the marker.
*/
public void addNestedOpener(final T opener, final T group) {
- if (opener == null)
- throw new NullPointerException("Opener must not be null");
+ if (opener == null) throw new NullPointerException("Opener must not be null");
else if (group == null) throw new NullPointerException("Group to open must not be null");
nestedOpenDelimiters.put(opener, group);
@@ -474,14 +495,10 @@ public class DelimiterGroup<T> {
* The subgroup to imply.
*/
public void implySubgroup(final T closer, final T subgroup) {
- if (closer == null)
- throw new NullPointerException("Closer must not be null");
- else if (subgroup == null)
- throw new NullPointerException("Subgroup must not be null");
- else if (!closingDelimiters.contains(closer))
- throw new IllegalArgumentException(String.format("No closing delimiter '%s' defined", closer));
- else if (!subgroups.containsKey(subgroup))
- throw new IllegalArgumentException(String.format("No subgroup '%s' defined", subgroup));
+ if (closer == null) throw new NullPointerException("Closer must not be null");
+ else if (subgroup == null) throw new NullPointerException("Subgroup must not be null");
+ else if (!closingDelimiters.contains(closer)) throw new IllegalArgumentException(String.format("No closing delimiter '%s' defined", closer));
+ else if (!subgroups.containsKey(subgroup)) throw new IllegalArgumentException(String.format("No subgroup '%s' defined", subgroup));
impliedSubgroups.put(closer, subgroup);
}
@@ -573,4 +590,4 @@ public class DelimiterGroup<T> {
public void setForgetful(final boolean forgetful) {
this.forgetful = forgetful;
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java
index 98c1dc1..ee93b73 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java
@@ -8,7 +8,7 @@ import bjc.utils.data.IPair;
import bjc.utils.data.Pair;
/**
- * A predicated opener for use with {@link RegexOpener}
+ * A predicated opener for use with {@link RegexCloser}
*
* @author bjculkin
*
@@ -51,4 +51,4 @@ public class RegexOpener implements Function<String, IPair<String, String[]>> {
return new Pair<>(null, null);
}
-} \ No newline at end of file
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java
index 48d85c1..ccfaffb 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java
@@ -32,6 +32,9 @@ public class SequenceDelimiter<T> {
*/
private final Map<T, DelimiterGroup<T>> groups;
+ /*
+ * The initial group to start with.
+ */
private DelimiterGroup<T> initialGroup;
/**
@@ -49,14 +52,14 @@ public class SequenceDelimiter<T> {
* following grammar while obeying the defined grouping rules.
*
* <pre>
- * <tree> -> (<data> | <subgroup> | <group>)*
- * <subgroup> -> <tree> <marker>
- * <group> -> <open> <tree> <close>
+ * <tree> → (<data> | <subgroup> | <group>)*
+ * <subgroup> → <tree> <marker>
+ * <group> → <open> <tree> <close>
*
- * <data> -> STRING
- * <open> -> STRING
- * <close> -> STRING
- * <marker> -> STRING
+ * <data> → STRING
+ * <open> → STRING
+ * <close> → STRING
+ * <marker> → STRING
* </pre>
*
* @param chars
@@ -92,9 +95,8 @@ public class SequenceDelimiter<T> {
*/
public ITree<T> delimitSequence(final SequenceCharacteristics<T> chars,
@SuppressWarnings("unchecked") final T... seq) throws DelimiterException {
- if (initialGroup == null)
- throw new NullPointerException("Initial group must be specified.");
- else if (chars == null) throw new NullPointerException("Sequence characteristics must not be null");
+ if (initialGroup == null) throw new NullPointerException("Initial group must be specified.");
+ else if (chars == null) throw new NullPointerException("Sequence characteristics must not be null");
/*
* The stack of opened and not yet closed groups.
@@ -123,9 +125,15 @@ public class SequenceDelimiter<T> {
*/
final IMap<T, T> whoForbid = new PushdownMap<>();
+ /*
+ * Process each member of the sequence.
+ */
for (int i = 0; i < seq.length; i++) {
final T tok = seq[i];
+ /*
+ * Check if this token could open a group.
+ */
final IPair<T, T[]> possibleOpenPar = groupStack.top().doesOpen(tok);
T possibleOpen = possibleOpenPar.getLeft();
@@ -156,8 +164,6 @@ public class SequenceDelimiter<T> {
* exclusions from all enclosing groups.
*/
if (isForbidden(groupStack, forbiddenDelimiters, possibleOpen)) {
- final StringBuilder msgBuilder = new StringBuilder();
-
T forbiddenBy;
if (whoForbid.containsKey(tok)) {
@@ -168,15 +174,9 @@ public class SequenceDelimiter<T> {
final String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then");
- msgBuilder.append("Group '");
- msgBuilder.append(group);
- msgBuilder.append("' can't be opened in this context.");
- msgBuilder.append(" (forbidden by '");
- msgBuilder.append(forbiddenBy);
- msgBuilder.append("')\nContext stack: ");
- msgBuilder.append(ctxList);
+ final String fmt = "Group '%s' can't be opened in this context. (forbidden by '%s')\nContext Stack: %s";
- throw new DelimiterException(msgBuilder.toString());
+ throw new DelimiterException(String.format(fmt, group, forbiddenBy, ctxList));
}
/*
@@ -244,8 +244,14 @@ public class SequenceDelimiter<T> {
forbiddenDelimiters.drop();
}
} else if (!groupStack.empty() && groupStack.top().marksSubgroup(tok)) {
+ /*
+ * Mark a subgroup.
+ */
groupStack.top().markSubgroup(tok, chars);
} else {
+ /*
+ * Add an item to the group.
+ */
groupStack.top().addItem(new Tree<>(tok));
}
}
@@ -270,16 +276,24 @@ public class SequenceDelimiter<T> {
msgBuilder.append(" to close it\nOpen groups: ");
msgBuilder.append(ctxList);
- throw new DelimiterException(msgBuilder.toString());
+ final String fmt = "Unclosed group '%s'. Expected one of %s to close it.\nOpen groups: %n";
+
+ throw new DelimiterException(String.format(fmt, group.getName(), closingDelims, ctxList));
}
return groupStack.pop().toTree(chars.root, chars);
}
+ /*
+ * Check if a group is forbidden to open in a context.
+ */
private boolean isForbidden(final Stack<DelimiterGroup<T>.OpenGroup> groupStack,
final Stack<Multiset<T>> forbiddenDelimiters, final T groupName) {
boolean localForbid;
+ /*
+ * Check if a delimiter is locally forbidden.
+ */
if (groupStack.empty()) {
localForbid = false;
} else {
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
index d483f7a..c357886 100644
--- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java
@@ -43,4 +43,4 @@ public class SimpleTokenSplitter implements TokenSplitter {
public String toString() {
return String.format("SimpleTokenSplitter [spliter=%s, keepDelim=%s]", spliter, keepDelim);
}
-} \ No newline at end of file
+}