From d766896972c9e9be4a9e0021ec5f4f0665901865 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sat, 9 Sep 2017 21:46:16 -0300 Subject: Update Most of it is documentation changes. The rest is more work on BlockReaders, as well as a simple command language for configuring them. --- BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java | 20 +- .../java/bjc/utils/cli/objects/BlockReaderCLI.java | 325 +++++++++++++++++++++ .../main/java/bjc/utils/cli/objects/Command.java | 65 +++++ .../src/main/java/bjc/utils/esodata/AbbrevMap.java | 4 +- .../bjc/utils/funcdata/theory/package-info.java | 7 - .../bjc/utils/funcutils/FunctionalFileVisitor.java | 3 + .../main/java/bjc/utils/funcutils/ListUtils.java | 45 ++- .../main/java/bjc/utils/funcutils/TreeUtils.java | 56 ++++ .../bjc/utils/ioutils/RuleBasedConfigReader.java | 120 +++++--- .../bjc/utils/ioutils/RuleBasedReaderPragmas.java | 48 ++- .../main/java/bjc/utils/ioutils/blocks/Block.java | 7 +- .../java/bjc/utils/ioutils/blocks/BlockReader.java | 26 +- .../utils/ioutils/blocks/FilteredBlockReader.java | 97 ++++++ .../utils/ioutils/blocks/LayeredBlockReader.java | 17 +- .../utils/ioutils/blocks/MappedBlockReader.java | 44 +++ .../utils/ioutils/blocks/PushbackBlockReader.java | 8 +- .../utils/ioutils/blocks/SerialBlockReader.java | 12 +- .../utils/ioutils/blocks/SimpleBlockReader.java | 15 +- .../utils/ioutils/blocks/TriggeredBlockReader.java | 5 +- .../java/bjc/utils/parserutils/DoubleMatcher.java | 8 +- .../java/bjc/utils/parserutils/ShuntingYard.java | 84 ++++-- .../bjc/utils/parserutils/TokenTransformer.java | 59 ++-- .../java/bjc/utils/parserutils/TokenUtils.java | 17 +- .../bjc/utils/parserutils/TreeConstructor.java | 22 +- .../utils/parserutils/delims/DelimiterGroup.java | 47 ++- .../bjc/utils/parserutils/delims/RegexOpener.java | 4 +- .../parserutils/delims/SequenceDelimiter.java | 56 ++-- .../parserutils/splitter/SimpleTokenSplitter.java | 2 +- 28 files changed, 1030 insertions(+), 193 deletions(-) create mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/package-info.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java (limited to 'BJC-Utils2/src/main/java') 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 readers; + + /* + * All of the I/O sources. + */ + private Map sources; + + /** + * Create a new CLI for configuring BlockReaders. + * + * @param srcs + * The container of initial I/O sources. + */ + public BlockReaderCLI(Map srcs) { + readers = new HashMap(); + + sources = srcs; + } + + public static void main(String[] args) { + /* + * Create/configure I/O sources. + */ + Map 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 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 { private final BiPredicate predicate; private final BiPredicate 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 IList> outlineTree(ITree tre, Predicate leafMarker) { + IList> paths = new FunctionalList<>(); + + LinkedList path = new LinkedList<>(); + path.add(tre.getHead()); + + tre.doForChildren((child) -> findPath(child, path, leafMarker, paths)); + + return paths; + } + + private static void findPath(ITree subtree, LinkedList path, Predicate leafMarker, IList> paths) { + if(subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) { + /* + * We're at a matching leaf node. Add it. + */ + IList 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 { - // 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> 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 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 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> pragmas; /** @@ -71,8 +77,7 @@ public class RuleBasedConfigReader { * The function to execute when this pragma is read */ public void addPragma(final String name, final BiConsumer 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 { 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 { 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 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 { } 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 BiConsumer buildInteger(final String name, final BiConsumer 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 BiConsumer buildStringCollapser( final String name, final BiConsumer 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 @@ -33,6 +33,20 @@ public interface BlockReader extends AutoCloseable, Iterator { */ 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. * @@ -45,16 +59,6 @@ public interface BlockReader extends AutoCloseable, Iterator { } } - /** - * 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 { 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 pred; + + /* + * The action to call on failure, if there is one. + */ + private Consumer failAction; + + public FilteredBlockReader(BlockReader src, Predicate predic) { + this(src, predic, null); + } + + public FilteredBlockReader(BlockReader src, Predicate predic, Consumer 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 transform; + + public MappedBlockReader(BlockReader source, UnaryOperator 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 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 { } } + /* + * Function that shunts tokens. + */ private final class TokenShunter implements Consumer { private final IList output; private final Deque stack; @@ -71,34 +74,47 @@ public class ShuntingYard { @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 { 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 { } 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 { * @return A list of tokens in postfix notation. */ public IList postfix(final IList input, final Function 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 output = new FunctionalList<>(); - // The stack to put operators on + /* + * The stack to put operators on + */ final Deque 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 { * 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 implements Consumer { - // Handle operators + /* + * Handle operators + */ private final class OperatorHandler implements UnaryOperator> { private final TokenType element; @@ -24,23 +29,29 @@ final class TokenTransformer implements Consumer { @Override public ConstructorState apply(final ConstructorState 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 handleOperator(final Deque> queuedASTs) { - // The AST we're going to hand back + /* + * The AST we're going to hand back + */ ITree 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 implements Consumer { throw new IllegalStateException(msg); } - // Grab the two operands + /* + * Grab the two operands + */ final ITree right = queuedASTs.pop(); final ITree 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 implements Consumer { private final Predicate isSpecialOperator; private final Function> handleSpecialOperator; - // Create a new transformer + /* + * Create a new transformer + */ public TokenTransformer(final IHolder> initialState, final Predicate operatorPredicate, final Predicate isSpecialOperator, final Function> handleSpecialOperator) { @@ -84,17 +105,21 @@ final class TokenTransformer implements Consumer { @Override public void accept(final TokenType element) { - // Handle operators + /* + * Handle operators + */ if (operatorPredicate.test(element)) { initialState.transform(new OperatorHandler(element)); } else { final ITree 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 ITree constructTree(final IList tokens, final Predicate 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 ITree constructTree(final IList tokens, final Predicate isOperator, final Predicate isSpecialOperator, final Function> 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> 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 { * */ public class OpenGroup { + /* + * The contents of this group. + */ private final Deque> contents; + /* + * The contents of the current subgroup. + */ private IList> 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 { * The characteristics for building the tree. */ public void markSubgroup(final T marker, final SequenceCharacteristics chars) { + /* + * Add all of the contents to the subgroup. + */ final ITree subgroupContents = new Tree<>(chars.contents); for (final ITree itm : currentGroup) { subgroupContents.addChild(itm); } + /* + * Handle subordinate sub-groups. + */ while (!contents.isEmpty()) { final ITree 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 { final Tree 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 { * @return This group as a tree. */ public ITree toTree(final T closer, final SequenceCharacteristics chars) { + /* + * Mark any implied subgroups. + */ if (impliedSubgroups.containsKey(closer)) { markSubgroup(impliedSubgroups.get(closer), chars); } final ITree 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 { * 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 { * 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 { * 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 { 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> { 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 { */ private final Map> groups; + /* + * The initial group to start with. + */ private DelimiterGroup initialGroup; /** @@ -49,14 +52,14 @@ public class SequenceDelimiter { * following grammar while obeying the defined grouping rules. * *
-	 *              -> ( |  | )*
-	 *          ->  
-	 *             ->   
+	 *              → ( |  | )*
+	 *          
+	 *           
 	 *
-	 *              -> STRING
-	 *              -> STRING
-	 *             -> STRING
-	 *            -> STRING
+	 *              → STRING
+	 *              → STRING
+	 *             → STRING
+	 *            → STRING
 	 * 
* * @param chars @@ -92,9 +95,8 @@ public class SequenceDelimiter { */ public ITree delimitSequence(final SequenceCharacteristics 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 { */ final IMap 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 possibleOpenPar = groupStack.top().doesOpen(tok); T possibleOpen = possibleOpenPar.getLeft(); @@ -156,8 +164,6 @@ public class SequenceDelimiter { * 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 { 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 { 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 { 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.OpenGroup> groupStack, final Stack> 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 +} -- cgit v1.2.3