|
|
@@ -5,15 +5,20 @@ import net.ranides.assira.collection.maps.CrossMap;
|
|
|
import net.ranides.assira.collection.maps.OpenCrossMap;
|
|
|
import net.ranides.assira.g4.G4Parser;
|
|
|
import org.antlr.runtime.RecognitionException;
|
|
|
+import org.antlr.runtime.tree.CommonTree;
|
|
|
+import org.antlr.v4.analysis.LeftRecursiveRuleAltInfo;
|
|
|
import org.antlr.v4.runtime.CharStreams;
|
|
|
import org.antlr.v4.runtime.CommonTokenStream;
|
|
|
import org.antlr.v4.runtime.LexerInterpreter;
|
|
|
import org.antlr.v4.runtime.ParserInterpreter;
|
|
|
import org.antlr.v4.runtime.ParserRuleContext;
|
|
|
import org.antlr.v4.runtime.misc.Pair;
|
|
|
+import org.antlr.v4.tool.Alternative;
|
|
|
import org.antlr.v4.tool.Grammar;
|
|
|
+import org.antlr.v4.tool.LabelElementPair;
|
|
|
import org.antlr.v4.tool.Rule;
|
|
|
import org.antlr.v4.tool.ast.AltAST;
|
|
|
+import org.antlr.v4.tool.ast.GrammarAST;
|
|
|
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -22,53 +27,138 @@ public class BasicParser implements G4Parser {
|
|
|
|
|
|
protected final Grammar grammar;
|
|
|
|
|
|
+ protected final CrossMap<Integer, Integer, String> labels;
|
|
|
+
|
|
|
+ protected final CrossMap<String, String, Integer> expressionLabels;
|
|
|
+
|
|
|
public BasicParser(String grammar) {
|
|
|
try {
|
|
|
this.grammar = new Grammar(grammar);
|
|
|
+
|
|
|
+ this.labels = new OpenCrossMap<>();
|
|
|
+
|
|
|
+ // tricky fragment: we deduce alternative labels for every rule
|
|
|
+
|
|
|
+ for(Rule currentRule : this.grammar.rules.values()) {
|
|
|
+ Map<String, List<Pair<Integer, AltAST>>> altLabels = currentRule.getAltLabels();
|
|
|
+ if(altLabels != null) {
|
|
|
+ for(List<Pair<Integer, AltAST>> list : altLabels.values()) {
|
|
|
+ for(Pair<Integer, AltAST> pair : list) {
|
|
|
+ int ruleIndex = currentRule.index;
|
|
|
+ Integer altIndex = pair.a;
|
|
|
+ GrammarAST altLabel = pair.b.altLabel;
|
|
|
+ labels.put(ruleIndex, altIndex, altLabel.toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // tricky fragment: we deduce node positions for labels used inside each rule
|
|
|
+
|
|
|
+ expressionLabels = new OpenCrossMap<>();
|
|
|
+
|
|
|
+ for (Rule currentRule : this.grammar.rules.values()) {
|
|
|
+ String currentRuleName = currentRule.name;
|
|
|
+
|
|
|
+ for (Alternative currentAlt : currentRule.alt) {
|
|
|
+ if(currentAlt == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (Map.Entry<String, List<LabelElementPair>> entry : currentAlt.labelDefs.entrySet()) {
|
|
|
+ String labelName = entry.getKey();
|
|
|
+
|
|
|
+ for (LabelElementPair pair : entry.getValue()) {
|
|
|
+ int elementIndex = pair.element.parent.childIndex;
|
|
|
+
|
|
|
+ String currentAltName = decudeAltName(pair.element);
|
|
|
+
|
|
|
+ if (currentAltName == null) {
|
|
|
+ throw new UnsupportedOperationException("Awkward label '" + labelName + "' inside rule: " + currentRule);
|
|
|
+ }
|
|
|
+
|
|
|
+ expressionLabels.put(currentRuleName +currentAltName, labelName, elementIndex);
|
|
|
+
|
|
|
+ System.out.printf(" - %20s %s %s => %s%n", currentRuleName,
|
|
|
+ currentAltName,
|
|
|
+ labelName,
|
|
|
+ elementIndex
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
} catch (RecognitionException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private String decudeAltName(GrammarAST element) {
|
|
|
+ for (CommonTree now = element; now!=null; now = now.parent) {
|
|
|
+ if (!(now instanceof AltAST)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ AltAST alt = (AltAST) now;
|
|
|
+
|
|
|
+ LeftRecursiveRuleAltInfo left = alt.leftRecursiveAltInfo;
|
|
|
+
|
|
|
+ if (left != null) {
|
|
|
+ if (left.altLabel != null) {
|
|
|
+ return "#" + left.altLabel;
|
|
|
+ } else {
|
|
|
+ return "$" + left.altNum;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (alt.altLabel != null) {
|
|
|
+ return "#" + alt.altLabel;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (alt.alt != null) {
|
|
|
+ return "$" + alt.alt.altNum;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (alt.parent == null) {
|
|
|
+ return "$" + (now.childIndex + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public NodeForRule parse(String rule, String text) {
|
|
|
- return new Shared(grammar, rule, text).root;
|
|
|
+ return new Shared(rule, text).root;
|
|
|
}
|
|
|
|
|
|
- static class Shared {
|
|
|
+ class Shared {
|
|
|
|
|
|
- protected final Grammar grammar;
|
|
|
protected final LexerInterpreter lexer;
|
|
|
protected final CommonTokenStream tokens;
|
|
|
- protected final ParserInterpreter parser;
|
|
|
+ protected final ParserInterpreter interpreter;
|
|
|
protected final ParserRuleContext tree;
|
|
|
protected final NodeForRule root;
|
|
|
- protected final CrossMap<Integer, Integer, String> labels;
|
|
|
|
|
|
- public Shared(Grammar grammar, String rule, String text) {
|
|
|
- this.grammar = grammar;
|
|
|
+ public Shared(String rule, String text) {
|
|
|
this.lexer = grammar.createLexerInterpreter(CharStreams.fromString(text));
|
|
|
this.tokens = new CommonTokenStream(lexer);
|
|
|
- this.parser = grammar.createGrammarParserInterpreter(tokens);
|
|
|
+ this.interpreter = grammar.createGrammarParserInterpreter(tokens);
|
|
|
|
|
|
- this.tree = parser.parse(grammar.getRule(rule).index);
|
|
|
+ this.tree = interpreter.parse(grammar.getRule(rule).index);
|
|
|
this.root = new NodeForRule(this, null, this.tree);
|
|
|
+ }
|
|
|
|
|
|
- this.labels = new OpenCrossMap<>();
|
|
|
-
|
|
|
- // tricky fragment: we deduce alternative labels for every rule
|
|
|
+ public Grammar grammar() {
|
|
|
+ return BasicParser.this.grammar;
|
|
|
+ }
|
|
|
|
|
|
- for(Rule currentRule : grammar.rules.values()) {
|
|
|
- Map<String, List<Pair<Integer, AltAST>>> altLabels = currentRule.getAltLabels();
|
|
|
- if(altLabels != null) {
|
|
|
- for(List<Pair<Integer, AltAST>> list : altLabels.values()) {
|
|
|
- for(Pair<Integer, AltAST> pair : list) {
|
|
|
- labels.put(currentRule.index, pair.a, pair.b.altLabel.toString());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ public CrossMap<Integer, Integer, String> labels() {
|
|
|
+ return BasicParser.this.labels;
|
|
|
+ }
|
|
|
|
|
|
+ public CrossMap<String, String, Integer> expressionLabels() {
|
|
|
+ return BasicParser.this.expressionLabels;
|
|
|
}
|
|
|
|
|
|
}
|