From 5444cd4db8a0fa41d25cd303c1145cadd112e12f Mon Sep 17 00:00:00 2001 From: bjculkin Date: Tue, 21 Mar 2017 19:29:27 -0400 Subject: Add formatter Adds a formatter capable of taking in a parsed grammar and printing it out in a formatted form, capable of being reparsed. --- .../main/java/bjc/rgens/newparser/CaseElement.java | 24 ++++++ .../main/java/bjc/rgens/newparser/RGrammar.java | 45 +++++++--- .../java/bjc/rgens/newparser/RGrammarBuilder.java | 12 ++- .../bjc/rgens/newparser/RGrammarFormatter.java | 98 ++++++++++++++++++++++ .../java/bjc/rgens/newparser/RGrammarParser.java | 8 +- .../java/bjc/rgens/newparser/RGrammarTest.java | 11 ++- RGens/src/main/java/bjc/rgens/newparser/Rule.java | 57 +++++++++++++ 7 files changed, 236 insertions(+), 19 deletions(-) create mode 100644 RGens/src/main/java/bjc/rgens/newparser/RGrammarFormatter.java (limited to 'RGens/src/main/java') diff --git a/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java b/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java index fe088b8..a15cb9b 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java +++ b/RGens/src/main/java/bjc/rgens/newparser/CaseElement.java @@ -91,12 +91,36 @@ public class CaseElement { literalVal = val; } + /** + /** * Get the literal string value for this element. * * @return The literal string value for this element. + * + * @throws IllegalStateException + * If this type doesn't have a literal string value. */ public String getLiteral() { + switch(type) { + case LITERAL: + case RULEREF: + break; + default: + throw new IllegalStateException(String.format("Type '%s' doesn't have a literal string value")); + } + return literalVal; } + + @Override + public String toString() { + switch(type) { + case LITERAL: + case RULEREF: + return literalVal; + default: + return String.format("Unknown type '%s'", type); + } + } } \ No newline at end of file diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java index 35fc356..3e82779 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammar.java @@ -114,18 +114,7 @@ public class RGrammar { state.contents.append(elm.getLiteral() + " "); break; case RULEREF: - if(rules.containsKey(elm.getLiteral())) { - RuleCase cse = rules.get(elm.getLiteral()).getCase(); - - generateCase(cse, state); - } else if(importRules.containsKey(elm.getLiteral())) { - RGrammar dst = importRules.get(elm.getLiteral()); - - state.contents.append(dst.generate(elm.getLiteral())); - } else { - throw new GrammarException( - String.format("No rule by name '%s' found", elm.getLiteral())); - } + generateRuleReference(elm, state); break; default: throw new GrammarException(String.format("Unknown element type '%s'", elm.type)); @@ -135,6 +124,29 @@ public class RGrammar { } } + /* + * Generate a rule reference. + */ + private void generateRuleReference(CaseElement elm, GenerationState state) { + String refersTo = elm.getLiteral(); + + GenerationState newState = new GenerationState(new StringBuilder()); + + if(rules.containsKey(refersTo)) { + RuleCase cse = rules.get(refersTo).getCase(); + + generateCase(cse, newState); + } else if(importRules.containsKey(refersTo)) { + RGrammar dst = importRules.get(refersTo); + + newState.contents.append(dst.generate(refersTo)); + } else { + throw new GrammarException(String.format("No rule by name '%s' found", refersTo)); + } + + state.contents.append(newState.contents.toString()); + } + /** * Get the initial rule of this grammar. * @@ -205,4 +217,13 @@ public class RGrammar { public void setExportedRules(Set exportRules) { this.exportRules = exportRules; } + + /** + * Get all the rules in this grammar. + * + * @return All the rules in this grammar. + */ + public Map getRules() { + return rules; + } } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java index 346e588..fb8003a 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java @@ -18,6 +18,10 @@ import static bjc.rgens.newparser.RuleCase.CaseType.*; * */ public class RGrammarBuilder { + private static final String REFER_CASELEM = "\\[[^\\]]+\\]"; + + private static final String SPECIAL_CASELEM = "{[^}]}"; + private IList currentCase; private Rule currRule; @@ -91,8 +95,12 @@ public class RGrammarBuilder { * @param csepart */ public void addCasePart(String csepart) { - if(csepart.matches("\\[[^\\]]+\\]")) { - currentCase.add(new CaseElement(RULEREF, csepart)); + if(csepart.matches(SPECIAL_CASELEM)) { + /* + * Handle other cases. + */ + } else if(csepart.matches(REFER_CASELEM)) { + currentCase.add(new CaseElement(RULEREF, csepart)); } else { currentCase.add(new CaseElement(LITERAL, csepart)); } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarFormatter.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarFormatter.java new file mode 100644 index 0000000..231e191 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarFormatter.java @@ -0,0 +1,98 @@ +package bjc.rgens.newparser; + +import bjc.utils.funcdata.IList; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Format randomized grammars to strings properly. + * + * @author EVE + * + */ +public class RGrammarFormatter { + /** + * Format a grammar into a file that represents that grammar. + * + * @param gram + * The grammar to format. + * + * @return The formatted grammar. + */ + public static String formatGrammar(RGrammar gram) { + StringBuilder sb = new StringBuilder(); + + Map rules = gram.getRules(); + + String initRuleName = gram.getInitialRule(); + + Set processedRules = new HashSet<>(); + + if(initRuleName != null) { + processRule(rules.get(initRuleName), sb); + + processedRules.add(initRuleName); + } + + for(Rule rule : rules.values()) { + if(!processedRules.contains(rule.ruleName)) { + sb.append("\n\n"); + + processRule(rule, sb); + } + + processedRules.add(rule.ruleName); + } + + return sb.toString().trim(); + } + + private static void processRule(Rule rule, StringBuilder sb) { + IList cases = rule.getCases(); + + StringBuilder ruleBuilder = new StringBuilder(); + + ruleBuilder.append(rule.ruleName); + ruleBuilder.append(" \u2192 "); + + int markerPos = ruleBuilder.length(); + + processCase(cases.first(), ruleBuilder); + + sb.append(ruleBuilder.toString().trim()); + + ruleBuilder = new StringBuilder(); + + for(RuleCase cse : cases.tail()) { + sb.append("\n\t"); + + for(int i = 8; i < markerPos; i++) { + ruleBuilder.append(" "); + } + + processCase(cse, ruleBuilder); + + sb.append(ruleBuilder.toString()); + + ruleBuilder = new StringBuilder(); + } + + } + + private static void processCase(RuleCase cse, StringBuilder sb) { + /* + * Process each element, adding a space. + */ + for(CaseElement element : cse.getElements()) { + sb.append(element.toString()); + sb.append(" "); + } + + /* + * Remove the trailing space. + */ + sb.deleteCharAt(sb.length() - 1); + } +} diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java index 64d7b54..3d7b708 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java @@ -20,13 +20,13 @@ public class RGrammarParser { /* * Templates for level-dependent delimiters. */ - private static final String TMPL_PRAGMA_BLOCK_DELIM = "\\n\\t{%d}(?!\\t)"; - private static final String TMPL_RULEDECL_BLOCK_DELIM = "\\n\\t\\t{%d}"; + private static final String TMPL_PRAGMA_BLOCK_DELIM = "\\r?\\n\\t{%d}(?!\\t)"; + private static final String TMPL_RULEDECL_BLOCK_DELIM = "\\r?\\n\\t\\t{%d}"; /* * Templates for non-level-dependent delimiters. */ - private static final String TOPLEVEL_BLOCK_DELIM = "\\n\\.?\\n"; + private static final String TOPLEVEL_BLOCK_DELIM = "\\r?\\n\\.?\\r?\\n"; /* * Pragma impls. @@ -276,7 +276,7 @@ public class RGrammarParser { for(String csepart : cse.split(" ")) { String partToAdd = csepart.trim(); - + /* * Ignore empty parts */ diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java index 48977e9..76efa86 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarTest.java @@ -16,7 +16,7 @@ public class RGrammarTest { * Unused CLI args. */ public static void main(String[] args) { - InputStream stream = RGrammarTest.class.getResourceAsStream("/sample-grammars/24hr-rpg.gram"); + InputStream stream = RGrammarTest.class.getResourceAsStream("/sample-grammars/web.gram"); RGrammarSet grammarSet = new RGrammarSet(); @@ -29,5 +29,14 @@ public class RGrammarTest { for(int i = 0; i < 10; i++) { System.out.println(grammar.generate(null)); } + + System.out.println(); + System.out.println(); + + System.out.println("Formatted grammar: "); + + String formattedGrammar = RGrammarFormatter.formatGrammar(grammar); + + System.out.print(formattedGrammar); } } diff --git a/RGens/src/main/java/bjc/rgens/newparser/Rule.java b/RGens/src/main/java/bjc/rgens/newparser/Rule.java index c479c87..a65f1ce 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/Rule.java +++ b/RGens/src/main/java/bjc/rgens/newparser/Rule.java @@ -22,8 +22,17 @@ public class Rule { * * @param ruleName * The name of the grammar rule. + * + * @throws IllegalArgumentException + * If the rule name is invalid. */ public Rule(String ruleName) { + if(ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } else if(ruleName.equals("")) { + throw new IllegalArgumentException("The empty string is not a valid rule name"); + } + this.ruleName = ruleName; ruleCases = new FunctionalList<>(); @@ -36,6 +45,10 @@ public class Rule { * The case to add. */ public void addCase(RuleCase cse) { + if(cse == null) { + throw new NullPointerException("Case must not be null"); + } + ruleCases.add(cse); } @@ -47,4 +60,48 @@ public class Rule { public RuleCase getCase() { return ruleCases.randItem(); } + + /** + * Get all the cases of this rule. + * + * @return All the cases in this rule. + */ + public IList getCases() { + return ruleCases; + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + ((ruleCases == null) ? 0 : ruleCases.hashCode()); + result = prime * result + ((ruleName == null) ? 0 : ruleName.hashCode()); + + return result; + } + + @Override + public boolean equals(Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof Rule)) return false; + + Rule other = (Rule) obj; + + if(ruleCases == null) { + if(other.ruleCases != null) return false; + } else if(!ruleCases.equals(other.ruleCases)) return false; + + if(ruleName == null) { + if(other.ruleName != null) return false; + } else if(!ruleName.equals(other.ruleName)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("Rule [ruleName='%s', ruleCases=%s]", ruleName, ruleCases); + } } \ No newline at end of file -- cgit v1.2.3