tentative checkpoint ROLL THIS BACK BUT INCLUDES CRUCIAL FIX
[sbp.git] / src / edu / berkeley / sbp / misc / MetaGrammar.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.io.*;
7
8 public class MetaGrammar extends StringWalker {
9
10     public static Object repeatTag = null;
11
12     public static Union make() throws Exception { return make(MetaGrammarTree.meta, "s"); }
13     public static Union make(Tree<String> tree, String nt) throws Exception {
14         Meta.MetaGrammarFile mgf = new MetaGrammar.Meta.MetaGrammarFile(tree);
15         BuildContext bc = new BuildContext(mgf);
16         return mgf.get(nt).build(bc);
17     }
18
19     ////////////////////////////////////////////////////////////////////////////////
20
21     private static Element  set(Range.Set r) { return CharRange.set(r); }
22     private static Element  string(String s) { return CharRange.string(s); }
23     private static Atom infer(Element e)  { return infer((Topology<Character>)Atom.toAtom(e)); }
24     private static Atom infer(Topology<Character> t) { return new CharRange(new CharTopology(t)); }
25
26     private MetaGrammar() { }
27
28     public static String string(Iterable<Tree<String>> children) {
29         String ret = "";
30         for(Tree<String> t : children) ret += string(t);
31         return ret;
32     }
33     public static String string(Tree<String> tree) {
34         String ret = "";
35         if (tree.head()!=null) ret += tree.head();
36         ret += string(tree.children());
37         return ret;
38     }
39
40     public static class BuildContext extends HashMap<String,Union> {
41         private final Meta.MetaGrammarFile mgf;
42         public BuildContext(Meta.MetaGrammarFile mgf) { this.mgf = mgf; }
43         public Union build(String s) {
44             Union ret = get(s);
45             if (ret != null) return ret;
46             Meta.MetaNonterminal mnt = mgf.get(s);
47             if (mnt==null) throw new Error("undeclared nonterminal \""+s+"\"");
48             return mnt.build(this);
49         }
50     }
51
52     public static class Meta {
53         public static class MetaGrammarFile extends HashMap<String,MetaNonterminal> {
54             public MetaGrammarFile(Tree<String> tree) {
55                 if (!tree.head().equals("grammar")) throw new Error();
56                 for(Tree<String> nt : tree.child(0))
57                     add(new MetaNonterminal(nt));
58             }
59             private void add(MetaNonterminal mnt) {
60                 if (this.get(mnt.name)!=null) throw new Error("duplicate definition of nonterminal \""+mnt.name+"\"");
61                 this.put(mnt.name, mnt);
62             }
63             public String toString() {
64                 String ret = "";
65                 for(MetaNonterminal mnt : this.values()) ret += mnt + "\n";
66                 return ret;
67             }
68         }
69         public static class MetaNonterminal {
70             public String    name;
71             public MetaUnion rhs;
72             public MetaNonterminal(Tree<String> tree) {
73                 name = string(tree.child(0));
74                 rhs = rhs(tree.child(1));
75             }
76             public String toString() { return name + " = " + rhs; }
77             public Union build(BuildContext bc) { return rhs.build(bc, name); }
78         }
79         public static MetaUnion rhs(Tree<String> t) {
80             return t.numChildren()==1
81                 ? new MetaUnion(t.child(0), false)
82                 : new MetaUnion(t, true);
83         }
84         public static class MetaUnion implements MetaSequence {
85             public boolean prioritized;
86             public MetaSequence[] sequences;
87             public Sequence buildSequence(BuildContext bc) {
88                 return Sequence.singleton(new Element[] { buildAnon(bc) }, 0);
89             }
90             public Union buildAnon(BuildContext bc) {
91                 String s = "";
92                 for(int i=0; i<sequences.length; i++)
93                     s += (i>0?"\n                "+(prioritized?">":"|")+" ":"")+sequences[i].buildSequence(bc);
94                 return build(bc, s);
95             }
96             public Union build(BuildContext bc, String name) {
97                 Union u = bc.get(name);
98                 if (u != null) return u;
99                 u = new Union(name);
100                 bc.put(name, u);
101                 HashSet<Sequence> seqs = new HashSet<Sequence>();
102                 for(MetaSequence s : sequences) {
103                     Sequence seq = s.buildSequence(bc);
104                     if (seq != null) {
105                         Sequence oseq = seq;
106                         if (prioritized)
107                             for(Sequence seqprev : seqs)
108                                 seq = seq.not(seqprev);
109                         u.add(seq);
110                         seqs.add(seq);
111                     }
112                 }
113                 return u;
114             }
115             public MetaUnion(Tree<String> t, boolean prioritized) {
116                 //System.err.println("metaunion: " + t);
117                 this.prioritized = prioritized;
118                 int i = 0;
119                 this.sequences = new MetaSequence[t.numChildren()];
120                 for(Tree<String> tt : t)
121                     sequences[i++] = prioritized
122                         ? new MetaUnion(tt, false)
123                         : makeMetaSequence(tt);
124             }
125             public String toString() {
126                 String ret = "\n     ";
127                 for(int i=0; i<sequences.length; i++) {
128                     ret += sequences[i];
129                     if (i<sequences.length-1)
130                         ret += (prioritized ? "\n    > " : "\n    | ");
131                 }
132                 return ret;
133             }
134         }
135
136         public interface MetaSequence {
137             public abstract Sequence buildSequence(BuildContext bc);
138         }
139
140         public static MetaSequence makeMetaSequence(Tree<String> t) {
141             if ("psx".equals(t.head())) return MetaConjunct.make(t.child(0));
142             if (t.head().equals("&"))  return new MetaAnd(makeMetaSequence(t.child(0)), MetaConjunct.make(t.child(1)), false);
143             if (t.head().equals("&~")) return new MetaAnd(makeMetaSequence(t.child(0)), MetaConjunct.make(t.child(1)), true);
144             return null;
145         }
146
147         public static class MetaAnd implements MetaSequence {
148             boolean not;
149             MetaSequence left;
150             MetaSequence right;
151             public Sequence buildSequence(BuildContext bc) {
152                 Union u = new Union(toString());
153                 Sequence ret = left.buildSequence(bc);
154                 Sequence rs = right.buildSequence(bc);
155                 rs.lame = true;
156                 if (not) ret = ret.not(rs);
157                 else     ret = ret.and(rs);
158                 u.add(rs);
159                 u.add(ret);
160                 return Sequence.singleton(u);
161             }
162             public MetaAnd(MetaSequence left, MetaSequence right, boolean not) {
163                 this.left = left;
164                 this.right = right;
165                 this.not = not;
166             }
167             public String toString() { return left + " &"+(not?"~":"")+" "+right; }
168         }
169
170         public static class MetaConjunct implements MetaSequence {
171             public boolean negated = false;
172             public MetaClause[] elements;
173             public MetaClause followedBy = null;
174             public String tag;
175             public MetaClause separator;
176             public Sequence buildSequence(BuildContext bc) {
177                 Element[] els = new Element[elements.length + (separator==null?0:(elements.length-1))];
178                 boolean[] drops = new boolean[elements.length + (separator==null?0:(elements.length-1))];
179                 boolean unwrap = false;
180                 boolean dropAll = false;
181                 if (tag!=null && tag.equals("[]")) unwrap = true;
182                 if (tag!=null && "()".equals(tag)) dropAll=true;
183                 for(int i=0; i<elements.length; i++) {
184                     int j = separator==null ? i : i*2;
185                     els[j] = elements[i].build(bc);
186                     drops[j] = elements[i].drop;
187                     if (separator!=null && i<elements.length-1) {
188                         els[j+1] = separator.build(bc);
189                         drops[j+1] = true;
190                     }
191                 }
192                 Sequence ret = null;
193                 if (dropAll)     ret = Sequence.drop(els, false);
194                 else if (unwrap) ret = Sequence.unwrap(els, /*repeatTag FIXME*/null, drops);
195                 else if (tag!=null) ret = Sequence.rewritingSequence(tag, els, new Object[els.length], drops);
196                 else {
197                     int idx = -1;
198                     for(int i=0; i<els.length; i++)
199                         if (!drops[i])
200                             if (idx==-1) idx = i;
201                             else throw new Error("multiple non-dropped elements in sequence: " + Sequence.drop(els,false));
202                     if (idx != -1) ret = Sequence.singleton(els, idx);
203                     else           ret = Sequence.drop(els, false);
204                 }
205                 if (this.followedBy != null)
206                     ret.follow = infer(this.followedBy.build(bc));
207                 return ret;
208             }
209             private MetaConjunct(Tree<String> t) {
210                 //System.err.println("MetaConjunct("+t+")");
211                 elements = new MetaClause[t.numChildren()];
212                 int i = 0; for(Tree<String> tt : t)
213                     elements[i++] = MetaClause.make(tt, this);
214             }
215             public String toString() {
216                 String ret = (tag==null ? "" : (tag+":: ")) + (negated ? "~" : "");
217                 if (elements.length > 1) ret += "(";
218                 for(MetaClause mc : elements) ret += (" " + mc + " ");
219                 if (separator != null) ret += " /" + separator;
220                 if (followedBy != null) ret += " -> " + followedBy;
221                 if (elements.length > 1) ret += ")";
222                 return ret;
223             }
224             public static MetaConjunct make(Tree<String> t) {
225                 //System.err.println("MetaConjunct.make("+t+")");
226                 if ("/".equals(t.head())) {
227                     MetaConjunct ret = make(t.child(0));
228                     ret.separator = MetaClause.make(t.child(1), ret);
229                     return ret;
230                 }
231                 if ("->".equals(t.head())) {
232                     MetaConjunct ret = make(t.child(0));
233                     ret.followedBy = MetaClause.make(t.child(1), ret);
234                     return ret;
235                 }
236                 if ("::".equals(t.head())) {
237                     MetaConjunct ret = make(t.child(1));
238                     ret.tag = string(t.child(0));
239                     return ret;
240                 }
241                 if ("ps".equals(t.head())) {
242                     return new MetaConjunct(t.child(0));
243                 }
244                 return new MetaConjunct(t);
245             }
246         }
247
248         public static abstract class MetaClause {
249             public String label = null;
250             public boolean drop = false;
251             public boolean lift = false;
252             public abstract Element build(BuildContext bc);
253             public static MetaClause make(Tree<String> t, MetaConjunct c) {
254                 //System.err.println("MetaClause.make("+t+")");
255                 if (t==null) return new MetaEpsilon();
256                 if (t.head()==null) return new MetaEpsilon();
257                 if (t.head().equals("{")) throw new Error("metatree: " + t);
258                 if (t.head().equals("*")) return new MetaRepeat(make(t.child(0), c), false, null, true, true);
259                 if (t.head().equals("+")) return new MetaRepeat(make(t.child(0), c), false, null, false, true);
260                 if (t.head().equals("?")) return new MetaRepeat(make(t.child(0), c), false, null, true, false);
261                 if (t.head().equals("**")) return new MetaRepeat(make(t.child(0), c), true, null, true, true);
262                 if (t.head().equals("++")) return new MetaRepeat(make(t.child(0), c), true, null, false, true);
263                 if (t.head().equals("*/")) return new MetaRepeat(make(t.child(0), c), false, make(t.child(1), c), true, true);
264                 if (t.head().equals("+/")) return new MetaRepeat(make(t.child(0), c), false, make(t.child(1), c), false, true);
265                 if (t.head().equals("**/")) return new MetaRepeat(make(t.child(0), c), true, make(t.child(1), c), true, true);
266                 if (t.head().equals("++/")) return new MetaRepeat(make(t.child(0), c), true, make(t.child(1), c), false, true);
267                 if (t.head().equals("()")) return new MetaEpsilon();
268                 if (t.head().equals("[")) return new MetaRange(t.child(0));
269                 if (t.head().equals("literal")) return new MetaStringLiteral(t.child(0));
270                 if (t.head().equals("nonTerminal")) return new MetaNonterminalReference(t.child(0));
271                 if (t.head().equals(")")) return new MetaSelfReference();
272                 if (t.head().equals("(")) return new MetaParens(t.child(0));
273                 if (t.head().equals("~")) return new MetaInvert(t.child(0), c);
274                 if (t.head().equals("!")) { MetaClause mc = make(t.child(0), c); mc.drop = true; return mc; }
275                 if (t.head().equals("^")) { c.tag = string(t.child(0)); return new MetaStringLiteral(t.child(0)); }
276                 if (t.head().equals("^^")) throw new Error("carets: " + t);
277                 throw new Error("unknown: " + t);
278             }
279             public static class MetaRepeat extends MetaClause {
280                 public MetaClause element, separator;
281                 public boolean maximal, zero, many;
282                 public Element build(BuildContext bc) {
283                     return !maximal
284                         ? (separator==null
285                            ? Sequence.repeat(element.build(bc), zero, many, null, null)
286                            : Sequence.repeat(element.build(bc), zero, many, separator.build(bc), null))
287                         : (separator==null
288                            ? Sequence.repeatMaximal(infer(element.build(bc)), zero, many, null)
289                            : Sequence.repeatMaximal(element.build(bc), zero, many, infer(separator.build(bc)), null));
290                 }
291                 public MetaRepeat(MetaClause element, boolean maximal, MetaClause separator, boolean zero, boolean many) {
292                     this.separator = separator;
293                     this.element = element;
294                     this.maximal = maximal;
295                     this.zero = zero;
296                     this.many = many;
297                 }
298                 public String toString() {
299                     return element+
300                         ((zero&&!many)?"?":zero?"*":"+")+
301                         (!maximal?"":zero?"*":"+")+
302                         (separator==null?"":(" /"+separator));
303                 }
304             }
305             public static class MetaParens extends MetaClause {
306                 public MetaUnion body;
307                 public MetaParens(Tree<String> t) { this.body = rhs(t); }
308                 public String toString() { return "( " + body + " )"; }
309                 public Element build(BuildContext bc) { return body.buildAnon(bc); }
310             }
311             /*
312             public static class MetaTree extends MetaClause {
313                 public MetaConjunct body;
314                 public MetaTree(Tree<String> t) { this.body = MetaConjunct.make(t); }
315                 public String toString() { return "{ " + body + " }"; }
316                 public Element build(BuildContext bc) {
317                     return new Union("{}");// body.buildSequence();
318                 }
319             }
320             */
321             public static class MetaEpsilon extends MetaClause {
322                 public String toString() { return "()"; }
323                 public Element build(BuildContext bc) { return Union.epsilon; }
324             }
325             public static class MetaRange extends MetaClause {
326                 Range.Set range = new Range.Set();
327                 public String toString() { return range.toString(); }
328                 public Element build(BuildContext bc) { return set(range); }
329                 public MetaRange(Tree<String> t) {
330                     for(Tree<String> tt : t) {
331                         if (tt.head().equals("range")) {
332                             range.add(tt.child(0).head().charAt(0));
333                         } else if (tt.head().equals("-")) {
334                             range.add(new Range(string(tt.child(0)).charAt(0),
335                                                 string(tt.child(1)).charAt(0)));
336                         }
337                     }
338                 }
339             }
340             public static class MetaStringLiteral extends MetaClause {
341                 public String literal;
342                 public Element build(BuildContext bc) { return string(literal); }
343                 public MetaStringLiteral(Tree<String> literal) { this.literal = string(literal); this.drop = true; }
344                 public String toString() { return "\""+StringUtil.escapify(literal, "\"\r\n\\")+"\""; }
345             }
346             public static class MetaNonterminalReference extends MetaClause {
347                 public String name;
348                 public MetaNonterminalReference(Tree<String> name) { this.name = string(name); }
349                 public Element build(BuildContext bc) { return bc.build(name); }
350                 public String toString() { return name; } 
351             }
352             public static class MetaSelfReference extends MetaClause {
353                 public String toString() { return "(*)"; } 
354                 public Element build(BuildContext bc) { return new Union("(*)"); /* FIXME */ }
355             }
356             public static class MetaInvert extends MetaClause {
357                 public MetaClause element;
358                 public MetaInvert(Tree<String> t, MetaConjunct c) { this.element = make(t, c); }
359                 public String toString() { return "~"+element; }
360                 public Element build(BuildContext bc) { return infer((Topology<Character>)Atom.toAtom(element.build(bc)).complement()); }
361             }
362         }
363
364     }
365
366     public static void main(String[] args) throws Exception {
367         if (args.length != 2) {
368             System.err.println("usage: java " + MetaGrammar.class.getName() + " grammarfile.g com.yourdomain.package.ClassName");
369             System.exit(-1);
370         }
371         //StringBuffer sbs = new StringBuffer();
372         //((MetaGrammar)new MetaGrammar().walk(meta)).nt.get("e").toString(sbs);
373         //System.err.println(sbs);
374         String className   = args[1].substring(args[1].lastIndexOf('.')+1);
375         String packageName = args[1].substring(0, args[1].lastIndexOf('.'));
376         String fileName    = packageName.replace('.', '/') + "/" + className + ".java";
377
378         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
379         StringBuffer out = new StringBuffer();
380
381         boolean skip = false;
382         for(String s = br.readLine(); s != null; s = br.readLine()) {
383             if (s.indexOf("DO NOT EDIT STUFF BELOW: IT IS AUTOMATICALLY GENERATED") != -1 && s.indexOf("\"")==-1) skip = true;
384             if (s.indexOf("DO NOT EDIT STUFF ABOVE: IT IS AUTOMATICALLY GENERATED") != -1 && s.indexOf("\"")==-1) break;
385             if (!skip) out.append(s+"\n");
386         }
387
388         out.append("\n        // DO NOT EDIT STUFF BELOW: IT IS AUTOMATICALLY GENERATED\n");
389         Tree<String> ts = new CharParser(MetaGrammar.make()).parse(new FileInputStream(args[0])).expand1();
390
391         //Forest<String> fs = new CharParser(make()).parse(new FileInputStream(args[0]));
392         //System.out.println(fs.expand1());
393
394         //GraphViz gv = new GraphViz();
395         //fs.toGraphViz(gv);
396         //FileOutputStream fox = new FileOutputStream("out.dot");
397         //gv.dump(fox);
398         //fox.close();
399
400         ts.toJava(out);
401         out.append("\n        // DO NOT EDIT STUFF ABOVE: IT IS AUTOMATICALLY GENERATED\n");
402
403         for(String s = br.readLine(); s != null; s = br.readLine()) out.append(s+"\n");
404         br.close();
405
406         OutputStream os = new FileOutputStream(fileName);
407         PrintWriter p = new PrintWriter(new OutputStreamWriter(os));
408         p.println(out.toString());
409         p.flush();
410         os.close();
411     }
412
413 }