Jelajahi Sumber

WIP: grammar

Ranides Atterwim 2 tahun lalu
induk
melakukan
d9f2d73536

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

@@ -1,8 +1,8 @@
 package net.ranides.assira.g4;
 
+import net.ranides.assira.collection.query.CQuery;
 import net.ranides.assira.g4.visitors.G4NodeVisitor;
 
-import java.util.List;
 import java.util.Optional;
 
 public interface G4Node {
@@ -19,11 +19,15 @@ public interface G4Node {
 
     Optional<? extends G4Node> parent();
 
-    List<? extends G4Node> children();
+    CQuery<? extends G4Node> children();
+
+    CQuery<? extends G4Node> children(String name);
+
+    Optional<? extends G4Node> child(int index);
 
     String path();
 
-    String value();
+    String text();
 
     <T> T accept(G4NodeVisitor<? extends T> visitor);
 

+ 7 - 6
assira.grammar/src/main/java/net/ranides/assira/g4/G4SelectorBuilder.java

@@ -395,13 +395,14 @@ public class G4SelectorBuilder<V> {
 
         @Override
         public Optional<Supplier<V>> match(G4Node node) {
-            String contextName = node.name();
-            if(matchRules.containsKey(contextName)) {
-                return Optional.of(matchRules.get(contextName)).map(f -> () -> f.apply(node, this));
+            if(matchRules.containsKey(node.name())) {
+                return Optional.of(matchRules.get(node.name())).map(f -> () -> f.apply(node, this));
             }
-
-            if (matchTokens.containsKey(contextName)) {
-                return Optional.of(matchTokens.get(contextName)).map(f -> () -> f.apply(node, this));
+            if(matchRules.containsKey(node.shortname())) {
+                return Optional.of(matchRules.get(node.shortname())).map(f -> () -> f.apply(node, this));
+            }
+            if (matchTokens.containsKey(node.name())) {
+                return Optional.of(matchTokens.get(node.name())).map(f -> () -> f.apply(node, this));
             }
 
             Optional<Supplier<V>> matched = matchProperties.match(node, this);

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

@@ -1,49 +1,58 @@
 package net.ranides.assira.g4.impl.antlr;
 
-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.LinkedList;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 
-abstract class NodeAbstract<N extends ParseTree> implements G4Node {
+abstract class NodeAbstract implements G4Node {
 
     protected final BasicParser.Shared shared;
 
-    protected final NodeAbstract<?> parent;
+    protected final NodeAbstract parent;
 
-    protected final N node;
-
-    public NodeAbstract(BasicParser.Shared shared, NodeAbstract<?> parent, N node) {
+    public NodeAbstract(BasicParser.Shared shared, NodeAbstract parent) {
         this.shared = shared;
         this.parent = parent;
-        this.node = node;
     }
 
+    protected abstract ParseTree 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)));
+    public final CQuery<G4Node> children() {
+        return CQuery.from().finite(node()::getChildCount, node()::getChild).map(this::wrap);
+    }
+
+    @Override
+    public CQuery<G4Node> children(String name) {
+        return children().filter(v -> name.equals(v.name()) || name.equals(v.shortname()));
+    }
+
+    @Override
+    public Optional<G4Node> child(int index) {
+        if(index >=0 && index<node().getChildCount()) {
+            return Optional.ofNullable(node().getChild(index)).map(this::wrap);
+        } else {
+            return Optional.empty();
+        }
     }
 
     @Override
     public String name() {
-        return qualifier().map(q -> shortname() + "<" + q + ">").orElseGet(this::shortname);
+        return qualifier().map(q -> shortname() + "#" + q).orElseGet(this::shortname);
     }
 
     @Override
-    public final Optional<NodeAbstract<?>> parent() {
+    public final Optional<NodeAbstract> parent() {
         return Optional.ofNullable(parent);
     }
 
@@ -57,21 +66,13 @@ abstract class NodeAbstract<N extends ParseTree> implements G4Node {
 
     @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), "/");
+        return CQuery.from().finite(this, n -> n.parent())
+            .map(n -> n.getPathComponent())
+            .reverse()
+            .join("/");
     }
 
-    private String getPathComponent() {
+    protected final String getPathComponent() {
         Optional<Integer> index = getPathComponentIndex();
         if(index.isPresent()) {
             return shortname() + "[" + index.get() + "]";
@@ -113,9 +114,9 @@ abstract class NodeAbstract<N extends ParseTree> implements G4Node {
 
     @Override
     public boolean equals(Object o) {
-        if(o instanceof NodeAbstract<?>) {
-            NodeAbstract<?> that = (NodeAbstract<?>)o;
-            return this.node.equals(that.node);
+        if(o instanceof NodeAbstract) {
+            NodeAbstract that = (NodeAbstract)o;
+            return this.node().equals(that.node());
         }
 
         return false;
@@ -123,6 +124,6 @@ abstract class NodeAbstract<N extends ParseTree> implements G4Node {
 
     @Override
     public int hashCode() {
-        return Objects.hash(node);
+        return Objects.hash(node());
     }
 }

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

@@ -4,16 +4,25 @@ import net.ranides.assira.g4.G4Node;
 import net.ranides.assira.g4.visitors.G4NodeVisitor;
 import org.antlr.v4.runtime.ParserRuleContext;
 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<RuleNode> implements G4Node {
+class NodeForRule extends NodeAbstract implements G4Node {
 
     private static final long BASE_TYPE = 0x8_0000_0000L;
 
-    public NodeForRule(BasicParser.Shared shared, NodeAbstract<?> parent, RuleNode node) {
-        super(shared, parent, node);
+    private final RuleNode node;
+
+    public NodeForRule(BasicParser.Shared shared, NodeAbstract parent, RuleNode node) {
+        super(shared, parent);
+        this.node = node;
+    }
+
+    @Override
+    protected ParseTree node() {
+        return node;
     }
 
     @Override
@@ -35,7 +44,7 @@ class NodeForRule extends NodeAbstract<RuleNode> implements G4Node {
     }
 
     @Override
-    public String value() {
+    public String text() {
         if(node instanceof ParserRuleContext) {
             ParserRuleContext ctx = (ParserRuleContext) node;
             if(hasValidInterval(ctx)) {

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

@@ -2,16 +2,25 @@ package net.ranides.assira.g4.impl.antlr;
 
 import net.ranides.assira.g4.G4Node;
 import net.ranides.assira.g4.visitors.G4NodeVisitor;
+import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
 
 import java.util.Optional;
 
-class NodeForTerm extends NodeAbstract<TerminalNode> implements G4Node {
+class NodeForTerm extends NodeAbstract implements G4Node {
 
     private static final long BASE_TYPE = 0x4_0000_0000L;
 
-    public NodeForTerm(BasicParser.Shared shared, NodeAbstract<?> parent, TerminalNode node) {
-        super(shared, parent, node);
+    private final TerminalNode node;
+
+    public NodeForTerm(BasicParser.Shared shared, NodeAbstract parent, TerminalNode node) {
+        super(shared, parent);
+        this.node = node;
+    }
+
+    @Override
+    protected ParseTree node() {
+        return node;
     }
 
     @Override
@@ -30,7 +39,7 @@ class NodeForTerm extends NodeAbstract<TerminalNode> implements G4Node {
     }
 
     @Override
-    public String value() {
+    public String text() {
         return node.getText();
     }
 

+ 67 - 19
assira.grammar/src/test/java/net/ranides/assira/g4/G4ParserTest.java

@@ -1,6 +1,7 @@
 package net.ranides.assira.g4;
 
 import net.ranides.assira.io.ResourceUtils;
+import net.ranides.assira.text.StrBuilder;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -8,39 +9,86 @@ import java.io.IOException;
 public class G4ParserTest {
 
     @Test
-    public void simple() throws IOException {
+    public void selector() throws IOException {
+        String calc = ResourceUtils.text("Calculator.g4");
+        G4Parser g4 = G4Parser.newInstance(calc);
+
+        G4Selector<Number> s4num = new G4SelectorBuilder<Number>("number")
+            .mapRule("numberInt", node -> Integer.parseInt(node.text()))
+            .mapRule("numberReal", node -> Double.parseDouble(node.text()))
+            .prepare();
+
+        G4Selector<Number> s4ops = new G4SelectorBuilder<Number>("operators")
+            .inherit(s4num)
+            .mapRule("expression#exprǂ3", (node, that) -> {
+                Number a = that.first(node.child(0).get());
+                Number b = that.first(node.child(2).get());
+
+                switch (node.child(1).get().text()) {
+                    case "+": return a.doubleValue() + b.doubleValue();
+                    case "-": return a.doubleValue() - b.doubleValue();
+                }
+
+                throw new IllegalArgumentException(node.text());
+            })
+            .mapRule("expression#exprǂ2", (node, that) -> {
+
+                // @todo support for children BY LABEL
+                Number a = that.first(node.children("expression").at(0).get());
+                Number b = that.first(node.children("expression").at(1).get());
+
+                switch (node.children("operator2").single().get().text()) {
+                    case "*": return a.doubleValue() * b.doubleValue();
+                    case "/": return a.doubleValue() / b.doubleValue();
+                }
+
+                throw new IllegalArgumentException(node.text());
+            })
+            .prepare();
+
+        G4Node expr = g4.parse("expression", "4+2*3");
+
+        System.out.println(s4num.first(expr));
+
+        System.out.println(s4ops.aggregate(g4.parse("expression", "4 + 2 * 3"))); // works: 10
+        System.out.println(s4ops.aggregate(g4.parse("expression", "(4 + 2) * 3"))); // works: 18
+
+    }
+
+    @Test
+    public void printer() throws IOException {
         String calc = ResourceUtils.text("Calculator.g4");
         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)");
+//        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());
+        System.out.println(paths(tree2) );
+    }
 
-//        dump("1  > ", tree1);
-//        dump("2  > ", tree2);
-//        dump2(tree3);
-        dump2(tree4);
+    static String names(G4Node node) {
+        return names(new StrBuilder(), "", node).toString();
     }
 
-    static void dump(String indent, G4Node node) {
-        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());
-        }
+    static StrBuilder names(StrBuilder writer, String indent, G4Node node) {
+        writer.printf("%s%s = '%s'%n", indent, node.name(), node.text());
         node.children().forEach(n -> {
-            dump(indent+"   ", n);
+            names(writer, indent+"   ", n);
         });
+        return writer;
+    }
+
+    static String paths(G4Node node) {
+        return paths(new StrBuilder(), node).toString();
     }
 
-    static void dump2(G4Node node) {
-        System.out.printf("%s%n", node.path());
+    static StrBuilder paths(StrBuilder writer, G4Node node) {
+        writer.printf("%s = '%s'%n", node.path(), node.text());
         node.children().forEach(n -> {
-            dump2(n);
+            paths(writer, n);
         });
+        return writer;
     }
 }

+ 8 - 8
assira.grammar/src/test/resources/Calculator.g4

@@ -1,14 +1,14 @@
 grammar Calculator;
 
 expression
-    : functionCall                      #exprǂcall
-    | operatorPrefix expression         #exprǂpref
-    | expression operatorSuffix         #exprǂsuff
-    | SYMBOL_LB expression SYMBOL_RB    #exprǂbracket
-    | expression operator1 expression   #exprǂ1
-    | expression operator2 expression   #exprǂ2
-    | expression operator3 expression   #exprǂ3
-    | number                            #exprǂnumber
+    : 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