diff options
| author | student <student@student-OptiPlex-9020> | 2017-03-17 10:49:27 -0400 |
|---|---|---|
| committer | student <student@student-OptiPlex-9020> | 2017-03-17 10:49:27 -0400 |
| commit | 0ea49dd4a52358f053c9be7138c392b16de05899 (patch) | |
| tree | 802e275aaf279480ee8626136f56bfa1fbab6845 /RGens/src/main/java/bjc/rgens/server | |
| parent | 36cf3a0f0604ef43ce838ff6e9a7fc4e7c299522 (diff) | |
Move things around, and start on new parser.
Diffstat (limited to 'RGens/src/main/java/bjc/rgens/server')
5 files changed, 1294 insertions, 0 deletions
diff --git a/RGens/src/main/java/bjc/rgens/server/CLIArgsParser.java b/RGens/src/main/java/bjc/rgens/server/CLIArgsParser.java new file mode 100644 index 0000000..fa0d399 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/server/CLIArgsParser.java @@ -0,0 +1,141 @@ +package bjc.rgens.server; + +import java.io.File; + +public class CLIArgsParser { + public static void parseArgs(String[] args, GrammarServerEngine eng) { + boolean didTerminalOp = false; + boolean forceInteractive = false; + + // @TODO report error status + boolean didError = false; + + if(args.length < 0) return; + + if(args.length == 1 && args[0].equals("--help")) { + // @TODO show help + } else { + for(int i = 0; i < args.length; i++) { + String arg = args[i]; + + switch(arg) { + case "-lc": + case "--load-config-file": + String configFileName = args[++i]; + + eng.doLoadConfig(configFileName); + break; + case "-lg": + case "--load-grammar-file": + String grammarFileName = args[++i]; + + File grammarFile = new File(grammarFileName); + + String ruleName = grammarFile.getName(); + ruleName = ruleName.substring(0, ruleName.lastIndexOf('.')); + + if(!args[i+1].startsWith("-")) { + ruleName = args[++i]; + } + + eng.doLoadGrammar(ruleName, grammarFileName); + + break; + case "-ll": + case "--load-long-file": + String longRuleFileName = args[++i]; + + File longRuleFile = new File(longRuleFileName); + + String longRuleName = longRuleFile.getName(); + longRuleName = longRuleName.substring(0, longRuleName.lastIndexOf('.')); + + if(!args[i+1].startsWith("-")) { + longRuleName = args[++i]; + } + + eng.doLoadLongRule(longRuleName, longRuleFileName); + + break; + case "-ge": + case "--generate-exported-rule": + String exportedRuleName = args[++i]; + + if(eng.hasExportedRule(exportedRuleName)) { + eng.doGenerateExportedRule(exportedRuleName); + didTerminalOp = true; + } else { + System.out.printf("Error: No exported rule named %s\n", exportedRuleName); + } + break; + case "-gg": + case "--generate-grammar-rule": + String grammarName = args[++i]; + + if(!eng.hasLoadedGrammar(grammarName)) { + System.out.printf("Error: No grammar named %s\n", grammarName); + } else { + String ruleToGenerate = ""; + + if(!args[i+1].startsWith("-")) { + ruleToGenerate = args[++i]; + } else if(eng.hasInitialRule(grammarName)) { + ruleToGenerate = eng.getInitialRule(grammarName); + } else { + System.out.printf("Error: Grammar %s has no initial rule. A " + + "rule must be provided.",grammarName); + } + + eng.doGenerateGrammar(grammarName, ruleToGenerate); + didTerminalOp = true; + } + break; + case "-st": + case "--stress-test": + String thingToTest = args[++i]; + + // @TODO support testing rules from grammars + // as well as a specified number of times + if(thingToTest.equals("*")) { + eng.doStressTest(10000); + } else { + eng.doStressTest(thingToTest, 1000); + } + + didTerminalOp = true; + + break; + case "-d": + case "--debug": + if(eng.debugMode) { + System.out.println("Warning: debug mode is already on. Use -nd or --no-debug" + + " to turn it off"); + } else { + eng.debugMode = true; + } + break; + case "-nd": + case "--no-debug": + if(!eng.debugMode) { + System.out.println("Warning: debug mode is already off. Use -d or --debug" + + " to turn it on"); + } else { + eng.debugMode = false; + } + break; + case "-i": + case "--interactive": + forceInteractive = true; + break; + default: + System.out.println("Error: Unrecognized argument " + arg); + break; + } + } + } + + if(!forceInteractive && didTerminalOp) { + System.exit(didError ? 1 : 0); + } + } +} diff --git a/RGens/src/main/java/bjc/rgens/server/GrammarServer.java b/RGens/src/main/java/bjc/rgens/server/GrammarServer.java new file mode 100644 index 0000000..1a47de2 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/server/GrammarServer.java @@ -0,0 +1,346 @@ +package bjc.rgens.server; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; +import bjc.utils.gen.WeightedGrammar; + +import java.io.File; + +import java.util.Scanner; + +public class GrammarServer { + private Scanner scn; + + private IMap<String, WeightedGrammar<String>> loadedGrammars; + private IMap<String, WeightedGrammar<String>> exportedRules; + + private GrammarServerEngine eng; + + public GrammarServer(Scanner scn) { + this.scn = scn; + + this.loadedGrammars = new FunctionalMap<>(); + this.exportedRules = new FunctionalMap<>(); + + eng = new GrammarServerEngine(loadedGrammars, exportedRules); + + ServerGrammarReader.setExportedRules(exportedRules); + } + + public static void main(String[] args) { + System.out.println("GrammarServer 1.0"); + + Scanner scn = new Scanner(System.in); + + GrammarServer serv = new GrammarServer(scn); + + CLIArgsParser.parseArgs(args, serv.eng); + + System.out.print("Enter a command (m for help): "); + + char comm = scn.nextLine().charAt(0); + + while(comm != 'e') { + switch(comm) { + case 'm': + System.out.println("GrammarServer Commands:"); + System.out.println("\tm: Print command help"); + System.out.println("\te: Exit GrammarServer"); + System.out.println("\tl: Load from file"); + System.out.println("\ts: Show information"); + System.out.println("\tg: Generate text"); + break; + case 'g': + serv.generateMode(); + break; + case 's': + serv.showMode(); + break; + case 'l': + serv.loadMode(); + break; + default: + System.out.println("? Unrecognized Command"); + } + + System.out.print("Enter a command (m for help): "); + + comm = scn.nextLine().charAt(0); + } + + System.out.println("GrammarServer exiting"); + } + + + + private void loadMode() { + System.out.println("Entering Load Mode"); + + System.out.print("(Load Mode) Enter a command (m for help): "); + char comm = scn.nextLine().charAt(0); + + while(comm != 'e') { + switch(comm) { + case 'm': + System.out.println("GrammarServer Load Mode Commands"); + System.out.println("\tm: Show command help"); + System.out.println("\te: Exit Load Mode"); + System.out.println("\tg: Load grammar from a file"); + System.out.println("\tl: Load long rule from a file"); + System.out.println("\tc: Load configuration from a file"); + break; + case 'g': + loadGrammar(); + break; + case 'c': + loadConfig(); + break; + case 'l': + loadLongRule(); + break; + default: + System.out.println("? Unrecognized Command"); + } + + System.out.print("(Load Mode) Enter a command (m for help): "); + comm = scn.nextLine().charAt(0); + } + + System.out.println("Exiting Load Mode"); + } + + private void loadGrammar() { + System.out.print("Enter path to load grammar from: "); + + String grammarPath = scn.nextLine().trim(); + + File grammarFile = new File(grammarPath); + + String grammarName = grammarFile.getName().trim(); + + grammarName = grammarName.substring(0, grammarName.lastIndexOf(".")); + + System.out.printf("Enter grammar name or press enter for" + + " the default (%s): ", grammarName); + + String inputName = scn.nextLine(); + + if(!inputName.equals("")) { + grammarName = inputName; + } + + eng.doLoadGrammar(grammarName, grammarPath); + + return; + } + + private void loadLongRule() { + System.out.print("Enter the file to load a long rule from: "); + + String fileName = scn.nextLine().trim(); + File ruleFile = new File(fileName); + + String tempName = ruleFile.getName(); + tempName = tempName.substring(0, tempName.lastIndexOf('.')); + + System.out.printf("Enter the name for the long rule (default %s): ", tempName); + + String ruleName = scn.nextLine().trim(); + + if(ruleName.equals("")) { + ruleName = tempName; + } + + eng.doLoadLongRule(ruleName, fileName); + } + + private void loadConfig() { + System.out.print("Enter the file to load configuration from: "); + + String fileName = scn.nextLine().trim(); + + eng.doLoadConfig(fileName); + } + + private void showMode() { + System.out.println("Entering Show Mode"); + + System.out.print("(Show Mode) Enter a command (m for help): "); + + char comm = scn.nextLine().charAt(0); + + while(comm != 'e') { + switch(comm) { + case 'm': + System.out.println("GrammarServer Show Mode Commands: "); + System.out.println("\tm: Show command help"); + System.out.println("\tl: Show loaded grammars"); + System.out.println("\tr: Show rules from a grammar"); + System.out.println("\tx: Show exported rules"); + System.out.println("\te: Exit Show Mode"); + break; + case 'r': + showGrammarRules(); + break; + case 'x': + eng.doShowExportedRules(); + break; + case 'l': + eng.doShowLoadedGrammars(); + break; + default: + System.out.println("? Unrecognized command"); + break; + } + + System.out.print("(Show Mode) Enter a command (m for help): "); + + comm = scn.nextLine().charAt(0); + } + + + + System.out.println("Exiting Show Mode"); + } + + private void generateMode() { + System.out.println("Entering Generate Mode"); + + System.out.print("(Generate Mode) Enter a command (m for help): "); + + char comm = scn.nextLine().charAt(0); + + while(comm != 'e') { + switch(comm) { + case 'm': + System.out.println("GrammarServer Generate Mode Commands: "); + System.out.println("\tm: Show command help"); + System.out.println("\tx: Generate from exported rules"); + System.out.println("\tr: Generate from a grammar"); + System.out.println("\te: Exit Generate Mode"); + break; + case 'x': + generateExportedRule(); + break; + case 'r': + generateGrammar(); + break; + default: + System.out.println("? Unrecognized command"); + } + + System.out.print("(Generate Mode) Enter a command (m for help): "); + + comm = scn.nextLine().charAt(0); + } + + System.out.println("Exiting Generate Mode"); + } + + private void generateExportedRule() { + System.out.print("Enter the name of the rule to generate" + + " (l to list, enter to cancel): "); + + String ruleName = scn.nextLine().trim(); + + while(true) { + if(ruleName.equals("")) break; + + if(ruleName.equals("l")) { + eng.doShowExportedRules(); + } else if (exportedRules.containsKey(ruleName)) { + eng.doGenerateExportedRule(ruleName); + + System.out.print("Generate again from this rule? (yes/no) (yes by default): "); + + String resp = scn.nextLine().trim(); + + if(resp.equalsIgnoreCase("yes") || resp.equals("")) { + continue; + } + } else { + System.out.println("? Unrecognized external rule"); + } + + System.out.print("Enter the name of the rule to generate" + + " (l to list, enter to cancel): "); + + ruleName = scn.nextLine().trim(); + } + } + + private void generateGrammar() { + System.out.print("Enter the name of the grammar to generate from (l to list, enter to cancel): "); + + String grammarName = scn.nextLine().trim(); + + while(true) { + if(grammarName.equals("")) break; + + if(grammarName.equals("l")) { + eng.doShowLoadedGrammars(); + } else if(loadedGrammars.containsKey(grammarName)) { + WeightedGrammar<String> currentGram = loadedGrammars.get(grammarName); + + System.out.print("Enter the name of the rule to generate" + + " (l to list, enter to cancel): "); + + String ruleName = scn.nextLine().trim(); + + while(true) { + if(ruleName.equals("")) break; + + if(ruleName.equals("l")) { + eng.doShowGrammarRules(grammarName); + } else if (currentGram.hasRule(ruleName)) { + eng.doGenerateGrammar(currentGram, ruleName); + + System.out.print("Generate again from this rule? (yes/no) (yes by default): "); + + String resp = scn.nextLine().trim(); + + if(resp.equalsIgnoreCase("yes") || resp.equals("")) { + continue; + } + } else { + System.out.println("? Unrecognized grammar rule"); + } + + System.out.print("Enter the name of the rule to generate" + + " (l to list, enter to cancel): "); + + ruleName = scn.nextLine().trim(); + } + + } else { + System.out.println("? Unrecognized grammar name"); + } + + System.out.print("Enter the name of the grammar to generate from " + + "(l to list, enter to cancel): "); + + grammarName = scn.nextLine().trim(); + } + } + + private void showGrammarRules() { + System.out.print("Enter the name of the grammar (l to list): "); + String gramName = scn.nextLine().trim(); + + do { + if(gramName.equals("")) break; + + if(gramName.equals("l")) { + eng.doShowLoadedGrammars(); + } else if (loadedGrammars.containsKey(gramName)) { + eng.doShowGrammarRules(gramName); + break; + } else { + System.out.println("? Unrecognized grammar name"); + } + + System.out.print("Enter the name of the grammar (l to list): "); + gramName = scn.nextLine().trim(); + } while(true); + } +} diff --git a/RGens/src/main/java/bjc/rgens/server/GrammarServerEngine.java b/RGens/src/main/java/bjc/rgens/server/GrammarServerEngine.java new file mode 100644 index 0000000..2f608ec --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/server/GrammarServerEngine.java @@ -0,0 +1,300 @@ +package bjc.rgens.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Scanner; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.ListUtils; +import bjc.utils.gen.WeightedGrammar; + +public class GrammarServerEngine { + private IMap<String, WeightedGrammar<String>> loadedGrammars; + private IMap<String, WeightedGrammar<String>> exportedRules; + + public static boolean debugMode = false; + + public GrammarServerEngine(IMap<String, WeightedGrammar<String>> loadedGrammars, + IMap<String, WeightedGrammar<String>> exportedRules) { + this.loadedGrammars = loadedGrammars; + this.exportedRules = exportedRules; + } + + public String getInitialRule(String grammarName) { + return loadedGrammars.get(grammarName).getInitialRule(); + } + + public boolean hasInitialRule(String grammarName) { + return loadedGrammars.get(grammarName).hasInitialRule(); + } + + public boolean hasExportedRule(String ruleName) { + return exportedRules.containsKey(ruleName); + } + + public boolean hasLoadedGrammar(String grammarName) { + return loadedGrammars.containsKey(grammarName); + } + + public void doLoadConfig(String fileName) { + File inputFile = new File(fileName); + + try(FileInputStream inputStream = new FileInputStream(inputFile)) { + try(Scanner fle = new Scanner(inputStream)) { + while(fle.hasNextLine()) { + String line = fle.nextLine().trim(); + + // Handle comments + if(line.equals("") || + line.startsWith("#") || + line.startsWith("//")) { + continue; + } + + // Handle mixed whitespace in input + line = line.replaceAll("\\s+", " "); + + String path; + String name; + + if(line.lastIndexOf(' ') != -1) { + path = line.substring(0, line.lastIndexOf(' ')); + name = line.substring(line.lastIndexOf(' ') + 1, line.length()); + } else { + path = line; + + File pathFile = new File(path); + String pathName = pathFile.getName(); + + if(pathFile.isDirectory()) { + // Load all the files in the directory recursively + Queue<File> entries = new LinkedList<>(); + + for (File entry : pathFile.listFiles()) { + entries.add(entry); + } + + while(!entries.isEmpty()) { + File entry = entries.poll(); + + String entryPath = entry.getName(); + + if(entry.isHidden()) continue; + if(entry.isDirectory()) { + for (File newEntry : entry.listFiles()) { + entries.add(newEntry); + } + + continue; + } + + name = entryPath.substring(0, entryPath.lastIndexOf('.')); + + doLoadGrammarEntry(entry.toString(), name); + } + + continue; + } + + name = pathName.substring(0, pathName.lastIndexOf('.')); + } + + doLoadGrammarEntry(path, name); + } + } + } catch(IOException ioex) { + System.out.printf("? Error reading configuration from file" + + " (reason: %s)\n", ioex.getMessage()); + } + } + + private void doLoadGrammarEntry(String path, String name) { + if(path.endsWith(".gram")) { + doLoadGrammar(name, path); + } else if(path.endsWith(".template")) { + System.out.println("Error: Templates are not supported yet"); + } else if(path.endsWith(".long")) { + doLoadLongRule(name, path); + } else { + System.out.println("Error: Unknown filetype " + + path.substring(path.lastIndexOf("."), path.length())); + } + } + + public void doLoadLongRule(String ruleName, String ruleFile) { + ruleName = "[" + ruleName + "]"; + + if(debugMode) { + System.out.printf("Loading long rule (named %s) from path %s\n", + ruleName, ruleFile); + } + + try (FileInputStream inputStream = new FileInputStream(ruleFile)) { + try (Scanner fle = new Scanner(inputStream)) { + IList<IList<String>> ruleParts = new FunctionalList<>(); + + while(fle.hasNextLine()) { + ruleParts.add(new FunctionalList<>(fle.nextLine().trim().split(" "))); + } + + WeightedGrammar<String> longGram = new WeightedGrammar<>(); + + longGram.addSpecialRule(ruleName, () -> ruleParts.randItem()); + longGram.setInitialRule(ruleName); + + exportedRules.put(ruleName, longGram); + + if(debugMode) { + System.out.printf("Loaded long rule (named %s) from path %s\n", + ruleName, ruleFile); + } + } + } catch (IOException ioex) { + System.out.printf("Error reading long rule (%s)\n", ioex.getMessage()); + } + } + + public void doLoadGrammar(String grammarName, String grammarPath) { + if(debugMode) { + System.out.printf("Loading grammar (named %s) from path %s\n", + grammarName, grammarPath); + } + + try (FileInputStream inputStream = new FileInputStream(grammarPath)) { + WeightedGrammar<String> newGram = + ServerGrammarReader.fromStream(inputStream).merge((gram, exports) -> { + for(String export : exports.toIterable()) { + if(debugMode) { + System.out.printf("\tLoaded exported rule %s from grammar %s\n", + export, grammarName); + + if(exportedRules.containsKey(export)) { + System.out.printf("\tWarning: Exported rule %s from grammar %s" + + " shadows a pre-existing rule\n", export, grammarName); + } + } + + exportedRules.put(export, gram); + } + + return gram; + }); + + loadedGrammars.put(grammarName, newGram); + } catch (IOException ioex) { + System.out.printf("? Error reading grammar from file" + + " (reason: %s)\n", ioex.getMessage()); + } + + if(debugMode) { + System.out.printf("Loaded grammar (named %s) from path %s\n", + grammarName, grammarPath); + } + } + + public void doGenerateExportedRule(String ruleName) { + String ruleResult = ListUtils.collapseTokens( + exportedRules.get(ruleName) + .generateListValues(ruleName, " ")); + + System.out.println("Generated Result: "); + System.out.println("\t" + ruleResult.replaceAll("\\s+", " ")); + } + + public void doGenerateGrammar(String currentGram, String ruleName) { + doGenerateGrammar(loadedGrammars.get(currentGram), ruleName); + } + + public void doGenerateGrammar(WeightedGrammar<String> currentGram, String ruleName) { + String ruleResult = ListUtils.collapseTokens( + currentGram.generateListValues(ruleName, " ")); + + System.out.println("Generated Result: "); + System.out.println("\t" + ruleResult.replaceAll("\\s+", " ")); + } + + public void doShowExportedRules() { + System.out.printf("Currently exported rules (%d total):\n", + exportedRules.getSize()); + + exportedRules.forEachKey(key -> { + System.out.println("\t" + key); + }); + } + + public void doShowGrammarRules(String gramName) { + WeightedGrammar<String> gram = loadedGrammars.get(gramName); + + IList<String> ruleNames = gram.getRuleNames(); + + System.out.printf("Rules for grammar %s (%d total)\n", + gramName, ruleNames.getSize()); + + ruleNames.forEach(rule -> { + System.out.println("\t" + rule); + }); + } + + public void doShowLoadedGrammars() { + System.out.printf("Currently loaded grammars (%d total):\n", + loadedGrammars.getSize()); + + loadedGrammars.forEachKey(key -> { + System.out.println("\t" + key); + }); + } + + public void doStressTest(int count) { + exportedRules.forEachKey(key -> { + doStressTest(key, count); + }); + } + + public void doStressTest(String ruleName, int count) { + doStressTest(exportedRules.get(ruleName), ruleName, count); + } + + public void doStressTest(WeightedGrammar<String> gram, String ruleName, int count) { + if(debugMode) System.out.println("Stress-testing rule " + ruleName); + + IList<String> res = new FunctionalList<>(); + IList<String> foundTags = new FunctionalList<>(); + + boolean foundBroken = false; + + for(int i = 0; i < count; i++) { + res = gram.generateListValues(ruleName, " "); + + for(String tok : res.toIterable()) { + if(tok.matches("\\[\\S+\\]") && !foundTags.contains(tok)) { + System.out.println("\tWarning: Possible un-expanded rule " + tok + " found" + + " in expansion of " + ruleName); + + doFindRule(tok); + + foundBroken = true; + + foundTags.add(tok); + } + } + } + + if(debugMode) { + if(!foundBroken) System.out.printf("Rule %s succesfully passed stress-testing\n", ruleName); + else System.out.printf("Rule %s failed stress-testing\n", ruleName); + } + } + + private void doFindRule(String ruleName) { + loadedGrammars.forEach((gramName, gram) -> { + if(gram.hasRule(ruleName)) { + System.out.printf("\t\tFound rule %s in grammar %s\n", ruleName, gramName); + } + }); + } +} diff --git a/RGens/src/main/java/bjc/rgens/server/ReaderState.java b/RGens/src/main/java/bjc/rgens/server/ReaderState.java new file mode 100644 index 0000000..3f5021e --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/server/ReaderState.java @@ -0,0 +1,181 @@ +package bjc.rgens.server; + +import java.util.function.Supplier; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.gen.WeightedGrammar; + +/** + * Represents the internal state of reader + * + * @author ben + * + */ +public class ReaderState { + private WeightedGrammar<String> currentGrammar; + private String currentRule; + private boolean isUniform; + + private IList<String> exports; + + /** + * Create a new reader state + */ + public ReaderState() { + currentGrammar = new WeightedGrammar<>(); + + // Grammars start out uniform + isUniform = true; + + exports = new FunctionalList<>(); + } + + public void addExport(String export) { + exports.add(export); + } + + public IList<String> getExports() { + return exports; + } + + /** + * Get the rule names for the current grammar + * + * @return The rule names for the current grammar + */ + public IList<String> getRuleNames() { + return currentGrammar.getRuleNames(); + } + + /** + * Check if this reader is currently in uniform mode + * + * @return Whether this grammar is in uniform mode + */ + public boolean isUniform() { + return isUniform; + } + + /** + * Set the current grammar to be the specified one + * + * @param newWorkingGrammar + * The new grammar to use + */ + public void setCurrentGrammar(WeightedGrammar<String> newWorkingGrammar) { + currentGrammar = newWorkingGrammar; + } + + /** + * Set the rule currently being worked on + * + * @param ruleName + * The rule currently being worked on + */ + public void setCurrentRule(String ruleName) { + currentRule = ruleName; + } + + /** + * Set the initial rule of this grammar + * + * @param ruleName + * The initial rule of this grammar + */ + public void setInitialRule(String ruleName) { + currentGrammar.setInitialRule(ruleName); + } + + /** + * Toggle this uniformity setting for this grammar + */ + public void toggleUniformity() { + isUniform = !isUniform; + } + + /** + * Add a case to the current grammar + * + * @param ruleProbability + * The probability for this case to occur + * @param ruleParts + * The parts that make up this case + */ + public void addCase(int ruleProbability, IList<String> ruleParts) { + currentGrammar.addCase(currentRule, ruleProbability, ruleParts); + } + + /** + * Add a special rule to the grammar + * + * @param ruleName The name of the special rule + * @param cse The special case for the rule + */ + public void addSpecialRule(String ruleName, Supplier<IList<String>> cse) { + currentGrammar.addSpecialRule(ruleName, cse); + } + + /** + * Start editing a new rule in the current grammar + * + * @param ruleName + * The name of the new rule to edit + */ + public void startNewRule(String ruleName) { + currentGrammar.addRule(ruleName); + + currentRule = ruleName; + } + + /** + * Convert this package of state into a weighted grammar + * + * @return The grammar represented by this state + */ + public WeightedGrammar<String> getGrammar() { + return currentGrammar; + } + + /** + * Prefix a token onto all of the cases for the specified rule + * + * @param ruleName + * The rule to do prefixing on + * @param prefixToken + * The token to prefix onto each case + * @param additionalProbability + * The probability modification of the prefixed cases + */ + public void prefixRule(String ruleName, String prefixToken, + int additionalProbability) { + currentGrammar.prefixRule(ruleName, prefixToken, + additionalProbability); + } + + /** + * Delete a rule from the current grammar + * + * @param ruleName + * The name of the rule to delete + */ + public void deleteRule(String ruleName) { + currentGrammar.deleteRule(ruleName); + } + + /** + * Suffix a token onto all of the cases for the specified rule + * + * @param ruleName + * The rule to do suffixing on + * @param suffixToken + * The token to suffix onto each case + * @param additionalProbability + * The probability modification of the suffixed cases + */ + public void suffixRule(String ruleName, String suffixToken, + int additionalProbability) { + currentGrammar.suffixRule(ruleName, suffixToken, + additionalProbability); + } +} diff --git a/RGens/src/main/java/bjc/rgens/server/ServerGrammarReader.java b/RGens/src/main/java/bjc/rgens/server/ServerGrammarReader.java new file mode 100644 index 0000000..77b1e8b --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/server/ServerGrammarReader.java @@ -0,0 +1,326 @@ +package bjc.rgens.server; + +import com.mifmif.common.regex.Generex; + +import java.io.InputStream; +import java.util.Random; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; +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; + +/** + * Read a grammar from a stream + * + * @author ben + * + */ +public class ServerGrammarReader { + static { + setupReader(); + + initPragmas(); + } + + private static RuleBasedConfigReader<ReaderState> reader; + + private static Random numgen = new Random(); + + private static IMap<String, WeightedGrammar<String>> exportedRules; + + public static void setExportedRules(IMap<String, WeightedGrammar<String>> rules) { + exportedRules = rules; + } + + /** + * Read a grammar from a stream + * + * @param inputStream + * The stream to load the grammar from + * + * @return A grammar read from the stream + * + */ + public static IPair<WeightedGrammar<String>, IList<String>> + fromStream(InputStream inputStream) { + ReaderState initialState = new ReaderState(); + + WeightedGrammar<String> gram = reader.fromStream(inputStream, initialState).getGrammar(); + + IMap<String, IList<String>> vars = new FunctionalMap<>(); + + Predicate<String> specialPredicate = (strang) -> { + if(strang.matches("\\[\\S+\\]")) { + return true; + } + + return false; + }; + + BiFunction<String, WeightedGrammar<String>, IList<String>> + specialAction = (strang, gramm) -> { + return handleSpecialRule(vars, strang, gramm); + }; + + gram.configureSpecial(specialPredicate, specialAction); + + IList<String> exports = initialState.getExports(); + + if(gram.getInitialRule() != null && !exports.contains(gram.getInitialRule())) { + exports.add(gram.getInitialRule()); + } + + return new Pair<>(gram, exports); + } + + private static void initPragmas() { + reader.addPragma("debug", (tokenizer, state) -> { + debugGrammar(state); + }); + + reader.addPragma("uniform", (tokenizer, state) -> { + state.toggleUniformity(); + }); + + reader.addPragma("initial-rule", ServerGrammarReader::initialRule); + + reader.addPragma("remove-rule", ServerGrammarReader::removeRule); + + reader.addPragma("prefix-with", ServerGrammarReader::prefixRule); + reader.addPragma("suffix-with", ServerGrammarReader::suffixRule); + + reader.addPragma("regex-rule", ServerGrammarReader::handleRegexRule); + + reader.addPragma("range-rule", ServerGrammarReader::handleRangeRule); + + reader.addPragma("export-rule", (tokenizer, state) -> { + String ruleName = tokenizer.nextToken(); + + state.addExport(ruleName); + }); + } + + private static void setupReader() { + reader = new RuleBasedConfigReader<>(null, null, null); + + reader.setStartRule((tokenizer, stateTokenPair) -> { + stateTokenPair.doWith((initToken, state) -> { + state.startNewRule(initToken); + + doCase(tokenizer, state); + }); + }); + + reader.setContinueRule((tokenizer, state) -> { + doCase(tokenizer, state); + }); + + reader.setEndRule((tokenizer) -> { + tokenizer.setCurrentRule(null); + }); + } + + private static void debugGrammar(ReaderState state) { + System.out.println("Printing rule names: "); + + for (String currentRule : state.getRuleNames().toIterable()) { + System.out.println("\t" + currentRule); + } + + System.out.println(); + } + + private static IList<String> handleSpecialRule(IMap<String, IList<String>> vars, String strang, + WeightedGrammar<String> gramm) { + IList<String> retList = new FunctionalList<>(); + + if(strang.matches("\\[\\[\\S+\\]\\]")) { + if(strang.matches("\\[\\[\\S+:=\\S+\\]\\]")) { + doDefineExpandedVariable(vars, strang, gramm); + } else if(strang.matches("\\[\\[\\S+=\\S+\\]\\]")) { + doDefineVariable(vars, strang); + } else if(strang.matches("\\[\\[\\S+\\]\\]")) { + if(GrammarServerEngine.debugMode) { + if(strang.contains("+")) + System.out.println("Double-triggering no-space rule " + strang); + else + System.out.println("Triggered alternate no-space rule " + strang); + } + + doNoSpaceRule(strang, gramm, retList); + } else if (strang.contains("+")) { + if(GrammarServerEngine.debugMode) + System.out.println("Triggered alternate no-space rule " + strang); + + doNoSpaceRule(strang, gramm, retList); + } else { + // @FIXME notify the user they did something wrong + retList.add(strang); + } + } else { + if(strang.matches("\\[\\$\\S+\\-\\S+\\]")) { + retList = doExpandVariableReference(vars, strang, gramm); + } else if(strang.matches("\\[\\$\\S+\\]")) { + String varName = strang.substring(2, strang.length()); + + retList = vars.get(varName); + } else if (exportedRules.containsKey(strang) && + exportedRules.get(strang) != gramm && + !gramm.hasRule(strang)) { + // Only pick external rules if they are both + // a) in a different grammar + // b) not shadowed in the current grammar + WeightedGrammar<String> exportGram = exportedRules.get(strang); + + retList = exportGram.generateGenericValues(strang, (s) -> s, " "); + } else if (strang.contains("+")) { + doNoSpaceRule(strang, gramm, retList); + } else { + // @FIXME notify the user they did something wrong + retList.add(strang); + } + } + + return retList; + } + + private static void handleRangeRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String ruleName = tokenizer.nextToken(); + + int start = Integer.parseInt(tokenizer.nextToken()); + int end = Integer.parseInt(tokenizer.nextToken()); + + state.addSpecialRule(ruleName, () -> { + int genNum = numgen.nextInt((end - start) + 1) + start; + + return new FunctionalList<>(Integer.toString(genNum)); + }); + } + + private static void handleRegexRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String ruleName = tokenizer.nextToken(); + + IList<String> regx = tokenizer.toList(); + Generex regex = new Generex(ListUtils.collapseTokens(regx)); + + state.addSpecialRule(ruleName, () -> { + return new FunctionalList<>(regex.random().split(" ")); + }); + } + + private static int readOptionalProbability(FunctionalStringTokenizer tokenizer, ReaderState state) { + if (state.isUniform()) { + return 1; + } + + return Integer.parseInt(tokenizer.nextToken()); + } + + private static void initialRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String initialRuleName = tokenizer.nextToken(); + + state.setInitialRule(initialRuleName); + } + + private static void prefixRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String ruleName = tokenizer.nextToken(); + String prefixToken = tokenizer.nextToken(); + + int additionalProbability = readOptionalProbability(tokenizer, state); + + state.prefixRule(ruleName, prefixToken, additionalProbability); + } + + private static void removeRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String ruleName = tokenizer.nextToken(); + + state.deleteRule(ruleName); + } + + private static void suffixRule(FunctionalStringTokenizer tokenizer, ReaderState state) { + String ruleName = tokenizer.nextToken(); + String suffixToken = tokenizer.nextToken(); + + int additionalProbability = readOptionalProbability(tokenizer, state); + + state.suffixRule(ruleName, suffixToken, additionalProbability); + } + + private static void doCase(FunctionalStringTokenizer tokenizer, ReaderState state) { + int ruleProbability = readOptionalProbability(tokenizer, state); + + state.addCase(ruleProbability, tokenizer.toList()); + } + + private static void doDefineExpandedVariable(IMap<String, IList<String>> vars, String strang, WeightedGrammar<String> gramm) { + String[] varParts = strang.split(":="); + + String varName = varParts[0].substring(2); + String ruleName = varParts[1].substring(0, varParts[1].length() - 2); + + IList<String> varValue = gramm.generateGenericValues( + ruleName, (s) -> s, " "); + + vars.put(varName, varValue); + } + + private static void doDefineVariable(IMap<String, IList<String>> vars, String strang) { + String[] varParts = strang.split("="); + + String varName = varParts[0].substring(2); + String varValue = varParts[1].substring(0, varParts[1].length() - 2); + + vars.put(varName, new FunctionalList<>(varValue)); + } + + private static IList<String> doExpandVariableReference(IMap<String, IList<String>> vars, String strang, + WeightedGrammar<String> gramm) { + IList<String> retList; + String[] varParts = strang.substring(1, strang.length()).split("-"); + + StringBuilder actualName = new StringBuilder("["); + + for(String varPart : varParts) { + if(varPart.startsWith("$")) { + IList<String> 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); + + retList = gramm.generateGenericValues(actualName.toString(), (s) -> s, " "); + return retList; + } + + private static void doNoSpaceRule(String strang, WeightedGrammar<String> gramm, IList<String> retList) { + if(!GrammarServerEngine.debugMode) { + IList<String> ruleValue = gramm.generateGenericValues( + strang, (s) -> s.trim(), ""); + + retList.add(ListUtils.collapseTokens(ruleValue)); + } else { + // if(!gramm.hasRule(strang)) + // System.out.println("Warning: Possible unexpanded rule " + strang); + + retList.add(strang); + } + } +} |
