From 2cc00686b50d5a28f0ab04ad52e5b075290698d9 Mon Sep 17 00:00:00 2001 From: bjculkin Date: Tue, 21 Mar 2017 19:42:12 -0400 Subject: Add ranges Ranges can now be used with the syntax [start..end] inline to generate numbers instead of having to create a special rule for them. --- .../main/java/bjc/rgens/newparser/CaseElement.java | 112 ++++++++++++++++++++- .../main/java/bjc/rgens/newparser/RGrammar.java | 46 +++++++-- .../java/bjc/rgens/newparser/RGrammarBuilder.java | 14 +++ .../java/bjc/rgens/newparser/RGrammarTest.java | 2 +- RGens/src/main/java/bjc/rgens/newparser/Rule.java | 14 +++ 5 files changed, 174 insertions(+), 14 deletions(-) (limited to 'RGens/src/main/java/bjc/rgens/newparser') diff --git a/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java b/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java index a15cb9b..2eb2e3a 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java +++ b/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java @@ -14,13 +14,17 @@ public class CaseElement { */ public static enum ElementType { /** - * A element that represents a literal string. + * An element that represents a literal string. */ LITERAL, /** - * A element that represents a rule reference. + * An element that represents a rule reference. */ - RULEREF; + RULEREF, + /** + * An element that represents a random range. + */ + RANGE; } /** @@ -44,6 +48,28 @@ public class CaseElement { */ private String literalVal; + /** + * The starting integer value of this element. + * + *

Used For

+ *
+ *
RANGE
+ *
The inclusive start of the range
+ *
+ */ + private int start; + + /** + * The starting integer value of this element. + * + *

Used For

+ *
+ *
RANGE
+ *
The inclusive end of the range
+ *
+ */ + private int snd; + /** * Create a new case element. * @@ -53,11 +79,13 @@ public class CaseElement { * @throws IllegalArgumentException * If the specified type needs parameters. */ - public CaseElement(CaseElement.ElementType typ) { + public CaseElement(ElementType typ) { switch(typ) { case LITERAL: case RULEREF: throw new IllegalArgumentException("This type requires a string parameter."); + case RANGE: + throw new IllegalArgumentException("This type requires two int parameters."); default: break; } @@ -77,11 +105,13 @@ public class CaseElement { * If the specified type doesn't take a single string * parameter. */ - public CaseElement(CaseElement.ElementType typ, String val) { + public CaseElement(ElementType typ, String val) { switch(typ) { case LITERAL: case RULEREF: break; + case RANGE: + throw new IllegalArgumentException("This type requires two int parameters."); default: throw new IllegalArgumentException("This type doesn't have a string parameter."); } @@ -92,6 +122,35 @@ public class CaseElement { } /** + * Create a new case element that has two integer values. + * + * @param typ + * The type of this element. + * @param first + * The first integer value for this element. + * @param second + * The second integer value for this element. + * + * @throws IllegalArgumentException + * If the specified type doesn't take two integer + * parameters. + */ + public CaseElement(ElementType typ, int first, int second) { + switch(typ) { + case LITERAL: + case RULEREF: + throw new IllegalArgumentException("This type requires a string parameter."); + case RANGE: + break; + default: + throw new IllegalArgumentException("This type doesn't have two int parameters"); + } + + type = typ; + + this.start = first; + this.snd = second; + } /** * Get the literal string value for this element. @@ -113,12 +172,55 @@ public class CaseElement { return literalVal; } + /** + * Get the starting integer value for this element. + * + * @return The starting integer value for this element. + * + * @throws IllegalStateException + * If this type doesn't have a starting integer value. + */ + public int getStart() { + switch(type) { + case RANGE: + break; + default: + throw new IllegalStateException( + String.format("Type '%s' doesn't have a starting integer value", type)); + + } + return start; + } + + /** + * Get the ending integer value for this element. + * + * @return The ending integer value for this element. + * + * @throws IllegalStateException + * If this type doesn't have a ending integer value. + */ + public int getEnd() { + switch(type) { + case RANGE: + break; + default: + throw new IllegalStateException( + String.format("Type '%s' doesn't have a ending integer value", type)); + + } + + return snd; + } + @Override public String toString() { switch(type) { case LITERAL: case RULEREF: return literalVal; + case RANGE: + return String.format("[%d..%d]", start, snd); default: return String.format("Unknown type '%s'", type); } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java index 3e82779..aa5307d 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java @@ -2,6 +2,7 @@ package bjc.rgens.newparser; import java.util.HashSet; import java.util.Map; +import java.util.Random; import java.util.Set; /** @@ -12,9 +13,10 @@ import java.util.Set; */ public class RGrammar { private static class GenerationState { - public StringBuilder contents; + public StringBuilder contents; + public Random rnd; - public GenerationState(StringBuilder contents) { + public GenerationState(StringBuilder contents, Random rnd) { this.contents = contents; } } @@ -61,6 +63,23 @@ public class RGrammar { * @return A possible string from the grammar. */ public String generate(String startRule) { + return generate(startRule, new Random()); + } + + /** + * Generate a string from this grammar, starting from the specified + * rule. + * + * @param startRule + * The rule to start generating at, or null to use the + * initial rule for this grammar. + * + * @param rnd + * The random number generator to use. + * + * @return A possible string from the grammar. + */ + public String generate(String startRule, Random rnd) { String fromRule = startRule; if(startRule == null) { @@ -76,11 +95,11 @@ public class RGrammar { } } - RuleCase start = rules.get(fromRule).getCase(); + RuleCase start = rules.get(fromRule).getCase(rnd); StringBuilder contents = new StringBuilder(); - generateCase(start, new GenerationState(contents)); + generateCase(start, new GenerationState(contents, rnd)); return contents.toString(); } @@ -111,11 +130,22 @@ public class RGrammar { try { switch(elm.type) { case LITERAL: - state.contents.append(elm.getLiteral() + " "); + state.contents.append(elm.getLiteral()); + state.contents.append(" "); break; case RULEREF: generateRuleReference(elm, state); break; + case RANGE: + int start = elm.getStart(); + int end = elm.getEnd(); + + int val = state.rnd.nextInt(end - start); + val += start; + + state.contents.append(val); + state.contents.append(" "); + break; default: throw new GrammarException(String.format("Unknown element type '%s'", elm.type)); } @@ -130,16 +160,16 @@ public class RGrammar { private void generateRuleReference(CaseElement elm, GenerationState state) { String refersTo = elm.getLiteral(); - GenerationState newState = new GenerationState(new StringBuilder()); + GenerationState newState = new GenerationState(new StringBuilder(), state.rnd); if(rules.containsKey(refersTo)) { - RuleCase cse = rules.get(refersTo).getCase(); + RuleCase cse = rules.get(refersTo).getCase(state.rnd); generateCase(cse, newState); } else if(importRules.containsKey(refersTo)) { RGrammar dst = importRules.get(refersTo); - newState.contents.append(dst.generate(refersTo)); + newState.contents.append(dst.generate(refersTo, state.rnd)); } else { throw new GrammarException(String.format("No rule by name '%s' found", refersTo)); } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java index fb8003a..52304f5 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java @@ -18,6 +18,8 @@ import static bjc.rgens.newparser.RuleCase.CaseType.*; * */ public class RGrammarBuilder { + private static final String RANGE_CASELM = "\\[\\d+\\.\\.\\d+\\]"; + private static final String REFER_CASELEM = "\\[[^\\]]+\\]"; private static final String SPECIAL_CASELEM = "{[^}]}"; @@ -100,7 +102,19 @@ public class RGrammarBuilder { * Handle other cases. */ } else if(csepart.matches(REFER_CASELEM)) { + if(csepart.matches(RANGE_CASELM)) { + /* + * Handle ranges + */ + String rawRange = csepart.substring(1, csepart.length() - 1); + + int firstNum = Integer.parseInt(rawRange.substring(0, rawRange.indexOf('.'))); + int secondNum = Integer.parseInt(rawRange.substring(rawRange.lastIndexOf('.') + 1)); + + currentCase.add(new CaseElement(RANGE, firstNum, secondNum)); + } else { currentCase.add(new CaseElement(RULEREF, csepart)); + } } else { currentCase.add(new CaseElement(LITERAL, csepart)); } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java index 76efa86..ff23d79 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java @@ -27,7 +27,7 @@ public class RGrammarTest { grammarSet.addGrammar("rpg", grammar); for(int i = 0; i < 10; i++) { - System.out.println(grammar.generate(null)); + System.out.println(grammar.generate(null, null)); } System.out.println(); diff --git a/RGens/src/main/java/bjc/rgens/newparser/Rule.java b/RGens/src/main/java/bjc/rgens/newparser/Rule.java index a65f1ce..eca856f 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/Rule.java +++ b/RGens/src/main/java/bjc/rgens/newparser/Rule.java @@ -3,6 +3,8 @@ package bjc.rgens.newparser; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; +import java.util.Random; + /** * A rule in a randomized grammar. * @@ -61,6 +63,18 @@ public class Rule { return ruleCases.randItem(); } + /** + * Get a random case from this rule. + * + * @param rnd + * The random number generator to use. + * + * @return A random case from this rule. + */ + public RuleCase getCase(Random rnd) { + return ruleCases.randItem(rnd::nextInt); + } + /** * Get all the cases of this rule. * -- cgit v1.2.3