summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/bjc/rgens/parser/GenerationState.java71
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammar.java231
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammarBuilder.java16
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammarParser.java1
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/CaseElement.java56
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java38
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java6
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java7
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/RangeCaseElement.java9
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/RuleCaseElement.java122
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/StringCaseElement.java2
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/elements/VariableCaseElement.java2
12 files changed, 344 insertions, 217 deletions
diff --git a/src/main/java/bjc/rgens/parser/GenerationState.java b/src/main/java/bjc/rgens/parser/GenerationState.java
new file mode 100644
index 0000000..7ab55bf
--- /dev/null
+++ b/src/main/java/bjc/rgens/parser/GenerationState.java
@@ -0,0 +1,71 @@
+package bjc.rgens.parser;
+
+import java.util.Map;
+import java.util.Random;
+
+/*
+ * The current state during generation.
+ *
+ */
+public class GenerationState {
+ /** The current string. */
+ public StringBuilder contents;
+ /** The RNG. */
+ public Random rnd;
+
+ /*
+ * @NOTE
+ *
+ * Once the planned refactor for importing rules happens, this will need
+ * to be changed.
+ */
+ /** The current grammar. */
+ public RGrammar gram;
+ /** The rules of the grammar. */
+ public Map<String, Rule> rules;
+ /** The rules imported from other grammars. */
+ public Map<String, RGrammar> importRules;
+
+ /*
+ * @NOTE
+ *
+ * For planned features, this will need to be refactored.
+ */
+ /** The current set of variables. */
+ public Map<String, String> vars;
+
+ /**
+ * Create a new generation state.
+ *
+ * @param cont
+ * The string being generated.
+ *
+ * @param rand
+ * The RNG to use.
+ *
+ * @param vs
+ * The variables to use.
+ */
+ public GenerationState(StringBuilder cont, Random rand, Map<String, String> vs, RGrammar gram) {
+ contents = cont;
+ rnd = rand;
+ vars = vs;
+
+ this.gram = gram;
+
+ this.rules = gram.getRules();
+ this.importRules = gram.getImportRules();
+ }
+
+ public void swapGrammar(RGrammar gram) {
+ this.gram = gram;
+
+ rules = gram.getRules();
+
+ importRules = gram.getImportRules();
+ }
+
+ public GenerationState newBuf() {
+ return new GenerationState(new StringBuilder(), rnd, vars, gram);
+ }
+}
diff --git a/src/main/java/bjc/rgens/parser/RGrammar.java b/src/main/java/bjc/rgens/parser/RGrammar.java
index 38f38c8..9d17171 100755
--- a/src/main/java/bjc/rgens/parser/RGrammar.java
+++ b/src/main/java/bjc/rgens/parser/RGrammar.java
@@ -48,35 +48,6 @@ public class RGrammar {
}
}
- /* The current state during generation. */
- private static class GenerationState {
- /* The current string. */
- public StringBuilder contents;
- /* The RNG. */
- public Random rnd;
-
- /* The current set of variables. */
- public Map<String, String> vars;
-
- /**
- * Create a new generation state.
- *
- * @param cont
- * The string being generated.
- *
- * @param rand
- * The RNG to use.
- *
- * @param vs
- * The variables to use.
- */
- public GenerationState(StringBuilder cont, Random rand, Map<String, String> vs) {
- contents = cont;
- rnd = rand;
- vars = vs;
- }
- }
-
/* The pattern for matching the name of a variable. */
private static Pattern NAMEVAR_PATTERN = Pattern.compile("\\$(\\w+)");
@@ -156,6 +127,20 @@ public class RGrammar {
* @return A possible string from the grammar.
*/
public String generate(String startRule, Random rnd, Map<String, String> vars) {
+ return generate(startRule, new GenerationState(new StringBuilder(), rnd, vars, this));
+ }
+
+ /**
+ * 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 state
+ * The generation state.
+ */
+ public String generate(String startRule, GenerationState state) {
String fromRule = startRule;
if (startRule == null) {
@@ -170,13 +155,19 @@ public class RGrammar {
}
}
- RuleCase start = rules.get(fromRule).getCase(rnd);
+ RuleCase start = rules.get(fromRule).getCase(state.rnd);
- StringBuilder contents = new StringBuilder();
+ generateCase(start, state);
- generateCase(start, new GenerationState(contents, rnd, vars));
+ /*
+ * @NOTE
+ *
+ * :Postprocessing
+ *
+ * Do we want to perform this post-processing here, or elsewhere?
+ */
+ String body = state.contents.toString();
- String body = contents.toString();
/*
* Collapse duplicate spaces.
*/
@@ -211,16 +202,24 @@ public class RGrammar {
body = body.replaceAll("\\s+", " ");
/*
- * @TODO 11/01/17 Ben Culkin :RegexRule Replace this once it is no longer
- * needed.
+ * @TODO 11/01/17 Ben Culkin :RegexRule
+ *
+ * Replace this once it is no longer needed.
*/
body = body.replaceAll("\\s(ish|burg|ton|ville|opolis|field|boro|dale)", "$1");
return body;
}
- /* Generate a rule case. */
- private void generateCase(RuleCase start, GenerationState state) {
+ /**
+ * Generate a rule case.
+ *
+ * @param start
+ * The rule case to generate.
+ * @param state
+ * The current generation state.
+ */
+ public void generateCase(RuleCase start, GenerationState state) {
try {
switch (start.type) {
case NORMAL:
@@ -250,165 +249,13 @@ public class RGrammar {
/* Generate a case element. */
private void generateElement(CaseElement elm, GenerationState state) {
try {
- switch (elm.type) {
- case LITERAL: {
- LiteralCaseElement lit = (LiteralCaseElement)elm;
-
- state.contents.append(lit.val);
- break;
- }
- case RULEREF: {
- RuleCaseElement rle = (RuleCaseElement)elm;
-
- generateRuleReference(rle, state);
- break;
- }
- case RANGE: {
- RangeCaseElement rang = (RangeCaseElement)elm;
-
- int val = state.rnd.nextInt(rang.end - rang.begin);
- val += rang.begin;
-
- state.contents.append(val);
- break;
- }
- case VARDEF:
- generateVarDef(((VariableCaseElement)elm).varName, ((VariableCaseElement)elm).varDef, state);
- break;
- case EXPVARDEF:
- generateExpVarDef(((VariableCaseElement)elm).varName, ((VariableCaseElement)elm).varDef, state);
- break;
- default:
- String msg = String.format("Unknown element type '%s'", elm.type);
- throw new GrammarException(msg);
- }
+ elm.generate(state);
} catch (GrammarException gex) {
String msg = String.format("Error in generating case element (%s)", elm);
throw new GrammarException(msg, gex);
}
}
- /* Generate a expanding variable definition. */
- private void generateExpVarDef(String name, String defn, GenerationState state) {
- GenerationState newState = new GenerationState(new StringBuilder(), state.rnd, state.vars);
-
- if (rules.containsKey(defn)) {
- RuleCase destCase = rules.get(defn).getCase();
-
- generateCase(destCase, newState);
- } else if (importRules.containsKey(defn)) {
- RGrammar destGrammar = importRules.get(defn);
- String res = destGrammar.generate(defn, state.rnd, state.vars);
-
- newState.contents.append(res);
- } else {
- String msg = String.format("No rule '%s' defined", defn);
- throw new GrammarException(msg);
- }
-
- state.vars.put(name, newState.contents.toString());
- }
-
- /* Generate a variable definition. */
- private static void generateVarDef(String name, String defn, GenerationState state) {
- state.vars.put(name, defn);
- }
-
- /* Generate a rule reference. */
- private void generateRuleReference(RuleCaseElement elm, GenerationState state) {
- String refersTo = elm.val;
-
- GenerationState newState = new GenerationState(new StringBuilder(), state.rnd, state.vars);
-
- if (refersTo.contains("$")) {
- /* Parse variables */
- String refBody = refersTo.substring(1, refersTo.length() - 1);
-
- if (refBody.contains("-")) {
- /* Handle dependent rule names. */
- StringBuffer nameBuffer = new StringBuffer();
-
- Matcher nameMatcher = NAMEVAR_PATTERN.matcher(refBody);
-
- while (nameMatcher.find()) {
- String var = nameMatcher.group(1);
-
- if (!state.vars.containsKey(var)) {
- String msg = String.format("No variable '%s' defined", var);
- throw new GrammarException(msg);
- }
-
- String name = state.vars.get(var);
-
- if (name.contains(" ")) {
- throw new GrammarException("Variables substituted into names cannot contain spaces");
- } else if (name.equals("")) {
- throw new GrammarException("Variables substituted into names cannot be empty");
- }
-
- nameMatcher.appendReplacement(nameBuffer, name);
- }
-
- nameMatcher.appendTail(nameBuffer);
-
- refersTo = "[" + nameBuffer.toString() + "]";
- } else {
- /* Handle string references. */
- if (refBody.equals("$")) {
- throw new GrammarException("Cannot refer to unnamed variables");
- }
-
- String key = refBody.substring(1);
-
- if (!state.vars.containsKey(key)) {
- String msg = String.format("No variable '%s' defined", key);
- throw new GrammarException(msg);
- }
-
- state.contents.append(state.vars.get(key));
-
- return;
- }
- }
-
- if (refersTo.startsWith("[^")) {
- refersTo = "[" + refersTo.substring(2);
-
- RGrammar dst = importRules.get(refersTo);
-
- newState.contents.append(dst.generate(refersTo, state.rnd, state.vars));
- } else if (rules.containsKey(refersTo)) {
- 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, state.rnd, state.vars));
- } else {
- if (ruleSearcher != null) {
- Set<Match<? extends String>> results = ruleSearcher.search(refersTo, MAX_DISTANCE);
-
- String[] resArray = results.stream().map(Match::getMatch).toArray((i) -> new String[i]);
-
- String msg = String.format("No rule '%s' defined (perhaps you meant %s?)", refersTo,
- StringUtils.toEnglishList(resArray, false));
-
- throw new GrammarException(msg);
- }
-
- String msg = String.format("No rule '%s' defined", refersTo);
- throw new GrammarException(msg);
- }
-
- if (refersTo.contains("+")) {
- /* Rule names with pluses in them get space-flattened */
- state.contents.append(newState.contents.toString().replaceAll("\\s+", ""));
- } else {
- state.contents.append(newState.contents.toString());
- }
- }
-
/**
* Get the initial rule of this grammar.
*
@@ -488,4 +335,8 @@ public class RGrammar {
public Map<String, Rule> getRules() {
return rules;
}
+
+ public Map<String, RGrammar> getImportRules() {
+ return importRules;
+ }
}
diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
index 284d1a2..f34a418 100755
--- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
+++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
@@ -66,8 +66,20 @@ public class RGrammarBuilder {
public RGrammar toRGrammar() {
RGrammar grammar = new RGrammar(rules);
+ if(initialRule != null) {
+ if(!rules.containsKey(initialRule)) {
+ throw new GrammarException(String.format("Rule '%s' doesn't exist\n", initialRule));
+ }
+ }
+
grammar.setInitialRule(initialRule);
+ for(String export : exportedRules) {
+ if(!rules.containsKey(export)) {
+ throw new GrammarException(String.format("Rule '%s' doesn't exist\n", export));
+ }
+ }
+
grammar.setExportedRules(exportedRules);
return grammar;
@@ -87,8 +99,6 @@ public class RGrammarBuilder {
throw new NullPointerException("init must not be null");
} else if (init.equals("")) {
throw new IllegalArgumentException("The empty string is not a valid rule name");
- } else if (!rules.containsKey(init)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", init));
}
initialRule = init;
@@ -108,8 +118,6 @@ public class RGrammarBuilder {
throw new NullPointerException("Export name must not be null");
} else if (export.equals("")) {
throw new NullPointerException("The empty string is not a valid rule name");
- } else if(!rules.containsKey(export)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", rules));
}
exportedRules.add(export);
diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java
index 3a357b1..506256d 100755
--- a/src/main/java/bjc/rgens/parser/RGrammarParser.java
+++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java
@@ -182,6 +182,7 @@ public class RGrammarParser {
* Comment block.
*
* @TODO 10/11/17 Ben Culkin :GrammarComment
+ *
* Attach these to the grammar somehow so that they
* can be re-output during formatting.
*/
diff --git a/src/main/java/bjc/rgens/parser/elements/CaseElement.java b/src/main/java/bjc/rgens/parser/elements/CaseElement.java
index d74ab52..522de7a 100755
--- a/src/main/java/bjc/rgens/parser/elements/CaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/CaseElement.java
@@ -1,17 +1,14 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.GenerationState;
import bjc.rgens.parser.GrammarException;
-/*
- * @TODO 10/11/17 Ben Culkin :CaseElementSplit Split this into multiple
- * subclasses based off of a value of ElementType.
- */
/**
* A element in a rule case.
*
* @author EVE
*/
-public class CaseElement {
+public abstract class CaseElement {
/**
* The possible types of an element.
*
@@ -34,8 +31,6 @@ public class CaseElement {
}
/* Regexps for marking rule types. */
- private static final String SPECIAL_CASELEM = "\\{[^}]+\\}";
- private static final String REFER_CASELEM = "\\[[^\\]]+\\]";
private static final String RANGE_CASELM = "\\[\\d+\\.\\.\\d+\\]";
/** The type of this element. */
@@ -53,13 +48,18 @@ public class CaseElement {
@Override
public String toString() {
- switch (type) {
- default:
- return String.format("Unknown type '%s'", type);
- }
+ return String.format("Unknown type '%s'", type);
}
/**
+ * Generate this case element.
+ *
+ * @param state
+ * The current state of generation.
+ */
+ public abstract void generate(GenerationState state);
+
+ /**
* Create a case element from a string.
*
* @param csepart
@@ -72,8 +72,11 @@ public class CaseElement {
throw new NullPointerException("Case part cannot be null");
}
- if (csepart.matches(SPECIAL_CASELEM)) {
- /* Handle special cases. */
+ if (csepart.matches("\\{[^}]+\\}")) {
+ /*
+ * Handle special cases.
+ *
+ */
String specialBody = csepart.substring(1, csepart.length() - 1);
System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody);
@@ -88,6 +91,12 @@ public class CaseElement {
throw new GrammarException(msg);
}
+ /*
+ * @NOTE
+ *
+ * This should maybe check that parts[1] is a
+ * valid rule name, since it gets used as one.
+ */
return new ExpVariableCaseElement(parts[0], parts[1]);
} else if (specialBody.matches("\\S+=\\S+")) {
/* Handle regular variable definitions. */
@@ -100,23 +109,28 @@ public class CaseElement {
}
return new LitVariableCaseElement(parts[0], parts[1]);
- } else if (specialBody.matches("{empty}")) {
+ } else if (specialBody.matches("empty")) {
/* Literal blank, for empty cases. */
return new BlankCaseElement();
} else {
throw new IllegalArgumentException(String.format("Unknown special case part '%s'", specialBody));
}
- } else if (csepart.matches(REFER_CASELEM)) {
- if (csepart.matches(RANGE_CASELM)) {
- /* Handle ranges */
- String rawRange = csepart.substring(1, csepart.length() - 1);
+ } else if (csepart.matches("\\[[^\\]]+\\]")) {
+ String rawCase = csepart.substring(1, csepart.length() - 1);
- int firstNum = Integer.parseInt(rawRange.substring(0, rawRange.indexOf('.')));
- int secondNum = Integer.parseInt(rawRange.substring(rawRange.lastIndexOf('.') + 1));
+ if (rawCase.matches("\\d+\\.\\.\\d+")) {
+ int firstNum = Integer.parseInt(rawCase.substring(0, rawCase.indexOf('.')));
+ int secondNum = Integer.parseInt(rawCase.substring(rawCase.lastIndexOf('.') + 1));
return new RangeCaseElement(firstNum, secondNum);
}
+ /*
+ * @NOTE
+ *
+ * Once the rule element execution has been refactored,
+ * pass rawCase instead.
+ */
return new RuleCaseElement(csepart);
} else {
return new LiteralCaseElement(csepart);
@@ -144,4 +158,4 @@ public class CaseElement {
return false;
return true;
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java
index 30925e2..8598fbd 100755
--- a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java
@@ -1,7 +1,45 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.GenerationState;
+import bjc.rgens.parser.GrammarException;
+import bjc.rgens.parser.RGrammar;
+import bjc.rgens.parser.RuleCase;
+
public class ExpVariableCaseElement extends VariableCaseElement {
public ExpVariableCaseElement(String name, String def) {
super(name, def, true);
}
+
+ @Override
+ public void generate(GenerationState state) {
+ GenerationState newState = state.newBuf();
+
+ if (state.rules.containsKey(varDef)) {
+ RuleCase destCase = state.rules.get(varDef).getCase();
+
+ state.gram.generateCase(destCase, newState);
+ } else if (state.importRules.containsKey(varDef)) {
+ RGrammar destGrammar = state.importRules.get(varDef);
+
+ newState.swapGrammar(destGrammar);
+
+ String res = destGrammar.generate(varDef, state);
+
+ /*
+ * @NOTE
+ *
+ * :Postprocessing
+ *
+ * This is because generate() returns a processed
+ * string, but modifies the passed in StringBuilder.
+ */
+ newState.contents = new StringBuilder(res);
+ } else {
+ String msg = String.format("No rule '%s' defined", varDef);
+ throw new GrammarException(msg);
+ }
+
+ state.vars.put(varName, newState.contents.toString());
+
+ }
}
diff --git a/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java
index 11035b1..91b8a2b 100755
--- a/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java
@@ -1,7 +1,13 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.GenerationState;
+
public class LitVariableCaseElement extends VariableCaseElement {
public LitVariableCaseElement(String name, String def) {
super(name, def, false);
}
+
+ public void generate(GenerationState state) {
+ state.vars.put(varName, varDef);
+ }
}
diff --git a/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java b/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java
index d96a32d..18c3482 100755
--- a/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java
@@ -1,7 +1,14 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.GenerationState;
+
public class LiteralCaseElement extends StringCaseElement {
public LiteralCaseElement(String vl) {
super(vl, true);
}
+
+ @Override
+ public void generate(GenerationState state) {
+ state.contents.append(val);
+ }
}
diff --git a/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java b/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java
index d98bc61..7cebde9 100755
--- a/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java
@@ -1,5 +1,7 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.GenerationState;
+
public class RangeCaseElement extends CaseElement {
public final int begin;
public final int end;
@@ -11,6 +13,13 @@ public class RangeCaseElement extends CaseElement {
end = en;
}
+ public void generate(GenerationState state) {
+ int val = state.rnd.nextInt(end - begin);
+ val += begin;
+
+ state.contents.append(val);
+ }
+
@Override
public int hashCode() {
final int prime = 31;
diff --git a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java
index f4d3512..d91bebc 100755
--- a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java
@@ -1,7 +1,129 @@
package bjc.rgens.parser.elements;
+import bjc.rgens.parser.*;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
public class RuleCaseElement extends StringCaseElement {
+ private static Pattern NAMEVAR_PATTERN = Pattern.compile("\\$(\\w+)");
+
public RuleCaseElement(String vl) {
super(vl, false);
}
+
+ @Override
+ public void generate(GenerationState state) {
+ /*
+ * @NOTE
+ *
+ * :VarRefactor
+ *
+ * Figure out if this can be refactored in some way.
+ */
+ String refersTo = val;
+
+ GenerationState newState = state.newBuf();
+
+ if (refersTo.contains("$")) {
+ /* Parse variables */
+ String refBody = refersTo.substring(1, refersTo.length() - 1);
+
+ if (refBody.contains("-")) {
+ /* Handle dependent rule names. */
+ StringBuffer nameBuffer = new StringBuffer();
+
+ Matcher nameMatcher = NAMEVAR_PATTERN.matcher(refBody);
+
+ while (nameMatcher.find()) {
+ String var = nameMatcher.group(1);
+
+ if (!state.vars.containsKey(var)) {
+ String msg = String.format("No variable '%s' defined", var);
+ throw new GrammarException(msg);
+ }
+
+ String name = state.vars.get(var);
+
+ if (name.contains(" ")) {
+ throw new GrammarException("Variables substituted into names cannot contain spaces");
+ } else if (name.equals("")) {
+ throw new GrammarException("Variables substituted into names cannot be empty");
+ }
+
+ nameMatcher.appendReplacement(nameBuffer, name);
+ }
+
+ nameMatcher.appendTail(nameBuffer);
+
+ refersTo = "[" + nameBuffer.toString() + "]";
+ } else {
+ /* Handle string references. */
+ if (refBody.equals("$")) {
+ throw new GrammarException("Cannot refer to unnamed variables");
+ }
+
+ String key = refBody.substring(1);
+
+ if (!state.vars.containsKey(key)) {
+ String msg = String.format("No variable '%s' defined", key);
+ throw new GrammarException(msg);
+ }
+
+ state.contents.append(state.vars.get(key));
+
+ return;
+ }
+ }
+
+ if (refersTo.startsWith("[^")) {
+ refersTo = "[" + refersTo.substring(2);
+
+ RGrammar dst = state.importRules.get(refersTo);
+
+ newState.swapGrammar(dst);
+
+ /* :Postprocessing */
+ newState.contents = new StringBuilder(dst.generate(refersTo, state.rnd, state.vars));
+ } else if (state.rules.containsKey(refersTo)) {
+ RuleCase cse = state.rules.get(refersTo).getCase(state.rnd);
+
+ state.gram.generateCase(cse, newState);
+ } else if (state.importRules.containsKey(refersTo)) {
+ RGrammar dst = state.importRules.get(refersTo);
+
+ newState.swapGrammar(dst);
+
+ /* :Postprocessing */
+ newState.contents = new StringBuilder(dst.generate(refersTo, state.rnd, state.vars));
+ } else {
+ /*
+ * @TODO 5/29/18 Ben Culkin :RuleSuggesting
+ *
+ * Re-get this working again.
+ */
+ /*
+ if (ruleSearcher != null) {
+ Set<Match<? extends String>> results = ruleSearcher.search(refersTo, MAX_DISTANCE);
+
+ String[] resArray = results.stream().map(Match::getMatch).toArray((i) -> new String[i]);
+
+ String msg = String.format("No rule '%s' defined (perhaps you meant %s?)", refersTo,
+ StringUtils.toEnglishList(resArray, false));
+
+ throw new GrammarException(msg);
+ }
+ */
+
+ String msg = String.format("No rule '%s' defined", refersTo);
+ throw new GrammarException(msg);
+ }
+
+ if (refersTo.contains("+")) {
+ /* Rule names with pluses in them get space-flattened */
+ state.contents.append(newState.contents.toString().replaceAll("\\s+", ""));
+ } else {
+ state.contents.append(newState.contents.toString());
+ }
+ }
}
diff --git a/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java b/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java
index 0e64fd3..61ad48a 100755
--- a/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java
@@ -1,6 +1,6 @@
package bjc.rgens.parser.elements;
-public class StringCaseElement extends CaseElement {
+public abstract class StringCaseElement extends CaseElement {
public final String val;
protected StringCaseElement(String vl, boolean isLiteral) {
diff --git a/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java
index 920445a..3e691d9 100755
--- a/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java
@@ -1,6 +1,6 @@
package bjc.rgens.parser.elements;
-public class VariableCaseElement extends CaseElement {
+public abstract class VariableCaseElement extends CaseElement {
/**
* The name of the variable this element defines.
*/