From 67edd098d89aa86cac315f4f52343e4f42923b16 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Mon, 6 Feb 2017 10:26:18 -0500 Subject: Grammar feature extensions Grammars now have support for several addional features. Namely, the following * Regex-rules: You can define a rule as a regex and it will generate strings that match the regex * Range-rules: You can define a rule that will return a random int from a name * Variables: You can define and reference variables for context dependant statements. --- .../java/bjc/RGens/parser/RBGrammarReader.java | 119 ++++++++++++++++++++- .../main/java/bjc/RGens/parser/ReaderState.java | 4 + 2 files changed, 122 insertions(+), 1 deletion(-) (limited to 'RGens/src/main/java/bjc') diff --git a/RGens/src/main/java/bjc/RGens/parser/RBGrammarReader.java b/RGens/src/main/java/bjc/RGens/parser/RBGrammarReader.java index 84fee01..48e21b1 100644 --- a/RGens/src/main/java/bjc/RGens/parser/RBGrammarReader.java +++ b/RGens/src/main/java/bjc/RGens/parser/RBGrammarReader.java @@ -1,10 +1,20 @@ package bjc.RGens.parser; +import com.mifmif.common.regex.Generex; + import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Path; +import java.util.Random; +import java.util.function.BiFunction; +import java.util.function.Predicate; import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.ListUtils; import bjc.utils.gen.WeightedGrammar; import bjc.utils.parserutils.RuleBasedConfigReader; @@ -17,6 +27,8 @@ import bjc.utils.parserutils.RuleBasedConfigReader; public class RBGrammarReader { private static RuleBasedConfigReader reader; + private static Random numgen = new Random(); + static { setupReader(); @@ -80,7 +92,89 @@ public class RBGrammarReader { ReaderState initialState = new ReaderState(inputPath); try (FileInputStream inputStream = new FileInputStream(inputPath.toFile())) { - return reader.fromStream(inputStream, initialState).getGrammar(); + WeightedGrammar gram = reader.fromStream(inputStream, initialState).getGrammar(); + + IMap> vars = new FunctionalMap<>(); + + Predicate specialPredicate = (strang) -> { + if(strang.matches("\\{\\S+\\}") || strang.matches("\\[\\S+\\}")) { + return true; + } + + return false; + }; + + BiFunction, IList> + specialAction = (strang, gramm) -> { + IList retList = new FunctionalList<>(); + + if(strang.matches("\\{\\S+\\}")) { + if(strang.matches("\\{\\S+:=\\S+\\}")) { + String[] varParts = strang.split(":="); + + String varName = varParts[0].substring(1); + String ruleName = varParts[1].substring(0, varParts[1].length()); + + IList varValue = gramm.generateGenericValues( + ruleName, (s) -> s, " "); + + vars.put(varName, varValue); + } else if(strang.matches("\\{\\S+=\\S+\\}")) { + String[] varParts = strang.split("="); + + String varName = varParts[0].substring(1); + String varValue = varParts[1].substring(0, varParts[1].length()); + + vars.put(varName, new FunctionalList<>(varValue)); + } else { + // @FIXME notify the user they did something wrong + retList.add(strang); + } + } else { + if(strang.matches("\\[\\$\\S+\\]")) { + String varName = strang.substring(2, strang.length()); + + retList = vars.get(varName); + } else if(strang.matches("\\[\\$\\S+\\-\\S+\\]")) { + String[] varParts = strang.substring(1, strang.length()).split("-"); + + StringBuilder actualName = new StringBuilder("["); + + for(String varPart : varParts) { + if(varPart.startsWith("$")) { + IList varName = vars.get(varPart.substring(1)); + + if(varName.getSize() != 1) { + // @FIXME notify the user they did something wrong + } + + actualName.append(varName.first() + "-"); + } else { + actualName.append(varPart + "-"); + } + } + + // Trim trailing - + actualName.deleteCharAt(actualName.length() - 1); + actualName.append("]"); + + retList = gram.generateGenericValues(actualName.toString(), (s) -> s, " "); + } else { + // @FIXME notify the user they did something wrong + retList.add(strang); + } + } + + return retList; + }; + + Runnable specialReset = () -> { + vars.clear(); + }; + + gram.configureSpecial(specialPredicate, specialAction, specialReset); + + return gram; } catch (IOException ioex) { throw ioex; } @@ -118,6 +212,29 @@ public class RBGrammarReader { reader.addPragma("prefix-with", RBGrammarReader::prefixRule); reader.addPragma("suffix-with", RBGrammarReader::suffixRule); + + reader.addPragma("regex-rule", (tokenizer, state) -> { + String ruleName = tokenizer.nextToken(); + + IList regx = tokenizer.toList(); + Generex regex = new Generex(ListUtils.collapseTokens(regx)); + + state.addSpecialRule(ruleName, () -> { + return new FunctionalList<>(regex.random().split(" ")); + }); + }); + + reader.addPragma("range-rule", (tokenizer, state) -> { + String ruleName = tokenizer.nextToken(); + + int start = Integer.parseInt(tokenizer.nextToken()); + int end = Integer.parseInt(tokenizer.nextToken()); + + state.addSpecialRule(ruleName, () -> { + return new FunctionalList<>(Integer.toString( + numgen.nextInt((end - start) + 1) + start)); + }); + }); } private static void loadSubGrammar(FunctionalStringTokenizer stk, ReaderState rs) { diff --git a/RGens/src/main/java/bjc/RGens/parser/ReaderState.java b/RGens/src/main/java/bjc/RGens/parser/ReaderState.java index b455844..8e846cf 100644 --- a/RGens/src/main/java/bjc/RGens/parser/ReaderState.java +++ b/RGens/src/main/java/bjc/RGens/parser/ReaderState.java @@ -3,6 +3,7 @@ package bjc.RGens.parser; import java.io.IOException; import java.nio.file.Path; import java.util.Stack; +import java.util.function.Supplier; import bjc.utils.funcdata.IList; import bjc.utils.gen.WeightedGrammar; @@ -124,6 +125,9 @@ public class ReaderState { currentGrammar.addCase(currentRule, ruleProbability, ruleParts); } + public void addSpecialRule(String ruleName, Supplier> cse) { + currentGrammar.addSpecialRule(ruleName, cse); + } /** * Edit a subgrammar of the current grammar * -- cgit v1.2.3