Ranides Atterwim 2 سال پیش
والد
کامیت
057f8850ac

+ 5 - 2
assira.grammar/pom.xml

@@ -19,6 +19,9 @@
         <assira.junit.debug>false</assira.junit.debug>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
+
+        <!-- this is last version of "antlr4" artifact compatible with JDK 8 (ANTLR 4 Tool) -->
+        <antlr.version>4.9.3</antlr.version>
     </properties>
 
     <dependencies>
@@ -42,12 +45,12 @@
         <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>antlr4-runtime</artifactId>
-            <version>4.13.1</version>
+            <version>${antlr.version}</version>
         </dependency>
         <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>antlr4</artifactId>
-            <version>4.13.1</version>
+            <version>${antlr.version}</version>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>

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

@@ -1,17 +1,26 @@
 package net.ranides.assira.g4;
 
 import java.util.List;
+import java.util.Optional;
 
 public interface G4Node {
 
-    G4Node root();
+    long type();
 
     String name();
 
-    String alt();
+    String shortname();
 
-    String value();
+    Optional<String> qualifier();
+
+    G4Node root();
 
-    List<G4Node> children();
+    Optional<? extends G4Node> parent();
+
+    List<? extends G4Node> children();
+
+    String path();
+
+    String value();
 
 }

+ 2 - 1
assira.grammar/src/main/java/net/ranides/assira/g4/antlr/BasicParser.java

@@ -30,6 +30,7 @@ public class BasicParser implements G4Parser {
         }
     }
 
+    @Override
     public NodeForRule parse(String rule, String text) {
         return new Shared(grammar, rule, text).root;
     }
