added ~~ operator and tests for ~~ and ... operators
[sbp.git] / src / edu / berkeley / sbp / meta / GrammarBuilder.java
index 3651be3..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) */
@@ -264,7 +274,7 @@ public class GrammarBuilder {
     public class NonTerminalNode extends UnionNode {
         public boolean alwaysDrop;
         public String  name = null;
-        public boolean drop() { return alwaysDrop; }
+        public boolean drop(Context cx) { return alwaysDrop; }
         public NonTerminalNode(String name, Seq[][] sequences, boolean rep, String sep, boolean alwaysDrop) {
             super(sequences, rep, sep==null?null:(prefix + sep));
             this.name = prefix + name;
@@ -275,7 +285,7 @@ public class GrammarBuilder {
 
     public class Seq {
         public boolean alwaysDrop = false;
-        public boolean drop() { return alwaysDrop; }
+        public boolean drop(Context cx) { return alwaysDrop; }
         HashSet<Seq> and = new HashSet<Seq>();
         HashSet<Seq> not = new HashSet<Seq>();
         ElementNode[] elements;
@@ -326,13 +336,10 @@ public class GrammarBuilder {
         public Sequence build0(Context cx, NonTerminalNode cnt, boolean dropall) {
             boolean[] drops = new boolean[elements.length];
             Element[] els = new Element[elements.length];
-            dropall |= drop();
+            dropall |= drop(cx);
             for(int i=0; i<elements.length; i++) {
                 if (dropall) drops[i] = true;
-                else         drops[i] = elements[i].drop();
-                if (elements[i] instanceof ReferenceNode)
-                    if (((ReferenceNode)elements[i]).resolve(cx).drop())
-                        drops[i] = true;
+                else         drops[i] = elements[i].drop(cx);
                 if (elements[i].getOwnerTag() != null)
                     tag = elements[i].getOwnerTag();
             }
@@ -352,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;
@@ -371,9 +381,14 @@ 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); }
         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
             if (!this.nonTerminal.startsWith(prefix)) nonTerminal = prefix + nonTerminal;
             Element ret = cx.get(nonTerminal);
@@ -393,7 +408,7 @@ public class GrammarBuilder {
         }
         public String getOwnerTag() { return caret ? thePrefix+string : super.getOwnerTag(); }
         public String toString() { return "\""+string+"\""; }
-        public boolean drop() { return true; }
+        public boolean drop(Context cx) { return true; }
         public Atom toAtom(Context cx) {
             if (string.length()!=1) return super.toAtom(cx);
             edu.berkeley.sbp.util.Range.Set set = new edu.berkeley.sbp.util.Range.Set();
@@ -405,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); }
@@ -423,14 +439,20 @@ public class GrammarBuilder {
 
     public class RepeatNode extends ElementNode {
         public ElementNode e, sep;
-        public boolean zero, many, max;
+        public final boolean zero, many, max;
         public RepeatNode(ElementNode e, ElementNode sep, boolean zero, boolean many, boolean max) {
-            this.e = e; this.sep = sep; this.zero = zero; this.many = many; this.max = max;}
+            this.e = e; this.sep = sep; this.zero = zero; this.many = many; this.max = max;
+        }
         public Atom toAtom(Context cx) { return sep==null ? e.toAtom(cx) : super.toAtom(cx); }
+        public boolean drop(Context cx) { return e.drop(cx); }
         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
-            if (!dropall && !drop() && !e.drop())
-                throw new Error("you need a tag on this repetition: " + build(cx, cnt, dropall, ""));
-            return build(cx, cnt, dropall, illegalTag);
+            Element ret = build(cx, cnt, dropall, illegalTag);
+            String must = "must be tagged unless they appear within a dropped expression or their contents are dropped: ";
+            if (!dropall && !drop(cx) && !e.drop(cx))
+                if (!many)      throw new RuntimeException("options (?) " + must + ret);
+                else if (zero)  throw new RuntimeException("zero-or-more repetitions (*) " + must + ret);
+                else            throw new RuntimeException("one-or-more repetitions (+) " + must + ret);
+            return ret;
         }
         public Element build(Context cx, NonTerminalNode cnt, boolean dropall, Object repeatTag) {
             return (!max)
@@ -442,8 +464,9 @@ public class GrammarBuilder {
     }
 
     public abstract class ElementNode {
+        public boolean lifted = false;
         public String getOwnerTag() { return null; }
-        public boolean drop() { return false; }
+        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); }
         public abstract Element build(Context cx, NonTerminalNode cnt, boolean dropall);
     }
@@ -452,7 +475,7 @@ public class GrammarBuilder {
         protected ElementNode _e;
         public ElementNodeWrapper(ElementNode e) { this._e = e; }
         public String getOwnerTag() { return _e.getOwnerTag(); }
-        public boolean drop() { return _e.drop(); }
+        public boolean drop(Context cx) { return _e.drop(cx); }
         public Atom toAtom(Context cx) { return _e.toAtom(cx); }
         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return _e.build(cx, cnt, dropall); }
     }
@@ -465,7 +488,7 @@ public class GrammarBuilder {
 
     public class DropNode extends ElementNodeWrapper {
         public DropNode(ElementNode e) { super(e); }
-        public boolean drop() { return true; }
+        public boolean drop(Context cx) { return true; }
     }
 
     public    Seq  and2(Seq s,        Seq a)   { a.alwaysDrop = true;  return s.and(a); }
@@ -482,7 +505,7 @@ public class GrammarBuilder {
     public    ElementNode star(final ElementNode e)                            { return new RepeatNode(e, null, true,  true, false); }
     public  ElementNode starmaxfollow(final ElementNode e, final ElementNode sep)     { return new RepeatNode(e, sep,  true,  true, true); }
     public   ElementNode starfollow(final ElementNode e, final ElementNode sep)        { return new RepeatNode(e, sep,  true,  true, false); }
-    public    ElementNode question(final ElementNode e)                        { return new RepeatNode(e, null, true,  true, false); }
+    public    ElementNode question(final ElementNode e)                        { return new RepeatNode(e, null, true,  false, false); }
 
     //////////////////////////////////////////////////////////////////////////////
 
@@ -511,7 +534,7 @@ public class GrammarBuilder {
             } else {
                 ret = new Union(name, false);
                 map.put(name, ret);
-                nt.buildIntoPreallocatedUnion(this, nt, nt.drop(), ret);
+                nt.buildIntoPreallocatedUnion(this, nt, nt.drop(this), ret);
             }
             return ret;
         }
@@ -527,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;
         }
     }