checkpoint
[sbp.git] / src / edu / berkeley / sbp / misc / Demo.java
1 package edu.berkeley.sbp.misc;
2 import edu.berkeley.sbp.util.*;
3 import edu.berkeley.sbp.*;
4 import edu.berkeley.sbp.chr.*;
5 import java.util.*;
6 import java.lang.annotation.*;
7 import java.lang.reflect.*;
8 import java.io.*;
9
10 public class Demo {
11     
12     public static boolean harsh = false;
13
14     public static void main(String[] s) throws Exception {
15
16         ReflectiveMeta m = new ReflectiveMeta();
17         Tree<String> res = new CharParser(MetaGrammar.make()).parse(new FileInputStream(s[0])).expand1();
18         MetaGrammar.Meta.MetaGrammarFile mgf = m.new MetaGrammarFile(res);
19         MetaGrammar.BuildContext bc = new MetaGrammar.BuildContext(mgf);
20
21         Union meta = mgf.get("s").build(bc);
22         Tree t = new CharParser(meta).parse(new FileInputStream(s[1])).expand1();
23
24         Union u = Demo.make(t, "s");
25
26         System.err.println();
27         System.err.println("== parsing with parsed grammar =================================================================================");
28         t = new CharParser((Union)u).parse(new FileInputStream(s[1])).expand1();
29         System.out.println(t.toPrettyString());
30
31         System.err.println("== parsing with parsed-parsed grammar ==========================================================================");
32         t = new CharParser(new Context(t, m).build()).parse(new FileInputStream(s[1])).expand1();
33         System.out.println(t.toPrettyString());
34     }
35
36     public static class ReflectiveMeta extends MetaGrammar.Meta {
37         private final Class _cl;
38         private final Class[] _inner;
39         public ReflectiveMeta() {
40             this(MG.class,
41                  new Class[] {
42                      MG.Grammar.class,
43                      MG.NonTerminal.class,
44                      MG.AnonUn.class,
45                      MG.Range.class,
46                      MG.El.class,
47                      MG.Seq.class,
48                      MG.NonTerminalReference.class,
49                      MG.StringLiteral.class,
50                      MG.Tree.class,
51                      MG.CharClass.class
52                  });
53         }
54         public ReflectiveMeta(Class c, Class[] inner) {
55             this._cl = c;
56             this._inner = inner;
57         }
58         private boolean match(Method m, String s) { return match(m.getAnnotation(tag.class), null, s); }
59         private boolean match(tag t, Class c, String s) {
60             if (t==null) return false;
61             if (t.value().equals(s)) return true;
62             if (c != null && t.equals("") && c.getSimpleName().equals(s)) return true;
63             return false;
64         }
65         private boolean match(nonterminal t, Class c, String s) {
66             if (t==null) return false;
67             if (t.value().equals(s)) return true;
68             if (c != null && t.equals("") && c.getSimpleName().equals(s)) return true;
69             return false;
70         }
71         private boolean match(Class c, String s, String nonTerminalName) {
72             if (match((tag)c.getAnnotation(tag.class), c, s)) return true;
73             if (match((nonterminal)c.getAnnotation(nonterminal.class), c, nonTerminalName)) return true;
74             return false;
75         }
76         public boolean match(Constructor con, String s, String nonTerminalName) {
77             Class c = con.getDeclaringClass();
78             if (match((tag)con.getAnnotation(tag.class), null, s)) return true;
79             if (match((nonterminal)con.getAnnotation(nonterminal.class), c, s)) return true;
80             return false;
81         }
82         public Object repeatTag() {
83             return new Reducer() {
84                     public String toString() { return ""; }
85                     public Object reduce(Tree t) {
86                         Object[] ret = new Object[t.numChildren()];
87                         for(int i=0; i<t.numChildren(); i++) {
88                             Tree tc = t.child(i);
89                             if (tc.head() != null && tc.head() instanceof Reducer)
90                                 ret[i] = ((Reducer)tc.head()).reduce(tc);
91                             else if (tc.numChildren() == 0)
92                                 ret[i] = tc.head();
93                             else {
94                                 System.err.println("FIXME: don't know what to do about " + tc);
95                                 ret[i] = null;
96                             }
97                         }
98                         return ret;
99                     }
100                 };
101         }
102         public Sequence tryResolveTag(String tag, String nonTerminalName, Element[] els, Object[] labels, boolean[] drops) {
103             Production p = new Production(tag, nonTerminalName, els, labels, drops);
104             for(Method m : _cl.getMethods())
105                 if (new TargetMethod(m).isCompatible(p))
106                     return new TargetMethod(m).makeSequence(p);
107             for(Class c : _inner)
108                 for(Constructor con : c.getConstructors())
109                     if (new TargetConstructor(con).isCompatible(p))
110                         return new TargetConstructor(con).makeSequence(p);
111             for(Class c : _inner)
112                 if (new TargetClass(c).isCompatible(p))
113                     return new TargetClass(c).makeSequence(p);
114             return null;
115         }
116         public Sequence resolveTag(String tag, String nonTerminalName, Element[] els, Object[] labels, boolean[] drops) {
117             Sequence ret = tryResolveTag(tag, nonTerminalName, els, labels, drops);
118             if (ret != null) return ret;
119             String message = "could not find a Java method/class/ctor matching tag \""+tag+
120                 "\", nonterminal \""+nonTerminalName+"\" with " + els.length + " arguments";
121             if (harsh) {
122                 throw new RuntimeException(message);
123             } else {
124                 System.err.println(message);
125                 return Sequence.rewritingSequence(tag, els, labels, drops);
126             }
127         }
128     }
129
130     
131     /**
132      *  Constructors, classes, and methods with this attribute will
133      *  match every production of the nonterminal called "value()"
134      *  that is arg-compatible.  If value() is undefined, then the
135      *  class/constructor/method name is used.
136      */ 
137     @Retention(RetentionPolicy.RUNTIME) public static @interface nonterminal { String value() default ""; }
138
139     /**
140      *  Constructors, classes, and methods with this attribute will
141      *  match every tree tagged with "value()" that is arg-compatible.
142      *  If value() is undefined, then the class/constructor/method
143      *  name is used.
144      */ 
145     @Retention(RetentionPolicy.RUNTIME) public static @interface tag         { String value() default ""; }
146
147     /**
148      *  If any parameter to a method or field in a class has a named
149      *  arg-tag, that parameter/field matches the child of the tree
150      *  which either has that label or else is a reference to a
151      *  nonterminal with the corresponding name.
152      *  
153      *  The remaining non-named arg-tags match the remaining children
154      *  of the tree in sequential order.
155      *
156      *  If any arg-tagged parameters/fields remain, the match fails.
157      *  If there were no arg-tagged parameters-fields, it is as if all
158      *  of them were non-named and arg-tagged.
159      *
160      *  A method/constructor is arg-compatible if all of its arguments
161      *  are arg-compatible.
162      *
163      *  A class is arg-compatible if all of its fields are
164      *  arg-compatible, or if one of its constructors is arg-compatible.
165      *
166      */
167     @Retention(RetentionPolicy.RUNTIME) public static @interface arg         { String value() default ""; }
168
169     public static class Production {
170         public String tag;
171         public String nonTerminal;
172         public Object[] labels;
173         public boolean[] drops;
174         public Element[] elements;
175         public int count = 0;
176         public Production(String tag, String nonTerminal, Element[] elements, Object[] labels, boolean[] drops) {
177             this.tag = tag;
178             this.elements = elements;
179             this.nonTerminal = nonTerminal;
180             this.labels = labels;
181             this.drops = drops;
182             for(int i=0; i<drops.length; i++)
183                 if (!drops[i])
184                     count++;
185         }
186     }
187
188     public static abstract class Target {
189         public abstract String getName();
190         public abstract tag getTag();
191         public abstract nonterminal getNonTerminal();
192         public abstract int[] buildSequence(Production p);
193         public boolean isCompatible(Production p) {
194             tag t = getTag();
195             if (t != null &&
196                 (t.value().equals(p.tag) ||
197                  (t.value().equals("") && getName().equals(p.tag))))
198                 return buildSequence(p)!=null;
199
200             nonterminal n = getNonTerminal();
201             if (n != null &&
202                 (n.value().equals(p.nonTerminal) ||
203                  (n.value().equals("") && getName().equals(p.nonTerminal))))
204                 return buildSequence(p)!=null;
205
206             return false;
207         }
208         public int[] buildSequence(Production p, String[] names, arg[] argtags) {
209             int argTagged = 0;
210             for(int i=0; i<argtags.length; i++)
211                 if (argtags[i] != null)
212                     argTagged++;
213
214             // FIXME: can be smarter here
215             if (names.length==p.count) {
216                 int[] ret = new int[p.count];
217                 for(int i=0; i<p.count; i++) ret[i] = i;
218                 return ret;
219             } else if (argTagged==p.count) {
220                 int[] ret = new int[argtags.length];
221                 int j = 0;
222                 for(int i=0; i<argtags.length; i++)
223                     ret[i] = argtags[i]==null ? -1 : (j++);
224                 return ret;
225             } else {
226                 return null;
227             }
228         }
229         public Sequence makeSequence(Production p) {
230             return Sequence.rewritingSequence(new TargetReducer(p), p.elements, p.labels, p.drops);
231         }
232         public abstract Object plant(Object[] fields, int[] map);
233         public class TargetReducer implements Reducer {
234             private Production p;
235             private int[] map;
236             public TargetReducer(Production p) {
237                 this.p = p;
238                 this.map = buildSequence(p);
239             }
240             public String toString() { return "reducer-"+Target.this; }
241             public Object reduce(Tree t) {
242                 Object[] objects = new Object[t.numChildren()];
243                 for(int i=0; i<t.numChildren(); i++) {
244                     Tree tc = t.child(i);
245                     if (tc.head() != null && tc.head() instanceof Reducer)
246                         objects[i] = ((Reducer)tc.head()).reduce(tc);
247                     else if (tc.numChildren() == 0)
248                         objects[i] = tc.head();
249                     else {
250                         System.err.println("FIXME: don't know what to do about " + tc);
251                         objects[i] = null;
252                     }
253                 }
254                 System.err.println("input tree: " + t);
255                 return plant(objects, map);
256             }
257         }
258     }
259
260     public static interface Reducer {
261         public Object reduce(Tree t);
262     }
263
264     public static class TargetClass extends Target {
265         public final Class _class;
266         public TargetClass(Class _class) { this._class = _class; }
267         public String getName() { return _class.getSimpleName(); }
268         public tag getTag() { return (tag)_class.getAnnotation(tag.class); }
269         public nonterminal getNonTerminal() { return (nonterminal)_class.getAnnotation(nonterminal.class); }
270         public String toString() { return _class.getSimpleName(); }
271         public int[] buildSequence(Production p) {
272             Field[]  f       = _class.getDeclaredFields();
273             String[] names   = new String[f.length];
274             arg[]    argtags = new arg[f.length];
275             for(int i=0; i<f.length; i++) {
276                 names[i]   = f[i].getName();
277                 argtags[i] = f[i].getAnnotation(arg.class);
278             }
279             int[] ret = buildSequence(p, names, argtags);
280             if (ret!=null) return ret;
281             for(Constructor c : _class.getConstructors())
282                 if (new TargetConstructor(c).buildSequence(p)!=null)
283                     return new TargetConstructor(c).buildSequence(p);
284             return null;
285         }
286         public Object plant(Object[] fields, int[] map) {
287             try {
288                 Object ret = _class.newInstance();
289                 Field[] f = _class.getFields();
290                 int j = 0;
291                 for(int i=0; i<f.length; i++)
292                     if (map[i] != -1) {
293                         Object tgt = Reflection.lub(fields[map[i]]);
294                         if (f[i].getType() == String.class) tgt = stringify(tgt);
295                         // FUGLY
296                         tgt = coerce(tgt, f[i].getType());
297                         System.err.println("setting a " + f[i].getType().getName() + " to " + Reflection.show(tgt));
298                         f[i].set(ret, tgt);
299                     }
300                 return ret;
301             } catch (Exception e) {
302                 e.printStackTrace();
303                 throw new RuntimeException(e);
304             }
305         }
306     }
307
308     public static String stringify(Object o) {
309         if (o==null) return "";
310         if (!(o instanceof Object[])) return o.toString();
311         Object[] arr = (Object[])o;
312         StringBuffer ret = new StringBuffer();
313         for(int i=0; i<arr.length; i++)
314             ret.append(arr[i]);
315         return ret.toString();
316     }
317
318     public static class TargetConstructor extends Target {
319         public final Constructor _ctor;
320         public TargetConstructor(Constructor _ctor) { this._ctor = _ctor; }
321         public String getName() { return _ctor.getName(); }
322         public tag getTag() { return (tag)_ctor.getAnnotation(tag.class); }
323         public nonterminal getNonTerminal() { return (nonterminal)_ctor.getAnnotation(nonterminal.class); }
324         public String toString() { return _ctor.getName(); }
325         public int[] buildSequence(Production p) {
326             Annotation[][] annotations = _ctor.getParameterAnnotations();
327             int len = annotations.length;
328             int ofs = 0;
329             String name = _ctor.getDeclaringClass().getName();
330             /*
331             if (name.indexOf('$') > name.lastIndexOf('.')) {
332                 len--;
333                 ofs++;
334             }
335             */
336             String[] names   = new String[len];
337             arg[]    argtags = new arg[len];
338             for(int i=0; i<names.length; i++)
339                 for(Annotation a : annotations[i+ofs])
340                     if (a instanceof arg)
341                         argtags[i+ofs] = (arg)a;
342             return buildSequence(p, names, argtags);
343         }
344         public Object plant(Object[] fields, int[] map) {
345             try {
346                 Class[] argTypes = _ctor.getParameterTypes();
347                 Object[] args = new Object[argTypes.length];
348                 int j = 0;
349                 for(int i=0; i<args.length; i++)
350                     if (map[i] != -1) {
351                         Object tgt = Reflection.lub(fields[map[i]]);
352                         if (argTypes[i] == String.class) tgt = stringify(tgt);
353                         // FUGLY
354                         tgt = coerce(tgt, argTypes[i]);
355                         System.err.println("setting a " + argTypes[i].getName() + " to " + Reflection.show(tgt));
356                         args[i] = tgt;
357                     }
358                 return _ctor.newInstance(args);
359             } catch (Exception e) {
360                 throw new RuntimeException(e);
361             }
362         }
363     }
364     public static class TargetMethod extends Target {
365         public final Method _method;
366         public TargetMethod(Method _method) { this._method = _method; }
367         public String getName() { return _method.getName(); }
368         public String toString() { return _method.getName(); }
369         public tag getTag() { return (tag)_method.getAnnotation(tag.class); }
370         public nonterminal getNonTerminal() { return (nonterminal)_method.getAnnotation(nonterminal.class); }
371         public int[] buildSequence(Production p) {
372             Annotation[][] annotations = _method.getParameterAnnotations();
373             String[] names   = new String[annotations.length];
374             arg[]    argtags = new arg[annotations.length];
375             for(int i=0; i<names.length; i++)
376                 for(Annotation a : annotations[i])
377                     if (a instanceof arg)
378                         argtags[i] = (arg)a;
379             int[] ret = buildSequence(p, names, argtags);
380             return ret;
381         }
382         public Object plant(Object[] fields, int[] map) {
383             try {
384                 Class[] argTypes = _method.getParameterTypes();
385                 Object[] args = new Object[argTypes.length];
386                 int j = 0;
387                 for(int i=0; i<args.length; i++)
388                     if (map[i] != -1) {
389                         Object tgt = Reflection.lub(fields[map[i]]);
390                         if (argTypes[i] == String.class) tgt = stringify(tgt);
391                         // FUGLY
392                         tgt = coerce(tgt, argTypes[i]);
393                         System.err.println("setting a " + argTypes[i].getName() + " to " + Reflection.show(tgt));
394                         args[i] = tgt;
395                     }
396                 System.err.println("invoking " + _method + " with " + Reflection.show(args));
397                 return _method.invoke(null, args);
398             } catch (Exception e) {
399                 throw new RuntimeException(e);
400             }
401         }
402     }
403
404     public static Object coerce(Object o, Class c) {
405         if (o==null) return null;
406         if (c.isInstance(o)) return o;
407         if (c == char.class) {
408             return o.toString().charAt(0);
409         }
410         if (c.isArray() && (c.getComponentType().isInstance(o))) {
411             Object[] ret = (Object[])Array.newInstance(c.getComponentType(), 1);
412             ret[0] = o;
413             return ret;
414         }
415         if (o.getClass().isArray() && c.isArray()) {
416             boolean ok = true;
417             for(int i=0; i<((Object[])o).length; i++) {
418                 Object ob = (((Object[])o)[i]);
419                 if (ob != null) {
420                     System.err.println("no hit with " + c.getComponentType().getName() + " on " + Reflection.show(((Object[])o)[i]));
421                     ok = false;
422                 }
423             }
424             if (ok) {
425                 System.err.println("hit with " + c.getComponentType().getName());
426                 return Array.newInstance(c.getComponentType(), ((Object[])o).length);
427             }
428         }
429         return o;
430     }
431
432     public static Union make(Tree t, String s) {
433         ReflectiveMeta rm = new ReflectiveMeta();
434         Reducer red = (Reducer)t.head();
435         MG.Grammar g = (MG.Grammar)red.reduce(t);
436         Context cx = new Context(g,rm);
437         Union u = null;
438         for(MG.NonTerminal nt : g.nonterminals) {
439             Union el = (Union)cx.get(nt.name);
440             StringBuffer st = new StringBuffer();
441             el.toString(st);
442             System.err.println(st);
443             if (nt.name.equals(s)) u = el;
444         }
445         return u;
446     }
447
448     public static class MG {
449         public static @tag("grammar") class Grammar {
450             public NonTerminal get(String s) {
451                 for(NonTerminal nt : nonterminals)
452                     if (nt.name.equals(s))
453                         return nt;
454                 return null;
455             }
456             public @arg("NonTerminal") NonTerminal[] nonterminals;
457             public String toString() {
458                 String ret = "[ ";
459                 for(NonTerminal nt : nonterminals) ret += nt + ", ";
460                 return ret + " ]";
461             }
462         }
463         public abstract static class Un extends El {
464             public Seq[][] sequences;
465             public void build(Context cx, Union u) {
466                 HashSet<Sequence> bad2 = new HashSet<Sequence>();
467                 for(int i=0; i<sequences.length; i++) {
468                     Seq[] group = sequences[i];
469                     Union u2 = new Union();
470                     if (sequences.length==1) u2 = u;
471                     for(int j=0; j<group.length; j++) {
472                         group[j].build(cx, u2, false);
473                     }
474                     if (sequences.length==1) break;
475                     Sequence seq = Sequence.singleton(u2);
476                     for(Sequence s : bad2) {
477                         s.lame = true;
478                         seq = seq.not(s);
479                     }
480                     u.add(seq);
481                     bad2.add(Sequence.singleton(u2));
482                 }
483             }
484         }
485         public static class NonTerminal extends Un {
486             public String  name = null;
487             public @nonterminal("NonTerminal") NonTerminal(@arg("Word") String name,
488                                                            @arg("RHS") Seq[][] sequences) {
489                 this.name = name;
490                 this.sequences = sequences;
491             }
492             public Element build(Context cx) { return cx.get(name); }
493         }
494
495         public static class AnonUn extends Un {
496             public @tag("(") AnonUn(Seq[][] sequences) {
497                 this.sequences = sequences;
498             }
499             public Element build(Context cx) {
500                 Union ret = new Union();
501                 build(cx, ret);
502                 return ret;
503             }
504         }
505
506         //public static @tag void range(char c) { }
507         public static class Range {
508             public @tag("range") Range(char only) { first = only; last = only; }
509             public @tag("-")     Range(char first, char last) { this.first = first; this.last = last; }
510             public char first;
511             public char last;
512         }
513         public static abstract class El {
514             public String getLabel() { return null; }
515             public String getOwnerTag() { return null; }
516             public boolean drop() { return false; }
517             public abstract Element build(Context cx);
518         }
519         public static class Drop extends El {
520             public El e;
521             public Drop(El e) { this.e = e; }
522             public String getLabel() { return null; }
523             public boolean drop() { return true; }
524             public String getOwnerTag() { return e.getOwnerTag(); }
525             public Element build(Context cx) { return e.build(cx); }
526         }
527         public static class Label extends El {
528             public String label;
529             public El e;
530             public Label(String label, El e) { this.e = e; this.label = label; }
531             public String getLabel() { return label; }
532             public String getOwnerTag() { return e.getOwnerTag(); }
533             public Element build(Context cx) { return e.build(cx); }
534         }
535         public static /*abstract*/ class Seq {
536             HashSet<Seq> and = new HashSet<Seq>();
537             HashSet<Seq> not = new HashSet<Seq>();
538             El[] elements;
539             El follow;
540             String tag = null;
541             boolean lame;
542             public Seq(El e) { this(new El[] { e }); }
543             public Seq(El[] elements) { this.elements = elements; }
544             public Seq tag(String tag) { this.tag = tag; return this; }
545             public Seq follow(El follow) { this.follow = follow; return this; }
546             public Seq dup() {
547                 Seq ret = new Seq(elements);
548                 ret.and.addAll(and);
549                 ret.not.addAll(not);
550                 ret.follow = follow;
551                 ret.tag = tag;
552                 return ret;
553             }
554             public Seq and(Seq s) { and.add(s); s.lame = true; return this; }
555             public Seq andnot(Seq s) { not.add(s); s.lame = true; return this; }
556             public Seq separate(El sep) {
557                 El[] elements = new El[this.elements.length * 2 - 1];
558                 for(int i=0; i<this.elements.length; i++) {
559                     elements[i*2]   = this.elements[i];
560                     if (i<this.elements.length-1)
561                         elements[i*2+1] = new Drop(sep);
562                 }
563                 this.elements = elements;
564                 return this;
565             }
566             public Sequence build(Context cx, Union u, boolean lame) {
567                 Sequence ret = build0(cx, lame || this.lame);
568                 for(Seq s : and) { Sequence dork = s.build(cx, u, true); ret = ret.and(dork); }
569                 for(Seq s : not) { Sequence dork = s.build(cx, u, true); ret = ret.not(dork); }
570                 u.add(ret);
571                 ret.lame = lame;
572                 return ret;
573             }
574             public Sequence build0(Context cx, boolean lame) {
575                 boolean unwrap = false;
576                 boolean dropAll = lame;
577                 if (tag!=null && tag.equals("[]")) unwrap  = true;
578                 if (tag!=null && "()".equals(tag)) dropAll = true;
579                 Object[] labels = new Object[elements.length];
580                 boolean[] drops = new boolean[elements.length];
581                 Element[] els = new Element[elements.length];
582                 for(int i=0; i<elements.length; i++) {
583                     labels[i] = elements[i].getLabel();
584                     drops[i]  = elements[i].drop();
585                     els[i] = elements[i].build(cx);
586                     if (elements[i].getOwnerTag() != null)
587                         tag = elements[i].getOwnerTag();
588                 }
589                 Sequence ret = null;
590                 if (dropAll)     ret = Sequence.drop(els, false);
591                 else if (unwrap) ret = Sequence.unwrap(els, cx.rm.repeatTag(), drops);
592                 else if (tag!=null) {
593                     ret = cx.rm.resolveTag(tag, cx.cnt, els, labels, drops);
594                 } else {
595                     int idx = -1;
596                     for(int i=0; i<els.length; i++)
597                         if (!drops[i])
598                             if (idx==-1) idx = i;
599                             else throw new Error("multiple non-dropped elements in sequence: " + Sequence.drop(els,false));
600                     if (idx != -1) ret = Sequence.singleton(els, idx);
601                     else           ret = Sequence.drop(els, false);
602                 }
603                 if (this.follow != null)
604                     ret.follow = MetaGrammar.infer(this.follow.build(cx));
605                 ret.lame = this.lame;
606                 return ret;
607             }
608         }
609         public static @tag("&")   Seq  and(Seq s,         El[] elements) { return s.and(seq(elements)); }
610         public static @tag("&~")  Seq  andnot(Seq s,      El[] elements) { return s.andnot(seq(elements)); }
611         public static @tag("->")  Seq  arrow(Seq s, El e)                { return s.follow(e); }
612         public static @tag("::")  Seq  tag(String tagname, Seq s)        { return s.tag(tagname); }
613         public static @tag("/")   Seq  slash(Seq s, El e)                { return s.separate(e); }
614
615         public static @tag("ps")  Seq  seq(El[] elements)                { return new Seq(elements); }
616         public static @tag        Seq  psx(Seq s)                        { return s; }
617         public static @tag(":")   El   colon(String s, El e)             { return new Label(s, e); }
618         public static @tag(")")   void close(String foo)                 { throw new Error("not supported"); }
619         public static @tag("()")  El   epsilon()                         { return new Constant(Union.epsilon); }
620
621         public static @tag("nonTerminal") class NonTerminalReference extends El {
622             public @arg String nonTerminal;
623             public Element build(Context cx) {
624                 return cx.get(nonTerminal);
625             }
626         }
627         public static class StringLiteral        extends Constant {
628             public @tag("literal") StringLiteral(String string) { super(CharRange.string(string)); }
629             public boolean drop() { return true; }
630         }
631         public static                     class CharClass            extends El {
632             Range[] ranges;
633             public @tag("[") CharClass(Range[] ranges) { this.ranges = ranges; }
634             public Element build(Context cx) {
635                 edu.berkeley.sbp.util.Range.Set set = new edu.berkeley.sbp.util.Range.Set();
636                 for(Range r : ranges)
637                         set.add(r.first, r.last);
638                 return CharRange.set(set);
639             }
640         }
641
642         public static @tag("{")           class Tree                 extends El {
643             public @arg Seq body;
644             public Element build(Context cx) {
645                 throw new Error();
646             }
647         }
648
649         public static class Rep extends El {
650             public El e, sep;
651             public boolean zero, many, max;
652             public Rep(El e, El sep, boolean zero, boolean many, boolean max) {
653                 this.e = e; this.sep = sep; this.zero = zero; this.many = many; this.max = max;}
654             public Element build(Context cx) {
655                 return (!max)
656                     ? Sequence.repeat(e.build(cx),        zero, many, sep==null ? null : sep.build(cx), cx.rm.repeatTag())
657                     : sep==null
658                     ? Sequence.repeatMaximal(MetaGrammar.infer(e.build(cx)), zero, many,                                   cx.rm.repeatTag())
659                     : Sequence.repeatMaximal(e.build(cx),                    zero, many, MetaGrammar.infer(sep.build(cx)), cx.rm.repeatTag());
660             }
661         }
662         public static class Constant extends El {
663             Element constant;
664             public Constant(Element constant) { this.constant = constant; }
665             public Element build(Context cx) { return constant; }
666         }
667         public abstract static class PostProcess extends El {
668             El e;
669             public PostProcess(El e) { this.e = e; }
670             public Element build(Context cx) { return postProcess(e.build(cx)); }
671             public abstract Element postProcess(Element e);
672         }
673
674         // FIXME: it would be nice if we could hoist this into "Rep"
675         public static @tag("++")  El plusmax(final El e)                     { return new Rep(e, null, false, true, true); }
676         public static @tag("+")   El plus(final El e)                        { return new Rep(e, null, false, true, false); }
677         public static @tag("++/") El plusmaxfollow(final El e, final El sep) { return new Rep(e, sep,  false, true, true); }
678         public static @tag("+/")  El plusfollow(final El e, final El sep)    { return new Rep(e, sep,  false, true, false); }
679         public static @tag("**")  El starmax(final El e)                     { return new Rep(e, null, true,  true, true); }
680         public static @tag("*")   El star(final El e)                        { return new Rep(e, null, true,  true, false); }
681         public static @tag("**/") El starmaxfollow(final El e, final El sep) { return new Rep(e, sep,  true,  true, true); }
682         public static @tag("*/")  El starfollow(final El e, final El sep)    { return new Rep(e, sep,  true,  true, false); }
683         public static @tag("?")   El question(final El e)                    { return new Rep(e, null, true,  true, false); }
684
685         public static @tag("!")   El bang(final El e)                        { return new Drop(e); }
686
687         public static @tag("^")   El caret(final String s) {
688             return new Drop(new Constant(CharRange.string(s)) {
689                     public String getOwnerTag() { return s; }
690                 });
691         }
692
693         public static @tag("~")   El tilde(final El e) {
694             return new PostProcess(e) {
695                     public Element postProcess(Element e) {
696                         return MetaGrammar.infer((Topology<Character>)Atom.toAtom(e).complement()); 
697                     } }; }
698
699         public static @tag("^^")  void doublecaret(final El e)                 { throw new Error("not implemented"); }
700
701         //public static @tag("(")   El subexpression(Seq[][] rhs)                { return new NonTerminal(rhs); }
702
703         public static @nonterminal("Word")    String word(String s) { return s; }
704         public static @nonterminal("Quoted")  String quoted(String s) { return s; }
705         public static @nonterminal("escaped") String c(char c) { return c+""; }
706         public static @tag("\"\"")            String emptystring() { return ""; }
707         public static @tag("\n")              String retur() { return "\n"; }
708         public static @tag("\r")              String lf() { return "\r"; }
709
710     }
711     public static class Context {
712         HashMap<String,Union> map = new HashMap<String,Union>();
713         private MG.Grammar grammar;
714         public String cnt = null;
715         private ReflectiveMeta rm;
716         public Context(MG.Grammar g, ReflectiveMeta rm) {
717             this.grammar = g;
718             this.rm = rm;
719         }
720         public Union build() {
721             Union ret = null;
722             for(MG.NonTerminal nt : grammar.nonterminals) {
723                 Union u = get(nt.name);
724                 if ("s".equals(nt.name))
725                     ret = u;
726             }
727             return ret;
728         }
729         public Context(Tree t, ReflectiveMeta rm) {
730             this.rm = rm;
731             Reducer red = (Reducer)t.head();
732             this.grammar = (MG.Grammar)red.reduce(t);
733         }
734         public Union peek(String name) { return map.get(name); }
735         public void  put(String name, Union u) { map.put(name, u); }
736         public Union get(String name) {
737             Union ret = map.get(name);
738             if (ret != null) return ret;
739             ret = new Union(name);
740             map.put(name, ret);
741             MG.NonTerminal nt = grammar.get(name);
742             if (nt==null) {
743                 System.err.println("*** warning could not find " + name);
744             } else {
745                 String old = cnt;
746                 cnt = name;
747                 nt.build(this, ret);
748                 cnt = old;
749             }
750             return ret;
751         }
752
753     }
754 }