diff options
| -rwxr-xr-x | src/main/java/bjc/rgens/parser/RGrammarBuilder.java | 37 | ||||
| -rwxr-xr-x | src/main/java/bjc/rgens/parser/RGrammarParser.java | 30 | ||||
| -rwxr-xr-x | src/main/java/bjc/rgens/parser/Rule.java | 81 |
3 files changed, 114 insertions, 34 deletions
diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index 1bc849b..535d818 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -350,33 +350,40 @@ public class RGrammarBuilder { public void addAutoRlVar(String name, CaseElement elm) { autoRlVars.put(name, elm); } - /* - * @TODO - * - * Figure out how this should work, and get this working. - */ - /* - public void regexizeRule(String rule, String pattern) { + + public void rejectRule(String rule, String reject) { if (rule == null) { throw new NullPointerException("rule must not be null"); - } else if(pattern == null) { - throw new NullPointerException("pattern must not be null"); + } else if(reject == null) { + throw new NullPointerException("reject must not be null"); } else if (rule.equals("")) { throw new IllegalArgumentException("The empty string is not a valid rule name"); } else if(!rules.containsKey(rule)) { throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", rule)); } - IList<RuleCase> caseList = rules.get(rule).getCases(); + Rule rl = rules.get(rule); - IList<RuleCase> newCaseList = new FunctionalList<>(); + rl.addRejection(reject); + } - for(RuleCase cse : caseList) { - newCaseList.add(new RegexRuleCase(cse.getElements(), pattern)); + public void findReplaceRule(String rule, String find, String replace) { + if (rule == null) { + throw new NullPointerException("rule must not be null"); + } else if(find == null) { + throw new NullPointerException("find must not be null"); + } else if(replace == null) { + throw new NullPointerException("replace must not be null"); + } else if (rule.equals("")) { + throw new IllegalArgumentException("The empty string is not a valid rule name"); + } else if(!rules.containsKey(rule)) { + throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", rule)); } - rules.get(rule).replaceCases(newCaseList); - }*/ + Rule rl = rules.get(rule); + + rl.addFindReplace(find, replace); + } /* * @NOTE diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index 4014baa..a95cefc 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -169,18 +169,32 @@ public class RGrammarParser { build.setBinomial(parts.get(0), Integer.parseInt(parts.get(1)), Integer.parseInt(parts.get(2)), Integer.parseInt(parts.get(3))); }); - pragmas.put("regex-rule", (body, build, level) -> { - int nameIndex = body.indexOf(" "); + /* + * @NOTE 4/9/18 + * + * Consider if we want to replace this with something more akin + * to the `definer` feature from DiceLang. This will work fine + * in most cases, but there are some cases where you'd want the + * extra power. No examples are apparent at the moment. + */ + pragmas.put("find-replace-rule", (body, build, level) -> { + List<String> bits = StringUtils.levelSplit(body, " "); - if(nameIndex == -1) { - throw new GrammarException("Regex-rule pragma takes two arguments: the name of the rule to process, then the regex to apply after the rule has been generated."); + if(bits.size() != 3) { + throw new GrammarException("Regex-rule pragma takes three arguments: the name of the rule to process, then the find/replace pair to apply after the rule has been generated."); } - String name = body.substring(0, nameIndex).trim(); - String patt = body.substring(nameIndex + 1).trim(); + build.findReplaceRule(bits.get(0), bits.get(1), bits.get(2)); + }); + + pragmas.put("reject-rule", (body, build, level) -> { + List<String> bits = StringUtils.levelSplit(body, " "); + + if(bits.size() != 3) { + throw new GrammarException("Reject-rule pragma takes two arguments: the name of the rule to process, then the rejection pattern to apply after the rule has been generated."); + } - throw new GrammarException("Regexize-rule pragma not yet supported"); - //build.regexizeRule(name, patt); + build.rejectRule(bits.get(0), bits.get(1)); }); pragmas.put("prefix-with", (body, build, level) -> { diff --git a/src/main/java/bjc/rgens/parser/Rule.java b/src/main/java/bjc/rgens/parser/Rule.java index a86f84e..21d197d 100755 --- a/src/main/java/bjc/rgens/parser/Rule.java +++ b/src/main/java/bjc/rgens/parser/Rule.java @@ -5,9 +5,15 @@ import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; import bjc.utils.gen.WeightedRandom; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + import static bjc.rgens.parser.RGrammarLogging.*; +import static bjc.utils.data.IPair.pair; /** * A rule in a randomized grammar. @@ -37,12 +43,17 @@ public class Rule { public ProbType prob; - public int descentFactor; - + // Probability vars + /* Descent vars */ + public int descentFactor; + /* Binomial vars */ public int target; public int bound; public int trials; + private List<String> rejectionPreds; + private List<IPair<String, String>> findReplaces; + // @TODO This default should be configurable in some way public int recurLimit = 5; private int currentRecur; @@ -77,6 +88,9 @@ public class Rule { cases = new WeightedRandom<>(); prob = ProbType.NORMAL; + + rejectionPreds = new ArrayList<>(); + findReplaces = new ArrayList<>(); } /** @@ -106,6 +120,26 @@ public class Rule { cases.addProbability(weight, cse); } + public void addRejection(String reject) { + try { + Pattern.compile(reject); + } catch (PatternSyntaxException psex) { + throw new GrammarException(String.format("String %s is not a valid regex for rejection", reject), psex); + } + + rejectionPreds.add(reject); + } + + public void addFindReplace(String find, String replace) { + try { + Pattern.compile(find); + } catch (PatternSyntaxException psex) { + throw new GrammarException(String.format("String %s is not a valid regex for finding", find), psex); + } + + findReplaces.add(pair(find, replace)); + } + /** * Get a random case from this rule. * @@ -248,18 +282,43 @@ public class Rule { public void generate(GenerationState state) { state.swapGrammar(belongsTo); - if(doRecur()) { - RuleCase cse = getCase(state.rnd); + boolean rejected; - fine("Generating %s (from %s)", cse, belongsTo.name); + do { + rejected = false; - belongsTo.generateCase(cse, state); + if(doRecur()) { + RuleCase cse = getCase(state.rnd); - endRecur(); - } + fine("Generating %s (from %s)", cse, belongsTo.name); - if(name.contains("+")) { - state.contents = new StringBuilder(state.contents.toString().replaceAll("\\s+", "")); - } + belongsTo.generateCase(cse, state); + + endRecur(); + } + + if(name.contains("+")) { + state.contents = new StringBuilder(state.contents.toString().replaceAll("\\s+", "")); + } + + String conts = state.contents.toString(); + + for(IPair<String, String> findRep : findReplaces) { + conts = conts.replaceAll(findRep.getLeft(), findRep.getRight()); + } + + state.contents = new StringBuilder(conts); + + for(String pat : rejectionPreds) { + if(!conts.matches(pat)) { + fine("Rejected %s by %s (from %s)", conts, pat, belongsTo.name); + + rejected = true; + state.contents = new StringBuilder(); + + break; + } + } + } while (rejected); } } |
