Kaynağa Gözat

WIP: grammar (labels inside expressions)

Ranides Atterwim 2 yıl önce
ebeveyn
işleme
bacb20b3bc

+ 1 - 1
assira.grammar/src/main/java/net/ranides/assira/g4/G4Node.java

@@ -13,7 +13,7 @@ public interface G4Node {
 
     String shortname();
 
-    Optional<String> qualifier();
+    String qualifier();
 
     G4Node root();
 

+ 112 - 22
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/BasicParser.java

@@ -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;
         }
 
     }

+ 1 - 1
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeAbstract.java

@@ -48,7 +48,7 @@ abstract class NodeAbstract implements G4Node {
 
     @Override
     public String name() {
-        return qualifier().map(q -> shortname() + "#" + q).orElseGet(this::shortname);
+        return shortname() + qualifier();
     }
 
     @Override

+ 5 - 5
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForRule.java

@@ -7,8 +7,6 @@ import org.antlr.v4.runtime.misc.Interval;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.RuleNode;
 
-import java.util.Optional;
-
 class NodeForRule extends NodeAbstract implements G4Node {
 
     private static final long BASE_TYPE = 0x8_0000_0000L;
@@ -33,14 +31,16 @@ class NodeForRule extends NodeAbstract implements G4Node {
     @Override
     public String shortname() {
         int ruleIndex = node.getRuleContext().getRuleIndex();
-        return shared.parser.getRuleNames()[ruleIndex];
+        return shared.interpreter.getRuleNames()[ruleIndex];
     }
 
     @Override
-    public Optional<String> qualifier() {
+    public String qualifier() {
         int ruleIndex = node.getRuleContext().getRuleIndex();
         int ruleAlt = node.getRuleContext().getAltNumber();
-        return Optional.ofNullable(shared.labels.get(ruleIndex, ruleAlt));
+
+        String label = shared.labels().get(ruleIndex, ruleAlt);
+        return label != null ? "#" + label : "$" + ruleAlt;
     }
 
     @Override

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForTerm.java

@@ -34,8 +34,8 @@ class NodeForTerm extends NodeAbstract implements G4Node {
     }
 
     @Override
-    public Optional<String> qualifier() {
-        return Optional.empty();
+    public String qualifier() {
+        return "";
     }
 
     @Override

+ 12 - 6
assira.grammar/src/test/java/net/ranides/assira/g4/G4ParserTest.java

@@ -13,6 +13,8 @@ public class G4ParserTest {
         String calc = ResourceUtils.text("Calculator.g4");
         G4Parser g4 = G4Parser.newInstance(calc);
 
+        System.out.printf("%n%n++++++++++++++++++++%n%n");
+
         G4Selector<Number> s4num = new G4SelectorBuilder<Number>("number")
             .mapRule("numberInt", node -> Integer.parseInt(node.text()))
             .mapRule("numberReal", node -> Double.parseDouble(node.text()))
@@ -20,7 +22,7 @@ public class G4ParserTest {
 
         G4Selector<Number> s4ops = new G4SelectorBuilder<Number>("operators")
             .inherit(s4num)
-            .mapRule("expression#exprǂ3", (node, that) -> {
+            .mapRule("expression#expr_3", (node, that) -> {
                 Number a = that.first(node.child(0).get());
                 Number b = that.first(node.child(2).get());
 
@@ -31,7 +33,7 @@ public class G4ParserTest {
 
                 throw new IllegalArgumentException(node.text());
             })
-            .mapRule("expression#exprǂ2", (node, that) -> {
+            .mapRule("expression#expr_2", (node, that) -> {
 
                 // @todo support for children BY LABEL
                 Number a = that.first(node.children("expression").at(0).get());
@@ -46,6 +48,8 @@ public class G4ParserTest {
             })
             .prepare();
 
+
+
         G4Node expr = g4.parse("expression", "4+2*3");
 
         System.out.println(s4num.first(expr));
@@ -59,13 +63,15 @@ public class G4ParserTest {
     public void printer() throws IOException {
         String calc = ResourceUtils.text("Calculator.g4");
         G4Parser g4 = G4Parser.newInstance(calc);
+        System.out.printf("%n%n++++++++++++++++++++%n%n");
+
 
-//        G4Node tree1 = g4.parse("expression", "(4 + 2) * 3 -- 3*9! / log(2+7, 6+5)");
+        G4Node tree1 = g4.parse("anonymous", "(4 + 2) * 3 ");
         G4Node tree2 = g4.parse("expression", "(4 + 2) * 3 ");
-//        G4Node tree3 = g4.parse("expression", "<4, 5, 72, 2>");
-//        G4Node tree4 = g4.parse("expression", "hello(2*2, 3+1, -2)");
 
-        System.out.println(paths(tree2) );
+        System.out.println(names(tree1) );
+        System.out.printf("%n%n++++++++++++++++++++%n%n");
+        System.out.println(names(tree2) );
     }
 
     static String names(G4Node node) {

+ 43 - 9
assira.grammar/src/test/resources/Calculator.g4

@@ -1,18 +1,50 @@
 grammar Calculator;
 
 expression
-    : functionCall                                  #exprǂcall
-    | operatorPrefix expression                     #exprǂpref
-    | expression operatorSuffix                     #exprǂsuff
-    | SYMBOL_LB expression SYMBOL_RB                #exprǂbracket
-    | a=expression operator1 b=expression           #exprǂ1
-    | hello=expression operator2 kitty=expression   #exprǂ2
-    | kitkat=expression operator3 joe=expression    #exprǂ3
-    | number                                        #exprǂnumber
+    : functionCall                                  #expr_call
+    | operatorPrefix great=expression                     #expr_pref
+    | expression operatorSuffix                     #expr_suff
+    | SYMBOL_LB expression SYMBOL_RB                #expr_bracket
+    | some=expression operator1 b=expression           #expr_1
+    | hello=expression operator2 kitty=expression   #expr_2
+    | kitkat=expression operator3 joe=expression    #expr_3
+    | kitkat=expression operator3 joe=expression    #expr_3
+    | number                                        #expr_number
+    | SYMBOL_ASK q1=number q2=number other=number       #expr_ask
+    ;
+
+complexNames
+    : SYMBOL_FACT SYMBOL_LB label2=number label3=number SYMBOL_ADD number SYMBOL_ADD label7=number label8=number SYMBOL_FACT
+    ;
+
+anonymous
+    : functionCall
+    | operatorPrefix great=expression
+    | expression operatorSuffix
+    | SYMBOL_LB expression SYMBOL_RB
+    | some=expression operator1 b=expression
+    | hello=expression operator2 kitty=expression
+    | kitkat=expression operator3 joe=expression
+    | kitkat=expression operator3 joe=expression
+    | number
+    | SYMBOL_ASK q1=number q2=number other=number
+    ;
+
+strange
+    : functionCall
+    | operatorPrefix great=strange
+    | strange operatorSuffix
+    | SYMBOL_LB strange SYMBOL_RB
+    | some=strange operator1 b=strange
+    | hello=strange operator2 kitty=strange
+    | kitkat=strange operator3 joe=strange
+    | kitkat=strange operator3 joe=strange
+    | number
+    | SYMBOL_ASK q1=number q2=number other=number
     ;
 
 functionCall
-    : NAME SYMBOL_LB functionArguments SYMBOL_RB
+    : NAME SYMBOL_LB args=functionArguments SYMBOL_RB
     ;
 
 functionArguments
@@ -87,4 +119,6 @@ SYMBOL_DIV : '/' ;
 SYMBOL_ADD : '+' ;
 SYMBOL_SUB : '-' ;
 
+SYMBOL_ASK : '?' ;
+
 WS :  [ \t\r\n]+ -> skip ;