From 2c842ce759095879036bb93321a528f83b77d1ee Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Mon, 4 Jun 2018 00:01:38 -0300 Subject: Add syntax features This adds a few new syntax features, as well as allowing certain ones to be used in pragmas. The next syntax feature to be added will be some sort of quote feature, to allow the inclusion of spaces where they would otherwise not be permitted --- .../java/bjc/rgens/parser/RGrammarBuilder.java | 16 ++-- src/main/java/bjc/rgens/parser/RGrammarParser.java | 96 ++++++++++++++++------ .../parser/elements/InlineRuleCaseElement.java | 3 +- .../rgens/parser/elements/SerialCaseElement.java | 30 +++++++ 4 files changed, 109 insertions(+), 36 deletions(-) create mode 100644 src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java (limited to 'src') diff --git a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java index bc93fa6..20cba93 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarBuilder.java +++ b/src/main/java/bjc/rgens/parser/RGrammarBuilder.java @@ -144,7 +144,7 @@ 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, String... suffixes) { + public void suffixWith(String ruleName, IList suffixes) { if (ruleName == null) { throw new NullPointerException("Rule name must not be null"); } else if (ruleName.equals("")) { @@ -155,9 +155,9 @@ public class RGrammarBuilder { throw new IllegalArgumentException(msg); } - Set elements = new HashSet<>(suffixes.length); - for(String suffix : suffixes) { - elements.add(CaseElement.createElement(suffix)); + Set elements = new HashSet<>(suffixes.getSize()); + for(CaseElement suffix : suffixes) { + elements.add(suffix); } List> suffixLists = powerList(elements); @@ -206,7 +206,7 @@ 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, String... prefixes) { + public void prefixWith(String ruleName, IList prefixes) { if (ruleName == null) { throw new NullPointerException("Rule name must not be null"); } else if (ruleName.equals("")) { @@ -217,9 +217,9 @@ public class RGrammarBuilder { throw new IllegalArgumentException(msg); } - Set elements = new HashSet<>(prefixes.length); - for(String prefix : prefixes) { - elements.add(CaseElement.createElement(prefix)); + Set elements = new HashSet<>(prefixes.getSize()); + for(CaseElement prefix : prefixes) { + elements.add(prefix); } List> prefixLists = powerList(elements); diff --git a/src/main/java/bjc/rgens/parser/RGrammarParser.java b/src/main/java/bjc/rgens/parser/RGrammarParser.java index 827cbb6..463f2ac 100755 --- a/src/main/java/bjc/rgens/parser/RGrammarParser.java +++ b/src/main/java/bjc/rgens/parser/RGrammarParser.java @@ -1,6 +1,10 @@ package bjc.rgens.parser; import bjc.rgens.parser.elements.CaseElement; +import bjc.rgens.parser.elements.SerialCaseElement; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; import bjc.utils.funcutils.ListUtils; @@ -109,27 +113,27 @@ public class RGrammarParser { }); pragmas.put("suffix-with", (body, build, level) -> { - String[] parts = body.trim().split(" "); + int idx = body.indexOf(" "); - if (parts.length < 2) { + if (idx == -1) { String msg = "Suffix-with pragma takes at least two arguments, the name of the rule to suffix, then what to suffix it with\n\tThis can be more than one token, to get them suffixed as a group"; throw new GrammarException(msg); } - build.suffixWith(parts[0], Arrays.copyOfRange(parts, 1, parts.length)); + build.suffixWith(body.substring(0, idx), parseElementString(body.substring(idx + 1)).getLeft()); }); pragmas.put("prefix-with", (body, build, level) -> { - String[] parts = body.trim().split(" "); + int idx = body.indexOf(" "); - if (parts.length < 2) { + if (idx == -1) { String msg = "Prefix-with pragma takes at least two arguments, the name of the rule to prefix, then what to prefix it with\n\tThis can be more than one token, to get them prefixed as a group"; throw new GrammarException(msg); } - build.prefixWith(parts[0], Arrays.copyOfRange(parts, 1, parts.length)); + build.prefixWith(body.substring(0, idx), parseElementString(body.substring(idx + 1)).getLeft()); }); } @@ -360,30 +364,14 @@ public class RGrammarParser { /* Handle a single case of a rule. */ private static void handleRuleCase(String cse, RGrammarBuilder build, Rule rul) { - IList caseParts = new FunctionalList<>(); - - int weight = 1; - - for (String csepart : cse.split(" ")) { - String partToAdd = csepart.trim(); - - if (partToAdd.equals("")) { - /* Ignore empty parts */ - continue; - } else if(partToAdd.matches("\\{\\^\\d+\\}")) { - /* Set case weights */ - weight = Integer.parseInt(partToAdd.substring(2, partToAdd.length() - 1)); - } else { - caseParts.add(CaseElement.createElement(partToAdd)); - } - } + Pair, Integer> caseParts = parseElementString(cse); - rul.addCase(new NormalRuleCase(caseParts), weight); + rul.addCase(new NormalRuleCase(caseParts.getLeft()), caseParts.getRight()); } /* Handle a where block (a block with local rules). */ private static void handleWhereBlock(String block, RGrammarBuilder build, - int level) throws GrammarException { + int level) throws GrammarException { int nlIndex = block.indexOf("\\nin"); if (nlIndex == -1) { @@ -395,7 +383,7 @@ public class RGrammarParser { String whereDelim = String.format(TMPL_WHERE_BLOCK_DELIM, level); try (BlockReader whereReader = new SimpleBlockReader(whereDelim, - new StringReader(trimBlock))) { + new StringReader(trimBlock))) { try { Block whereCtx = whereReader.next(); @@ -418,10 +406,64 @@ public class RGrammarParser { } } catch (GrammarException gex) { throw new GrammarException(String.format("Error in where block (%s)", - whereReader.getBlock()), gex); + whereReader.getBlock()), gex); } } catch (Exception ex) { throw new GrammarException("Unknown error in where block", ex); } } + + public static Pair, Integer> parseElementString(String cses) { + return parseElementString(cses.split(" ")); + } + + public static Pair, Integer> parseElementString(String... cses) { + IList caseParts = new FunctionalList<>(); + + int weight = 1; + + int repCount = 1; + + int serialLower = -1; + int serialUpper = -1; + + boolean doSerial = false; + + for (String csepart : cses) { + String partToAdd = csepart.trim(); + + if (partToAdd.equals("")) { + /* Ignore empty parts */ + continue; + } else if(partToAdd.matches("\\{\\^\\d+\\}")) { + /* Set case weights */ + weight = Integer.parseInt(partToAdd.substring(2, partToAdd.length() - 1)); + } else if(partToAdd.matches("\\{&\\d+\\}")) { + repCount = Integer.parseInt(partToAdd.substring(2, partToAdd.length() - 1)); + } else if(partToAdd.matches("\\{&\\d+\\.\\.\\d+\\}")) { + serialLower = Integer.parseInt(partToAdd.substring(2, partToAdd.indexOf("."))); + serialUpper = Integer.parseInt(partToAdd.substring(partToAdd.lastIndexOf(".") + 1, partToAdd.length() - 1)); + + doSerial = true; + } else { + CaseElement elm = CaseElement.createElement(partToAdd); + + if(repCount == 0) { + /* Skip no-reps */ + } else if(doSerial) { + caseParts.add(new SerialCaseElement(elm, serialLower, serialUpper)); + + doSerial = false; + } else { + for(int i = 1; i <= repCount; i++) { + caseParts.add(elm); + } + } + + repCount = 1; + } + } + + return new Pair<>(caseParts, weight); + } } diff --git a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java index 95bb5f9..6cb0ce3 100644 --- a/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java +++ b/src/main/java/bjc/rgens/parser/elements/InlineRuleCaseElement.java @@ -1,6 +1,7 @@ package bjc.rgens.parser.elements; import bjc.rgens.parser.GenerationState; +import bjc.rgens.parser.RGrammarParser; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IList; @@ -9,7 +10,7 @@ public class InlineRuleCaseElement extends CaseElement { public final IList elements; public InlineRuleCaseElement(String... elements) { - this(new FunctionalList<>(elements).map(CaseElement::createElement)); + this(RGrammarParser.parseElementString(elements).getLeft()); } public InlineRuleCaseElement(IList elements) { diff --git a/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java b/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java new file mode 100644 index 0000000..594595e --- /dev/null +++ b/src/main/java/bjc/rgens/parser/elements/SerialCaseElement.java @@ -0,0 +1,30 @@ +package bjc.rgens.parser.elements; + +import bjc.rgens.parser.GenerationState; + +public class SerialCaseElement extends CaseElement { + public final CaseElement rep; + + public final int lower; + public final int upper; + + public SerialCaseElement(CaseElement rep, int lower, int upper) { + super(rep.type); + + this.rep = rep; + + this.lower = lower; + this.upper = upper; + } + + public void generate(GenerationState state) { + int num = state.rnd.nextInt(upper - lower) + lower; + + for(int i = 0; i < num; i++) { + rep.generate(state); + + if(rep.type.spacing) + state.contents.append(" "); + } + } +} -- cgit v1.2.3