diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2018-09-05 16:48:15 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2018-09-05 16:48:15 -0300 |
| commit | e26cdec45a32c2fc3069dea7cddceab5e40a4a8b (patch) | |
| tree | 244b3bc1ed2a1f0c42b78d295127a5b98818567b | |
| parent | 15f0bf5207df703ffbb53c18a147b440dcf43546 (diff) | |
Autovivify vars
Enable autovivifying variables. These will have their definition
automatically ran when they are first referenced.
6 files changed, 126 insertions, 8 deletions
diff --git a/src/main/java/bjc/rgens/parser/GenerationState.java b/src/main/java/bjc/rgens/parser/GenerationState.java index ebe7ca4..b147dfd 100644 --- a/src/main/java/bjc/rgens/parser/GenerationState.java +++ b/src/main/java/bjc/rgens/parser/GenerationState.java @@ -113,28 +113,44 @@ public class GenerationState { public void defineVar(String name, String val) { if(vars.containsKey(name)) - warn("Shadowing variable %s with value %s (old value %s)", name, vars.get(name), val); + warn("Shadowing variable %s with value %s (old value %s)", + name, val, vars.get(name)); + else if (gram.autoVars.containsKey(name)) + warn("Shadowing autovariable %s with value %s (defn. %s)", + name, val, gram.autoVars.get(name)); vars.put(name, val); } public void defineRuleVar(String name, Rule rle) { if(rlVars.containsKey(name)) - warn("Shadowing rule variable %s with value %s (old value %s)", name, rlVars.get(name), rle); + warn("Shadowing rule variable %s with value %s (old value %s)", + name, rlVars.get(name), rle); + else if (gram.autoRlVars.containsKey(name)) + warn("Shadowing rule autovariable %s with value %s (defn. %s)", + name, rle, gram.autoRlVars.get(name)); rlVars.put(name, rle); } - public String findVar(String name, GenerationState stat) { + public String findVar(String name) { if(!vars.containsKey(name)) - throw new GrammarException(String.format("Variable %s not defined", name)); + if(gram.autoVars.containsKey(name)) { + gram.autoVars.get(name).generate(this); + } else { + throw new GrammarException(String.format("Variable %s not defined", name)); + } return vars.get(name); } - public Rule findRuleVar(String name, GenerationState stat) { + public Rule findRuleVar(String name) { if(!rlVars.containsKey(name)) - throw new GrammarException(String.format("Rule variable %s not defined", name)); + if(gram.autoRlVars.containsKey(name)) { + gram.autoRlVars.get(name).generate(this); + } else { + throw new GrammarException(String.format("Rule variable %s not defined", name)); + } return rlVars.get(name); } diff --git a/src/main/java/bjc/rgens/parser/RGrammar.java b/src/main/java/bjc/rgens/parser/RGrammar.java index 5e3142b..03962b2 100755 --- a/src/main/java/bjc/rgens/parser/RGrammar.java +++ b/src/main/java/bjc/rgens/parser/RGrammar.java @@ -67,9 +67,13 @@ public class RGrammar { private Map<String, Rule> importRules; /* The rules exported from this grammar. */ private Set<String> exportRules; + /* The initial rule of this grammar. */ private String initialRule; + public Map<String, CaseElement> autoVars; + public Map<String, CaseElement> autoRlVars; + /* The tree to use for finding rule suggestions. */ private BkTreeSearcher<String> ruleSearcher; @@ -349,4 +353,9 @@ public class RGrammar { public Map<String, Rule> getImportRules() { return importRules; } + + public void setAutoVars(Map<String, CaseElement> aVars, Map<String, CaseElement> aRlVars) { + autoVars = aVars; + autoRlVars = aRlVars; + } } diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index 534d5ec..f1d0938 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -1,6 +1,7 @@ package bjc.rgens.parser; import bjc.rgens.parser.elements.CaseElement; +import bjc.rgens.parser.elements.VariableDefCaseElement; import bjc.utils.data.IPair; import bjc.utils.data.Pair; @@ -34,11 +35,18 @@ public class RGrammarBuilder { /* The current grammar name. */ public String name; + /* Autovivify variables */ + private Map<String, CaseElement> autoVars; + private Map<String, CaseElement> autoRlVars; + /** Create a new randomized grammar builder. */ public RGrammarBuilder() { rules = new HashMap<>(); exportedRules = new HashSet<>(); + + autoVars = new HashMap<>(); + autoRlVars = new HashMap<>(); } /** @@ -93,6 +101,8 @@ public class RGrammarBuilder { grammar.setExportedRules(exportedRules); + grammar.setAutoVars(autoVars, autoRlVars); + return grammar; } @@ -315,6 +325,13 @@ public class RGrammarBuilder { rl.trials = trials; } + public void addAutoVar(String name, CaseElement elm) { + autoVars.put(name, elm); + } + + public void addAutoRlVar(String name, CaseElement elm) { + autoRlVars.put(name, elm); + } /* * @TODO * diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index aae5c4a..ba1bd8d 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -205,6 +205,82 @@ public class RGrammarParser { build.prefixWith(body.substring(0, idx), parseElementString(body.substring(idx + 1)).getLeft()); }); + + /* + * @NOTE 9/4/18 + * + * Right now, we ignore additional elements to autovivify. Not + * sure yet if this is the desired behavior. + * + * As I see it, there are a couple of alternatives: + * + * 1) Continue what we're doing. This is simple, but seems + * somewhat inelegant. + * + * 2) Error if more than one is provided. Even simpler, but also + * seems inelegant. + * + * 3) Parse them independantly. Each element is treated as a + * seperate autovar. Seems simple, but may + * cause issues with mixing rule & nonrule + * variables, as well as naming. + * + * 4) Parse them together. Autovars are stored as cases instead + * of case elements. Also simple, but may have + * some odd corner cases, and I can't think of + * any cases where the additional power would + * be useful. + * + * + * + * + * + * + * As an additional aside, we currently error if we provide + * something that isn't a variable definition. This is because + * we pull the name for the auto-vivify variable from the + * element. If we go with option 4 above, the user will have to + * specify a name for the variable, and we should likely add + * some check when the variable is made live that it actually + * created the variable it said it would. + * + */ + pragmas.put("autovivify", (body, build, level) -> { + doAutoVar(body, build, level, false); + }); + + pragmas.put("autovivify-rule", (body, build, level) -> { + doAutoVar(body, build, level, true); + }); + } + + private static void doAutoVar(String body, RGrammarBuilder build, int level, boolean isRule) { + List<String> bits = StringUtils.levelSplit(body, " "); + + if (bits.size() < 1) { + String msg = "Must specify name of variable and definition to autovivify"; + throw new GrammarException(msg); + } + + String[] bitArr = bits.toArray(new String[0]); + + IList<CaseElement> elmList = parseElementString(bitArr).getLeft(); + CaseElement elm = elmList.first(); + + if (elmList.getSize() > 1) { + warn("Ignoring %d additional elements for autovivify: %s", elmList.getSize(), elmList.tail()); + } + + if (!(elm instanceof VariableDefCaseElement)) { + throw new GrammarException(String.format("Autovivify expression must be a variable defn. (expr. %s)", elm)); + } + + { + String name = ((VariableDefCaseElement)elm).varName; + + if (isRule) build.addAutoRlVar(name, elm); + else build.addAutoVar(name, elm); + } } /** diff --git a/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java index 5e8fe05..99af354 100644 --- a/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java +++ b/src/main/java/bjc/rgens/parser/elements/vars/ARefVariableElement.java @@ -14,7 +14,7 @@ public class ARefVariableElement extends VariableElement { } public void generate(GenerationState state) { - Rule rl = state.findRuleVar(value, state); + Rule rl = state.findRuleVar(value); GenerationState newState = state.newBuf(); diff --git a/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java b/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java index a16503f..1facd38 100644 --- a/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java +++ b/src/main/java/bjc/rgens/parser/elements/vars/VRefVariableElement.java @@ -13,7 +13,7 @@ public class VRefVariableElement extends VariableElement { } public void generate(GenerationState state) { - String strang = state.findVar(nam, state); + String strang = state.findVar(nam); if(forbidSpaces && strang.contains(" ")) { throw new GrammarException(String.format("Cannot include variable %s w/ spaces in body in rule name", nam)); |
