From 44a8d9d2d56a311293ec86ea40df7126748300a1 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Thu, 7 Jun 2018 20:37:51 -0300 Subject: Refactoring The main refactoring here is removing the type field from the various classes, but there are a few other smaller ones. This also contains the grounds for a refactoring on variable use --- src/main/java/bjc/rgens/parser/ConfigLoader.java | 216 +++++++++++---------- src/main/java/bjc/rgens/parser/FlatRuleCase.java | 6 +- src/main/java/bjc/rgens/parser/NormalRuleCase.java | 8 +- src/main/java/bjc/rgens/parser/RGrammar.java | 111 +++++++---- .../java/bjc/rgens/parser/RGrammarBuilder.java | 28 +-- .../java/bjc/rgens/parser/RGrammarFormatter.java | 2 +- src/main/java/bjc/rgens/parser/RGrammarParser.java | 50 ++++- src/main/java/bjc/rgens/parser/RegexRuleCase.java | 31 ++- src/main/java/bjc/rgens/parser/RuleCase.java | 73 +------ .../bjc/rgens/parser/elements/CaseElement.java | 49 ++--- .../rgens/parser/elements/ChanceCaseElement.java | 20 ++ .../parser/elements/ExpVariableCaseElement.java | 14 +- .../parser/elements/InlineRuleCaseElement.java | 31 ++- .../rgens/parser/elements/RangeCaseElement.java | 2 +- .../bjc/rgens/parser/elements/RuleCaseElement.java | 13 +- .../rgens/parser/elements/SerialCaseElement.java | 4 +- .../rgens/parser/elements/StringCaseElement.java | 2 +- .../rgens/parser/elements/VariableCaseElement.java | 11 +- .../parser/elements/vars/ARefVariableElement.java | 35 ++++ .../elements/vars/LiteralVariableElement.java | 15 ++ .../parser/elements/vars/RRefVariableElement.java | 35 ++++ .../parser/elements/vars/TRefVariableElement.java | 43 ++++ .../parser/elements/vars/VRefVariableElement.java | 29 +++ .../parser/elements/vars/VariableElement.java | 59 ++++++ .../rgens/parser/templates/GrammarTemplate.java | 2 +- .../parser/templates/LiteralTemplateElement.java | 2 +- .../parser/templates/LiveTemplateElement.java | 4 +- .../rgens/parser/templates/TemplateElement.java | 18 +- 28 files changed, 564 insertions(+), 349 deletions(-) create mode 100644 src/main/java/bjc/rgens/parser/elements/ChanceCaseElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/LiteralVariableElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/RRefVariableElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/TRefVariableElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java create mode 100644 src/main/java/bjc/rgens/parser/elements/vars/VariableElement.java (limited to 'src/main/java/bjc') diff --git a/src/main/java/bjc/rgens/parser/ConfigLoader.java b/src/main/java/bjc/rgens/parser/ConfigLoader.java index 4cad368..6e9da16 100644 --- a/src/main/java/bjc/rgens/parser/ConfigLoader.java +++ b/src/main/java/bjc/rgens/parser/ConfigLoader.java @@ -70,9 +70,9 @@ public class ConfigLoader { throw new GrammarException("Unknown config line type " + type); } } catch(GrammarException gex) { - System.out.printf("ERROR: Line %s of grammar set %s\n", lno, cfgFile); + System.out.printf("ERROR: Line %s of config set %s\n", lno, cfgFile); - System.err.printf("ERROR: Line %s of grammar set %s\n", lno, cfgFile); + System.err.printf("ERROR: Line %s of config set %s\n", lno, cfgFile); gex.printStackTrace(); System.out.println(); @@ -117,62 +117,7 @@ public class ConfigLoader { switch(tag) { case "template": - { - Path path = Paths.get(ln); - - /* - * Convert from configuration relative path to - * absolute path. - */ - Path convPath = cfgParent.resolve(path.toString()); - - if(Files.isDirectory(convPath)) { - throw new GrammarException("Can't load grammar from directory" + convPath.toString()); - } else { - /* Load template file. */ - try { - long startFileTime = System.nanoTime(); - - BufferedReader fis = Files.newBufferedReader(convPath); - GrammarTemplate template = GrammarTemplate.readTemplate(fis); - template.belongsTo = cfgSet; - - if(template.name == null) { - System.err.printf("\tINFO: Naming unnamed template loaded from %s off config name '%s'\n", - convPath, name); - - template.name = name; - } - - fis.close(); - - long endFileTime = System.nanoTime(); - - long fileTime = endFileTime - startFileTime; - - System.err.printf("\tPERF: Read template %s (from %s) in %d ns (%f s)\n", - template.name, convPath, fileTime, fileTime / 1000000000.0); - - /* Add grammar to the set. */ - cfgSet.templates.put(name, template); - - /* - * @NOTE - * - * Do we need to do this - * for templates? - * - * Mark where the - * template came - * from. - */ - //set.loadedFrom.put(name, path.toString()); - } catch (GrammarException gex) { - String msg = String.format("Error loading file '%s'", path); - throw new GrammarException(msg, gex); - } - } - } + loadTemplate(name, ln, cfgSet, set, cfgParent); break; case "subset": { @@ -181,58 +126,117 @@ public class ConfigLoader { } case "gram": case "grammar": - { - Path path = Paths.get(ln); - - /* - * Convert from configuration relative path to - * absolute path. - */ - Path convPath = cfgParent.resolve(path.toString()); - - if(Files.isDirectory(convPath)) { - throw new GrammarException("Can't load grammar from directory" + convPath.toString()); - } else { - /* Load grammar file. */ - try { - long startFileTime = System.nanoTime(); - - BufferedReader fis = Files.newBufferedReader(convPath); - RGrammar gram = RGrammarParser.readGrammar(fis); - if(gram.name == null) { - System.err.printf("\tINFO: Naming unnamed grammar loaded from %s off config name '%s'\n", - convPath, name); - - gram.name = name; - } - - fis.close(); - - long endFileTime = System.nanoTime(); - - long fileTime = endFileTime - startFileTime; - - System.err.printf("\tPERF: Read grammar %s (from %s) in %d ns (%f s)\n", - gram.name, convPath, fileTime, fileTime / 1000000000.0); - - /* Add grammar to the set. */ - set.addGrammar(name, gram); - - /* - * Mark where the grammar came - * from. - */ - set.loadedFrom.put(name, path.toString()); - } catch (GrammarException gex) { - String msg = String.format("Error loading file '%s'", path); - throw new GrammarException(msg, gex); - } - } - } + loadGrammar(name, ln, cfgSet, set, cfgParent); break; default: String msg = String.format("Unrecognized tag type '%s'", tag); throw new GrammarException(msg); } } + + private static void loadTemplate(String name, String ln, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException { + Path path = Paths.get(ln); + + /* + * Convert from configuration relative path to + * absolute path. + */ + Path convPath = cfgParent.resolve(path.toString()); + + if(Files.isDirectory(convPath)) { + throw new GrammarException("Can't load grammar from directory" + convPath.toString()); + } else { + /* Load template file. */ + try { + long startFileTime = System.nanoTime(); + + BufferedReader fis = Files.newBufferedReader(convPath); + GrammarTemplate template = GrammarTemplate.readTemplate(fis); + template.belongsTo = cfgSet; + + if(template.name == null) { + System.err.printf("\tINFO: Naming unnamed template loaded from %s off config name '%s'\n", + convPath, name); + + template.name = name; + } + + fis.close(); + + long endFileTime = System.nanoTime(); + + long fileTime = endFileTime - startFileTime; + + System.err.printf("\tPERF: Read template %s (from %s) in %d ns (%f s)\n", + template.name, convPath, fileTime, fileTime / 1000000000.0); + + /* Add grammar to the set. */ + cfgSet.templates.put(name, template); + + /* + * @NOTE + * + * Do we need to do this + * for templates? + * + * Mark where the + * template came + * from. + */ + //set.loadedFrom.put(name, path.toString()); + } catch (GrammarException gex) { + String msg = String.format("Error loading template file '%s'", path); + throw new GrammarException(msg, gex); + } + } + } + + private static void loadGrammar(String name, String ln, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException { + Path path = Paths.get(ln); + + /* + * Convert from configuration relative path to + * absolute path. + */ + Path convPath = cfgParent.resolve(path.toString()); + + if(Files.isDirectory(convPath)) { + throw new GrammarException("Can't load grammar from directory" + convPath.toString()); + } else { + /* Load grammar file. */ + try { + long startFileTime = System.nanoTime(); + + BufferedReader fis = Files.newBufferedReader(convPath); + RGrammar gram = RGrammarParser.readGrammar(fis); + if(gram.name == null) { + System.err.printf("\tINFO: Naming unnamed grammar loaded from %s off config name '%s'\n", + convPath, name); + + gram.name = name; + } + + fis.close(); + + long endFileTime = System.nanoTime(); + + long fileTime = endFileTime - startFileTime; + + System.err.printf("\tPERF: Read grammar %s (from %s) in %d ns (%f s)\n", + gram.name, convPath, fileTime, fileTime / 1000000000.0); + + /* Add grammar to the set. */ + set.addGrammar(name, gram); + + /* + * Mark where the grammar came + * from. + */ + set.loadedFrom.put(name, path.toString()); + } catch (GrammarException gex) { + String msg = String.format("Error loading template '%s'", path); + throw new GrammarException(msg, gex); + } + } + } } diff --git a/src/main/java/bjc/rgens/parser/FlatRuleCase.java b/src/main/java/bjc/rgens/parser/FlatRuleCase.java index 58f3d54..4bbd1cc 100644 --- a/src/main/java/bjc/rgens/parser/FlatRuleCase.java +++ b/src/main/java/bjc/rgens/parser/FlatRuleCase.java @@ -6,7 +6,7 @@ import bjc.rgens.parser.elements.CaseElement; public class FlatRuleCase extends RuleCase { public FlatRuleCase(IList elms) { - super(CaseType.SPACEFLATTEN, elms); + super(elms); } @Override @@ -15,5 +15,9 @@ public class FlatRuleCase extends RuleCase { elm.generate(state); } } + + public FlatRuleCase withElements(IList elms) { + return new FlatRuleCase(elms); + } } diff --git a/src/main/java/bjc/rgens/parser/NormalRuleCase.java b/src/main/java/bjc/rgens/parser/NormalRuleCase.java index 19fa8af..c8891a2 100644 --- a/src/main/java/bjc/rgens/parser/NormalRuleCase.java +++ b/src/main/java/bjc/rgens/parser/NormalRuleCase.java @@ -6,7 +6,7 @@ import bjc.rgens.parser.elements.CaseElement; public class NormalRuleCase extends RuleCase { public NormalRuleCase(IList elms) { - super(CaseType.NORMAL, elms); + super(elms); } @Override @@ -14,9 +14,13 @@ public class NormalRuleCase extends RuleCase { for(CaseElement elm : elementList) { elm.generate(state); - if(elm.type.spacing) { + if(elm.spacing) { state.contents.append(" "); } } } + + public NormalRuleCase withElements(IList elms) { + return new NormalRuleCase(elms); + } } diff --git a/src/main/java/bjc/rgens/parser/RGrammar.java b/src/main/java/bjc/rgens/parser/RGrammar.java index d523479..e537781 100755 --- a/src/main/java/bjc/rgens/parser/RGrammar.java +++ b/src/main/java/bjc/rgens/parser/RGrammar.java @@ -10,8 +10,11 @@ import bjc.rgens.parser.elements.RangeCaseElement; import bjc.rgens.parser.elements.RuleCaseElement; import bjc.rgens.parser.elements.VariableCaseElement; +import java.util.Arrays; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; @@ -25,6 +28,8 @@ import edu.gatech.gtri.bktree.BkTreeSearcher.Match; import edu.gatech.gtri.bktree.Metric; import edu.gatech.gtri.bktree.MutableBkTree; +import static bjc.utils.data.IPair.pair; + /** * Represents a randomized grammar. * @@ -35,6 +40,11 @@ public class RGrammar { public String name; + public List> postprocs; + + private static final List> builtinPostprocs; + public boolean useBuiltinPostprocs = true; + /* The max distance between possible alternate rules. */ private static final int MAX_DISTANCE = 6; @@ -67,6 +77,43 @@ public class RGrammar { /* The tree to use for finding rule suggestions. */ private BkTreeSearcher ruleSearcher; + static { + /* Collapse duplicate spaces */ + IPair collapseDupSpaces = pair("\\s+", " "); + + /* Built-in post-processing steps */ + builtinPostprocs = Arrays.asList( + collapseDupSpaces, + + /* + * Remove extraneous spaces around punctuation + * marks, forced by the way the language syntax + * works. + * + * This can be done in grammars, but it is quite + * tedious to do so. + */ + + + /* Handle 's */ + pair(" 's ", "'s "), + /* Handle opening/closing punctuation. */ + pair("([(\\[]) ", " $1"), + pair(" ([)\\]'\"])", "$1 "), + /* Remove spaces around series of opening/closing punctuation. */ + pair("([(\\[])\\s+([(\\[])", "$1$2"), + pair("([)\\]])\\s+([)\\]])", "$1$2"), + /* Handle inter-word punctuation. */ + pair(" ([,:.!])", "$1 "), + /* Handle intra-word punctuation. */ + pair("\\s?([-/])\\s?", "$1"), + + collapseDupSpaces, + + /* Replace this once it is no longer needed. */ + pair("\\s(ish|burg|ton|ville|opolis|field|boro|dale)", "$1") + ); + } /** * Create a new randomized grammar using the specified set of rules. * @@ -79,6 +126,8 @@ public class RGrammar { for(Rule rl : ruls.values()) { rl.belongsTo = this; } + + postprocs = new ArrayList<>(); } /** @@ -150,6 +199,10 @@ public class RGrammar { * The generation state. */ public String generate(String startRule, GenerationState state) { + return generate(startRule, state, true); + } + + public String generate(String startRule, GenerationState state, boolean doPostprocess) { String fromRule = startRule; if (startRule == null) { @@ -175,53 +228,27 @@ public class RGrammar { rl.generate(state); - /* - * @NOTE - * - * :Postprocessing - * - * Do we want to perform this post-processing here, or elsewhere? - */ - return postprocessRes(state.contents.toString()); - } - - private String postprocessRes(String strang) { - String body = strang.replaceAll("\\s+", " "); + String body = state.contents.toString(); - /* - * Remove extraneous spaces around punctutation marks. - * - * This can be done in the grammars, but it is very tedious to do so. - */ - - /* Handle 's */ - body = body.replaceAll(" 's ", "'s "); - - /* Handle opening/closing punctuation. */ - body = body.replaceAll("([(\\[]) ", " $1"); - body = body.replaceAll(" ([)\\]'\"])", "$1 "); - - /* Remove spaces around series of opening/closing punctuation. */ - body = body.replaceAll("([(\\[])\\s+([(\\[])", "$1$2"); - body = body.replaceAll("([)\\]])\\s+([)\\]])", "$1$2"); + if(doPostprocess) { + body = postprocessRes(body); + } - /* Handle inter-word punctuation. */ - body = body.replaceAll(" ([,:.!])", "$1 "); + return body; + } - /* Handle intra-word punctuation. */ - body = body.replaceAll("\\s?([-/])\\s?", "$1"); + private String postprocessRes(String strang) { + String body = strang; - /* - * Collapse duplicate spaces. - */ - body = body.replaceAll("\\s+", " "); + if(useBuiltinPostprocs) { + for(IPair par : builtinPostprocs) { + body = body.replaceAll(par.getLeft(), par.getRight()); + } + } - /* - * @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"); + for(IPair par : postprocs) { + body = body.replaceAll(par.getLeft(), par.getRight()); + } return body.trim(); } diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index 453be05..8f0a2d1 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -9,8 +9,6 @@ import bjc.utils.funcdata.IList; import bjc.utils.funcutils.ListUtils; import bjc.utils.funcutils.SetUtils; -import static bjc.rgens.parser.RuleCase.CaseType.*; - import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -166,10 +164,12 @@ public class RGrammarBuilder { IList> caseList = rules.get(ruleName).getCases(); for (IPair ruleCase : caseList) { + RuleCase cas = ruleCase.getRight(); + for(List suffixList : suffixLists) { FunctionalList newCase = new FunctionalList<>(); - for(CaseElement elm : ruleCase.getRight().getElements()) { + for(CaseElement elm : cas.elementList) { newCase.add(elm); } @@ -177,13 +177,7 @@ public class RGrammarBuilder { newCase.add(element); } - /* - * @NOTE :AffixCasing - * - * Is this correct, or should we be mirroring the - * existing case type? - */ - newCases.add(new Pair<>(ruleCase.getLeft(), new NormalRuleCase(newCase))); + newCases.add(new Pair<>(ruleCase.getLeft(), cas.withElements(newCase))); } } @@ -228,6 +222,8 @@ public class RGrammarBuilder { IList> caseList = rules.get(ruleName).getCases(); for (IPair ruleCase : caseList) { + RuleCase cas = ruleCase.getRight(); + for(List prefixList : prefixLists) { FunctionalList newCase = new FunctionalList<>(); @@ -235,17 +231,11 @@ public class RGrammarBuilder { newCase.add(elm); } - for(CaseElement elm : ruleCase.getRight().getElements()) { + for(CaseElement elm :cas.elementList) { newCase.add(elm); } - /* - * @NOTE :AffixCasing - * - * Is this correct, or should we be mirroring the - * existing case type? - */ - newCases.add(new Pair<>(ruleCase.getLeft(), new NormalRuleCase(newCase))); + newCases.add(new Pair<>(ruleCase.getLeft(), cas.withElements(newCase))); } } @@ -269,7 +259,7 @@ public class RGrammarBuilder { IList> newCaseList = new FunctionalList<>(); for(IPair cse : caseList) { - newCaseList.add(new Pair<>(cse.getLeft(), new FlatRuleCase(cse.getRight().getElements()))); + newCaseList.add(new Pair<>(cse.getLeft(), new FlatRuleCase(cse.getRight().elementList))); } System.err.printf("\t\tTRACE: Despacing %d cases of rule %s\n", caseList.getSize(), ruleName); diff --git a/src/main/java/bjc/rgens/parser/RGrammarFormatter.java b/src/main/java/bjc/rgens/parser/RGrammarFormatter.java index 7e058b0..c571cb7 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarFormatter.java +++ b/src/main/java/bjc/rgens/parser/RGrammarFormatter.java @@ -89,7 +89,7 @@ public class RGrammarFormatter { /* Format a case. */ private static void processCase(RuleCase cse, StringBuilder sb) { /* Process each element, adding a space. */ - for (CaseElement element : cse.getElements()) { + for (CaseElement element : cse.elementList) { sb.append(element.toString()); sb.append(" "); } diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index c807f5f..a869179 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -1,7 +1,6 @@ package bjc.rgens.parser; -import bjc.rgens.parser.elements.CaseElement; -import bjc.rgens.parser.elements.SerialCaseElement; +import bjc.rgens.parser.elements.*; import bjc.utils.data.IPair; import bjc.utils.data.Pair; @@ -475,7 +474,9 @@ public class RGrammarParser { int serialLower = -1; int serialUpper = -1; + int chance = -1; boolean doSerial = false; + boolean doChance = false; for (String csepart : cses) { String partToAdd = csepart.trim(); @@ -493,6 +494,34 @@ public class RGrammarParser { serialUpper = Integer.parseInt(partToAdd.substring(partToAdd.lastIndexOf(".") + 1, partToAdd.length() - 1)); doSerial = true; + } else if(partToAdd.matches("\\<\\?\\d+\\>")) { + chance = Integer.parseInt(partToAdd.substring(2, partToAdd.length() - 1)); + + doChance = true; + } else if (partToAdd.matches("\\<\\<\\>")) { + CaseElement elm = caseParts.popLast(); + + if(repCount == 0) { + /* Skip no-reps */ + } else { + if(doChance) { + elm = new ChanceCaseElement(elm, chance); + + doChance = false; + } + + if(doSerial) { + elm = new SerialCaseElement(elm, serialLower, serialUpper); + + doSerial = false; + } + + for(int i = 1; i <= repCount; i++) { + caseParts.add(elm); + } + + repCount = 1; + } } else if(partToAdd.matches("\\<[^\\>]+\\>")) { throw new GrammarException("Unknown parser meta-rule " + partToAdd); } else { @@ -500,14 +529,23 @@ public class RGrammarParser { if(repCount == 0) { /* Skip no-reps */ - } else if(doSerial) { - caseParts.add(new SerialCaseElement(elm, serialLower, serialUpper)); - - doSerial = false; } else { + if(doChance) { + elm = new ChanceCaseElement(elm, chance); + + doChance = false; + } + + if(doSerial) { + elm = new SerialCaseElement(elm, serialLower, serialUpper); + + doSerial = false; + } + for(int i = 1; i <= repCount; i++) { caseParts.add(elm); } + } repCount = 1; diff --git a/src/main/java/bjc/rgens/parser/RegexRuleCase.java b/src/main/java/bjc/rgens/parser/RegexRuleCase.java index 3a8a8ad..3c57489 100755 --- a/src/main/java/bjc/rgens/parser/RegexRuleCase.java +++ b/src/main/java/bjc/rgens/parser/RegexRuleCase.java @@ -6,31 +6,22 @@ import bjc.utils.funcdata.IList; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +/* + * @TODO + * + * Actually implement this + */ public class RegexRuleCase extends RuleCase { - private Pattern patt; + public RegexRuleCase(IList elements) { + super(elements); - public RegexRuleCase(IList elements, String pattern) { - super(CaseType.REGEX, elements); - - elementList = elements; - - try { - patt = Pattern.compile(pattern); - } catch (PatternSyntaxException psex) { - IllegalArgumentException iaex = - new IllegalArgumentException("This type requires a valid regular expression parameter"); - - iaex.initCause(psex); - - throw iaex; - } - } - - public Pattern getPattern() { - return patt; } public void generate(GenerationState state) { } + + public RegexRuleCase withElements(IList elements) { + return new RegexRuleCase(elements); + } } diff --git a/src/main/java/bjc/rgens/parser/RuleCase.java b/src/main/java/bjc/rgens/parser/RuleCase.java index 369c588..33aea0c 100755 --- a/src/main/java/bjc/rgens/parser/RuleCase.java +++ b/src/main/java/bjc/rgens/parser/RuleCase.java @@ -20,91 +20,34 @@ public abstract class RuleCase { private static int nextSerial = 0; - /** - * The possible types of a case. - * - * @author EVE - */ - public static enum CaseType { - /** A normal case, composed from a list of elements. */ - NORMAL, - /** A case that doesn't insert spaces. */ - SPACEFLATTEN, - /** A case that applies a regex after generation. */ - REGEX; - } - - /** The type of this case. */ - public final CaseType type; - public Rule belongsTo; - /** - * The list of element values for this case. - * - *

Used For

- *
- *
NORMAL, SPACEFLATTEN
- *
Used as the list of elementList the rule is composed of.
- *
- */ - protected IList elementList; - - protected RuleCase(CaseType typ) { - serial = nextSerial; - - type = typ; - - nextSerial += 1; - } + public IList elementList; /** * Create a new case of the specified type that takes a element list * parameter. * - * @param typ - * The type of case to create. - * * @param elements * The element list parameter of the case. * - * @throws IllegalArgumentException - * If this type doesn't take a element list parameter. */ - public RuleCase(CaseType typ, IList elements) { - this(typ); - - switch (typ) { - case NORMAL: - case SPACEFLATTEN: - break; - case REGEX: - throw new IllegalArgumentException("This type requires an element list and a pattern"); - default: - throw new IllegalArgumentException("This type doesn't have a element list parameter"); - } - + protected RuleCase(IList elements) { elementList = elements; + + serial = nextSerial; + nextSerial += 1; } public abstract void generate(GenerationState state); - /** - * Get the element list value of this type. - * - * @return - * The element list value of this case, or null if this type - * doesn't have one. - */ - public IList getElements() { - return elementList; - } + public abstract RuleCase withElements(IList elements); public String toString() { if(debugName != null) { - return String.format("Case %d (%s) of %s", serial, debugName, belongsTo); + return String.format("Case %s (#%d) of %s", debugName, serial, belongsTo); } else { - return String.format("Case %d (%s-%d) of %s", serial, belongsTo, serial, belongsTo); + return String.format("Case #%d of %s", serial, belongsTo, serial, belongsTo); } } diff --git a/src/main/java/bjc/rgens/parser/elements/CaseElement.java b/src/main/java/bjc/rgens/parser/elements/CaseElement.java index 68f5368..d799dbe 100755 --- a/src/main/java/bjc/rgens/parser/elements/CaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/CaseElement.java @@ -1,5 +1,7 @@ package bjc.rgens.parser.elements; +import bjc.utils.funcutils.StringUtils; + import bjc.rgens.parser.GenerationState; import bjc.rgens.parser.GrammarException; @@ -32,7 +34,11 @@ public abstract class CaseElement { } /** The type of this element. */ - public final ElementType type; + public boolean spacing; + + protected CaseElement() { + this(true); + } /** * Create a new case element. @@ -40,13 +46,8 @@ public abstract class CaseElement { * @param typ * The type of this element. */ - protected CaseElement(ElementType typ) { - type = typ; - } - - @Override - public String toString() { - return String.format("Unknown type '%s'", type); + protected CaseElement(boolean spacing) { + this.spacing = spacing; } /** @@ -70,15 +71,13 @@ public abstract class CaseElement { throw new NullPointerException("Case part cannot be null"); } - if (csepart.matches("\\{[^}]+\\}")) { + if (csepart.matches("\\{\\S+\\}")) { /* * Handle special case elements. * */ String specialBody = csepart.substring(1, csepart.length() - 1); - //System.out.printf("\t\tTRACE: special body is '%s'\n", specialBody); - if (specialBody.matches("\\S+:=\\S+")) { String[] parts = specialBody.split(":="); if(parts.length != 2) { @@ -99,7 +98,7 @@ public abstract class CaseElement { } else { throw new IllegalArgumentException(String.format("Unknown special case part '%s'", specialBody)); } - } else if (csepart.matches("\\[[^\\]]+\\]")) { + } else if (csepart.matches("\\[\\S+\\]")) { String rawCase = csepart.substring(1, csepart.length() - 1); if (rawCase.matches("\\d+\\.\\.\\d+")) { @@ -107,23 +106,17 @@ public abstract class CaseElement { int secondNum = Integer.parseInt(rawCase.substring(rawCase.lastIndexOf('.') + 1)); return new RangeCaseElement(firstNum, secondNum); - } else if(rawCase.contains("|")) { - String[] elms = rawCase.split("\\|"); + } else if(rawCase.contains("||")) { + String[] elms = StringUtils.levelSplit(rawCase, "||").toArray(new String[0]); + //String[] elms = rawCase.split("\\|\\|"); - System.err.printf("\t\tTRACE: Split inline cases %s to ", rawCase); - for(String elm : elms) { - System.err.printf("%s, ", elm); - } - System.err.println(); + return new InlineRuleCaseElement(elms); + } else if(rawCase.contains("|")) { + System.err.println("\t\tWARN: Inline rule using | found, they use || now"); + String[] elms = StringUtils.levelSplit(rawCase, "|").toArray(new String[0]); return new InlineRuleCaseElement(elms); } else if(csepart.contains("$")) { - /* - * @NOTE - * - * Once the rule element execution has been refactored, - * pass rawCase instead. - */ if(csepart.contains("-")) { return new DependantRuleReference(csepart); } @@ -135,7 +128,7 @@ public abstract class CaseElement { } else { return new NormalRuleReference(csepart); } - } else if(csepart.startsWith("%")) { + } else if(csepart.startsWith("%") && !csepart.equals("%")) { String rName = String.format("[%s]", csepart.substring(1)); System.err.printf("\t\tTRACE: short ref to %s (%s)\n", rName, csepart); @@ -150,7 +143,7 @@ public abstract class CaseElement { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + (spacing ? 0 : 2); return result; } @@ -163,7 +156,7 @@ public abstract class CaseElement { if (getClass() != obj.getClass()) return false; CaseElement other = (CaseElement) obj; - if (type != other.type) + if (spacing != other.spacing) return false; return true; } diff --git a/src/main/java/bjc/rgens/parser/elements/ChanceCaseElement.java b/src/main/java/bjc/rgens/parser/elements/ChanceCaseElement.java new file mode 100644 index 0000000..483a103 --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/ChanceCaseElement.java @@ -0,0 +1,20 @@ +package bjc.rgens.parser.elements; + +import bjc.rgens.parser.GenerationState; + +public class ChanceCaseElement extends CaseElement { + public final CaseElement elm; + + public int chance; + + public ChanceCaseElement(CaseElement elm, int chance) { + super(elm.spacing); + + this.elm = elm; + this.chance = chance; + } + + public void generate(GenerationState state) { + if(state.rnd.nextInt(chance) == 0) elm.generate(state); + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java index 3972e7a..198b0db 100755 --- a/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/ExpVariableCaseElement.java @@ -23,16 +23,12 @@ public class ExpVariableCaseElement extends VariableCaseElement { if(rl != null) { RGrammar destGrammar = rl.belongsTo; 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. + /* + * Don't post-process the string, we should only do that + * once. */ + String res = destGrammar.generate(varDef, state, false); + newState.contents = new StringBuilder(res); } else { String msg = String.format("No rule '%s' defined", varDef); diff --git a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java index 6cb0ce3..ea22bb4 100644 --- a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java @@ -3,23 +3,38 @@ package bjc.rgens.parser.elements; import bjc.rgens.parser.GenerationState; import bjc.rgens.parser.RGrammarParser; +import bjc.utils.data.IPair; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; +import bjc.utils.gen.WeightedRandom; public class InlineRuleCaseElement extends CaseElement { - public final IList elements; + public final WeightedRandom elements; - public InlineRuleCaseElement(String... elements) { - this(RGrammarParser.parseElementString(elements).getLeft()); - } + public InlineRuleCaseElement(String... parts) { + super(true); + + this.elements = new WeightedRandom<>(); + + for(String part : parts) { + String[] partArr; + + if(part.contains("|")) { + partArr = part.split("\\|"); + } else { + partArr = new String[] {part}; + } - public InlineRuleCaseElement(IList elements) { - super(ElementType.RULEREF); + IPair, Integer> par = RGrammarParser.parseElementString(partArr); + int prob = par.getRight(); - this.elements = elements; + for(CaseElement elm :par.getLeft()) { + elements.addProbability(prob, elm); + } + } } public void generate(GenerationState state) { - elements.randItem(state.rnd::nextInt).generate(state); + elements.generateValue(state.rnd).generate(state); } } diff --git a/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java b/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java index cf8f161..e877dd1 100755 --- a/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/RangeCaseElement.java @@ -7,7 +7,7 @@ public class RangeCaseElement extends CaseElement { public final int end; public RangeCaseElement(int beg, int en) { - super(ElementType.RANGE); + super(true); begin = beg; end = en; diff --git a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java index e0c847a..6aa50fc 100755 --- a/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/RuleCaseElement.java @@ -39,16 +39,11 @@ public abstract class RuleCaseElement extends StringCaseElement { if(rl != null) { RGrammar destGrammar = rl.belongsTo; newState.swapGrammar(destGrammar); - String res = destGrammar.generate(actName, newState); - - /* - * @NOTE - * - * :Postprocessing - * - * This is because generate() returns a processed - * string, but modifies the passed in StringBuilder. + /* + * Don't postprocess the string, we should only do that + * once. */ + String res = destGrammar.generate(actName, newState, false); newState.contents = new StringBuilder(res); } else { /* diff --git a/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java b/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java index 594595e..348cfbb 100644 --- a/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java @@ -9,7 +9,7 @@ public class SerialCaseElement extends CaseElement { public final int upper; public SerialCaseElement(CaseElement rep, int lower, int upper) { - super(rep.type); + super(rep.spacing); this.rep = rep; @@ -23,7 +23,7 @@ public class SerialCaseElement extends CaseElement { for(int i = 0; i < num; i++) { rep.generate(state); - if(rep.type.spacing) + if(rep.spacing) state.contents.append(" "); } } diff --git a/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java b/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java index 61ad48a..57e2cc7 100755 --- a/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/StringCaseElement.java @@ -4,7 +4,7 @@ public abstract class StringCaseElement extends CaseElement { public final String val; protected StringCaseElement(String vl, boolean isLiteral) { - super(isLiteral ? ElementType.LITERAL : ElementType.RULEREF); + super(true); val = vl; } diff --git a/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java b/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java index a2c15a7..63701e0 100755 --- a/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/VariableCaseElement.java @@ -21,7 +21,7 @@ public abstract class VariableCaseElement extends CaseElement { public final VariableType varType; public VariableCaseElement(String name, String def, VariableType varType) { - super(ElementType.VARIABLE); + super(false); varName = name; varDef = def; @@ -60,15 +60,6 @@ public abstract class VariableCaseElement extends CaseElement { return true; } - @Override - public String toString() { - if (type == ElementType.VARIABLE) { - return String.format("{$%s:=%s}", varName, varDef); - } else { - return String.format("{$%s=%s}", varName, varDef); - } - } - public static CaseElement parseVariable(String varName, String varDef, boolean colon) { if(varName.startsWith("$")) { // Handle normal/expanding variable definitions diff --git a/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java new file mode 100644 index 0000000..7a8910c --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java @@ -0,0 +1,35 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.GrammarException; +import bjc.rgens.parser.Rule; + +public class ARefVariableElement extends VariableElement { + public String value; + + private boolean forbidSpaces; + + public ARefVariableElement(boolean forbidSpaces, String val) { + value = val; + } + + public void generate(GenerationState state) { + if(!state.rlVars.containsKey(value)) { + throw new GrammarException("No rule variable named " + value); + } + + Rule rl = state.rlVars.get(value); + + GenerationState newState = state.newBuf(); + + rl.generate(newState); + + String res = newState.contents.toString(); + + if(forbidSpaces && res.contains(" ")) { + throw new GrammarException("Spaces not allowed in this context (rule-var %s)"); + } + + state.contents.append(res); + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/vars/LiteralVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/LiteralVariableElement.java new file mode 100644 index 0000000..080f849 --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/LiteralVariableElement.java @@ -0,0 +1,15 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; + +public class LiteralVariableElement extends VariableElement { + public String val; + + public LiteralVariableElement(String val) { + this.val = val; + } + + public void generate(GenerationState state) { + state.contents.append(val); + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/vars/RRefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/RRefVariableElement.java new file mode 100644 index 0000000..6bf332f --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/RRefVariableElement.java @@ -0,0 +1,35 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.GrammarException; +import bjc.rgens.parser.Rule; + +public class RRefVariableElement extends VariableElement { + public String value; + + private boolean forbidSpaces; + + public RRefVariableElement(boolean forbidSpaces, String val) { + value = val; + } + + public void generate(GenerationState state) { + if(!state.rlVars.containsKey(value)) { + throw new GrammarException("No rule variable named " + value); + } + + Rule rl = state.findRule(value, true); + + GenerationState newState = state.newBuf(); + + rl.generate(newState); + + String res = newState.contents.toString(); + + if(forbidSpaces && res.contains(" ")) { + throw new GrammarException("Spaces not allowed in this context (rule-reference %s)"); + } + + state.contents.append(res); + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/vars/TRefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/TRefVariableElement.java new file mode 100644 index 0000000..b10af87 --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/TRefVariableElement.java @@ -0,0 +1,43 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.GrammarException; +import bjc.rgens.parser.templates.GrammarTemplate; + +/* + * @TODO + * + * finish when template vars are implemented. + */ +public class TRefVariableElement extends VariableElement { + /* + public String value; + + private boolean forbidSpaces; + + public TRefVariableElement(boolean forbidSpaces, String val) { + value = val; + }*/ + + public void generate(GenerationState state) { + /* + if(!state.rlVars.containsKey(val)) { + throw new GrammarException("No rule variable named " + val); + } + + Rule rl = state.rlVars.get(val); + + GenerationState newState = state.newBuf(); + + rl.generate(newState); + + String res = newState.contents.toString(); + + if(forbidSpaces && res.contains(" ")) { + throw new GrammarException("Spaces not allowed in this context (rule-var %s)"); + } + + return res; + */ + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java new file mode 100644 index 0000000..c6921ba --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java @@ -0,0 +1,29 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.GrammarException; + +public class VRefVariableElement extends VariableElement { + public final String nam; + + private final boolean forbidSpaces; + + public VRefVariableElement(boolean forbidSpaces, String nam) { + this.nam = nam; + + this.forbidSpaces = forbidSpaces; + } + + public void generate(GenerationState state) { + if (!state.vars.containsKey(nam)) { + throw new GrammarException(String.format("No variable '%s' defined", nam)); + } + + String strang = state.vars.get(nam); + if(forbidSpaces && strang.contains(" ")) { + throw new GrammarException(String.format("Cannot include variable %s w/ spaces in body in rule name", nam)); + } + + state.contents.append(strang); + } +} diff --git a/src/main/java/bjc/rgens/parser/elements/vars/VariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/VariableElement.java new file mode 100644 index 0000000..7a4260f --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/vars/VariableElement.java @@ -0,0 +1,59 @@ +package bjc.rgens.parser.elements.vars; + +import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.GrammarException; + +import java.util.ArrayList; +import java.util.List; + +public abstract class VariableElement { + public abstract void generate(GenerationState state); + + public static List parseVariableElements(String varElm) { + boolean forbidSpaces = varElm.contains("-"); + + String[] parts; + + if(forbidSpaces) { + parts = varElm.split("(?<=[+-])|(?=[+-])"); + } else { + parts = new String[] { varElm }; + } + + return parseVariableElements(forbidSpaces, parts); + } + + public static List parseVariableElements(boolean forbidSpaces, String... parts) { + List elms = new ArrayList<>(parts.length); + + VariableElement prevElement = null; + + for (String part : parts) { + VariableElement elm = null; + + if(part.startsWith("$")) { + elm = new VRefVariableElement(forbidSpaces, part.substring(1)); + } else if (part.startsWith("@")) { + elm = new ARefVariableElement(forbidSpaces, part.substring(1)); + } else if (part.startsWith("%")) { + elm = new RRefVariableElement(forbidSpaces, part.substring(1)); + } else if (part.startsWith("/")) { + throw new GrammarException("Template variables aren't implemented yet"); + } else { + if(prevElement instanceof LiteralVariableElement) { + /* Aggregate chain literals together */ + ((LiteralVariableElement)prevElement).val += elm; + } else { + elm = new LiteralVariableElement(part); + } + } + + if(elm != null) { + elms.add(elm); + prevElement = elm; + } + } + + return elms; + } +} diff --git a/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java b/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java index a257fbd..fa634a5 100644 --- a/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java +++ b/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java @@ -25,7 +25,7 @@ public class GrammarTemplate { for(TemplateElement element : elements) { element.generate(state); - if(doSpacing && element.type.spacing) + if(doSpacing && element.spacing) state.contents.append("\n"); } } diff --git a/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java b/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java index 19ebbc2..36cdb12 100644 --- a/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java +++ b/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java @@ -6,7 +6,7 @@ public class LiteralTemplateElement extends TemplateElement { public final String val; public LiteralTemplateElement(String val) { - super(ElementType.LITERAL); + super(true); this.val = val; } diff --git a/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java b/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java index 2487c83..154ea68 100644 --- a/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java +++ b/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java @@ -20,7 +20,7 @@ public class LiveTemplateElement extends TemplateElement { public final List> elements; public LiveTemplateElement(String val) { - super(ElementType.TEMPLATE); + super(true); elements = new ArrayList<>(); @@ -52,7 +52,7 @@ public class LiveTemplateElement extends TemplateElement { for(CaseElement elm : elmList) { elm.generate(state); - if(doSpacing && elm.type.spacing) + if(doSpacing && elm.spacing) state.contents.append(" "); } } diff --git a/src/main/java/bjc/rgens/parser/templates/TemplateElement.java b/src/main/java/bjc/rgens/parser/templates/TemplateElement.java index dc123f3..2d0724b 100644 --- a/src/main/java/bjc/rgens/parser/templates/TemplateElement.java +++ b/src/main/java/bjc/rgens/parser/templates/TemplateElement.java @@ -3,24 +3,12 @@ package bjc.rgens.parser.templates; import bjc.rgens.parser.GenerationState; public abstract class TemplateElement { - public static enum ElementType { - LITERAL(true), - TEMPLATE(true), - PRAGMA(false); - - public final boolean spacing; - - private ElementType(boolean spacing) { - this.spacing = spacing; - } - } - - public final ElementType type; + public boolean spacing; public GrammarTemplate belongsTo; - protected TemplateElement(ElementType type) { - this.type = type; + protected TemplateElement(boolean spacing) { + this.spacing = spacing; } public abstract void generate(GenerationState state); -- cgit v1.2.3