diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-09-09 21:46:16 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-09-09 21:47:34 -0300 |
| commit | d766896972c9e9be4a9e0021ec5f4f0665901865 (patch) | |
| tree | 1f6473300ef86e0697d682360bea0d28fc144baa /BJC-Utils2/src | |
| parent | 40f3a28569366c4357fbda11d2fff3b77686d84f (diff) | |
Update
Most of it is documentation changes.
The rest is more work on BlockReaders, as well as a simple command
language for configuring them.
Diffstat (limited to 'BJC-Utils2/src')
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 +} |
