summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/rgens
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/bjc/rgens')
-rw-r--r--src/main/java/bjc/rgens/parser/ConfigLoader.java333
-rw-r--r--src/main/java/bjc/rgens/parser/FlatRuleCase.java8
-rw-r--r--src/main/java/bjc/rgens/parser/LoadOptions.java50
-rw-r--r--src/main/java/bjc/rgens/parser/NormalRuleCase.java6
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammar.java17
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammarBuilder.java232
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammarParser.java798
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammarTest.java25
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RGrammars.java7
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RegexRuleCase.java6
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/Rule.java18
-rwxr-xr-xsrc/main/java/bjc/rgens/parser/RuleCase.java9
-rw-r--r--src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java17
-rw-r--r--src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java74
-rw-r--r--src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java19
-rw-r--r--src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java30
16 files changed, 1081 insertions, 568 deletions
diff --git a/src/main/java/bjc/rgens/parser/ConfigLoader.java b/src/main/java/bjc/rgens/parser/ConfigLoader.java
index 269a45b..c282f8a 100644
--- a/src/main/java/bjc/rgens/parser/ConfigLoader.java
+++ b/src/main/java/bjc/rgens/parser/ConfigLoader.java
@@ -1,19 +1,33 @@
package bjc.rgens.parser;
+import bjc.utils.data.ITree;
+import bjc.utils.data.QueuedIterator;
+import bjc.utils.data.Tree;
+
import bjc.utils.funcutils.FileUtils;
+import bjc.utils.funcutils.IteratorUtils;
import bjc.utils.funcutils.StringUtils;
-import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
import java.io.IOException;
+
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+
import java.util.Scanner;
import bjc.rgens.parser.templates.GrammarTemplate;
import static bjc.rgens.parser.RGrammarLogging.*;
+/**
+ * Class that performs loading of grammar sets from config files.
+ *
+ * @author Ben Culkin
+ */
public class ConfigLoader {
/**
* Load a grammar set from a configuration file.
@@ -27,57 +41,76 @@ public class ConfigLoader {
* @throws IOException
* If something goes wrong during configuration loading.
*/
- public static ConfigSet fromConfigFile(Path cfgFile) throws IOException {
- ConfigSet cfgSet = new ConfigSet();
+ public static ConfigSet fromConfigFile(Path cfgFile, LoadOptions lopts) throws IOException {
+ String msg = String.format("INFO: Loading config file %s", cfgFile);
+
+ ITree<String> errTree = new Tree<>(msg);
+
+ return fromConfigFile(cfgFile, lopts, errTree);
+ }
+
+ /**
+ * Load a grammar set from a configuration file.
+ *
+ * @param cfgFile
+ * The configuration file to load from.
+ *
+ * @param errs
+ * A place to add errors that occur during loading.
+ *
+ * @return
+ * The grammar set created by the configuration file.
+ *
+ * @throws IOException
+ * If something goes wrong during configuration loading.
+ */
+ public static ConfigSet fromConfigFile(Path cfgFile, LoadOptions lopts, ITree<String> errs) throws IOException {
+ lopts.cfgFile = cfgFile;
+ lopts.cfgSet = new ConfigSet();
/* The grammar set we're parsing into. */
- RGrammarSet set = cfgSet.createGSet("default");
+ lopts.gramSet = lopts.cfgSet.createGSet(lopts.defName);
long startCFGTime = System.nanoTime();
- /* Get the directory that contains the config file. */
- Path cfgParent = cfgFile.getParent();
+ // Get the directory that contains the config file.
+ if (lopts.parent == null) lopts.parent = cfgFile.getParent();
try(Scanner scn = new Scanner(cfgFile)) {
int lno = 0;
- /* Execute lines from the configuration file. */
while (scn.hasNextLine()) {
- String ln = scn.nextLine().trim();
-
+ // Execute a line from the configuration file.
lno += 1;
- try {
- /* Ignore blank/comment lines. */
- if (ln.equals("")) continue;
+ String ln = scn.nextLine().trim().replaceAll("\\s+", " ");
- if (ln.startsWith("#")) continue;
+ // Ignore blank/comment lines.
+ if (ln.equals("")) continue;
+ if (ln.startsWith("#")) continue;
- ln = ln.replaceAll("\\s+", " ");
- String[] parts = StringUtils.levelSplit(ln, " ").toArray(new String[0]);
+ ITree<String> header = new Tree<>(String.format("INFO: Processed line %d", lno));
- /* Get line type */
- if(parts.length < 1) {
- throw new GrammarException("Must specify config line type");
- }
+ String[] parts = StringUtils.levelSplit(ln, " ").toArray(new String[0]);
+
+ if(parts.length < 1) {
+ // Must specify a line type
+ header.addChild("ERROR: Must specify config line type");
+ } else {
String type = parts[0];
switch(type) {
- case "load":
- loadConfigLine(parts, cfgSet, set, cfgParent);
+ case "load":
+ loadConfigLine(parts, lopts, header);
break;
default:
- throw new GrammarException("Unknown config line type " + type);
- }
- } catch(GrammarException gex) {
- System.out.printf("ERROR: Line %s of config set %s (%s)\n", lno, cfgFile, gex.getRootMessage());
-
- error(gex, "Line %s of config set %s (%s)", lno, cfgFile, gex.getRootMessage());
- gex.printStackTrace();
+ String fmt = String.format("ERROR: Unknown config line type %s", type);
- System.out.println();
- System.out.println();
+ header.addChild(fmt);
+ }
}
+
+ errs.addChild(header);
}
}
@@ -85,57 +118,72 @@ public class ConfigLoader {
long cfgDur = endCFGTime - startCFGTime;
- perf("Read config file %s in %d ns (%f s)", cfgFile, cfgDur, cfgDur / 1000000000.0);
+ if (lopts.doPerf) {
+ String fmt = String.format("PERF: Read config file %s in %d ns (%f s)", cfgFile, cfgDur, cfgDur / 1000000000.0);
- return cfgSet;
+ errs.addChild(fmt);
+ }
+
+ return lopts.cfgSet;
}
- private static void loadConfigLine(String[] parts, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException {
- /*
- * Get the place where the tag ID ends
- */
+ private static void loadConfigLine(String[] parts, LoadOptions lopts, ITree<String> errs) throws IOException {
if(parts.length < 2) {
- throw new GrammarException("Must specify object tag");
+ // Must specify an object type
+ errs.addChild("ERROR: Must specify type for config object");
+
+ return;
}
+
String tag = parts[1];
- /*
- * Get the place where the name of the grammar
- * ends.
- */
if (parts.length < 3) {
- throw new GrammarException("Must specify a name for a loaded " + tag);
+ // Must specify an object name
+ String fmt = String.format("ERROR: Must specify a name for config object of type '%s'", tag);
+
+ errs.addChild(fmt);
+
+ return;
}
+
String name = parts[2];
switch(tag) {
case "template":
- loadTemplate(name, parts, cfgSet, set, cfgParent);
+ loadTemplate(name, parts, lopts, errs);
break;
case "subset":
{
+ String fmt = String.format("ERROR: Sub-grammar sets aren't implemented yet");
+
/*
- *@TODO Ben Culkin 9/8/17 :SubsetGrammar
- *
+ * @TODO Ben Culkin 9/8/17 :SubsetGrammar
* Implement subset grammars.
+ *
*/
- throw new GrammarException("Sub-grammar sets aren't implemented yet");
+ errs.addChild(fmt);
}
case "gram":
case "grammar":
- loadGrammar(name, parts, cfgSet, set, cfgParent);
+ loadGrammar(name, parts, lopts, errs);
break;
case "directory":
- loadDirectory(name, parts, cfgSet, set, cfgParent);
+ loadDirectory(name, parts, lopts, errs);
break;
default:
- throw new GrammarException(String.format("Unrecognized tag type '%s'", tag));
+ String fmt = String.format("ERROR: Unrecognized config object type '%s'", tag);
+
+ errs.addChild(fmt);
}
}
- private static void loadDirectory(String name, String[] parts, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException {
+ private static void loadDirectory(String name, String[] parts, LoadOptions lopts, ITree<String> errs) throws IOException {
if(parts.length < 4) {
- throw new GrammarException(String.format("Must specify a path to load directory '%s' from"));
+ String fmt = String.format("ERROR: Must specify a path to load directory '%s' from", name);
+
+ errs.addChild(fmt);
+
+ return;
}
Path path = Paths.get(parts[3]);
@@ -144,63 +192,82 @@ public class ConfigLoader {
* Convert from configuration relative path to
* absolute path.
*/
- Path dirPath = cfgParent.resolve(path.toString());
+ Path dirPath = lopts.parent.resolve(path.toString());
if(!Files.isDirectory(dirPath)) {
- throw new GrammarException(String.format("%s is not a valid directory", dirPath));
+ String fmt = String.format("ERROR: '%s' is not a valid directory", dirPath);
+
+ errs.addChild(fmt);
} else {
- FileUtils.traverseDirectory(dirPath, (fle, atts) -> {
- // We want to consider all the files
- return true;
- }, (fle, atts) -> {
- Path normFle = fle.normalize();
-
- String fleName = normFle.toString();
-
- try {
- if(fleName.endsWith(".gram")) {
- BufferedReader rdr = Files.newBufferedReader(normFle);
+ // Create an iterator over all of the files in the
+ // provided directory
+ QueuedIterator<File> dirItr = new QueuedIterator<>(dirPath.toFile().listFiles());
+
+ ITree<String> header = new Tree<>(String.format("INFO: Bulk-loading files from directory '%s'", lopts.parent));
- doLoadGrammar(rdr, null, cfgSet, set, dirPath, normFle);
- } else if(fleName.endsWith(".gtpl")) {
- BufferedReader rdr = Files.newBufferedReader(normFle);
+ while (dirItr.hasNext()) {
+ File curFile = dirItr.next();
- doLoadTemplate(rdr, null, cfgSet, set, dirPath);
- } else if(fleName.endsWith(".class")) {
- // Ignore these
+ String fName = curFile.toString();
+
+ ITree<String> kid = new Tree<>(String.format("INFO: Processing file '%s'", fName));
+
+ Path oldPar = lopts.parent;
+ lopts.parent = curFile.toPath().getParent();
+
+ try {
+ if (curFile.isDirectory()) {
+ dirItr.last(curFile.listFiles());
+ } else if (fName.endsWith(".gram")) {
+ Reader rdr = new FileReader(curFile);
+
+ doLoadGrammar(rdr, null, lopts, kid);
+ } else if (fName.endsWith(".gtpl")) {
+ Reader rdr = new FileReader(curFile);
+
+ doLoadTemplate(rdr, null, lopts, kid);
+ } else if (fName.endsWith(".class")) {
+ // These get ignored
} else {
- info("Ignoring file '%s' of unknown type", fleName);
+ String fmt = String.format("WARN: Ignoring unknown type of file '%s'");
+
+ kid.addChild(fmt);
+
}
- } catch (GrammarException gex) {
- error(gex, "Error loading file %s (%s)", normFle, gex.getRootMessage());
} catch (IOException ioex) {
- error(ioex, "Error loading file %s", normFle);
+ kid.addChild("ERROR: " + ioex.getMessage());
+ } finally {
+ lopts.parent = oldPar;
}
- return true;
- });
+ header.addChild(kid);
+ }
+
+ errs.addChild(header);
}
}
- private static void doLoadTemplate(BufferedReader rdr, String name, ConfigSet cfgSet, RGrammarSet set, Path convPath) throws IOException {
+ private static void doLoadTemplate(Reader rdr, String name, LoadOptions lopts, ITree<String> errs) throws IOException {
String actName;
long startFileTime = System.nanoTime();
- GrammarTemplate template = GrammarTemplate.readTemplate(rdr);
- template.belongsTo = cfgSet;
+ GrammarTemplate template = GrammarTemplate.readTemplate(rdr, errs);
+ template.belongsTo = lopts.cfgSet;
if(template.name == null) {
if(name == null) {
- info("Using default name for template from path '%s'", convPath);
+ String fmt = String.format("INFO: Using default name for template");
+
+ errs.addChild(fmt);
actName = "default-name";
} else {
actName = name;
}
- info("Naming unnamed template loaded from path %s off config name '%s'",
- convPath, actName);
+ String fmt = String.format("INFO: Naming unnamed template off name '%s' specified in config", actName);
+ errs.addChild(fmt);
template.name = actName;
}
@@ -211,11 +278,14 @@ public class ConfigLoader {
long fileTime = endFileTime - startFileTime;
- perf("Read template %s (from %s) in %d ns (%f s)",
- template.name, convPath, fileTime, fileTime / 1000000000.0);
+ if (lopts.doPerf) {
+ String fmt = String.format("PERF: Read template %s in %d ns (%f s)", template.name, fileTime, fileTime / 1000000000.0);
+
+ errs.addChild(fmt);
+ }
/* Add grammar to the set. */
- cfgSet.templates.put(template.name, template);
+ lopts.cfgSet.templates.put(template.name, template);
/*
* @NOTE
@@ -227,9 +297,13 @@ public class ConfigLoader {
//set.loadedFrom.put(template.name, path.toString());
}
- private static void loadTemplate(String name, String[] parts, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException {
+ private static void loadTemplate(String name, String[] parts, LoadOptions lopts, ITree<String> errs) throws IOException {
if(parts.length < 4) {
- throw new GrammarException(String.format("Must specify a path to load template '%s' from", name));
+ String fmt = String.format("ERROR: Must specify a path to load template '%s' from", name);
+
+ errs.addChild(fmt);
+
+ return;
}
Path path = Paths.get(parts[3]);
@@ -238,39 +312,48 @@ public class ConfigLoader {
* Convert from configuration relative path to
* absolute path.
*/
- Path convPath = cfgParent.resolve(path.toString());
+ Path convPath = lopts.parent.resolve(path.toString());
+
+ if(!Files.exists(convPath) || Files.isDirectory(convPath)) {
+ String fmt = String.format("ERROR: '%s' is not a valid grammar file", convPath);
- if(Files.isDirectory(convPath)) {
- throw new GrammarException(String.format("%s is not a valid grammar file", convPath));
+ errs.addChild(fmt);
} else {
/* Load template file. */
+ Reader rdr = new FileReader(convPath.toFile());
+
+ String fmt = String.format("INFO: Loading template '%s' from '%s'", name, convPath);
+ ITree<String> kid = new Tree<>(fmt);
+
+ Path oldPar = lopts.parent;
+ lopts.parent = convPath.getParent();
+
try {
- BufferedReader fis = Files.newBufferedReader(convPath);
- doLoadTemplate(fis, name, cfgSet, set, convPath);
- } catch (GrammarException gex) {
- String msg = String.format("Error loading template file '%s'", path);
- throw new GrammarException(msg, gex, gex.getRootMessage());
+ doLoadTemplate(rdr, name, lopts, kid);
+ } finally {
+ lopts.parent = oldPar;
}
+
+ errs.addChild(kid);
}
}
- private static void doLoadGrammar(BufferedReader rdr, String name, ConfigSet cfgSet, RGrammarSet set, Path convPath, Path pth) throws IOException {
+ private static void doLoadGrammar(Reader rdr, String name, LoadOptions lopts, ITree<String> errs) throws IOException {
String actName;
long startFileTime = System.nanoTime();
- RGrammar gram = RGrammarParser.readGrammar(rdr);
+ RGrammar gram = RGrammarParser.readGrammar(rdr, lopts, errs);
if(gram.name == null) {
if(name == null) {
- info("Using default name from grammar for '%s'", convPath);
+ errs.addChild("INFO: Using default name for grammar");
actName = "default-name";
} else {
actName = name;
}
- info("Naming unnamed grammar loaded from %s off config name '%s'",
- pth, actName);
+ String fmt = String.format("Naming unnamed grammar off config name '%s'", actName);
gram.name = actName;
}
@@ -281,22 +364,29 @@ public class ConfigLoader {
long fileTime = endFileTime - startFileTime;
- perf("Read grammar %s (from %s) in %d ns (%f s)",
- gram.name, convPath, fileTime, fileTime / 1000000000.0);
+ if (lopts.doPerf) {
+ String fmt = String.format("PERF: Read grammar %s in %d ns (%f s)", gram.name, fileTime, fileTime / 1000000000.0);
+
+ errs.addChild(fmt);
+ }
/* Add grammar to the set. */
- set.addGrammar(gram.name, gram);
+ lopts.gramSet.addGrammar(gram.name, gram);
/*
* Mark where the grammar came
* from.
*/
- set.loadedFrom.put(gram.name, pth.toString());
+ lopts.gramSet.loadedFrom.put(gram.name, lopts.parent.toString());
}
- private static void loadGrammar(String name, String[] parts, ConfigSet cfgSet, RGrammarSet set, Path cfgParent) throws IOException {
+ private static void loadGrammar(String name, String[] parts, LoadOptions lopts, ITree<String> errs) throws IOException {
if(parts.length < 4) {
- throw new GrammarException(String.format("Must provide a path to load grammar '%s' from", name));
+ String fmt = String.format("ERROR: Must provide a path to load grammar '%s' from", name);
+
+ errs.addChild(fmt);
+
+ return;
}
Path path = Paths.get(parts[3]);
@@ -305,23 +395,32 @@ public class ConfigLoader {
* Convert from configuration relative path to
* absolute path.
*/
- Path convPath = cfgParent.resolve(path.toString());
+ Path convPath = lopts.parent.resolve(path.toString());
+
+ if(!Files.exists(convPath) || Files.isDirectory(convPath)) {
+ String fmt = String.format("ERROR: %s is not a valid grammar file", convPath);
- if(Files.isDirectory(convPath)) {
- throw new GrammarException(String.format("%s is not a valid grammar file", convPath));
+ errs.addChild(fmt);
} else {
- /* Load grammar file. */
+ Path oldPar = lopts.parent;
+ lopts.parent = convPath.getParent();
+
try {
+ /* Load grammar file. */
long startFileTime = System.nanoTime();
- BufferedReader fis = Files.newBufferedReader(convPath);
- doLoadGrammar(fis, name, cfgSet, set, convPath, path);
- } catch(GrammarException gex) {
- String msg = String.format("Error loading grammar '%s'", path);
- throw new GrammarException(msg, gex, gex.getRootMessage());
+ Reader rdr = new FileReader(convPath.toFile());
+
+ ITree<String> kid = new Tree<>(String.format("INFO: Loading grammar '%s' from '%s'", name, convPath));
+ doLoadGrammar(rdr, name, lopts, kid);
+
+ errs.addChild(kid);
} catch (IOException ioex) {
- String msg = String.format("Error loading grammar '%s'", path);
- throw new GrammarException(msg, ioex);
+ String msg = String.format("ERROR: %s", ioex.getMessage());
+
+ errs.addChild(msg);
+ } finally {
+ lopts.parent = oldPar;
}
}
}
diff --git a/src/main/java/bjc/rgens/parser/FlatRuleCase.java b/src/main/java/bjc/rgens/parser/FlatRuleCase.java
index 4bbd1cc..ac6f554 100644
--- a/src/main/java/bjc/rgens/parser/FlatRuleCase.java
+++ b/src/main/java/bjc/rgens/parser/FlatRuleCase.java
@@ -1,11 +1,11 @@
package bjc.rgens.parser;
-import bjc.utils.funcdata.IList;
-
import bjc.rgens.parser.elements.CaseElement;
+import java.util.List;
+
public class FlatRuleCase extends RuleCase {
- public FlatRuleCase(IList<CaseElement> elms) {
+ public FlatRuleCase(List<CaseElement> elms) {
super(elms);
}
@@ -16,7 +16,7 @@ public class FlatRuleCase extends RuleCase {
}
}
- public FlatRuleCase withElements(IList<CaseElement> elms) {
+ public FlatRuleCase withElements(List<CaseElement> elms) {
return new FlatRuleCase(elms);
}
}
diff --git a/src/main/java/bjc/rgens/parser/LoadOptions.java b/src/main/java/bjc/rgens/parser/LoadOptions.java
new file mode 100644
index 0000000..1af0923
--- /dev/null
+++ b/src/main/java/bjc/rgens/parser/LoadOptions.java
@@ -0,0 +1,50 @@
+package bjc.rgens.parser;
+
+import java.nio.file.Path;
+
+/**
+ * Options used during the loading of config sets and config set related things.
+ *
+ * @author Ben Culkin.
+ */
+public class LoadOptions {
+ /**
+ * Should timings be tracked?
+ */
+ public boolean doPerf;
+
+ /**
+ * Should debug stats be tracked?
+ */
+ public boolean doDebug;
+
+ /**
+ * Should trace stats be tracked?
+ */
+ public boolean doTrace;
+
+ /**
+ * The default grammar set name.
+ */
+ public String defName;
+
+ /**
+ * The path the current file is in.
+ */
+ public Path parent;
+
+ /**
+ * The file the current config set is being loaded from.
+ */
+ public Path cfgFile;
+
+ /**
+ * The config set being loaded.
+ */
+ public ConfigSet cfgSet;
+
+ /**
+ * The grammar set being loaded.
+ */
+ public RGrammarSet gramSet;
+}
diff --git a/src/main/java/bjc/rgens/parser/NormalRuleCase.java b/src/main/java/bjc/rgens/parser/NormalRuleCase.java
index 7fee169..8fca86d 100644
--- a/src/main/java/bjc/rgens/parser/NormalRuleCase.java
+++ b/src/main/java/bjc/rgens/parser/NormalRuleCase.java
@@ -1,11 +1,11 @@
package bjc.rgens.parser;
-import bjc.utils.funcdata.IList;
+import java.util.List;
import bjc.rgens.parser.elements.CaseElement;
public class NormalRuleCase extends RuleCase {
- public NormalRuleCase(IList<CaseElement> elms) {
+ public NormalRuleCase(List<CaseElement> elms) {
super(elms);
}
@@ -20,7 +20,7 @@ public class NormalRuleCase extends RuleCase {
}
}
- public NormalRuleCase withElements(IList<CaseElement> elms) {
+ public NormalRuleCase withElements(List<CaseElement> 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 20ce320..018f2f0 100755
--- a/src/main/java/bjc/rgens/parser/RGrammar.java
+++ b/src/main/java/bjc/rgens/parser/RGrammar.java
@@ -1,7 +1,9 @@
package bjc.rgens.parser;
import bjc.utils.data.IPair;
+import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
+import bjc.utils.data.Tree;
import bjc.utils.funcutils.StringUtils;
import bjc.utils.ioutils.ReportWriter;
@@ -291,18 +293,27 @@ public class RGrammar {
* initial rule.
*/
public void setInitialRule(String initRule) {
+ setInitialRule(initRule, new Tree<>());
+ }
+
+ public void setInitialRule(String initRule, ITree<String> errs) {
/* Passing null, nulls our initial rule. */
if (initRule == null) {
this.initialRule = null;
+
return;
}
if (initRule.equals("")) {
- throw new GrammarException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(initRule)) {
- String msg = String.format("No rule '%s' local to this grammar (%s) defined.", initRule, name);
+ String msg = String.format("ERROR: No rule '%s' local to this grammar (%s) defined.", initRule, name);
+
+ errs.addChild(msg);
- throw new GrammarException(msg);
+ return;
}
initialRule = initRule;
diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
index 535d818..358c6a1 100755
--- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
+++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java
@@ -4,9 +4,13 @@ import bjc.rgens.parser.elements.CaseElement;
import bjc.rgens.parser.elements.VariableDefCaseElement;
import bjc.utils.data.IPair;
+import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
+import bjc.utils.data.Tree;
+
import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.IList;
+
import bjc.utils.funcutils.ListUtils;
import bjc.utils.funcutils.SetUtils;
@@ -59,10 +63,19 @@ public class RGrammarBuilder {
* The rule by that name, or a new one if none existed.
*/
public Rule getOrCreateRule(String rName) {
- if(rName == null)
- throw new NullPointerException("Rule name must not be null");
- else if(rName.equals(""))
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ return getOrCreateRule(rName, new Tree<>());
+ }
+
+ public Rule getOrCreateRule(String rName, ITree<String> errs) {
+ if(rName == null) {
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return null;
+ } else if(rName.equals("")) {
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return null;
+ }
if(rules.containsKey(rName)) {
return rules.get(rName);
@@ -116,10 +129,17 @@ public class RGrammarBuilder {
* If the rule is either not valid or not defined in the grammar.
*/
public void setInitialRule(String init) {
+ }
+
+ public void setInitialRule(String init, ITree<String> errs) {
if (init == null) {
- throw new NullPointerException("init must not be null");
+ errs.addChild("init must not be null");
+
+ return;
} else if (init.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("The empty string is not a valid rule name");
+
+ return;
}
initialRule = init;
@@ -135,10 +155,18 @@ public class RGrammarBuilder {
* If the rule is either not valid or not defined in the grammar.
*/
public void addExport(String export) {
+ addExport(export, new Tree<>());
+ }
+
+ public void addExport(String export, ITree<String> errs) {
if (export == null) {
- throw new NullPointerException("Export name must not be null");
+ errs.addChild("ERROR: Export name must not be null");
+
+ return;
} else if (export.equals("")) {
- throw new NullPointerException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
}
exportedRules.add(export);
@@ -157,8 +185,8 @@ public class RGrammarBuilder {
* If the rule name is either invalid or not defined by this
* grammar, or if the suffix is invalid.
*/
- public void suffixWith(String ruleName, IList<CaseElement> suffixes) {
- affixWith(ruleName, suffixes, AffixType.SUFFIX);
+ public void suffixWith(String ruleName, List<CaseElement> suffixes) {
+ affixWith(ruleName, suffixes, AffixType.SUFFIX, new Tree<>());
}
@@ -175,8 +203,8 @@ public class RGrammarBuilder {
* If the rule name is either invalid or not defined by this
* grammar, or if the prefix is invalid.
*/
- public void prefixWith(String ruleName, IList<CaseElement> prefixes) {
- affixWith(ruleName, prefixes, AffixType.PREFIX);
+ public void prefixWith(String ruleName, List<CaseElement> prefixes) {
+ affixWith(ruleName, prefixes, AffixType.PREFIX, new Tree<>());
}
/**
@@ -192,8 +220,8 @@ public class RGrammarBuilder {
* If the rule name is either invalid or not defined by this
* grammar, or if the prefix/suffix is invalid.
*/
- public void circumfixWith(String ruleName, IList<CaseElement> prefixes) {
- affixWith(ruleName, prefixes, AffixType.CIRCUMFIX);
+ public void circumfixWith(String ruleName, List<CaseElement> prefixes) {
+ affixWith(ruleName, prefixes, AffixType.CIRCUMFIX, new Tree<>());
}
public static enum AffixType {
@@ -210,32 +238,39 @@ public class RGrammarBuilder {
}
}
- public void affixWith(String ruleName, IList<CaseElement> affixes, AffixType type) {
+ public void affixWith(String ruleName, List<CaseElement> affixes, AffixType type) {
+ affixWith(ruleName, affixes, type, new Tree<>());
+ }
+
+ public void affixWith(String ruleName, List<CaseElement> affixes, AffixType type, ITree<String> errs) {
if (ruleName == null) {
- throw new NullPointerException("Rule name must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if(!rules.containsKey(ruleName)) {
- String msg = String.format("Rule '%s' is not a valid rule name");
+ String msg = String.format("ERROR: Rule '%s' is not a valid rule name");
- throw new IllegalArgumentException(msg);
- }
+ errs.addChild(msg);
- Set<CaseElement> elements = new HashSet<>(affixes.getSize());
- for(CaseElement affix : affixes) {
- elements.add(affix);
+ return;
}
+
+ Set<CaseElement> elements = new HashSet<>(affixes);
List<List<CaseElement>> affixLists = powerList(elements);
FunctionalList<IPair<Integer, RuleCase>> newCases = new FunctionalList<>();
-
IList<IPair<Integer, RuleCase>> caseList = rules.get(ruleName).getCases();
+
for (IPair<Integer, RuleCase> ruleCase : caseList) {
RuleCase cas = ruleCase.getRight();
for(List<CaseElement> affixList : affixLists) {
- FunctionalList<CaseElement> newCase = new FunctionalList<>();
+ List<CaseElement> newCase = new ArrayList<>();
if(type.isPrefix()) {
for(CaseElement element : affixList) {
@@ -265,12 +300,24 @@ public class RGrammarBuilder {
}
public void despaceRule(String ruleName) {
+ despaceRule(ruleName, new Tree<>(), false);
+ }
+
+ public void despaceRule(String ruleName, ITree<String> errs, boolean doTrace) {
if (ruleName == null) {
- throw new NullPointerException("ruleName must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(ruleName)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", ruleName);
+
+ errs.addChild(msg);
+
+ return;
}
IList<IPair<Integer, RuleCase>> caseList = rules.get(ruleName).getCases();
@@ -281,42 +328,82 @@ public class RGrammarBuilder {
newCaseList.add(new Pair<>(cse.getLeft(), new FlatRuleCase(cse.getRight().elementList)));
}
- trace("Despacing %d cases of rule %s", caseList.getSize(), ruleName);
+ if (doTrace) {
+ String msg = String.format("TRACE: Despacing %d cases of rule %s", caseList.getSize(), ruleName);
+
+ errs.addChild(msg);
+ }
rules.get(ruleName).replaceCases(newCaseList);
}
public void setWeight(String ruleName) {
+ setWeight(ruleName, new Tree<>());
+ }
+
+ public void setWeight(String ruleName, ITree<String> errs) {
if (ruleName == null) {
- throw new NullPointerException("ruleName must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(ruleName)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", ruleName);
+
+ errs.addChild(msg);
+
+ return;
}
rules.get(ruleName).prob = Rule.ProbType.NORMAL;
}
public void setRuleRecur(String ruleName, int recurLimit) {
+ setRuleRecur(ruleName, recurLimit, new Tree<>());
+ }
+
+ public void setRuleRecur(String ruleName, int recurLimit, ITree<String> errs) {
if (ruleName == null) {
- throw new NullPointerException("ruleName must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(ruleName)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", ruleName);
+
+ errs.addChild(msg);
+
+ return;
}
rules.get(ruleName).recurLimit = recurLimit;
}
public void setDescent(String ruleName, int descentFactor) {
+ setDescent(ruleName, descentFactor, new Tree<>());
+ }
+
+ public void setDescent(String ruleName, int descentFactor, ITree<String> errs) {
if (ruleName == null) {
- throw new NullPointerException("ruleName must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(ruleName)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", ruleName);
+
+ errs.addChild(msg);
+
+ return;
}
Rule rl = rules.get(ruleName);
@@ -326,12 +413,23 @@ public class RGrammarBuilder {
}
public void setBinomial(String ruleName, int target, int bound, int trials) {
+ setBinomial(ruleName, target, bound, trials, new Tree<>());
+ }
+
+ public void setBinomial(String ruleName, int target, int bound, int trials, ITree<String> errs) {
if (ruleName == null) {
- throw new NullPointerException("ruleName must not be null");
+ errs.addChild("ERROR: Rule name must not be null");
+
+ return;
} else if (ruleName.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if (!rules.containsKey(ruleName)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", ruleName));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", ruleName);
+ errs.addChild(msg);
+
+ return;
}
Rule rl = rules.get(ruleName);
@@ -352,37 +450,67 @@ public class RGrammarBuilder {
}
public void rejectRule(String rule, String reject) {
+ rejectRule(rule, reject, new Tree<>());
+ }
+
+ public void rejectRule(String rule, String reject, ITree<String> errs) {
if (rule == null) {
- throw new NullPointerException("rule must not be null");
+ errs.addChild("ERROR: Rule must not be null");
+
+ return;
} else if(reject == null) {
- throw new NullPointerException("reject must not be null");
+ errs.addChild("ERROR: Reject must not be null");
+
+ return;
} else if (rule.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if(!rules.containsKey(rule)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", rule));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", rule);
+
+ errs.addChild(msg);
+
+ return;
}
Rule rl = rules.get(rule);
- rl.addRejection(reject);
+ rl.addRejection(reject, errs);
}
public void findReplaceRule(String rule, String find, String replace) {
+ findReplaceRule(rule, find, replace, new Tree<>());
+ }
+
+ public void findReplaceRule(String rule, String find, String replace, ITree<String> errs) {
if (rule == null) {
- throw new NullPointerException("rule must not be null");
+ errs.addChild("ERROR: Rule must not be null");
+
+ return;
} else if(find == null) {
- throw new NullPointerException("find must not be null");
+ errs.addChild("ERROR: Find must not be null");
+
+ return;
} else if(replace == null) {
- throw new NullPointerException("replace must not be null");
+ errs.addChild("ERROR: Replace must not be null");
+
+ return;
} else if (rule.equals("")) {
- throw new IllegalArgumentException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return;
} else if(!rules.containsKey(rule)) {
- throw new IllegalArgumentException(String.format("The rule '%s' doesn't exist", rule));
+ String msg = String.format("ERROR: The rule '%s' doesn't exist", rule);
+
+ errs.addChild(msg);
+
+ return;
}
Rule rl = rules.get(rule);
- rl.addFindReplace(find, replace);
+ rl.addFindReplace(find, replace, errs);
}
/*
diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java
index a95cefc..dc4d82d 100755
--- a/src/main/java/bjc/rgens/parser/RGrammarParser.java
+++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java
@@ -3,19 +3,27 @@ package bjc.rgens.parser;
import bjc.rgens.parser.elements.*;
import bjc.utils.data.IPair;
+import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
+import bjc.utils.data.Tree;
+
import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.IList;
+
import bjc.utils.funcutils.ListUtils;
import bjc.utils.funcutils.SetUtils;
import bjc.utils.funcutils.StringUtils;
import bjc.utils.funcutils.TriConsumer;
+
+import bjc.utils.ioutils.LevelSplitter;
import bjc.utils.ioutils.blocks.Block;
import bjc.utils.ioutils.blocks.BlockReader;
import bjc.utils.ioutils.blocks.SimpleBlockReader;
+import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
+
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@@ -26,6 +34,7 @@ import java.util.Set;
import static bjc.rgens.parser.RGrammarLogging.*;
import static bjc.rgens.parser.RGrammarBuilder.AffixType;
+
/**
* Reads {@link RGrammar} from a input stream.
*
@@ -53,244 +62,58 @@ public class RGrammarParser {
/* Top-level block delimiter. */
private static final String TMPL_TOPLEVEL_BLOCK_DELIM = "\\R\\t{%d}\\.?\\R";
- /* Pragma impls. */
- private static Map<String, TriConsumer<String, RGrammarBuilder, Integer>> pragmas;
-
- /* Initialize pragmas. */
- static {
- pragmas = new HashMap<>();
-
- pragmas.put("initial-rule", (body, build, level) -> {
- List<String> bits = StringUtils.levelSplit(body, " ");
-
- if (bits.size() != 1) {
- String msg = "Must specify initial rule";
- throw new GrammarException(msg);
- }
-
- build.setInitialRule(bits.get(0));
- });
-
- pragmas.put("grammar-name", (body, build, level) -> {
- List<String> bits = StringUtils.levelSplit(body, " ");
-
- if (bits.size() != 1) {
- String msg = "Must specify grammar name";
- throw new GrammarException(msg);
- }
-
- build.name = bits.get(0);
- });
-
- pragmas.put("despace-rule", (body, build, level) -> {
- List<String> bits = StringUtils.levelSplit(body, " ");
-
- if (bits.size() < 1) {
- throw new GrammarException("Must specify rules to despace");
- }
-
- for(String bit : bits) {
- build.despaceRule(bit);
- }
- });
-
- pragmas.put("export-rule", (body, build, level) -> {
- List<String> exports = StringUtils.levelSplit(body, " ");
-
- if(exports.size() < 1) {
- throw new GrammarException("Must specify rules to export");
- }
-
- for (String export : exports) {
- build.addExport(export);
- }
- });
-
- pragmas.put("recur-limit", (body, build, level) -> {
- List<String> parts = StringUtils.levelSplit(body, " ");
-
- if(parts.size() != 2) {
- throw new GrammarException("Recur-limit pragma takes two arguments: the name of the rule to set the limit for, and the new value of the limit");
- }
-
- if(!parts.get(1).matches("\\A\\d+\\Z")) {
- throw new GrammarException("Limit value must be an integer");
- }
-
- build.setRuleRecur(parts.get(0), Integer.parseInt(parts.get(1)));
- });
-
- pragmas.put("enable-weight", (body, build, level) -> {
- List<String> parts = StringUtils.levelSplit(body, " ");
-
- if(parts.size() != 1) {
- throw new GrammarException("Enable-weight pragma takes one argument: the name of the rule to set the weight factor for");
- }
-
- build.setWeight(parts.get(0));
- });
-
- pragmas.put("enable-descent", (body, build, level) -> {
- List<String> parts = StringUtils.levelSplit(body, " ");
-
- if(parts.size() != 2) {
- throw new GrammarException("Enable-descent pragma takes two arguments: the name of the rule to set the descent factor for, and the new value of the factor");
- }
-
- if(!parts.get(1).matches("\\A\\d+\\Z")) {
- throw new GrammarException("Factor value must be an integer");
- }
-
- build.setDescent(parts.get(0), Integer.parseInt(parts.get(1)));
- });
-
- pragmas.put("enable-binomial", (body, build, level) -> {
- // @NOTE 9/4/18
- //
- // This can be kind of hard to read right off. Is there
- // a format to put stuff in that looks better and is
- // more readable?
- List<String> parts = StringUtils.levelSplit(body, " ");
-
- if(parts.size() != 4) {
- throw new GrammarException("Enable-descent pragma takes four arguments: the name of the rule to set the binomial factors for, and the three binomial parameters (target, bound trials)");
- }
-
- if(!parts.get(1).matches("\\A\\d+\\Z")) {
- throw new GrammarException("Target value must be an integer");
- }
- if(!parts.get(2).matches("\\A\\d+\\Z")) {
- throw new GrammarException("Bound value must be an integer");
- }
- if(!parts.get(3).matches("\\A\\d+\\Z")) {
- throw new GrammarException("Trials value must be an integer");
- }
-
- build.setBinomial(parts.get(0), Integer.parseInt(parts.get(1)), Integer.parseInt(parts.get(2)), Integer.parseInt(parts.get(3)));
- });
-
- /*
- * @NOTE 4/9/18
- *
- * Consider if we want to replace this with something more akin
- * to the `definer` feature from DiceLang. This will work fine
- * in most cases, but there are some cases where you'd want the
- * extra power. No examples are apparent at the moment.
- */
- pragmas.put("find-replace-rule", (body, build, level) -> {
- List<String> bits = StringUtils.levelSplit(body, " ");
-
- if(bits.size() != 3) {
- throw new GrammarException("Regex-rule pragma takes three arguments: the name of the rule to process, then the find/replace pair to apply after the rule has been generated.");
- }
+ private static void doAffixWith(String body, RGrammarBuilder build, int level, AffixType afxType, ITree<String> errs) {
+ int idx = body.indexOf(" ");
- build.findReplaceRule(bits.get(0), bits.get(1), bits.get(2));
- });
+ if (idx == -1) {
+ String fmt = "ERROR: Takes at least two arguments, the name of the rule to affix, then what to affix it with\n\tThis can be more than one token, to get them affixed as a group";
- pragmas.put("reject-rule", (body, build, level) -> {
- List<String> bits = StringUtils.levelSplit(body, " ");
+ String msg = String.format(fmt, afxType.toString().toLowerCase());
- if(bits.size() != 3) {
- throw new GrammarException("Reject-rule pragma takes two arguments: the name of the rule to process, then the rejection pattern to apply after the rule has been generated.");
- }
+ errs.addChild(msg);
- build.rejectRule(bits.get(0), bits.get(1));
- });
+ return;
+ }
- pragmas.put("prefix-with", (body, build, level) -> {
- doAffixWith(body, build, level, AffixType.PREFIX);
- });
+ String rName = body.substring(0, idx);
- pragmas.put("suffix-with", (body, build, level) -> {
- doAffixWith(body, build, level, AffixType.SUFFIX);
- });
+ List<CaseElement> elms = new ArrayList<>();
- pragmas.put("circumfix-with", (body, build, level) -> {
- doAffixWith(body, build, level, AffixType.CIRCUMFIX);
- });
- /*
- * @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);
- });
+ parseElementString(body.substring(idx + 1), elms, errs);
- pragmas.put("autovivify-rule", (body, build, level) -> {
- doAutoVar(body, build, level, true);
- });
+ build.affixWith(rName, elms, afxType, errs);
}
- private static void doAffixWith(String body, RGrammarBuilder build, int level, AffixType afxType) {
- int idx = body.indexOf(" ");
+ private static void doAutoVar(List<String> bits, RGrammarBuilder build, int level, boolean isRule, ITree<String> errs) {
+ if (bits.size() < 1) {
+ String msg = "Must specify name of variable and definition to autovivify";
- if (idx == -1) {
- String msg = "Affixing pragma %s-with takes at least two arguments, the name of the rule to affix, then what to affix it with\n\tThis can be more than one token, to get them affixed as a group";
+ errs.addChild(msg);
- throw new GrammarException(String.format(msg, afxType.toString().toLowerCase()));
+ return;
}
- String rName = body.substring(0, idx);
-
- IList<CaseElement> elms = parseElementString(body.substring(idx + 1)).getLeft();
-
- build.affixWith(rName, elms, afxType);
- }
+ String[] bitArr = bits.toArray(new String[0]);
- private static void doAutoVar(String body, RGrammarBuilder build, int level, boolean isRule) {
- List<String> bits = StringUtils.levelSplit(body, " ");
+ List<CaseElement> elmList = new ArrayList<>();
- if (bits.size() < 1) {
- String msg = "Must specify name of variable and definition to autovivify";
- throw new GrammarException(msg);
- }
+ parseElementString(bitArr, elmList, errs);
+ CaseElement elm = elmList.get(0);
- String[] bitArr = bits.toArray(new String[0]);
+ if (elmList.size() > 1) {
+ String msg = String.format("WARN: Ignoring %d additional elements for autovivify: %s", elmList.size(), elmList.subList(1, elmList.size()));
- IList<CaseElement> elmList = parseElementString(bitArr).getLeft();
- CaseElement elm = elmList.first();
+ errs.addChild(msg);
- if (elmList.getSize() > 1) {
- warn("Ignoring %d additional elements for autovivify: %s", elmList.getSize(), elmList.tail());
+ return;
}
if (!(elm instanceof VariableDefCaseElement)) {
- throw new GrammarException(String.format("Autovivify expression must be a variable defn. (expr. %s)", elm));
+ String msg = String.format("ERROR: Autovivify expression must be a variable defn. (expr. %s)", elm);
+
+ errs.addChild(msg);
+
+ return;
}
{
@@ -313,64 +136,80 @@ public class RGrammarParser {
* @throws GrammarException
* Thrown if the grammar has a syntax error.
*/
- public static RGrammar readGrammar(Reader is) throws GrammarException {
+ public static RGrammar readGrammar(Reader is, LoadOptions lopts, ITree<String> errs) throws GrammarException {
String dlm = String.format(TMPL_TOPLEVEL_BLOCK_DELIM, 0);
try (BlockReader reader = new SimpleBlockReader(dlm, is)) {
if (!reader.hasNextBlock()) {
- throw new GrammarException("At least one top-level block must be present");
+ errs.addChild("At least one top-level block must be present");
+
+ return null;
}
- try {
RGrammarBuilder build = new RGrammarBuilder();
for(Block block : reader) {
- if(DEBUG)
- System.err.printf("Handling top-level block (%s)\n", block);
+ if(lopts.doTrace) {
+ String msg = String.format("TRACE: Handling top-level block (%s)\n", block);
+
+ errs.addChild(msg);
+ }
+
+ String msg = String.format("INFO: Block %d (%d-%d) (offset %d, %d-%d)", block.blockNo, block.startLine, block.endLine, block.lineOffset, block.lineOffset + block.startLine, block.lineOffset + block.endLine);
+
+ ITree<String> kid = new Tree<>(msg);
+
+ handleBlock(build, block.contents, 0, block.startLine, lopts, kid);
- handleBlock(build, block.contents, 0, block.startLine);
+ if (kid.size() > 0) errs.addChild(kid);
}
- if(LINES)
- System.err.printf("%d ", reader.getBlock().endLine);
+ if(lopts.doTrace) {
+ errs.addChild(String.format("TRACE: Ended at line %d ", reader.getBlock().endLine));
+ }
return build.toRGrammar();
- } catch (GrammarException gex) {
- String msg = String.format("Error in block (%s)", reader.getBlock());
- throw new GrammarException(msg, gex, gex.getRootMessage());
- }
- } catch (Exception ex) {
- throw new GrammarException("Unknown error handling block", ex, ex.getMessage());
+ } catch (IOException ioex) {
+ String msg = String.format("ERROR: Unknown I/O error: %s", ioex.getMessage());
+
+ errs.addChild(msg);
}
+
+ return null;
}
- /* Throughout these, level indicates the nesting level of that construct. */
+ /*
+ * Throughout these, level indicates the nesting level of that construct,
+ * and lineOffset indicates the total number of lines to adjust the block
+ * line numbers by.
+ */
/* Handles an arbitrary block. */
- private static void handleBlock(RGrammarBuilder build, String block,
- int level, int lineOffset) throws GrammarException {
+ private static void handleBlock(RGrammarBuilder build, String block, int level, int lineOffset, LoadOptions lopts, ITree<String> errs) {
/* Discard empty blocks. */
- if (block.equals("") || block.matches("\\R"))
- return;
+ if (block.equals("") || block.matches("\\R")) return;
int typeSep = block.indexOf(' ');
if (typeSep == -1) {
- throw new GrammarException(
- "A block must start with a introducer, followed by a space, then the rest of the block");
+ errs.addChild("ERROR: A block must start with a introducer, followed by a space, then the rest of the block");
}
String blockType = block.substring(0, typeSep).trim();
if (blockType.equalsIgnoreCase("pragma")) {
- handlePragmaBlock(block, build, level, lineOffset);
+ handlePragmaBlock(block, build, level, lineOffset, lopts, errs);
} else if (blockType.startsWith("[")) {
- handleRuleBlock(block, build, level, lineOffset);
+ handleRuleBlock(block, build, level, lineOffset, lopts, errs);
} else if (blockType.equalsIgnoreCase("where")) {
- handleWhereBlock(block, build, level, lineOffset);
+ handleWhereBlock(block, build, level, lineOffset, lopts, errs);
} else if (blockType.startsWith("#")) {
- if(DEBUG)
- System.err.printf("Handled comment block (%s)\n", block);
+ if(lopts.doTrace) {
+ String msg = String.format("TRACE: Handled comment block (%s)\n", block);
+
+ errs.addChild(msg);
+ }
+
/*
* Comment block.
*
@@ -381,137 +220,368 @@ public class RGrammarParser {
*/
return;
} else {
- String msg = String.format("Unknown block type: '%s'", blockType);
- throw new GrammarException(msg);
+ String msg = String.format("ERROR: Unknown block type: '%s'", blockType);
+
+ errs.addChild(msg);
}
}
/* Handle reading a block of pragmas. */
- private static void handlePragmaBlock(String block, RGrammarBuilder build,
- int level, int lineOffset) throws GrammarException {
+ private static void handlePragmaBlock(String block, RGrammarBuilder build, int level, int lineOffset, LoadOptions lopts, ITree<String> errs) {
String dlm = String.format(TMPL_PRAGMA_BLOCK_DELIM, level);
- try (BlockReader pragmaReader = new SimpleBlockReader(dlm, new StringReader(block))) {
- try {
- for(Block pragma : pragmaReader) {
- pragma.lineOffset = lineOffset;
- if(DEBUG)
- System.err.printf("Handled pragma block (%s)\n", pragma);
+ try (BlockReader pragmaReader = new SimpleBlockReader(dlm, new StringReader(block))) {
+ for(Block pragma : pragmaReader) {
+ pragma.lineOffset = lineOffset;
- String pragmaContents = pragma.contents;
+ if(lopts.doTrace) {
+ System.err.printf("TRACE: Handled pragma block (%s)\n", pragma);
+ }
- int pragmaSep = pragmaContents.indexOf(' ');
+ String pragmaContents = pragma.contents;
- if (pragmaSep == -1) {
- String msg = "A pragma invocation must consist of the word pragma, followed by a space, then the body of the pragma";
+ int pragmaSep = pragmaContents.indexOf(' ');
- throw new GrammarException(msg);
- }
+ if (pragmaSep == -1) {
+ String msg = "ERROR: A pragma invocation must consist of the word pragma, followed by a space, then the body of the pragma";
- String pragmaLeader = pragmaContents.substring(0, pragmaSep);
- String pragmaBody = pragmaContents.substring(pragmaSep + 1);
+ errs.addChild(msg);
+ return;
+ }
- if (!pragmaLeader.equalsIgnoreCase("pragma")) {
- String msg = String.format("Illegal line leader in pragma block: '%s'", pragmaLeader);
+ String pragmaLeader = pragmaContents.substring(0, pragmaSep);
+ String pragmaBody = pragmaContents.substring(pragmaSep + 1);
- throw new GrammarException(msg);
- }
+ if (!pragmaLeader.equalsIgnoreCase("pragma")) {
+ String msg = String.format("ERROR: Illegal line leader in pragma block: '%s'", pragmaLeader);
- handlePragma(pragmaBody, build, level, pragma.startLine + lineOffset);
+ errs.addChild(msg);
+ return;
}
- } catch (GrammarException gex) {
- Block pragma = pragmaReader.getBlock();
- String msg = String.format("Error in pragma: (%s)", pragma);
- throw new GrammarException(msg, gex, gex.getRootMessage());
+ handlePragma(pragmaBody, build, level, pragma.startLine + lineOffset, lopts, errs);
}
- } catch (Exception ex) {
- throw new GrammarException("Unknown error handling pragma block", ex, ex.getMessage());
+ } catch (IOException ioex) {
+ String msg = String.format("ERROR: Unknown I/O error in pragma block: %s", ioex.getMessage());
+
+ errs.addChild(msg);
}
}
/* Handle an individual pragma in a block. */
- private static void handlePragma(String pragma, RGrammarBuilder build,
- int level, int lineOffset) throws GrammarException {
+ private static void handlePragma(String pragma, RGrammarBuilder build, int level, int lineOffset, LoadOptions lopts, ITree<String> errs) {
int bodySep = pragma.indexOf(' ');
- if (bodySep == -1)
- bodySep = pragma.length();
+ if (bodySep == -1) bodySep = pragma.length();
String pragmaName = pragma.substring(0, bodySep);
String pragmaBody = pragma.substring(bodySep + 1);
- if (pragmas.containsKey(pragmaName)) {
- try {
- if(DEBUG)
- System.err.printf("Handled pragma '%s'\n", pragmaName);
+ if(lopts.doTrace) {
+ String msg = String.format("TRACE: Handled pragma '%s'\n", pragmaName);
+
+ errs.addChild(msg);
+ }
+
+ // Pragma bits
+ List<String> bits = StringUtils.levelSplit(pragmaBody, " ");
+
+ String fmt = String.format("INFO: Pragma '%s'", pragmaName);
+ ITree<String> kid = new Tree<>(fmt);
- pragmas.get(pragmaName).accept(pragmaBody, build, level);
- } catch (GrammarException gex) {
- String msg = String.format("Error in pragma '%s'", pragmaName);
+ switch (pragmaName) {
+ case "initial-rule":
+ {
+ if (bits.size() != 1) {
+ kid.addChild("ERROR: Must specify initial rule");
- throw new GrammarException(msg, gex);
+ break;
+ }
+
+ build.setInitialRule(bits.get(0), kid);
}
- } else {
- String msg = String.format("Unknown pragma '%s'", pragmaName);
+ break;
+ case "grammar-name":
+ {
+ if (bits.size() != 1) {
+ kid.addChild("ERROR: Must specify grammar name");
+
+ break;
+ }
+
+ build.name = bits.get(0);
+ }
+ break;
+ case "despace-rule":
+ {
+ if (bits.size() < 1) {
+ kid.addChild("ERROR: Must specify at least one rule to despace");
+
+ break;
+ }
+
+ for(String bit : bits) {
+ build.despaceRule(bit, kid, lopts.doTrace);
+ }
+ }
+ break;
+ case "export-rule":
+ {
+ if(bits.size() < 1) {
+ kid.addChild("ERROR: Must specify rules to export");
+
+ break;
+ }
+
+ for (String export : bits) {
+ build.addExport(export);
+ }
+ }
+ break;
+ case "recur-limit":
+ {
+ if(bits.size() != 2) {
+ kid.addChild("ERROR: Takes two arguments: the name of the rule to set the limit for, and the new value of the limit");
+
+ break;
+ }
+
+ if(!bits.get(1).matches("\\A\\d+\\Z")) {
+ kid.addChild("ERROR: Limit value must be an integer");
+
+ break;
+ }
+
+ build.setRuleRecur(bits.get(0), Integer.parseInt(bits.get(1)), kid);
+ }
+ break;
+ case "enable-weight":
+ {
+ if(bits.size() != 1) {
+ kid.addChild("ERROR: Takes one argument: the name of the rule to enable standard weighting for");
+ }
+
+ build.setWeight(bits.get(0), kid);
+ }
+ break;
+ case "enable-descent":
+ {
+ if(bits.size() != 2) {
+ kid.addChild("ERROR: Takes two arguments: The name of the rule to set to descent mode, and the value of the descent factor");
+
+ break;
+ }
+
+ if(!bits.get(1).matches("\\A\\d+\\Z")) {
+ kid.addChild("ERROR: Factor value must be an integer");
+
+ break;
+ }
+
+ build.setDescent(bits.get(0), Integer.parseInt(bits.get(1)), kid);
+ }
+ break;
+ case "enable-binomial":
+ {
+ // @NOTE 9/4/18
+ //
+ // This can be kind of hard to read right off. Is there
+ // a format to put stuff in that looks better and is
+ // more readable?
+
+ if(bits.size() != 4) {
+ kid.addChild("ERROR: Takes four arguments: the name of the rule to set the binomial factors for, and the three binomial parameters (target, bound, trials) (target/bound chance of success)");
+
+ break;
+ }
+
+ if(!bits.get(1).matches("\\A\\d+\\Z")) {
+ kid.addChild("ERROR: Target value must be an integer");
+
+ break;
+ }
+
+ if(!bits.get(2).matches("\\A\\d+\\Z")) {
+ kid.addChild("ERROR: Bound value must be an integer");
+
+ break;
+ }
+
+ if(!bits.get(3).matches("\\A\\d+\\Z")) {
+ kid.addChild("ERROR: Trials value must be an integer");
+
+ break;
+ }
+
+ build.setBinomial(bits.get(0), Integer.parseInt(bits.get(1)), Integer.parseInt(bits.get(2)), Integer.parseInt(bits.get(3)), kid);
+ }
+ break;
+ case "find-replace-rule":
+ {
+ /*
+ * @NOTE 4/9/18
+ *
+ * Consider if we want to replace this with something more akin
+ * to the `definer` feature from DiceLang. This will work fine
+ * in most cases, but there are some cases where you'd want the
+ * extra power. No examples are apparent at the moment.
+ */
+ if(bits.size() != 3) {
+ kid.addChild("ERROR: Takes three arguments: the name of the rule to process, then the find/replace pair to apply after the rule has been generated.");
- throw new GrammarException(msg);
+ break;
+ }
+
+ build.findReplaceRule(bits.get(0), bits.get(1), bits.get(2), kid);
+ }
+ break;
+ case "reject-rule":
+ {
+ if(bits.size() != 2) {
+ kid.addChild("ERROR: Takes two arguments: the name of the rule to process, then the rejection pattern to apply after the rule has been generated.");
+
+ break;
+ }
+
+ build.rejectRule(bits.get(0), bits.get(1), kid);
+ }
+ break;
+ case "prefix-with":
+ {
+ doAffixWith(pragmaBody, build, level, AffixType.PREFIX, kid);
+ }
+ break;
+ case "suffix-with":
+ {
+ doAffixWith(pragmaBody, build, level, AffixType.SUFFIX, kid);
+ }
+ break;
+ case "circumfix-with":
+ {
+ doAffixWith(pragmaBody, build, level, AffixType.CIRCUMFIX, kid);
+ }
+ break;
+ /*
+ * @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.
+ *
+ */
+ case "autovivify":
+ {
+ doAutoVar(bits, build, level, false, kid);
+ }
+ break;
+ case "autovivify-rule":
+ {
+ doAutoVar(bits, build, level, true, kid);
+ }
+ break;
+ default:
+ {
+ String msg = String.format("ERROR: Unknown pragma '%s'", pragmaName);
+
+ kid.addChild(msg);
+ }
}
+
+ if (kid.size() > 0) errs.addChild(kid);
}
/* Handle a block of a rule declaration and one or more cases. */
- private static void handleRuleBlock(String ruleBlock, RGrammarBuilder build,
- int level, int lineOffset) throws GrammarException {
+ private static void handleRuleBlock(String ruleBlock, RGrammarBuilder build, int level, int lineOffset, LoadOptions lopts, ITree<String> errs) {
String dlm = String.format(TMPL_RULEDECL_BLOCK_DELIM, level);
+
try (BlockReader ruleReader = new SimpleBlockReader(dlm, new StringReader(ruleBlock))) {
- try {
- if (ruleReader.hasNextBlock()) {
- /* Rule with a declaration followed by multiple cases. */
- ruleReader.nextBlock();
- Block declBlock = ruleReader.getBlock();
- declBlock.lineOffset = lineOffset;
+ ITree<String> kid = new Tree<>();
- String declContents = declBlock.contents;
- Rule rl = handleRuleDecl(build, declContents, lineOffset + declBlock.startLine);
+ if (ruleReader.hasNextBlock()) {
+ /* Rule with a declaration followed by multiple cases. */
+ ruleReader.nextBlock();
+ Block declBlock = ruleReader.getBlock();
+ declBlock.lineOffset = lineOffset;
- for(Block block : ruleReader) {
- /* Ignore comment lines. */
- if(block.contents.trim().startsWith("#")) return;
+ String declContents = declBlock.contents;
+ Rule rl = handleRuleDecl(build, declContents, lineOffset + declBlock.startLine, kid);
- handleRuleCase(block.contents, build, rl, block.startLine + lineOffset);
- }
- } else {
- /* Rule with a declaration followed by a single case. */
- handleRuleDecl(build, ruleBlock, lineOffset);
- }
- } catch (GrammarException gex) {
- String msg = String.format("Error in rule case (%s)", ruleReader.getBlock());
+ // Error occured during rule processing
+ if (rl == null) return;
+
+ for(Block block : ruleReader) {
+ /* Ignore comment lines. */
+ if(block.contents.trim().startsWith("#")) return;
- throw new GrammarException(msg, gex, gex.getRootMessage());
+ handleRuleCase(block.contents, build, rl, block.startLine + lineOffset, kid);
+ }
+ } else {
+ /* Rule with a declaration followed by a single case. */
+ handleRuleDecl(build, ruleBlock, lineOffset, kid);
}
- } catch (Exception ex) {
- throw new GrammarException("Unknown error handling rule block", ex, ex.getMessage());
+
+ if (kid.size() > 0) errs.addChild(kid);
+ } catch (IOException ex) {
+ String msg = String.format("ERROR: Unknown error handling rule block (%s)",ex.getMessage());
+
+ errs.addChild(msg);
}
}
/* Handle a rule declaration and its initial case. */
- private static Rule handleRuleDecl(RGrammarBuilder build, String declContents, int lineOffset) {
+ private static Rule handleRuleDecl(RGrammarBuilder build, String declContents, int lineOffset, ITree<String> errs) {
int declSep = declContents.indexOf("\u2192");
if (declSep == -1) {
/*
* @NOTE
* We should maybe remove support for the old
- * syntax at some point. However, maybe we don't
- * want to do so so as to make inputting grammars
- * easier.
+ * syntax at some point.
+ *
+ * We don't want to do so so as to make inputting grammars easier,
+ * since that character is not easy to type on a normal keyboard,
+ * and takes 4 keystrokes in vim as composed to 1 for the normal
+ * one.
*/
declSep = declContents.indexOf(' ');
if (declSep == -1) {
String msg = "A rule must be given at least one case in its declaration, and seperated from that case by \u2192 or ' '";
- throw new GrammarException(msg);
+ errs.addChild(msg);
+
+ return null;
}
}
@@ -519,70 +589,78 @@ public class RGrammarParser {
String ruleBody = declContents.substring(declSep + 1).trim();
if (ruleName.equals("")) {
- throw new GrammarException("The empty string is not a valid rule name");
+ errs.addChild("ERROR: The empty string is not a valid rule name");
+
+ return null;
}
- Rule rul = build.getOrCreateRule(ruleName);
+ errs.setHead("INFO: Rule " + ruleName);
+
+ Rule rul = build.getOrCreateRule(ruleName, errs);
- handleRuleCase(ruleBody, build, rul, lineOffset);
+ if (rul == null) return null;
+
+ handleRuleCase(ruleBody, build, rul, lineOffset, errs);
return rul;
}
/* Handle a single case of a rule. */
- private static void handleRuleCase(String cse, RGrammarBuilder build, Rule rul, int lineOffset) {
- Pair<IList<CaseElement>, Integer> caseParts = parseElementString(cse);
+ private static void handleRuleCase(String cse, RGrammarBuilder build, Rule rul, int lineOffset, ITree<String> errs) {
+ List<CaseElement> elms = new ArrayList<>();
+
+ int weights = parseElementString(cse, elms, errs);
- rul.addCase(new NormalRuleCase(caseParts.getLeft()), caseParts.getRight());
+ rul.addCase(new NormalRuleCase(elms), weights);
}
/* Handle a where block (a block with local rules). */
- private static void handleWhereBlock(String block, RGrammarBuilder build,
- int level, int lineOffset) throws GrammarException {
+ private static void handleWhereBlock(String block, RGrammarBuilder build, int level, int lineOffset, LoadOptions lopts, ITree<String> errs) {
int nlIndex = block.indexOf("\\nin");
if (nlIndex == -1) {
- throw new GrammarException("Where block must be a context followed by a body");
+ errs.addChild("ERROR: Where block must be a context followed by a body");
+
+ return;
}
String trimBlock = block.substring(nlIndex).trim();
String whereDelim = String.format(TMPL_WHERE_BLOCK_DELIM, level);
- try (BlockReader whereReader = new SimpleBlockReader(whereDelim,
- new StringReader(trimBlock))) {
- try {
- Block whereCtx = whereReader.next();
- whereCtx.lineOffset = lineOffset;
-
- StringReader ctxReader = new StringReader(whereCtx.contents.trim());
- String ctxDelim = String.format(TMPL_TOPLEVEL_BLOCK_DELIM, level + 1);
-
- try (BlockReader bodyReader = new SimpleBlockReader(ctxDelim, ctxReader)) {
- @SuppressWarnings("unused")
- Block whereBody = whereReader.next();
- whereBody.lineOffset = lineOffset + whereCtx.startLine;
-
- System.err.printf("\tUNIMPLEMENTED WHERE:\n%s\n", whereBody.contents);
- /**
- * @TODO 10/11/17 Ben Culkin :WhereBlocks
- * Implement where blocks.
- *
- * A where block has the context evaluated
- * in a new context, and the body executed
- * in that context.
- */
- }
- } catch (GrammarException gex) {
- throw new GrammarException(String.format("Error in where block (%s)",
- whereReader.getBlock()), gex, gex.getRootMessage());
+ try (BlockReader whereReader = new SimpleBlockReader(whereDelim, new StringReader(trimBlock))) {
+ Block whereCtx = whereReader.next();
+ whereCtx.lineOffset = lineOffset;
+
+ StringReader ctxReader = new StringReader(whereCtx.contents.trim());
+ String ctxDelim = String.format(TMPL_TOPLEVEL_BLOCK_DELIM, level + 1);
+
+ try (BlockReader bodyReader = new SimpleBlockReader(ctxDelim, ctxReader)) {
+ @SuppressWarnings("unused")
+ Block whereBody = whereReader.next();
+ whereBody.lineOffset = lineOffset + whereCtx.startLine;
+
+ String msg = String.format("UNIMPLEMENTED WHERE:\n%s\n", whereBody.contents);
+ errs.addChild(msg);
+ /**
+ * @TODO 10/11/17 Ben Culkin :WhereBlocks
+ * Implement where blocks.
+ *
+ * A where block has the context evaluated
+ * in a new context, and the body executed
+ * in that context.
+ */
}
- } catch (Exception ex) {
- throw new GrammarException("Unknown error in where block", ex, ex.getMessage());
+ } catch (IOException ioex) {
+ String msg = String.format("Unknown error in where block (%s)", ioex.getMessage());
}
}
- public static Pair<IList<CaseElement>, Integer> parseElementString(String cses) {
+ public static int parseElementString(String cses, List<CaseElement> elms) {
+ return parseElementString(cses, elms, new Tree<>());
+ }
+
+ public static int parseElementString(String cses, List<CaseElement> elms, ITree<String> errs) {
/*
* @NOTE
*
@@ -595,12 +673,14 @@ public class RGrammarParser {
* return parseElementString(cseList.toArray(new String[0]));
*/
- return parseElementString(cses.split(" "));
+ return parseElementString(cses.split(" "), elms, errs);
}
- public static Pair<IList<CaseElement>, Integer> parseElementString(String... cses) {
- IList<CaseElement> caseParts = new FunctionalList<>();
+ public static int parseElementString(String[] cses, List<CaseElement> elms) {
+ return parseElementString(cses, elms, new Tree<>());
+ }
+ public static int parseElementString(String[] cses, List<CaseElement> caseParts, ITree<String> errs) {
int weight = 1;
int repCount = 1;
@@ -650,7 +730,7 @@ public class RGrammarParser {
* a setting on CaseElement, instead of having
* their own CaseElement type.
*/
- CaseElement elm = caseParts.popLast();
+ CaseElement elm = caseParts.remove(caseParts.size() - 1);
if(repCount == 0) {
/* Skip no-reps */
@@ -703,6 +783,6 @@ public class RGrammarParser {
}
}
- return new Pair<>(caseParts, weight);
+ return weight;
}
}
diff --git a/src/main/java/bjc/rgens/parser/RGrammarTest.java b/src/main/java/bjc/rgens/parser/RGrammarTest.java
index 788823a..36c87de 100755
--- a/src/main/java/bjc/rgens/parser/RGrammarTest.java
+++ b/src/main/java/bjc/rgens/parser/RGrammarTest.java
@@ -2,11 +2,17 @@ package bjc.rgens.parser;
import bjc.rgens.parser.templates.GrammarTemplate;
+import bjc.utils.data.ITree;
+import bjc.utils.data.Tree;
+
import java.io.IOException;
+
import java.net.URISyntaxException;
import java.net.URL;
+
import java.nio.file.Path;
import java.nio.file.Paths;
+
import java.util.Random;
import static bjc.rgens.parser.RGrammarLogging.*;
@@ -27,9 +33,24 @@ public class RGrammarTest {
URL rsc = RGrammarTest.class.getResource("/server-config-sample.gcfg");
try {
+ LoadOptions lopts = new LoadOptions();
+
+ // Set up load options
+ lopts.doPerf = true;
+ lopts.doDebug = false;
+ lopts.doTrace = false;
+
+ lopts.defName = "default";
+
/* Load a grammar set. */
- Path cfgPath = Paths.get(rsc.toURI());
- ConfigSet cfgSet = ConfigLoader.fromConfigFile(cfgPath);
+ Path cfgPath = Paths.get(rsc.toURI());
+
+ String msg = String.format("INFO: Loading config file %s", cfgPath);
+ ITree<String> errTree = new Tree<>(msg);
+
+ ConfigSet cfgSet = ConfigLoader.fromConfigFile(cfgPath, lopts, errTree);
+
+ System.err.print(errTree);
for(RGrammarSet gramSet : cfgSet.grammars.values()) {
testGrammarSet(gramSet);
diff --git a/src/main/java/bjc/rgens/parser/RGrammars.java b/src/main/java/bjc/rgens/parser/RGrammars.java
index cc31bad..51d9fe9 100755
--- a/src/main/java/bjc/rgens/parser/RGrammars.java
+++ b/src/main/java/bjc/rgens/parser/RGrammars.java
@@ -30,7 +30,12 @@ public class RGrammars {
Path cfgPath = Paths.get(rsc);
- cfgSet = ConfigLoader.fromConfigFile(cfgPath);
+ LoadOptions lopts = new LoadOptions();
+
+ lopts.doPerf = false;
+ lopts.defName = "default";
+
+ cfgSet = ConfigLoader.fromConfigFile(cfgPath, lopts);
} catch (IOException | URISyntaxException ex) {
RuntimeException rtex = new RuntimeException("Could not load grammars");
diff --git a/src/main/java/bjc/rgens/parser/RegexRuleCase.java b/src/main/java/bjc/rgens/parser/RegexRuleCase.java
index 3c57489..1712d57 100755
--- a/src/main/java/bjc/rgens/parser/RegexRuleCase.java
+++ b/src/main/java/bjc/rgens/parser/RegexRuleCase.java
@@ -1,8 +1,8 @@
package bjc.rgens.parser;
import bjc.rgens.parser.elements.CaseElement;
-import bjc.utils.funcdata.IList;
+import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -12,7 +12,7 @@ import java.util.regex.PatternSyntaxException;
* Actually implement this
*/
public class RegexRuleCase extends RuleCase {
- public RegexRuleCase(IList<CaseElement> elements) {
+ public RegexRuleCase(List<CaseElement> elements) {
super(elements);
}
@@ -21,7 +21,7 @@ public class RegexRuleCase extends RuleCase {
}
- public RegexRuleCase withElements(IList<CaseElement> elements) {
+ public RegexRuleCase withElements(List<CaseElement> elements) {
return new RegexRuleCase(elements);
}
}
diff --git a/src/main/java/bjc/rgens/parser/Rule.java b/src/main/java/bjc/rgens/parser/Rule.java
index 1a74352..377da9e 100755
--- a/src/main/java/bjc/rgens/parser/Rule.java
+++ b/src/main/java/bjc/rgens/parser/Rule.java
@@ -1,6 +1,8 @@
package bjc.rgens.parser;
import bjc.utils.data.IPair;
+import bjc.utils.data.ITree;
+import bjc.utils.data.Tree;
import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.IList;
import bjc.utils.gen.WeightedRandom;
@@ -121,20 +123,32 @@ public class Rule {
}
public void addRejection(String reject) {
+ addRejection(reject, new Tree<>());
+ }
+
+ public void addRejection(String reject, ITree<String> errs) {
try {
Pattern.compile(reject);
} catch (PatternSyntaxException psex) {
- throw new GrammarException(String.format("String %s is not a valid regex for rejection", reject), psex);
+ String msg = String.format("ERROR: '%s' is not a valid regex for rejection (%s)", reject, psex.getMessage());
}
rejectionPreds.add(reject);
}
public void addFindReplace(String find, String replace) {
+ addFindReplace(find, replace, new Tree<>());
+ }
+
+ public void addFindReplace(String find, String replace, ITree<String> errs) {
try {
Pattern.compile(find);
} catch (PatternSyntaxException psex) {
- throw new GrammarException(String.format("String %s is not a valid regex for finding", find), psex);
+ String msg = String.format("ERROR: '%s' is not a valid regex for finding (%s)", find, psex.getMessage());
+
+ errs.addChild(msg);
+
+ return;
}
findReplaces.add(pair(find, replace));
diff --git a/src/main/java/bjc/rgens/parser/RuleCase.java b/src/main/java/bjc/rgens/parser/RuleCase.java
index 33aea0c..dacb16e 100755
--- a/src/main/java/bjc/rgens/parser/RuleCase.java
+++ b/src/main/java/bjc/rgens/parser/RuleCase.java
@@ -1,7 +1,8 @@
package bjc.rgens.parser;
import bjc.rgens.parser.elements.CaseElement;
-import bjc.utils.funcdata.IList;
+
+import java.util.List;
/*
* @NOTE
@@ -22,7 +23,7 @@ public abstract class RuleCase {
public Rule belongsTo;
- public IList<CaseElement> elementList;
+ public List<CaseElement> elementList;
/**
* Create a new case of the specified type that takes a element list
@@ -32,7 +33,7 @@ public abstract class RuleCase {
* The element list parameter of the case.
*
*/
- protected RuleCase(IList<CaseElement> elements) {
+ protected RuleCase(List<CaseElement> elements) {
elementList = elements;
serial = nextSerial;
@@ -41,7 +42,7 @@ public abstract class RuleCase {
public abstract void generate(GenerationState state);
- public abstract RuleCase withElements(IList<CaseElement> elements);
+ public abstract RuleCase withElements(List<CaseElement> elements);
public String toString() {
if(debugName != null) {
diff --git a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java
index 917dd33..269cc4f 100644
--- a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java
+++ b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java
@@ -3,16 +3,23 @@ package bjc.rgens.parser.elements;
import bjc.rgens.parser.GenerationState;
import bjc.rgens.parser.RGrammarParser;
-import bjc.utils.data.IPair;
+import bjc.utils.data.ITree;
+import bjc.utils.data.Tree;
import bjc.utils.funcdata.FunctionalList;
-import bjc.utils.funcdata.IList;
import bjc.utils.funcutils.StringUtils;
import bjc.utils.gen.WeightedRandom;
+import java.util.ArrayList;
+import java.util.List;
+
public class InlineRuleCaseElement extends CaseElement {
public final WeightedRandom<CaseElement> elements;
public InlineRuleCaseElement(String... parts) {
+ this(new Tree<>(), parts);
+ }
+
+ public InlineRuleCaseElement(ITree<String> errs, String... parts) {
super(true);
this.elements = new WeightedRandom<>();
@@ -26,10 +33,10 @@ public class InlineRuleCaseElement extends CaseElement {
partArr = new String[] {part};
}
- IPair<IList<CaseElement>, Integer> par = RGrammarParser.parseElementString(partArr);
- int prob = par.getRight();
+ List<CaseElement> elms = new ArrayList<>();
+ int prob = RGrammarParser.parseElementString(partArr, elms, errs);
- for(CaseElement elm : par.getLeft()) {
+ for(CaseElement elm : elms) {
elements.addProbability(prob, elm);
}
}
diff --git a/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java b/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java
index 8a99188..64db166 100644
--- a/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java
+++ b/src/main/java/bjc/rgens/parser/templates/GrammarTemplate.java
@@ -2,25 +2,58 @@ package bjc.rgens.parser.templates;
import bjc.rgens.parser.ConfigSet;
import bjc.rgens.parser.GenerationState;
+import bjc.rgens.parser.LoadOptions;
+
+import bjc.utils.data.ITree;
+import bjc.utils.data.Tree;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
+/**
+ * Represents a grammar template.
+ *
+ * @author Ben Culkin
+ */
public class GrammarTemplate {
+ /**
+ * The config set the template belongs to.
+ */
public ConfigSet belongsTo;
+ /**
+ * The name of the template.
+ */
public String name;
+ /**
+ * The elements in the template.
+ */
public final List<TemplateElement> elements;
+ /**
+ * Whether or not to do spacing of elements.
+ */
public boolean doSpacing = true;
+ /**
+ * Create a new grammar template.
+ *
+ * @param elements
+ * The elements that belong to the template.
+ */
public GrammarTemplate(List<TemplateElement> elements) {
this.elements = elements;
}
+ /**
+ * Generate the template.
+ *
+ * @param state
+ * The state for generating a template.
+ */
public void generate(GenerationState state) {
for(TemplateElement element : elements) {
element.generate(state);
@@ -30,7 +63,18 @@ public class GrammarTemplate {
}
}
- public static GrammarTemplate readTemplate(Reader rdr) {
+ /**
+ * Read a template from an input source.
+ *
+ * @param rdr
+ * The reader to get input from.
+ *
+ * @param errs
+ * The errors/information to generate during loading.
+ *
+ * @return The generated template.
+ */
+ public static GrammarTemplate readTemplate(Reader rdr, ITree<String> errs) {
List<TemplateElement> elements = new ArrayList<>();
GrammarTemplate template = new GrammarTemplate(elements);
@@ -42,34 +86,46 @@ public class GrammarTemplate {
String ln = scn.nextLine();
lno += 1;
+ ITree<String> kid = new Tree<>(String.format("INFO: Line %d", lno));
switch(ln.charAt(0)) {
case '#':
// Ignore comments
break;
case '/':
- handlePragma(elements, template, ln.substring(1));
+ handlePragma(elements, template, ln.substring(1), kid);
break;
default:
- handleLine(elements, template, ln);
+ handleLine(elements, template, ln, kid);
}
- }
+ if (kid.size() > 0) {
+ errs.addChild(kid);
+ }
+ }
return template;
}
- private static void handleLine(List<TemplateElement> elements, GrammarTemplate template, String ln) {
+ private static void handleLine(List<TemplateElement> elements, GrammarTemplate template, String ln, ITree<String> errs) {
if(ln.matches("^.*?\\$@.+?@\\$.*$")) {
/*
* Handle live templates
*/
- elements.add(new LiveTemplateElement(ln));
+ elements.add(new LiveTemplateElement(ln, errs));
} else {
- elements.add(new LiteralTemplateElement(ln));
+ elements.add(new LiteralTemplateElement(ln, errs));
}
}
- private static void handlePragma(List<TemplateElement> elements, GrammarTemplate template, String ln) {
-
+ private static void handlePragma(List<TemplateElement> elements, GrammarTemplate template, String ln, ITree<String> errs) {
+ /*
+ * @TODO 2/8/2019 Ben Culkin :TemplatePragmas
+ * Implement template pragmas.
+ *
+ * Implement template pragmas. Mainly, this means that the 'choose'
+ * based ones need to be implemented based off of the provided sample
+ * template.
+ */
+ errs.addChild("ERROR: Template pragmas are not yet implemented");
}
}
diff --git a/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java b/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java
index ca4b32f..2f0a571 100644
--- a/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java
+++ b/src/main/java/bjc/rgens/parser/templates/LiteralTemplateElement.java
@@ -1,16 +1,33 @@
package bjc.rgens.parser.templates;
+import bjc.utils.data.ITree;
+
import bjc.rgens.parser.GenerationState;
+/**
+ * Represents a literal text element.
+ *
+ * @author Ben Culkin
+ */
public class LiteralTemplateElement extends TemplateElement {
+ /**
+ * The literal value of the element.
+ */
public final String val;
- public LiteralTemplateElement(String val) {
+ /**
+ * Create a new literal template element.
+ *
+ * @param val
+ * The string to insert.
+ */
+ public LiteralTemplateElement(String val, ITree<String> errs) {
super(true);
this.val = val;
}
+ @Override
public void generate(GenerationState state) {
state.appendContents(val);
}
diff --git a/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java b/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java
index 8dbde05..0aa3bd0 100644
--- a/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java
+++ b/src/main/java/bjc/rgens/parser/templates/LiveTemplateElement.java
@@ -1,7 +1,10 @@
package bjc.rgens.parser.templates;
import bjc.utils.data.BooleanToggle;
+import bjc.utils.data.ITree;
+import bjc.utils.data.Tree;
import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.IList;
import bjc.rgens.parser.GenerationState;
import bjc.rgens.parser.RGrammarParser;
@@ -14,12 +17,30 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+/**
+ * A template element that can contain rule elements.
+ *
+ * @author Ben Culkin.
+ */
public class LiveTemplateElement extends TemplateElement {
+ // Pattern for matching elements (any number of characters bracketed by '$@' and '@$')
private static final Pattern INSERT_PAT = Pattern.compile("\\$@(.+?)@\\$");
+ /**
+ * The sub-elements of this element.
+ */
public final List<List<CaseElement>> elements;
- public LiveTemplateElement(String val) {
+ /**
+ * Create a new template element.
+ *
+ * @param val
+ * The string to parse this element from.
+ *
+ * @param errs
+ * A tree to add errors &amp; information to.
+ */
+ public LiveTemplateElement(String val, ITree<String> errs) {
super(true);
elements = new ArrayList<>();
@@ -31,10 +52,12 @@ public class LiveTemplateElement extends TemplateElement {
mat.appendReplacement(sb, "");
String body = mat.group(1);
- FunctionalList<CaseElement> elms = (FunctionalList<CaseElement>)RGrammarParser.parseElementString(body).getLeft();
+ List<CaseElement> elms = new ArrayList<>();
+
+ int weight = RGrammarParser.parseElementString(body, elms, errs);
elements.add(Arrays.asList(new LiteralCaseElement(sb.toString())));
- elements.add(elms.getInternal());
+ elements.add(elms);
sb = new StringBuffer();
}
@@ -43,6 +66,7 @@ public class LiveTemplateElement extends TemplateElement {
elements.add(Arrays.asList(new LiteralCaseElement(sb.toString())));
}
+ @Override
public void generate(GenerationState state) {
BooleanToggle bt = new BooleanToggle(false);