From 5c416488ef63b5004ca424de56894eb17712f116 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sun, 3 Jun 2018 23:59:30 -0300 Subject: Add recurrance limit This controls the number of times a rule can be entered into during a single generations, and is set to 5 by default --- src/main/java/bjc/rgens/parser/RGrammar.java | 11 +++++-- .../java/bjc/rgens/parser/RGrammarBuilder.java | 11 +++++++ src/main/java/bjc/rgens/parser/RGrammarParser.java | 14 +++++++++ src/main/java/bjc/rgens/parser/RGrammarTest.java | 7 +++-- .../java/bjc/rgens/parser/RecurLimitException.java | 35 ++++++++++++++++++++++ src/main/java/bjc/rgens/parser/Rule.java | 28 +++++++++++++---- src/main/java/bjc/rgens/parser/RuleCase.java | 7 +++++ .../parser/elements/ExpVariableCaseElement.java | 15 ++++++++-- .../bjc/rgens/parser/elements/RuleCaseElement.java | 28 ++++++++++------- 9 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 src/main/java/bjc/rgens/parser/RecurLimitException.java (limited to 'src/main/java/bjc/rgens') diff --git a/src/main/java/bjc/rgens/parser/RGrammar.java b/src/main/java/bjc/rgens/parser/RGrammar.java index 0c572a7..e650a27 100755 --- a/src/main/java/bjc/rgens/parser/RGrammar.java +++ b/src/main/java/bjc/rgens/parser/RGrammar.java @@ -155,10 +155,17 @@ public class RGrammar { } } - RuleCase start = rules.get(fromRule).getCase(state.rnd); + Rule rl = rules.get(fromRule); - generateCase(start, state); + if(rl.doRecur()) { + RuleCase start = rules.get(fromRule).getCase(state.rnd); + generateCase(start, state); + + rl.endRecur(); + } else { + throw new RecurLimitException("Rule recurrence limit exceeded"); + } /* * @NOTE * diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index 047567d..bc93fa6 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -275,6 +275,17 @@ public class RGrammarBuilder { rules.get(ruleName).replaceCases(newCaseList); } + public void setRuleRecur(String ruleName, int recurLimit) { + if (ruleName == null) { + throw new NullPointerException("ruleName must not be null"); + } else if (ruleName.equals("")) { + throw new IllegalArgumentException("The empty string is not a valid rule name"); + } else if (!rules.containsKey(ruleName)) { + throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName)); + } + + rules.get(ruleName).recurLimit = recurLimit; + } /* * @TODO * diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index 9232013..827cbb6 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -81,6 +81,20 @@ public class RGrammarParser { } }); + pragmas.put("recur-limit", (body, build, level) -> { + String[] parts = body.split(" "); + + if(parts.length != 2) { + throw new GrammarException("Recur-limit pragma takes two arguments: the name of the rule to set the limit for, and the new value of the limit"); + } + + if(!parts[1].matches("\\A\\d+\\Z")) { + throw new GrammarException("Limit value must be an integer"); + } + + build.setRuleRecur(parts[0], Integer.parseInt(parts[1])); + }); + pragmas.put("regex-rule", (body, build, level) -> { int nameIndex = body.indexOf(" "); diff --git a/src/main/java/bjc/rgens/parser/RGrammarTest.java b/src/main/java/bjc/rgens/parser/RGrammarTest.java index 02a32f7..c86336c 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarTest.java +++ b/src/main/java/bjc/rgens/parser/RGrammarTest.java @@ -69,10 +69,13 @@ public class RGrammarTest { System.out.printf(fmt, exportName, loadSrc); System.out.println(); + System.out.println(); + + System.err.printf(fmt, exportName, loadSrc); gex.printStackTrace(); - System.out.println(); - System.out.println(); + System.err.println(); + System.err.println(); } } long endGenTime = System.nanoTime(); diff --git a/src/main/java/bjc/rgens/parser/RecurLimitException.java b/src/main/java/bjc/rgens/parser/RecurLimitException.java new file mode 100644 index 0000000..faeffb3 --- /dev/null +++ b/src/main/java/bjc/rgens/parser/RecurLimitException.java @@ -0,0 +1,35 @@ +package bjc.rgens.parser; + +/** + * The exception thrown when a rule exceeds its recurrence limit + * + * @author student + */ +public class RecurLimitException extends GrammarException { + /* Serialization ID. */ + private static final long serialVersionUID = -7287427479316953668L; + + /** + * Create a new grammar exception with the specified message. + * + * @param msg + * The message for this exception. + */ + public RecurLimitException(String msg) { + super(msg); + } + + /** + * Create a new grammar exception with the specified message and + * cause. + * + * @param msg + * The message for this exception. + * + * @param cause + * The cause of this exception. + */ + public RecurLimitException(String msg, Exception cause) { + super(msg, cause); + } +} diff --git a/src/main/java/bjc/rgens/parser/Rule.java b/src/main/java/bjc/rgens/parser/Rule.java index 0022732..4e43fd7 100755 --- a/src/main/java/bjc/rgens/parser/Rule.java +++ b/src/main/java/bjc/rgens/parser/Rule.java @@ -19,6 +19,9 @@ public class Rule { /* The cases for this rule. */ private WeightedRandom cases; + public int recurLimit = 5; + private int currentRecur; + /** * Create a new grammar rule. * @@ -47,11 +50,7 @@ public class Rule { * The case to add. */ public void addCase(RuleCase cse) { - if (cse == null) { - throw new NullPointerException("Case must not be null"); - } - - cases.addProbability(1, cse); + addCase(cse, 1); } /** @@ -65,6 +64,8 @@ public class Rule { throw new NullPointerException("Case must not be null"); } + cse.belongsTo = name; + cases.addProbability(weight, cse); } @@ -111,7 +112,10 @@ public class Rule { this.cases = new WeightedRandom<>(); for(IPair cse : cases) { - this.cases.addProbability(cse.getLeft(), cse.getRight()); + RuleCase cs = cse.getRight(); + cs.belongsTo = name; + + this.cases.addProbability(cse.getLeft(), cs); } } @@ -151,4 +155,16 @@ public class Rule { public String toString() { return String.format("Rule [ruleName='%s', ruleCases=%s]", name, cases); } + + public boolean doRecur() { + if(currentRecur > recurLimit) return false; + + currentRecur += 1; + + return true; + } + + public void endRecur() { + if(currentRecur > 0) currentRecur -= 1; + } } diff --git a/src/main/java/bjc/rgens/parser/RuleCase.java b/src/main/java/bjc/rgens/parser/RuleCase.java index bb82ff2..21a7ed6 100755 --- a/src/main/java/bjc/rgens/parser/RuleCase.java +++ b/src/main/java/bjc/rgens/parser/RuleCase.java @@ -31,6 +31,8 @@ public abstract class RuleCase { /** The type of this case. */ public final CaseType type; + public String belongsTo; + /** * The list of element values for this case. * @@ -87,4 +89,9 @@ public abstract class RuleCase { public IList getElements() { return elementList; } + + public String toString() { + return String.format("Case %d of %s", hashCode(), belongsTo); + } + } diff --git a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java index 8598fbd..9c5ad1b 100755 --- a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java @@ -2,7 +2,9 @@ package bjc.rgens.parser.elements; import bjc.rgens.parser.GenerationState; import bjc.rgens.parser.GrammarException; +import bjc.rgens.parser.RecurLimitException; import bjc.rgens.parser.RGrammar; +import bjc.rgens.parser.Rule; import bjc.rgens.parser.RuleCase; public class ExpVariableCaseElement extends VariableCaseElement { @@ -15,9 +17,17 @@ public class ExpVariableCaseElement extends VariableCaseElement { GenerationState newState = state.newBuf(); if (state.rules.containsKey(varDef)) { - RuleCase destCase = state.rules.get(varDef).getCase(); + Rule rl = state.rules.get(varDef); - state.gram.generateCase(destCase, newState); + if(rl.doRecur()) { + RuleCase destCase = state.rules.get(varDef).getCase(); + + state.gram.generateCase(destCase, newState); + + rl.endRecur(); + } else { + throw new RecurLimitException("Rule recurrence limit exceeded"); + } } else if (state.importRules.containsKey(varDef)) { RGrammar destGrammar = state.importRules.get(varDef); @@ -40,6 +50,5 @@ public class ExpVariableCaseElement extends VariableCaseElement { } state.vars.put(varName, newState.contents.toString()); - } } diff --git a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java index b4a4b58..0869a2f 100755 --- a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java @@ -33,9 +33,17 @@ public abstract class RuleCaseElement extends StringCaseElement { /* :Postprocessing */ newState.contents = new StringBuilder(dst.generate(actName, state.rnd, state.vars)); } else if (state.rules.containsKey(actName)) { - RuleCase cse = state.rules.get(actName).getCase(state.rnd); + Rule rl = state.rules.get(actName); - state.gram.generateCase(cse, newState); + if(rl.doRecur()) { + RuleCase cse = rl.getCase(state.rnd); + + state.gram.generateCase(cse, newState); + + rl.endRecur(); + } else { + throw new RecurLimitException("Rule recurrence limit exceeded"); + } } else if (state.importRules.containsKey(actName)) { RGrammar dst = state.importRules.get(actName); @@ -50,17 +58,17 @@ public abstract class RuleCaseElement extends StringCaseElement { * Re-get this working again. */ /* - if (ruleSearcher != null) { - Set> results = ruleSearcher.search(actName, MAX_DISTANCE); + if (ruleSearcher != null) { + Set> results = ruleSearcher.search(actName, MAX_DISTANCE); - String[] resArray = results.stream().map(Match::getMatch).toArray((i) -> new String[i]); + String[] resArray = results.stream().map(Match::getMatch).toArray((i) -> new String[i]); - String msg = String.format("No rule '%s' defined (perhaps you meant %s?)", actName, - StringUtils.toEnglishList(resArray, false)); + String msg = String.format("No rule '%s' defined (perhaps you meant %s?)", actName, + StringUtils.toEnglishList(resArray, false)); - throw new GrammarException(msg); - } - */ + throw new GrammarException(msg); + } + */ String msg = String.format("No rule '%s' defined", actName); throw new GrammarException(msg); -- cgit v1.2.3