summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbjculkin <bjculkin@mix.wvu.edu>2017-03-21 19:42:12 -0400
committerbjculkin <bjculkin@mix.wvu.edu>2017-03-21 19:42:12 -0400
commit2cc00686b50d5a28f0ab04ad52e5b075290698d9 (patch)
tree9157f35c50dd12acd87bacb4f30c8d018703adb5
parent5444cd4db8a0fa41d25cd303c1145cadd112e12f (diff)
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.
-rw-r--r--RGens/src/main/java/bjc/rgens/newparser/CaseElement.java112
-rw-r--r--RGens/src/main/java/bjc/rgens/newparser/RGrammar.java46
-rw-r--r--RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java14
-rw-r--r--RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java2
-rw-r--r--RGens/src/main/java/bjc/rgens/newparser/Rule.java14
5 files changed, 174 insertions, 14 deletions
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;
}
/**
@@ -45,6 +49,28 @@ public class CaseElement {
private String literalVal;
/**
+ * The starting integer value of this element.
+ *
+ * <h2>Used For</h2>
+ * <dl>
+ * <dt>RANGE</dt>
+ * <dd>The inclusive start of the range</dd>
+ * </dl>
+ */
+ private int start;
+
+ /**
+ * The starting integer value of this element.
+ *
+ * <h2>Used For</h2>
+ * <dl>
+ * <dt>RANGE</dt>
+ * <dd>The inclusive end of the range</dd>
+ * </dl>
+ */
+ private int snd;
+
+ /**
* Create a new case element.
*
* @param typ
@@ -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.
*
@@ -62,6 +64,18 @@ public class Rule {
}
/**
+ * 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.
*
* @return All the cases in this rule.