diff options
16 files changed, 404 insertions, 498 deletions
diff --git a/RGens/src/main/java/bjc/rgens/parser/CaseElement.java b/RGens/src/main/java/bjc/rgens/parser/CaseElement.java deleted file mode 100644 index e9e3a0a..0000000 --- a/RGens/src/main/java/bjc/rgens/parser/CaseElement.java +++ /dev/null @@ -1,423 +0,0 @@ -package bjc.rgens.parser; - -import static bjc.rgens.parser.CaseElement.ElementType.*; - -/* - * @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 { - /** - * The possible types of an element. - * - * @author EVE - */ - public static enum ElementType { - /** An element that represents a literal string. */ - LITERAL, - /** An element that represents a rule reference. */ - RULEREF, - /** An element that represents a random range. */ - RANGE, - /** An element that represents a variable that stores a string. */ - VARDEF, - /** - * An element that represents a variable that stores the result - * of generating a rule. - */ - EXPVARDEF; - } - - /* 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. */ - public final ElementType type; - - /** - * The literal string value of this element. - * - * This means that it is a string whose value should always mean the - * same thing. - * - * <h2>Used For</h2> - * <dl> - * <dt>LITERAL</dt> - * <dd>The string this element represents</dd> - * <dt>RULEREF</dt> - * <dd>The name of the rule this element references</dd> - * </dl> - */ - 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 end; - - /** - * The name of the variable this element defines. - * - * <h2>Used For</h2> - * <dl> - * <dt>VARDEF</dt> - * <dd>The name of the variable</dd> - * <dt>EXPVARDEF</dt> - * <dd>The name of the variable</dd> - * </dl> - */ - private String varName; - - /** - * The definition of the variable this element defines. - * - * <h2>Used For</h2> - * <dl> - * <dt>VARDEF</dt> - * <dd>The value of the variable</dd> - * <dt>EXPVARDEF</dt> - * <dd>The rule to expand for the value of this variable</dd> - * </dl> - */ - private String varDef; - - /** - * Create a new case element. - * - * @param typ - * The type of this element. - * - * @throws IllegalArgumentException - * If the specified type needs parameters. - */ - 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 integer parameters"); - case VARDEF: - case EXPVARDEF: - throw new IllegalArgumentException("This type requires two string parameters"); - default: - break; - } - - type = typ; - } - - /** - * Create a new case element that has a single string value. - * - * @param typ - * The type of this element. - * - * @param val - * The string value of this element. - * - * @throws IllegalArgumentException - * If the specified type doesn't take a single string parameter. - */ - public CaseElement(ElementType typ, String val) { - switch (typ) { - case LITERAL: - case RULEREF: - break; - case RANGE: - throw new IllegalArgumentException("This type requires two integer parameters"); - case VARDEF: - case EXPVARDEF: - throw new IllegalArgumentException("This type requires two string parameters"); - default: - throw new IllegalArgumentException("This type doesn't have a string parameter"); - } - - type = typ; - - literalVal = val; - } - - /** - * 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; - case VARDEF: - case EXPVARDEF: - throw new IllegalArgumentException("This type requires two string parameters"); - default: - throw new IllegalArgumentException("This type doesn't have two integer parameters"); - } - - type = typ; - - this.start = first; - this.end = second; - } - - /** - * Create a new case element that has two string values. - * - * @param typ - * The type of this element. - * - * @param name - * The first string value for this element. - * - * @param def - * The second string value for this element. - * - * @throws IllegalArgumentException - * If the specified type doesn't take two string parameters. - */ - public CaseElement(ElementType typ, String name, String def) { - switch (typ) { - case LITERAL: - case RULEREF: - throw new IllegalArgumentException("This type requires a string parameter"); - case RANGE: - throw new IllegalArgumentException("This type requires two integer parameters"); - case VARDEF: - case EXPVARDEF: - break; - default: - throw new IllegalArgumentException("This type doesn't have two string parameters"); - } - - type = typ; - - this.varName = name; - this.varDef = def; - } - - /** - * Get the literal string value for this element. - * - * @return - * The literal string value for this element. - * - * @throws IllegalStateException - * If this type doesn't have a literal string value. - */ - public String getLiteral() { - switch (type) { - case LITERAL: - case RULEREF: - break; - default: - throw new IllegalStateException( - String.format("Type '%s' doesn't have a literal string value")); - } - - 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 end; - } - - /** - * Get the variable name for this element. - * - * @return - * The variable name of this element. - * - * @throws IllegalStateException - * If the type doesn't have a variable name. - */ - public String getName() { - switch (type) { - case VARDEF: - case EXPVARDEF: - break; - default: - throw new IllegalStateException(String.format("Type '%s' doesn't have a name", type)); - } - - return varName; - } - - /** - * Get the variable definition for this element. - * - * @return - * The variable definition of this element. - * - * @throws IllegalStateException - * If the type doesn't have a variable definition. - */ - public String getDefn() { - switch (type) { - case VARDEF: - case EXPVARDEF: - break; - default: - throw new IllegalStateException(String.format("Type '%s' doesn't have a name", type)); - } - - return varDef; - } - - @Override - public String toString() { - switch (type) { - case LITERAL: - case RULEREF: - return literalVal; - case RANGE: - return String.format("[%d..%d]", start, end); - case VARDEF: - return String.format("{%s:=%s}", varName, varDef); - case EXPVARDEF: - return String.format("{%s=%s}", varName, varDef); - default: - return String.format("Unknown type '%s'", type); - } - } - - /** - * Create a case element from a string. - * - * @param csepart - * The string to convert. - * - * @return - * A case element representing the string. - */ - public static CaseElement createElement(String csepart) { - if (csepart == null) { - throw new NullPointerException("Case part cannot be null"); - } - - if (csepart.matches(SPECIAL_CASELEM)) { - /* Handle special cases. */ - String specialBody = csepart.substring(1, csepart.length() - 1); - - System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody); - - if (specialBody.matches("\\S+:=\\S+")) { - /* Handle expanding variable definitions. */ - String[] parts = specialBody.split(":="); - - if (parts.length != 2) { - String msg = "Expanded variables must be a name and a definition, seperated by :="; - - throw new GrammarException(msg); - } - - return new CaseElement(EXPVARDEF, parts[0], parts[1]); - } else if (specialBody.matches("\\S+=\\S+")) { - /* Handle regular variable definitions. */ - String[] parts = specialBody.split("="); - - if (parts.length != 2) { - String msg = "Variables must be a name and a definition, seperated by ="; - - throw new GrammarException(msg); - } - - return new CaseElement(VARDEF, parts[0], parts[1]); - } else if (specialBody.matches("{empty}")) { - /* Literal blank, for empty cases. */ - return new CaseElement(LITERAL, ""); - } 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); - - int firstNum = Integer.parseInt(rawRange.substring(0, rawRange.indexOf('.'))); - int secondNum = Integer.parseInt(rawRange.substring(rawRange.lastIndexOf('.') + 1)); - - return new CaseElement(RANGE, firstNum, secondNum); - } - - return new CaseElement(RULEREF, csepart); - } else { - return new CaseElement(LITERAL, csepart); - } - } -} diff --git a/RGens/src/main/java/bjc/rgens/parser/RGrammar.java b/RGens/src/main/java/bjc/rgens/parser/RGrammar.java index 17ca4fe..38f38c8 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RGrammar.java +++ b/RGens/src/main/java/bjc/rgens/parser/RGrammar.java @@ -1,5 +1,10 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; +import bjc.rgens.parser.elements.LiteralCaseElement; +import bjc.rgens.parser.elements.RangeCaseElement; +import bjc.rgens.parser.elements.RuleCaseElement; +import bjc.rgens.parser.elements.VariableCaseElement; import bjc.utils.funcutils.StringUtils; import java.util.HashMap; @@ -48,8 +53,8 @@ public class RGrammar { /* The current string. */ public StringBuilder contents; /* The RNG. */ - public Random rnd; - + public Random rnd; + /* The current set of variables. */ public Map<String, String> vars; @@ -57,13 +62,13 @@ public class RGrammar { * Create a new generation state. * * @param cont - * The string being generated. + * The string being generated. * * @param rand - * The RNG to use. + * The RNG to use. * * @param vs - * The variables to use. + * The variables to use. */ public GenerationState(StringBuilder cont, Random rand, Map<String, String> vs) { contents = cont; @@ -76,14 +81,14 @@ public class RGrammar { private static Pattern NAMEVAR_PATTERN = Pattern.compile("\\$(\\w+)"); /* The rules of the grammar. */ - private Map<String, Rule> rules; + private Map<String, Rule> rules; /* The rules imported from other grammars. */ - private Map<String, RGrammar> importRules; + private Map<String, RGrammar> importRules; /* The rules exported from this grammar. */ - private Set<String> exportRules; + private Set<String> exportRules; /* The initial rule of this grammar. */ - private String initialRule; - + private String initialRule; + /* The tree to use for finding rule suggestions. */ private BkTreeSearcher<String> ruleSearcher; @@ -91,7 +96,7 @@ public class RGrammar { * Create a new randomized grammar using the specified set of rules. * * @param ruls - * The rules to use. + * The rules to use. */ public RGrammar(Map<String, Rule> ruls) { rules = ruls; @@ -100,19 +105,18 @@ public class RGrammar { /** * Sets the imported rules to use. * - * Imported rules are checked for rule definitions after local - * definitions are checked. + * Imported rules are checked for rule definitions after local definitions are + * checked. * * @param importedRules - * The set of imported rules to use. + * The set of imported rules to use. */ public void setImportedRules(Map<String, RGrammar> importedRules) { importRules = importedRules; } /** - * Generates the data structure backing rule suggestions for unknown - * rules. + * Generates the data structure backing rule suggestions for unknown rules. */ public void generateSuggestions() { MutableBkTree<String> ruleSuggester = new MutableBkTree<>(new LevenshteinMetric()); @@ -124,44 +128,39 @@ public class RGrammar { } /** - * Generate a string from this grammar, starting from the specified - * rule. + * 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. + * The rule to start generating at, or null to use the initial rule + * for this grammar. * - * @return - * A possible string from the grammar. + * @return A possible string from the grammar. */ public String generate(String startRule) { return generate(startRule, new Random(), new HashMap<>()); } /** - * Generate a string from this grammar, starting from the specified - * rule. + * 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. + * The rule to start generating at, or null to use the initial rule + * for this grammar. * * @param rnd - * The random number generator to use. + * The random number generator to use. * * @param vars - * The set of variables to use. + * The set of variables to use. * - * @return - * A possible string from the grammar. + * @return A possible string from the grammar. */ public String generate(String startRule, Random rnd, Map<String, String> vars) { String fromRule = startRule; if (startRule == null) { if (initialRule == null) { - throw new GrammarException( - "Must specify a start rule for grammars with no initial rule"); + throw new GrammarException("Must specify a start rule for grammars with no initial rule"); } fromRule = initialRule; @@ -186,8 +185,7 @@ public class RGrammar { /* * Remove extraneous spaces around punctutation marks. * - * This can be done in the grammars, but it is very tedious to - * do so. + * This can be done in the grammars, but it is very tedious to do so. */ /* Handle 's */ @@ -212,8 +210,9 @@ 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"); @@ -228,7 +227,7 @@ public class RGrammar { for (CaseElement elm : start.getElements()) { generateElement(elm, state); - if(elm.type != CaseElement.ElementType.VARDEF) { + if (elm.type != CaseElement.ElementType.VARDEF) { state.contents.append(" "); } } @@ -252,26 +251,32 @@ public class RGrammar { private void generateElement(CaseElement elm, GenerationState state) { try { switch (elm.type) { - case LITERAL: - state.contents.append(elm.getLiteral()); + case LITERAL: { + LiteralCaseElement lit = (LiteralCaseElement)elm; + + state.contents.append(lit.val); break; - case RULEREF: - generateRuleReference(elm, state); + } + case RULEREF: { + RuleCaseElement rle = (RuleCaseElement)elm; + + generateRuleReference(rle, state); break; - case RANGE: - int start = elm.getStart(); - int end = elm.getEnd(); + } + case RANGE: { + RangeCaseElement rang = (RangeCaseElement)elm; - int val = state.rnd.nextInt(end - start); - val += start; + int val = state.rnd.nextInt(rang.end - rang.begin); + val += rang.begin; state.contents.append(val); break; + } case VARDEF: - generateVarDef(elm.getName(), elm.getDefn(), state); + generateVarDef(((VariableCaseElement)elm).varName, ((VariableCaseElement)elm).varDef, state); break; case EXPVARDEF: - generateExpVarDef(elm.getName(), elm.getDefn(), state); + generateExpVarDef(((VariableCaseElement)elm).varName, ((VariableCaseElement)elm).varDef, state); break; default: String msg = String.format("Unknown element type '%s'", elm.type); @@ -285,8 +290,7 @@ public class RGrammar { /* Generate a expanding variable definition. */ private void generateExpVarDef(String name, String defn, GenerationState state) { - GenerationState newState = new GenerationState( - new StringBuilder(), state.rnd, state.vars); + GenerationState newState = new GenerationState(new StringBuilder(), state.rnd, state.vars); if (rules.containsKey(defn)) { RuleCase destCase = rules.get(defn).getCase(); @@ -294,7 +298,7 @@ public class RGrammar { generateCase(destCase, newState); } else if (importRules.containsKey(defn)) { RGrammar destGrammar = importRules.get(defn); - String res = destGrammar.generate(defn, state.rnd, state.vars); + String res = destGrammar.generate(defn, state.rnd, state.vars); newState.contents.append(res); } else { @@ -311,11 +315,10 @@ public class RGrammar { } /* Generate a rule reference. */ - private void generateRuleReference(CaseElement elm, GenerationState state) { - String refersTo = elm.getLiteral(); + private void generateRuleReference(RuleCaseElement elm, GenerationState state) { + String refersTo = elm.val; - GenerationState newState = new GenerationState( - new StringBuilder(), state.rnd, state.vars); + GenerationState newState = new GenerationState(new StringBuilder(), state.rnd, state.vars); if (refersTo.contains("$")) { /* Parse variables */ @@ -338,11 +341,9 @@ public class RGrammar { String name = state.vars.get(var); if (name.contains(" ")) { - throw new GrammarException( - "Variables substituted into names cannot contain spaces"); + throw new GrammarException("Variables substituted into names cannot contain spaces"); } else if (name.equals("")) { - throw new GrammarException( - "Variables substituted into names cannot be empty"); + throw new GrammarException("Variables substituted into names cannot be empty"); } nameMatcher.appendReplacement(nameBuffer, name); @@ -370,7 +371,7 @@ public class RGrammar { } } - if(refersTo.startsWith("[^")) { + if (refersTo.startsWith("[^")) { refersTo = "[" + refersTo.substring(2); RGrammar dst = importRules.get(refersTo); @@ -388,11 +389,10 @@ public class RGrammar { 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[] 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)); + String msg = String.format("No rule '%s' defined (perhaps you meant %s?)", refersTo, + StringUtils.toEnglishList(resArray, false)); throw new GrammarException(msg); } @@ -412,8 +412,7 @@ public class RGrammar { /** * Get the initial rule of this grammar. * - * @return - * The initial rule of this grammar. + * @return The initial rule of this grammar. */ public String getInitialRule() { return initialRule; @@ -423,8 +422,8 @@ public class RGrammar { * Set the initial rule of this grammar. * * @param initRule - * The initial rule of this grammar, or null to say there is no - * initial rule. + * The initial rule of this grammar, or null to say there is no + * initial rule. */ public void setInitialRule(String initRule) { /* Passing null, nulls our initial rule. */ @@ -449,16 +448,14 @@ public class RGrammar { * * The initial rule is exported by default if specified. * - * @return - * The rules exported by this grammar. + * @return The rules exported by this grammar. */ public Set<Rule> getExportedRules() { Set<Rule> res = new HashSet<>(); for (String rname : exportRules) { if (!rules.containsKey(rname)) { - String msg = String.format("No rule '%s' local to this grammar defined", - initialRule); + String msg = String.format("No rule '%s' local to this grammar defined", initialRule); throw new GrammarException(msg); } @@ -477,7 +474,7 @@ public class RGrammar { * Set the rules exported by this grammar. * * @param exportedRules - * The rules exported by this grammar. + * The rules exported by this grammar. */ public void setExportedRules(Set<String> exportedRules) { exportRules = exportedRules; @@ -486,8 +483,7 @@ public class RGrammar { /** * Get all the rules in this grammar. * - * @return - * All the rules in this grammar. + * @return All the rules in this grammar. */ public Map<String, Rule> getRules() { return rules; diff --git a/RGens/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/RGens/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index 096decf..b4cb04a 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/RGens/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -1,5 +1,6 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; diff --git a/RGens/src/main/java/bjc/rgens/parser/RGrammarFormatter.java b/RGens/src/main/java/bjc/rgens/parser/RGrammarFormatter.java index 96bdee8..a2454dc 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RGrammarFormatter.java +++ b/RGens/src/main/java/bjc/rgens/parser/RGrammarFormatter.java @@ -1,5 +1,6 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.IList; import java.util.HashSet; diff --git a/RGens/src/main/java/bjc/rgens/parser/RGrammarParser.java b/RGens/src/main/java/bjc/rgens/parser/RGrammarParser.java index 83b295a..cffb896 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/RGens/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -1,5 +1,6 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; import bjc.utils.funcutils.TriConsumer; diff --git a/RGens/src/main/java/bjc/rgens/parser/RegexRuleCase.java b/RGens/src/main/java/bjc/rgens/parser/RegexRuleCase.java index 82417da..5e03cd6 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RegexRuleCase.java +++ b/RGens/src/main/java/bjc/rgens/parser/RegexRuleCase.java @@ -1,5 +1,6 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.IList; import java.util.regex.Pattern; diff --git a/RGens/src/main/java/bjc/rgens/parser/RuleCase.java b/RGens/src/main/java/bjc/rgens/parser/RuleCase.java index 764fa89..9c0a856 100644 --- a/RGens/src/main/java/bjc/rgens/parser/RuleCase.java +++ b/RGens/src/main/java/bjc/rgens/parser/RuleCase.java @@ -1,5 +1,6 @@ package bjc.rgens.parser; +import bjc.rgens.parser.elements.CaseElement; import bjc.utils.funcdata.IList; /* diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/BlankCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/BlankCaseElement.java new file mode 100644 index 0000000..7229e92 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/BlankCaseElement.java @@ -0,0 +1,7 @@ +package bjc.rgens.parser.elements; + +public class BlankCaseElement extends LiteralCaseElement { + public BlankCaseElement() { + super(""); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/CaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/CaseElement.java new file mode 100644 index 0000000..7cad599 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/CaseElement.java @@ -0,0 +1,149 @@ +package bjc.rgens.parser.elements; + +import static bjc.rgens.parser.elements.CaseElement.ElementType.*; + +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 { + /** + * The possible types of an element. + * + * @author EVE + */ + public static enum ElementType { + /** An element that represents a literal string. */ + LITERAL, + /** An element that represents a rule reference. */ + RULEREF, + /** An element that represents a random range. */ + RANGE, + /** An element that represents a variable that stores a string. */ + VARDEF, + /** + * An element that represents a variable that stores the result of generating a + * rule. + */ + EXPVARDEF; + } + + /* 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. */ + public final ElementType type; + + /** + * Create a new case element. + * + * @param typ + * The type of this element. + */ + protected CaseElement(ElementType typ) { + type = typ; + } + + @Override + public String toString() { + switch (type) { + default: + return String.format("Unknown type '%s'", type); + } + } + + /** + * Create a case element from a string. + * + * @param csepart + * The string to convert. + * + * @return A case element representing the string. + */ + public static CaseElement createElement(String csepart) { + if (csepart == null) { + throw new NullPointerException("Case part cannot be null"); + } + + if (csepart.matches(SPECIAL_CASELEM)) { + /* Handle special cases. */ + String specialBody = csepart.substring(1, csepart.length() - 1); + + System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody); + + if (specialBody.matches("\\S+:=\\S+")) { + /* Handle expanding variable definitions. */ + String[] parts = specialBody.split(":="); + + if (parts.length != 2) { + String msg = "Expanded variables must be a name and a definition, seperated by :="; + + throw new GrammarException(msg); + } + + return new ExpVariableCaseElement(parts[0], parts[1]); + } else if (specialBody.matches("\\S+=\\S+")) { + /* Handle regular variable definitions. */ + String[] parts = specialBody.split("="); + + if (parts.length != 2) { + String msg = "Variables must be a name and a definition, seperated by ="; + + throw new GrammarException(msg); + } + + return new LitVariableCaseElement(parts[0], parts[1]); + } 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); + + int firstNum = Integer.parseInt(rawRange.substring(0, rawRange.indexOf('.'))); + int secondNum = Integer.parseInt(rawRange.substring(rawRange.lastIndexOf('.') + 1)); + + return new RangeCaseElement(firstNum, secondNum); + } + + return new RuleCaseElement(csepart); + } else { + return new LiteralCaseElement(csepart); + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CaseElement other = (CaseElement) obj; + if (type != other.type) + return false; + return true; + } +}
\ No newline at end of file diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java new file mode 100644 index 0000000..30925e2 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java @@ -0,0 +1,7 @@ +package bjc.rgens.parser.elements; + +public class ExpVariableCaseElement extends VariableCaseElement { + public ExpVariableCaseElement(String name, String def) { + super(name, def, true); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java new file mode 100644 index 0000000..11035b1 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/LitVariableCaseElement.java @@ -0,0 +1,7 @@ +package bjc.rgens.parser.elements; + +public class LitVariableCaseElement extends VariableCaseElement { + public LitVariableCaseElement(String name, String def) { + super(name, def, false); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java new file mode 100644 index 0000000..d96a32d --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/LiteralCaseElement.java @@ -0,0 +1,7 @@ +package bjc.rgens.parser.elements; + +public class LiteralCaseElement extends StringCaseElement { + public LiteralCaseElement(String vl) { + super(vl, true); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java new file mode 100644 index 0000000..d98bc61 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java @@ -0,0 +1,43 @@ +package bjc.rgens.parser.elements; + +public class RangeCaseElement extends CaseElement { + public final int begin; + public final int end; + + public RangeCaseElement(int beg, int en) { + super(ElementType.RANGE); + + begin = beg; + end = en; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + begin; + result = prime * result + end; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + RangeCaseElement other = (RangeCaseElement) obj; + if (begin != other.begin) + return false; + if (end != other.end) + return false; + return true; + } + + @Override + public String toString() { + return String.format("[%d..%d]", begin, end); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java new file mode 100644 index 0000000..f4d3512 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java @@ -0,0 +1,7 @@ +package bjc.rgens.parser.elements; + +public class RuleCaseElement extends StringCaseElement { + public RuleCaseElement(String vl) { + super(vl, false); + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java new file mode 100644 index 0000000..0e64fd3 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java @@ -0,0 +1,41 @@ +package bjc.rgens.parser.elements; + +public class StringCaseElement extends CaseElement { + public final String val; + + protected StringCaseElement(String vl, boolean isLiteral) { + super(isLiteral ? ElementType.LITERAL : ElementType.RULEREF); + + val = vl; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((val == null) ? 0 : val.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + StringCaseElement other = (StringCaseElement) obj; + if (val == null) { + if (other.val != null) + return false; + } else if (!val.equals(other.val)) + return false; + return true; + } + + @Override + public String toString() { + return val; + } +} diff --git a/RGens/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java b/RGens/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java new file mode 100644 index 0000000..920445a --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java @@ -0,0 +1,60 @@ +package bjc.rgens.parser.elements; + +public class VariableCaseElement extends CaseElement { + /** + * The name of the variable this element defines. + */ + public final String varName; + + /** + * The definition of the variable this element defines. + */ + public final String varDef; + + public VariableCaseElement(String name, String def, boolean isExp) { + super(isExp ? ElementType.EXPVARDEF : ElementType.VARDEF); + + varName = name; + varDef = def; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((varDef == null) ? 0 : varDef.hashCode()); + result = prime * result + ((varName == null) ? 0 : varName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + VariableCaseElement other = (VariableCaseElement) obj; + if (varDef == null) { + if (other.varDef != null) + return false; + } else if (!varDef.equals(other.varDef)) + return false; + if (varName == null) { + if (other.varName != null) + return false; + } else if (!varName.equals(other.varName)) + return false; + return true; + } + + @Override + public String toString() { + if (type == ElementType.VARDEF) { + return String.format("{%s:=%s}", varName, varDef); + } else { + return String.format("{%s=%s}", varName, varDef); + } + } +} |