@@ -51,7 +52,7 @@ public class BasicParser implements G4Parser {
             this.parser = grammar.createGrammarParserInterpreter(tokens);
 
             this.tree = parser.parse(grammar.getRule(rule).index);
-            this.root = new NodeForRule(this, this.tree);
+            this.root = new NodeForRule(this, null, this.tree);
 
             this.labels = new OpenCrossMap<>();
 

+ 0 - 34
assira.grammar/src/main/java/net/ranides/assira/g4/antlr/BasicSupport.java

@@ -1,34 +0,0 @@
-package net.ranides.assira.g4.antlr;
-
-import lombok.experimental.UtilityClass;
-import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.misc.Interval;
-import org.antlr.v4.runtime.tree.ParseTree;
-
-@UtilityClass
-class BasicSupport {
-
-    public static String getFullText(ParseTree node) {
-        if(node instanceof ParserRuleContext) {
-            ParserRuleContext ctx = (ParserRuleContext) node;
-            if(hasValidInterval(ctx)) {
-                return ctx.getStart().getInputStream().getText(asInterval(ctx));
-            }
-        }
-        return node.getText();
-    }
-
-    private static boolean hasValidInterval(ParserRuleContext context) {
-        return context.start != null
-            && context.start.getStartIndex() >= 0
-            && context.stop != null
-            && context.stop.getStopIndex() > context.start.getStartIndex();
-    }
-
-    private static Interval asInterval(ParserRuleContext context) {
-        return new Interval(context.start.getStartIndex(), context.stop.getStopIndex());
-    }
-
-
-
-}

+ 133 - 0
assira.grammar/src/main/java/net/ranides/assira/g4/antlr/NodeAbstract.java

@@ -0,0 +1,133 @@
+package net.ranides.assira.g4.antlr;
+
+import net.ranides.assira.collection.iterators.IteratorUtils;
+import net.ranides.assira.collection.lists.ListUtils;
+import net.ranides.assira.collection.lists.ReversedList;
+import net.ranides.assira.collection.lists.VirtualList;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.text.StringUtils;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.RuleNode;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalInt;
+
+abstract class NodeAbstract<N extends ParseTree> implements G4Node {
+
+    protected final BasicParser.Shared shared;
+
+    protected final NodeAbstract<?> parent;
+
+    protected final N node;
+
+    public NodeAbstract(BasicParser.Shared shared, NodeAbstract<?> parent, N node) {
+        this.shared = shared;
+        this.parent = parent;
+        this.node = node;
+    }
+
+    @Override
+    public final G4Node root() {
+        return shared.root;
+    }
+
+    @Override
+    public final List<G4Node> children() {
+        return VirtualList.of(node.getChildCount(), i -> wrap(node.getChild(i)));
+    }
+
+    @Override
+    public String name() {
+        return qualifier().map(q -> shortname() + "<" + q + ">").orElseGet(this::shortname);
+    }
+
+    @Override
+    public final Optional<NodeAbstract<?>> parent() {
+        return Optional.ofNullable(parent);
+    }
+
+    protected G4Node wrap(ParseTree tree) {
+        if(tree instanceof RuleNode) {
+            return new NodeForRule(shared, this, (RuleNode) tree);
+        } else {
+            return new NodeForTerm(shared, this, (TerminalNode) tree);
+        }
+    }
+
+    @Override
+    public String path() {
+        List<String> path = new LinkedList<>();
+
+        Optional<NodeAbstract<?>> current = Optional.of(this);
+
+        while (current.isPresent()) {
+            path.add(current.get().getPathComponent());
+            current = current.get().parent();
+        }
+
+//        IteratorUtils.finite()
+
+        return StringUtils.join(new ReversedList<>(path), "/");
+    }
+
+    private String getPathComponent() {
+        Optional<Integer> index = getPathComponentIndex();
+        if(index.isPresent()) {
+            return shortname() + "[" + index.get() + "]";
+        } else {
+            return shortname();
+        }
+    }
+
+    private Optional<Integer> getPathComponentIndex() {
+        if (parent == null || parent.children().size() == 1) {
+            return Optional.empty();
+        }
+
+        int found = 0;
+        Optional<Integer> index = Optional.empty();
+
+        for (G4Node child : parent.children()) {
+            if (child.equals(this)) {
+                index = Optional.of(found);
+
+                // break unnecessary looping, indexes greater that zero are always displayed
+                if (found > 0) {
+                    return index;
+                }
+            }
+            if (child.type() == this.type()) {
+                found++;
+
+                // break unnecessary looping, zero index should be displayed if there is more than one similar element
+                if (found > 1 && index.isPresent()) {
+                    break;
+                }
+            }
+        }
+
+        // do not return zero index, if there is only one (i.e. this) child
+        return found > 1 ? index : Optional.empty();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if(o instanceof NodeAbstract<?>) {
+            NodeAbstract<?> that = (NodeAbstract<?>)o;
+            return this.node.equals(that.node);
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(node);
+    }
+}

+ 27 - 36
assira.grammar/src/main/java/net/ranides/assira/g4/antlr/NodeForRule.java

@@ -1,67 +1,58 @@
 package net.ranides.assira.g4.antlr;
 
-import net.ranides.assira.collection.lists.VirtualList;
 import net.ranides.assira.g4.G4Node;
-import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.Interval;
 import org.antlr.v4.runtime.tree.RuleNode;
-import org.antlr.v4.runtime.tree.TerminalNode;
 
-import java.util.List;
+import java.util.Optional;
 
-class NodeForRule implements G4Node {
+class NodeForRule extends NodeAbstract<RuleNode> implements G4Node {
 
-    protected final BasicParser.Shared shared;
+    private static final long BASE_TYPE = 0x8_0000_0000L;
 
-    protected final RuleNode node;
-
-    public NodeForRule(BasicParser.Shared shared, RuleNode node) {
-        this.shared = shared;
-        this.node = node;
+    public NodeForRule(BasicParser.Shared shared, NodeAbstract<?> parent, RuleNode node) {
+        super(shared, parent, node);
     }
 
     @Override
-    public G4Node root() {
-        return shared.root;
+    public long type() {
+        return BASE_TYPE + node.getRuleContext().getRuleIndex();
     }
 
     @Override
-    public String name() {
+    public String shortname() {
         int ruleIndex = node.getRuleContext().getRuleIndex();
         return shared.parser.getRuleNames()[ruleIndex];
     }
 
     @Override
-    public String alt() {
+    public Optional<String> qualifier() {
         int ruleIndex = node.getRuleContext().getRuleIndex();
         int ruleAlt = node.getRuleContext().getAltNumber();
-        return shared.labels.get(ruleIndex, ruleAlt);
+        return Optional.ofNullable(shared.labels.get(ruleIndex, ruleAlt));
     }
 
     @Override
     public String value() {
-        return BasicSupport.getFullText(node);
+        if(node instanceof ParserRuleContext) {
+            ParserRuleContext ctx = (ParserRuleContext) node;
+            if(hasValidInterval(ctx)) {
+                return ctx.getStart().getInputStream().getText(asInterval(ctx));
+            }
+        }
+        return node.getText();
     }
 
-    @Override
-    public List<G4Node> children() {
-        return VirtualList.of(node.getChildCount(), i -> {
-            ParseTree c = node.getChild(i);
-            if(c instanceof RuleNode) {
-                return new NodeForRule(shared, (RuleNode) c);
-            } else {
-                return new NodeForTerm(shared, (TerminalNode) c);
-            }
-        });
+    private static boolean hasValidInterval(ParserRuleContext context) {
+        return context.start != null
+            && context.start.getStartIndex() >= 0
+            && context.stop != null
+            && context.stop.getStopIndex() > context.start.getStartIndex();
     }
 
-    @Override
-    public String toString() {
-        String n = name();
-        String a = alt();
-        if(a != null) {
-            return n + "<" + a + ">";
-        } else {
-            return n;
-        }
+    private static Interval asInterval(ParserRuleContext context) {
+        return new Interval(context.start.getStartIndex(), context.stop.getStopIndex());
     }
+
 }

+ 10 - 20
assira.grammar/src/main/java/net/ranides/assira/g4/antlr/NodeForTerm.java

@@ -3,33 +3,29 @@ package net.ranides.assira.g4.antlr;
 import net.ranides.assira.g4.G4Node;
 import org.antlr.v4.runtime.tree.TerminalNode;
 
-import java.util.Collections;
-import java.util.List;
+import java.util.Optional;
 
-class NodeForTerm implements G4Node {
+class NodeForTerm extends NodeAbstract<TerminalNode> implements G4Node {
 
-    protected final BasicParser.Shared shared;
+    private static final long BASE_TYPE = 0x4_0000_0000L;
 
-    protected final TerminalNode node;
-
-    public NodeForTerm(BasicParser.Shared shared, TerminalNode node) {
-        this.shared = shared;
-        this.node = node;
+    public NodeForTerm(BasicParser.Shared shared, NodeAbstract<?> parent, TerminalNode node) {
+        super(shared, parent, node);
     }
 
     @Override
-    public G4Node root() {
-        return shared.root;
+    public long type() {
+        return BASE_TYPE + node.getSymbol().getType();
     }
 
     @Override
-    public String name() {
+    public String shortname() {
         return shared.lexer.getVocabulary().getSymbolicName(node.getSymbol().getType());
     }
 
     @Override
-    public String alt() {
-        return null;
+    public Optional<String> qualifier() {
+        return Optional.empty();
     }
 
     @Override
@@ -37,10 +33,4 @@ class NodeForTerm implements G4Node {
         return node.getText();
     }
 
-    @Override
-    public List<G4Node> children() {
-        return Collections.emptyList();
-    }
-
-
 }

+ 15 - 5
assira.grammar/src/test/java/net/ranides/assira/g4/G4ParserTest.java

@@ -1,7 +1,6 @@
 package net.ranides.assira.g4;
 
 import net.ranides.assira.io.ResourceUtils;
-import org.antlr.runtime.RecognitionException;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -14,18 +13,22 @@ public class G4ParserTest {
         G4Parser g4 = G4Parser.newInstance(calc);
 
 //        G4Node tree1 = g4.parse("expression", "(4 + 2) * 3 -- 3*9! / log(2+7, 6+5)");
-        G4Node tree2 = g4.parse("expression", "(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.printf("%s = %s%n", tree1.name(), tree1.value());
 //        System.out.printf("%s = %s%n", tree2.name(), tree2.value());
 
 //        dump("1  > ", tree1);
-        dump("2  > ", tree2);
+//        dump("2  > ", tree2);
+//        dump2(tree3);
+        dump2(tree4);
     }
 
     static void dump(String indent, G4Node node) {
-        if(node.alt() != null) {
-            System.out.printf("%s%s<%s> = %s%n", indent, node.name(), node.alt(), node.value());
+        if(node.qualifier() != null) {
+            System.out.printf("%s%s<%s> = %s%n", indent, node.name(), node.qualifier(), node.value());
         } else {
             System.out.printf("%s%s = %s%n", indent, node.name(), node.value());
         }
@@ -33,4 +36,11 @@ public class G4ParserTest {
             dump(indent+"   ", n);
         });
     }
+
+    static void dump2(G4Node node) {
+        System.out.printf("%s%n", node.path());
+        node.children().forEach(n -> {
+            dump2(n);
+        });
+    }
 }

+ 7 - 1
assira.grammar/src/test/resources/Calculator.g4

@@ -16,7 +16,11 @@ functionCall
     ;
 
 functionArguments
-    : expression (SYMBOL_COM expression)*
+    : functionArgument (SYMBOL_COM functionArgument)*
+    ;
+
+functionArgument
+    : expression
     ;
 
 operatorPrefix
@@ -71,6 +75,8 @@ NAME : [a-z]+ ;
 SYMBOL_LB : '(' ;
 SYMBOL_RB : ')' ;
 SYMBOL_COM : ',' ;
+SYMBOL_LT : '<' ;
+SYMBOL_GT : '>' ;
 
 SYMBOL_FACT: '!' ;
 SYMBOL_POW : '^' ;