added more flexible handling of Repeats -- now you can specify what they get tagged...
authoradam <adam@megacz.com>
Sun, 5 Mar 2006 07:01:05 +0000 (02:01 -0500)
committeradam <adam@megacz.com>
Sun, 5 Mar 2006 07:01:05 +0000 (02:01 -0500)
darcs-hash:20060305070105-5007d-94da880f63c6bfa30c0ceb607fbfde092515e1d4.gz

src/edu/berkeley/sbp/Repeat.java
src/edu/berkeley/sbp/Sequence.java
src/edu/berkeley/sbp/misc/MetaGrammar.java
src/edu/berkeley/sbp/misc/ReflectiveGrammar.java

index 4267b36..c68120e 100644 (file)
@@ -11,26 +11,34 @@ import java.lang.ref.*;
 public class Repeat extends Union {
 
     /** repeat zero or one times */
-    public  static Repeat maybe(Element e)              { return new Repeat(e, true, false); }
+    public  static Repeat maybe(Element e)              { return new Repeat(e, true, false, null, false); }
+    public  static Repeat maybe(Element e, Object tag)              { return new Repeat(e, true, false, null, false, tag); }
     /** repeat zero or more times */
-    public  static Repeat many0(Element e)              { return new Repeat(e, true, true); }
+    public  static Repeat many0(Element e)              { return new Repeat(e, true, true, null, false); }
+    public  static Repeat many0(Element e, Object tag)              { return new Repeat(e, true, true, null, false, tag); }
     /** repeat zero or more times, separated by <tt>sep</tt> */
-    public  static Repeat many0(Element e, Element sep) { return new Repeat(e, true, true, sep); }
+    public  static Repeat many0(Element e, Element sep) { return new Repeat(e, true, true, sep, false); }
+    public  static Repeat many0(Element e, Element sep, Object tag) { return new Repeat(e, true, true, sep, false, tag); }
     /** repeat one or more times */
-    public  static Repeat many1(Element e)              { return new Repeat(e, false, true); }
+    public  static Repeat many1(Element e)              { return new Repeat(e, false, true, null, false); }
+    public  static Repeat many1(Element e, Object tag)              { return new Repeat(e, false, true, null, false, tag); }
     /** repeat one or more times, separated by <tt>sep</tt> */
-    public  static Repeat many1(Element e, Element sep) { return new Repeat(e, false, true, sep); }
+    public  static Repeat many1(Element e, Element sep) { return new Repeat(e, false, true, sep, false); }
+    public  static Repeat many1(Element e, Element sep, Object tag) { return new Repeat(e, false, true, sep, false, tag); }
 
     /** repeat zero or more times, matching a maximal sequence of atoms */
     public  static Repeat maximal0(Element e)              { return new Repeat(e, true, true, null, true); }
+    public  static Repeat maximal0(Element e, Object tag)              { return new Repeat(e, true, true, null, true, tag); }
     /** repeat one or more times, matching a maximal sequence of atoms */
     public  static Repeat maximal1(Element e)              { return new Repeat(e, false, true, null, true); }
+    public  static Repeat maximal1(Element e, Object tag)              { return new Repeat(e, false, true, null, true, tag); }
     /** repeat one or more times, separated by an atom <tt>sep</tt>, matching a maximal sequence */
     public  static Repeat maximal1(Element e, Element sep) { return new Repeat(e, false, true, sep, true); }
+    public  static Repeat maximal1(Element e, Element sep, Object tag) { return new Repeat(e, false, true, sep, true, tag); }
 
-    private Repeat(final Element e, boolean zeroOkay, boolean manyOkay) { this(e, zeroOkay, manyOkay, null); }
-    private Repeat(final Element e, boolean zeroOkay, boolean manyOkay, Element separator) { this(e, zeroOkay, manyOkay, separator, false); }
     private Repeat(final Element e, boolean zeroOkay, boolean manyOkay, final Element separator, boolean maximal) {
+        this(e, zeroOkay, manyOkay, separator, maximal, null); }
+    private Repeat(final Element e, boolean zeroOkay, boolean manyOkay, final Element separator, boolean maximal, Object tag) {
         super(e+(!manyOkay ? "?" : (zeroOkay ? (maximal ? "**" : "*") : (maximal ? "++" : "+")))+(separator==null?"":("/"+separator)), true);
         if (maximal && zeroOkay && separator != null)
             throw new RuntimeException("cannot create a maximal repetition of zero or more items with a separator (yet): " + this);
@@ -39,11 +47,11 @@ public class Repeat extends Union {
             if (manyOkay) add(new Sequence.Singleton(many1(e, separator)));
             else          add(new Sequence.Singleton(e));
         } else {
-            add(new Sequence.RewritingSequence(null, new Element[] { e }, null));
+            add(new Sequence.RewritingSequence(tag, new Element[] { e }, null));
             if (separator==null)
-                add(new Sequence.Unwrap(new Element[] { e,                 Repeat.this }));
+                add(new Sequence.Unwrap(new Element[] { e,                 Repeat.this }, tag));
             else
-                add(new Sequence.Unwrap(new Element[] { e, separator,      Repeat.this }, new boolean[] { false, true, false }));
+                add(new Sequence.Unwrap(new Element[] { e, separator,      Repeat.this }, tag, new boolean[] { false, true, false }));
         }
         if (maximal) for(Sequence s : this) s.noFollow = separator==null ? e : separator;
     }
index 4d9e41f..7071ecb 100644 (file)
@@ -197,18 +197,19 @@ public abstract class Sequence extends Element implements Iterable<Element> {
 
     public static class Unwrap extends Sequence {
         private boolean[] drops;
-        public Unwrap(Element[] e)                  { super(e); this.drops = null; }
-        public Unwrap(Element[] e, boolean[] drops) { super(e); this.drops = drops; }
+        private final Object tag;
+        public Unwrap(Element[] e, Object tag)                  { super(e); this.drops = null; this.tag = tag; }
+        public Unwrap(Element[] e, Object tag, boolean[] drops) { super(e); this.drops = drops; this.tag = tag; }
         Sequence _clone() { return new Unwrap(elements, drops); }
         public <T> Forest<T> postReduce(Input.Location loc, Forest<T>[] args, Position p) {
             for(int i=0; i<args.length; i++) if (args[i]==null) throw new Error();
-            if (drops==null) return Forest.create(loc, null, args, new Object[args.length], true, false, p);
+            if (drops==null) return Forest.create(loc, (T)tag, args, new Object[args.length], true, false, p);
             int count = 0;
             for(int i=0; i<drops.length; i++) if (!drops[i]) count++;
             Forest<T>[] args2 = new Forest[count];
             int j = 0;
             for(int i=0; i<args.length; i++) if (!drops[i]) args2[j++] = args[i];
-            return Forest.create(loc, null, args2, new Object[args.length], true, false, p);
+            return Forest.create(loc, (T)tag, args2, new Object[args.length], true, false, p);
         }
     }
 
index 33b3b11..c96d2ff 100644 (file)
@@ -115,14 +115,14 @@ public class MetaGrammar extends StringWalker {
         if      ("\\n".equals(head)) return new Character('\n');
         else if ("\\r".equals(head)) return new Character('\r');
         else if ("grammar".equals(head)) { for(Tree<String> t : tree.children()) walk(t); return this; }
-        else if ("*".equals(head))   return Repeat.many0((Element)walk(tree.child(0)));
-        else if ("+".equals(head))   return Repeat.many1((Element)walk(tree.child(0)));
-        else if ("+/".equals(head))  return Repeat.many1((Element)walk(tree.child(0)), (Element)walk(tree.child(1)));
-        else if ("*/".equals(head))  return Repeat.many0((Element)walk(tree.child(0)), (Element)walk(tree.child(1)));
-        else if ("++/".equals(head)) return Repeat.maximal1((Element)walk(tree.child(0)), (Element)walk(tree.child(1)));
-        else if ("**".equals(head))  return Repeat.maximal0((Element)walk(tree.child(0)));
-        else if ("++".equals(head))  return Repeat.maximal1((Element)walk(tree.child(0)));
-        else if ("?".equals(head))   return Repeat.maybe((Element)walk(tree.child(0)));
+        else if ("*".equals(head))   return Repeat.many0((Element)walk(tree.child(0)), repeatTag());
+        else if ("+".equals(head))   return Repeat.many1((Element)walk(tree.child(0)), repeatTag());
+        else if ("+/".equals(head))  return Repeat.many1((Element)walk(tree.child(0)), (Element)walk(tree.child(1)), repeatTag());
+        else if ("*/".equals(head))  return Repeat.many0((Element)walk(tree.child(0)), (Element)walk(tree.child(1)), repeatTag());
+        else if ("++/".equals(head)) return Repeat.maximal1((Element)walk(tree.child(0)), (Element)walk(tree.child(1)), repeatTag());
+        else if ("**".equals(head))  return Repeat.maximal0((Element)walk(tree.child(0)), repeatTag());
+        else if ("++".equals(head))  return Repeat.maximal1((Element)walk(tree.child(0)), repeatTag());
+        else if ("?".equals(head))   return Repeat.maybe((Element)walk(tree.child(0)), repeatTag());
         else if ("!".equals(head))   { Element e = (Element)walk(tree.child(0)); dropAll.add(e); return e; }
         else if ("&".equals(head))   return ((Sequence)walk(tree,0)).and(sequence(walk(tree, 1), true));
         else if ("and".equals(head)) return ((Sequence)walk(tree,0)).and(sequence(walk(tree, 1), true));
@@ -186,6 +186,7 @@ public class MetaGrammar extends StringWalker {
     }
 
     public Object convertLabel(String label) { return label; }
+    public Object repeatTag() { return null; }
 
     public Object walk(String tag, Object[] argo) {
         if (argo.length==0) return super.walk(tag, argo);
@@ -276,6 +277,7 @@ public class MetaGrammar extends StringWalker {
                 for(int i=0; i<o.length; i++) {
                     Object oi = o[i];
                     if (oi instanceof Keep)    {
+                        if (!unwrap && tag==null) throw new Error("cannot have labels in an untagged sequence: " + ((Keep)oi).label);
                         if (!keeping) { for(int k=0; k<i; k++) drops[k]=true; }
                         drops[i] = false;
                         keeping = true;
@@ -298,7 +300,7 @@ public class MetaGrammar extends StringWalker {
             Element[] expansion = o2;
             Sequence ret = null;
             if (dropAll || lame) ret = Sequence.drop(expansion, lame);
-            else if (unwrap)    ret = new Sequence.Unwrap(expansion, drops);
+            else if (unwrap)    ret = new Sequence.Unwrap(expansion, repeatTag(), drops);
             else if (keeping || tag!=null) ret = Sequence.rewritingSequence(tag, expansion, labels, drops);
             else {
                 int idx = -1;
index ecf186d..146335c 100644 (file)
@@ -14,6 +14,10 @@ public class ReflectiveGrammar extends MetaGrammar {
     public ReflectiveGrammar(Class baseClass) { this.baseClass = baseClass; }
 
     public Object convertLabel(String label) { return new ClassMark(label); }
+    public Object repeatTag() { return arrayMark; }
+
+    private static class ArrayMark { }
+    private static final ArrayMark arrayMark = new ArrayMark();
 
     private static class ClassMark {
         public final String clazz;
@@ -33,44 +37,67 @@ public class ReflectiveGrammar extends MetaGrammar {
 
     public Object build(Tree<Object> t) throws Exception { return buildHead(t, null); }
     public Object buildHead(Tree<Object> t, Class c) throws Exception {
-        System.out.println("buildHead " + (c==null?null:c.getName()) + " " + t);
+        //System.out.println("buildHead " + (c==null?null:c.getName()) + " " + t);
         Object h = t.head();
 
-        if (h != null && h instanceof ClassMark) return buildBody(t, Class.forName(baseClass.getName()+"$"+((ClassMark)h).clazz));
+        if (h != null && h instanceof ClassMark) {
+            String clazz = ReflectiveWalker.mangle(((ClassMark)h).clazz);
+            Class c2 = (c!=null && c.getName().endsWith("$"+clazz)) ? c : Reflection.forNameOrNull(baseClass.getName()+"$"+clazz);
+            if (c2!=null) return buildBody(t, c2);
+            for(Method m : baseClass.getMethods()) {
+                if (!m.getName().equals(clazz)) continue;
+                Object[] o = new Object[m.getParameterTypes().length];
+                for(int i=0; i<o.length; i++)
+                    o[i] = i>=t.numChildren() ? null : buildHead(t.child(i), m.getParameterTypes()[i]);
+                return m.invoke(null, o);
+            }
+            throw new RuntimeException("couldn't figure out what to invoke for ClassMark " + clazz);
+        } else if (h==arrayMark && c==null) {
+            c = Object[].class;            
+        }
+        if (c==null) System.out.println(h + " -- " + t.toPrettyString());
         if (c.isArray()) {
             Object[] ret = new Object[t.numChildren()];
             for(int i=0; i<ret.length; i++)
                 ret[i] = buildHead(t.child(i), c.getComponentType());
             return Reflection.lub(ret);
         }
-        if (h==null)                return buildBody(t, c);
-        
         if (c==String.class) return stringify(t);
-        if (c==int.class)    return new Integer(stringify(t));
+        if (c==int.class)    { String s = stringify(t); if (s==null||"".equals(s)) return new Integer(0); return new Integer(s); }
+        if (h==null)         return buildBody(t, c);
 
-        if (t.numChildren() > 0) throw new RuntimeException("can't buildHead() on a tree with children when the head is of type " + h.getClass().getName());
+        if (t.numChildren() > 0)
+            throw new RuntimeException("can't buildHead() on a tree with children when the head is of type " +
+                                       h.getClass().getName() + "(c=="+(c==null?"null":c.getName())+")");
         return h;
     }
 
     public Object buildBody(Tree<Object> t, Class c) throws Exception {
         System.out.println("buildBody " + (c==null?null:c.getName()) + " " + t);
         c = resolveClass(t, c);
+        if (c==null) return buildHead(t, null);
         Object o = c.newInstance();
         Field[] f = c.getFields();
         OUTER: for(int i=0; i<t.numChildren(); i++) {
             Object label = t.label(i);
             Field field = null;
-            if (label!=null) try { field = c.getField(label+""); } catch (NoSuchFieldException _) { }
+            if (label!=null) field = Reflection.getField(c, label+"");
             if (field==null && label != null)
                 for(Method m : c.getMethods())
                     if (m.getName().equals(label)) {
                         m.invoke(o, new Object[] { buildHead(t.child(i), m.getParameterTypes()[0]) });
                         continue OUTER;
                     }
-            if (field==null) System.err.println("warning: skipping field " + label + " ("+i+") on class " + c.getName());
+            if (field==null && label==null) {
+                field = Reflection.getField(c, 0);
+            }
+            if (field==null) {
+                System.err.println("warning: skipping field " + label + " ("+i+") on class " + c.getName());
+                System.err.println("   tree: " + t);
+            }
             else {
                 Object tgt = Reflection.rebuild(buildHead(t.child(i), field.getType()), field.getType());
-                if (tgt instanceof Object[]) tgt = Reflection.lub(tgt);
+                //if (tgt instanceof Object[]) tgt = Reflection.lub(tgt);
                 System.err.println("setting field " + field.getName() + " on " + c.getName() + " to " + tgt);
                 try {
                     field.set(o, tgt);
@@ -84,6 +111,8 @@ public class ReflectiveGrammar extends MetaGrammar {
 
     public Class resolveClass(Tree<Object> t, Class c) throws Exception {
         if (c==null) return null;
+        if (c==int.class) return c;
+        if (c==String.class) return c;
         System.out.println("resolving " + c.getName());
         if (Reflection.isConcrete(c)) return c;
         Class ret = null;
@@ -99,6 +128,7 @@ public class ReflectiveGrammar extends MetaGrammar {
                 throw new RuntimeException("couldn't decide between two classes:\n  " + subs[i].getName() + "\n  " + ret.getName());
             ret = subs[i];
         }
+        if (t.head()==null) return null;
         if (ret==null) throw new RuntimeException("couldn't find a class to match tree: " + t);
         return ret;
     }