added ~~ operator and tests for ~~ and ... operators
[sbp.git] / src / edu / berkeley / sbp / meta / GrammarBuilder.java
index b3f4941..121f480 100644 (file)
@@ -85,7 +85,8 @@ public class GrammarBuilder {
 
     public Object walk(Tree t) {
         String head = (String)t.head();
-        while(head.indexOf('.') != -1) head = head.substring(head.indexOf('.')+1);
+        while(head.indexOf('.') > 0)
+            head = head.substring(head.indexOf('.')+1);
         if (head==null) throw new RuntimeException("head is null: " + t);
         if (head.equals("|")) return walkChildren(t);
         if (head.equals("RHS")) return walkChildren(t);
@@ -98,6 +99,7 @@ public class GrammarBuilder {
         if (head.equals("{")) return new XTree((Seq)walk(t.child(0)));
         if (head.equals("::")) return tag((String)walk(t.child(0)), (Seq)walk(t.child(1)));
         if (head.equals("++")) return plusmax((ElementNode)walk(t.child(0)));
+        if (head.equals("...")) return star(new TildeNode(new AtomNode()));
         if (head.equals("+")) return plus((ElementNode)walk(t.child(0)));
         if (head.equals("++/")) return plusmaxfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
         if (head.equals("+/")) return plusfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
@@ -108,6 +110,11 @@ public class GrammarBuilder {
         if (head.equals("?")) return question((ElementNode)walk(t.child(0)));
         if (head.equals("!")) return new DropNode((ElementNode)walk(t.child(0)));
         if (head.equals("^")) return new LiteralNode((String)walk(t.child(0)), true);
+        if (head.equals("`")) {
+            ElementNode ret = (ElementNode)walk(t.child(0));
+            ret.lifted = true;
+            return ret;
+        }
         if (head.equals("Quoted")) return stringifyChildren(t);
         if (head.equals("Literal")) return new LiteralNode((String)walk(t.child(0)));
         if (head.equals("->")) return arrow((Seq)walk(t.child(0)), (ElementNode)walk(t.child(1)));
@@ -122,6 +129,10 @@ public class GrammarBuilder {
         if (head.equals("\\{")) return new DropNode(new AtomNode(new AtomNodeRange(CharAtom.left, CharAtom.left)));
         if (head.equals("\\}")) return new DropNode(new AtomNode(new AtomNodeRange(CharAtom.right, CharAtom.right)));
         if (head.equals("~")) return new TildeNode((ElementNode)walk(t.child(0)));
+        if (head.equals("~~")) {
+            Seq seq = new Seq(star(new TildeNode(new AtomNode())));
+            return seq.andnot((Seq)walk(t.child(0)));
+        }
         if (head.equals("Range") && t.size()==1) return new AtomNodeRange(unescape(t).charAt(0));
         if (head.equals("Range")) return new AtomNodeRange(unescape(t).charAt(0), unescape(t).charAt(1));
         if (head.equals("\"\"")) return "";
@@ -153,8 +164,7 @@ public class GrammarBuilder {
                 File file = new File(f.getAbsolutePath()+File.separatorChar+fileName);
                 if (!file.exists()) continue;
                 try {
-                    String newPrefix = (String)walk(t.child(1));
-                    if (newPrefix.length() > 0) newPrefix += ".";
+                    String newPrefix = t.size()<2 ? "" : ((String)walk(t.child(1))+".");
                     FileInputStream fis = new FileInputStream(file);
                     Tree tr = new CharParser(MetaGrammar.newInstance()).parse(fis).expand1();
                     return (GrammarNode)new GrammarBuilder(includes, newPrefix).walk(tr);
@@ -164,7 +174,7 @@ public class GrammarBuilder {
             }
             throw new RuntimeException("unable to find #include file \""+fileName+"\"");
         }
-        throw new RuntimeException("unknown head: " + head + "\n"+t);
+        throw new RuntimeException("unknown head: \"" + head + "\" => " + (head.equals("...")));
     }
     
     /** A grammar (a set of nonterminals) */
@@ -349,16 +359,19 @@ public class GrammarBuilder {
             }
             if (tag==null && multiNonDrop)
                 throw new RuntimeException("multiple non-dropped elements in sequence: " + Sequence.create(els, ""));
+            boolean lift = false;
+            if (elements.length > 0 && elements[0].lifted)
+                lift = true;
             if (!multiNonDrop) {
                 if (idx == -1) 
                     ret = tag==null
                         ? Sequence.create(els, illegalTag)
-                        : Sequence.create(tag, els, drops, false);
+                        : Sequence.createLeft(tag, els, drops, lift);
                 else if (tag==null) ret = Sequence.create(els, idx);
-                else ret = Sequence.create(tag, els, drops, false);
+                else ret = Sequence.createLeft(tag, els, drops, lift);
             }
             if (multiNonDrop)
-                ret = Sequence.create(tag, els, drops, false);
+                ret = Sequence.createLeft(tag, els, drops, lift);
             if (this.follow != null)
                 ret = ret.followedBy(this.follow.toAtom(cx));
             return ret;
@@ -368,7 +381,11 @@ public class GrammarBuilder {
     public class ReferenceNode extends ElementNode {
         public String nonTerminal;
         public ReferenceNode() { }
-        public NonTerminalNode resolve(Context cx) { return cx.grammar.get(nonTerminal); }
+        public NonTerminalNode resolve(Context cx) {
+            NonTerminalNode ret = cx.grammar.get(nonTerminal);
+            if (ret==null) throw new RuntimeException("undefined nonterminal: " + nonTerminal);
+            return ret;
+        }
         public ReferenceNode(String nonTerminal) { this.nonTerminal = prefix + nonTerminal; }
         public Atom toAtom(Context cx) { return cx.grammar.get(nonTerminal).toAtom(cx); }
         public boolean drop(Context cx) { return resolve(cx).drop(cx); }
@@ -403,6 +420,7 @@ public class GrammarBuilder {
 
     public class AtomNode extends ElementNode {
         AtomNodeRange[] ranges;
+        public AtomNode() { this(new AtomNodeRange[0]); }
         public AtomNode(AtomNodeRange[] ranges) { this.ranges = ranges; }
         public AtomNode(AtomNodeRange range) { this.ranges = new AtomNodeRange[] { range }; }
         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return toAtom(cx); }
@@ -446,6 +464,7 @@ public class GrammarBuilder {
     }
 
     public abstract class ElementNode {
+        public boolean lifted = false;
         public String getOwnerTag() { return null; }
         public boolean drop(Context cx) { return false; }
         public Atom toAtom(Context cx) { throw new Error("can't convert a " + this.getClass().getName() + " to an atom: " + this); }
@@ -531,11 +550,9 @@ public class GrammarBuilder {
             Union u2 = new Union(null, false);
             u2.add(Sequence.create(new Element[] {
                 CharAtom.leftBrace,
-                cx.get("ws"),
                 u,
-                cx.get("ws"),
                 CharAtom.rightBrace
-            }, 2));
+            }, 1));
             return u2;
         }
     }