From a13f1f396d28c0b900a10bede57f30be3a35003a Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sun, 3 Jun 2018 19:27:43 -0300 Subject: Enable multi-prefixing/suffixing You can now provide multiple prefix/suffix elements, and all of the permutations of them will be applied. For example, providing the elements A & B, will yield the following permutations * A * B * A B * B A --- src/main/java/bjc/rgens/parser/RGrammar.java | 2 +- .../java/bjc/rgens/parser/RGrammarBuilder.java | 107 +++++++++++++++++---- src/main/java/bjc/rgens/parser/RGrammarParser.java | 24 +++-- src/main/java/bjc/rgens/parser/RGrammarSet.java | 21 ++-- .../bjc/rgens/parser/elements/CaseElement.java | 2 +- 5 files changed, 118 insertions(+), 38 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/bjc/rgens/parser/RGrammar.java b/src/main/java/bjc/rgens/parser/RGrammar.java index 4e21279..0c572a7 100755 --- a/src/main/java/bjc/rgens/parser/RGrammar.java +++ b/src/main/java/bjc/rgens/parser/RGrammar.java @@ -208,7 +208,7 @@ public class RGrammar { */ body = body.replaceAll("\\s(ish|burg|ton|ville|opolis|field|boro|dale)", "$1"); - return body; + return body.trim(); } /** diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index fc679f1..3aa220a 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -3,11 +3,16 @@ package bjc.rgens.parser; import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.ListUtils; +import bjc.utils.funcutils.SetUtils; import static bjc.rgens.parser.RuleCase.CaseType.*; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.LinkedList; import java.util.Map; import java.util.Set; @@ -136,7 +141,7 @@ public class RGrammarBuilder { * If the rule name is either invalid or not defined by this * grammar, or if the suffix is invalid. */ - public void suffixWith(String ruleName, String suffix) { + public void suffixWith(String ruleName, String... suffixes) { if (ruleName == null) { throw new NullPointerException("Rule name must not be null"); } else if (ruleName.equals("")) { @@ -147,19 +152,27 @@ public class RGrammarBuilder { throw new IllegalArgumentException(msg); } - CaseElement element = CaseElement.createElement(suffix); + Set elements = new HashSet<>(suffixes.length); + for(String suffix : suffixes) { + elements.add(CaseElement.createElement(suffix)); + } + + List> suffixLists = powerList(elements); FunctionalList newCases = new FunctionalList<>(); IList caseList = rules.get(ruleName).getCases(); for (RuleCase ruleCase : caseList) { + for(List suffixList : suffixLists) { FunctionalList newCase = new FunctionalList<>(); for(CaseElement elm : ruleCase.getElements()) { newCase.add(elm); } - newCase.add(element); + for(CaseElement element : suffixList) { + newCase.add(element); + } /* * @NOTE :AffixCasing @@ -168,6 +181,7 @@ public class RGrammarBuilder { * existing case type? */ newCases.add(new NormalRuleCase(newCase)); + } } @@ -189,7 +203,7 @@ public class RGrammarBuilder { * If the rule name is either invalid or not defined by this * grammar, or if the prefix is invalid. */ - public void prefixWith(String ruleName, String prefix) { + public void prefixWith(String ruleName, String... prefixes) { if (ruleName == null) { throw new NullPointerException("Rule name must not be null"); } else if (ruleName.equals("")) { @@ -200,27 +214,36 @@ public class RGrammarBuilder { throw new IllegalArgumentException(msg); } - CaseElement element = CaseElement.createElement(prefix); + Set elements = new HashSet<>(prefixes.length); + for(String prefix : prefixes) { + elements.add(CaseElement.createElement(prefix)); + } + + List> prefixLists = powerList(elements); FunctionalList newCases = new FunctionalList<>(); IList caseList = rules.get(ruleName).getCases(); for (RuleCase ruleCase : caseList) { - FunctionalList newCase = new FunctionalList<>(); - - newCase.add(element); - - for(CaseElement elm : ruleCase.getElements()) { - newCase.add(elm); + for(List prefixList : prefixLists) { + FunctionalList newCase = new FunctionalList<>(); + + for(CaseElement elm: prefixList) { + newCase.add(elm); + } + + for(CaseElement elm : ruleCase.getElements()) { + newCase.add(elm); + } + + /* + * @NOTE :AffixCasing + * + * Is this correct, or should we be mirroring the + * existing case type? + */ + newCases.add(new NormalRuleCase(newCase)); } - - /* - * @NOTE :AffixCasing - * - * Is this correct, or should we be mirroring the - * existing case type? - */ - newCases.add(new NormalRuleCase(newCase)); } @@ -269,6 +292,52 @@ public class RGrammarBuilder { } rules.get(rule).replaceCases(newCaseList); + } + + private static List> powerList(Set elements) { + /* + * Fast-case the most common usage + */ + if(elements.size() == 1) { + List> ret = new LinkedList<>(); + + List curr = new ArrayList<>(elements.size()); + for(T elem : elements) { + curr.add(elem); + } + + ret.add(curr); + + return ret; + } + + Set> powerSet = SetUtils.powerSet(elements); + + List> list = new LinkedList<>(); + + for(Set set : powerSet) { + /* + * Skip empty sets + */ + if(set.size() == 0) continue; + + List stor = new ArrayList<>(set.size()); + + for(T elm : set) { + stor.add(elm); + } + + for(List permute : ListUtils.permuteList(stor)) { + System.err.printf("\tTRACE: generated permute "); + for(T elm : permute) { + System.err.printf("%s ", elm); + } + System.err.println(); + + list.add(permute); + } + } + return list; } } diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index 5a09ca8..438688d 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -3,6 +3,8 @@ package bjc.rgens.parser; import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.ListUtils; +import bjc.utils.funcutils.SetUtils; import bjc.utils.funcutils.TriConsumer; import bjc.utils.ioutils.blocks.Block; import bjc.utils.ioutils.blocks.BlockReader; @@ -10,8 +12,13 @@ import bjc.utils.ioutils.blocks.SimpleBlockReader; import java.io.Reader; import java.io.StringReader; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.LinkedList; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * Reads {@link RGrammar} from a input stream. @@ -89,25 +96,25 @@ public class RGrammarParser { pragmas.put("suffix-with", (body, build, level) -> { String[] parts = body.trim().split(" "); - if (parts.length != 2) { - String msg = "Suffix-with pragma takes two arguments, the name of the rule to suffix, then what to suffix it with"; + if (parts.length < 2) { + String msg = "Suffix-with pragma takes at least two arguments, the name of the rule to suffix, then what to suffix it with\n\tThis can be more than one token, to get them suffixed as a group"; throw new GrammarException(msg); } - build.suffixWith(parts[0], parts[1]); + build.suffixWith(parts[0], Arrays.copyOfRange(parts, 1, parts.length)); }); pragmas.put("prefix-with", (body, build, level) -> { String[] parts = body.trim().split(" "); - if (parts.length != 2) { - String msg = "Prefix-with pragma takes two arguments, the name of the rule to prefix, then what to prefix it with"; + if (parts.length < 2) { + String msg = "Prefix-with pragma takes at least two arguments, the name of the rule to prefix, then what to prefix it with\n\tThis can be more than one token, to get them prefixed as a group"; throw new GrammarException(msg); } - build.prefixWith(parts[0], parts[1]); + build.prefixWith(parts[0], Arrays.copyOfRange(parts, 1, parts.length)); }); } @@ -175,7 +182,7 @@ public class RGrammarParser { handleRuleBlock(block, build, level); } else if (blockType.equalsIgnoreCase("where")) { handleWhereBlock(block, build, level); - } else if (blockType.equalsIgnoreCase("#")) { + } else if (blockType.startsWith("#")) { if(DEBUG) System.err.printf("Handled comment block (%s)\n", block); /* @@ -353,7 +360,7 @@ public class RGrammarParser { /* Handle a where block (a block with local rules). */ private static void handleWhereBlock(String block, RGrammarBuilder build, int level) throws GrammarException { - int nlIndex = block.indexOf("\\n"); + int nlIndex = block.indexOf("\\nin"); if (nlIndex == -1) { throw new GrammarException("Where block must be a context followed by a body"); @@ -375,6 +382,7 @@ public class RGrammarParser { @SuppressWarnings("unused") Block whereBody = whereReader.next(); + System.err.printf("\tUNIMPLEMENTED WHERE:\n%s\n", whereBody.contents); /** * @TODO 10/11/17 Ben Culkin :WhereBlocks * Implement where blocks. diff --git a/src/main/java/bjc/rgens/parser/RGrammarSet.java b/src/main/java/bjc/rgens/parser/RGrammarSet.java index 975510a..c797b8c 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarSet.java +++ b/src/main/java/bjc/rgens/parser/RGrammarSet.java @@ -7,6 +7,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import java.util.Scanner; import java.util.Set; @@ -34,7 +35,7 @@ public class RGrammarSet { exportedRules = new HashMap<>(); - exportFrom = new HashMap<>(); + exportFrom = new TreeMap<>(); loadedFrom = new HashMap<>(); } @@ -256,14 +257,16 @@ public class RGrammarSet { */ Path convPath = cfgParent.resolve(path.toString()); - //if(Files.isDirectory(convPath)) { - // /* @TODO implement subset grammars */ - // throw new GrammarException("Sub-grammar sets aren't implemented yet"); - //} else if (convPath.getFileName().endsWith(".gram")) { + if(Files.isDirectory(convPath)) { + /* @TODO implement subset grammars */ + throw new GrammarException("Sub-grammar sets aren't implemented yet"); + } else if (convPath.getFileName().toString().endsWith(".gram")) { /* Load grammar file. */ try { + BufferedReader fis = Files.newBufferedReader(convPath); RGrammar gram = RGrammarParser.readGrammar(fis); + fis.close(); /* Add grammar to the set. */ @@ -278,10 +281,10 @@ public class RGrammarSet { String msg = String.format("Error loading file '%s'", path); throw new GrammarException(msg, gex); } - //} else { - // String msg = String.format("Unrecognized file type '%s'", convPath.getFileName()); - // throw new GrammarException(msg); - //} + } else { + String msg = String.format("Unrecognized file type '%s'", convPath.getFileName()); + throw new GrammarException(msg); + } } } diff --git a/src/main/java/bjc/rgens/parser/elements/CaseElement.java b/src/main/java/bjc/rgens/parser/elements/CaseElement.java index 54c673f..785ac1a 100755 --- a/src/main/java/bjc/rgens/parser/elements/CaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/CaseElement.java @@ -85,7 +85,7 @@ public abstract class CaseElement { */ String specialBody = csepart.substring(1, csepart.length() - 1); - System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody); + //System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody); if (specialBody.matches("\\S+:=\\S+")) { /* Handle expanding variable definitions. */ -- cgit v1.2.3