d9b04c23dd0d63327530a12fd0b750b3d5b779b5
[sbp.git] / src / edu / berkeley / sbp / meta / GrammarBuilder.java
1 // Copyright 2006 all rights reserved; see LICENSE file for BSD-style license
2
3 package edu.berkeley.sbp.meta;
4 import edu.berkeley.sbp.util.*;
5 import edu.berkeley.sbp.*;
6 import edu.berkeley.sbp.chr.*;
7 import edu.berkeley.sbp.misc.*;
8 import java.util.*;
9 import java.lang.annotation.*;
10 import java.lang.reflect.*;
11 import java.io.*;
12
13 // FIXME: deal with question-marks
14
15 // FIXME: put back in associativity: ^")"
16 //            A = A "->" (A)
17 //        means that the FIRST "A" cannot match the SAME sequence in
18 //        which the (A) occurs
19 //        -- and add a test case
20
21 // FIXME: make it so that we can have multi-result nonterminals
22 //        so long as they always appear under double-colons?
23 //        auto-insert the unwrap?
24
25 // FIXME: put associativity back in
26
27 // FIXME: "flat" sequences (no subtrees contain "::"s?)
28
29 // freezing problem is related to comments in grammar files
30
31 /** The java classes typically used to represent a parsed grammar AST; each inner class is a type of AST node. */
32 public class GrammarBuilder {
33
34     public static Object illegalTag = ""; // this is the tag that should never appear in the non-dropped output FIXME
35
36     private final String prefix;
37     private final String path;
38
39     //public GrammarBuilder(String path) { this(path, ""); }
40     public GrammarBuilder(String path, String prefix) {
41         this.prefix = prefix;
42         this.path = path;
43     }
44
45     public Union buildGrammar(Tree t, String rootNonTerminal) {
46         return ((GrammarBuilder.GrammarNode)walk(t)).build(rootNonTerminal);
47     }
48
49     private ElementNode epsilon = new LiteralNode("");
50
51     public Object[] walkChildren(Tree t) {
52         Object[] ret = new Object[t.size()];
53         for(int i=0; i<ret.length; i++) {
54             ret[i] = walk(t.child(i));
55             if (ret[i] instanceof Object[])
56                 ret[i] = Reflection.lub((Object[])ret[i]);
57         }
58         return Reflection.lub(ret);
59     }
60     public String stringifyChildren(Tree t) {
61         StringBuffer sb = new StringBuffer();
62         for(int i=0; i<t.size(); i++) {
63             sb.append(t.child(i).head());
64             sb.append(stringifyChildren(t.child(i)));
65         }
66         return sb.toString();
67     }
68     public String unescape(Tree t) {
69         StringBuffer sb = new StringBuffer();
70         for(int i=0; i<t.size(); i++)
71             sb.append(t.child(i).head()+stringifyChildren(t.child(i)));
72         return sb.toString();
73     }
74
75     public Object walk(Tree t) {
76         String head = (String)t.head();
77         while(head.indexOf('.') != -1) head = head.substring(head.indexOf('.')+1);
78         if (head==null) throw new RuntimeException("head is null: " + t);
79         if (head.equals("|")) return walkChildren(t);
80         if (head.equals("RHS")) return walkChildren(t);
81         if (head.equals("Grammar")) return new GrammarNode(walkChildren(t));
82         if (head.equals("(")) return new UnionNode((Seq[][])walkChildren(t.child(0)));
83         if (head.equals("Word")) return stringifyChildren(t);
84         if (head.equals("Elements")) return seq2((ElementNode[])Reflection.rebuild(walkChildren(t), ElementNode[].class));
85         if (head.equals("NonTerminalReference")) return new ReferenceNode(stringifyChildren(t.child(0)));
86         //if (head.equals(")")) return new ReferenceNode(stringifyChildren(t.child(0)));
87         if (head.equals("::")) return tag((String)walk(t.child(0)), (Seq)walk(t.child(1)));
88         if (head.equals("++")) return plusmax((ElementNode)walk(t.child(0)));
89         if (head.equals("+")) return plus((ElementNode)walk(t.child(0)));
90         if (head.equals("++/")) return plusmaxfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
91         if (head.equals("+/")) return plusfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
92         if (head.equals("**")) return starmax((ElementNode)walk(t.child(0)));
93         if (head.equals("*")) return star((ElementNode)walk(t.child(0)));
94         if (head.equals("**/")) return starmaxfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
95         if (head.equals("*/")) return starfollow((ElementNode)walk(t.child(0)), (ElementNode)walk(t.child(1)));
96         if (head.equals("?")) return question((ElementNode)walk(t.child(0)));
97         if (head.equals("!")) return new DropNode((ElementNode)walk(t.child(0)));
98         if (head.equals("^")) return new LiteralNode((String)walk(t.child(0)), true);
99         if (head.equals("Quoted")) return stringifyChildren(t);
100         if (head.equals("Literal")) return new LiteralNode((String)walk(t.child(0)));
101         if (head.equals("->")) return arrow((Seq)walk(t.child(0)), (ElementNode)walk(t.child(1)));
102         if (head.equals("DropNT")) return new NonTerminalNode((String)walk(t.child(0)), (Seq[][])walk(t.child(1)), true, null, true);
103         if (head.equals("=") && t.size()==2) return new NonTerminalNode((String)walk(t.child(0)), (Seq[][])walk(t.child(1)), true, null, false);
104         if (head.equals("=")) return new NonTerminalNode((String)walk(t.child(0)), (Seq[][])walk(t.child(2)), true, (String)walk(t.child(1)), false);
105         if (head.equals("&")) return and2((Seq)walk(t.child(0)), (Seq)walk(t.child(1)));
106         if (head.equals("&~")) return andnot2((Seq)walk(t.child(0)), (Seq)walk(t.child(1)));
107         if (head.equals("/")) return ((Seq)walk(t.child(0))).separate((ElementNode)walk(t.child(1)));
108         if (head.equals("()")) return epsilon;
109         if (head.equals("[")) return new AtomNode((AtomNodeRange[])Reflection.rebuild(walkChildren(t), AtomNodeRange[].class));
110         if (head.equals("\\{")) return new DropNode(new AtomNode(new AtomNodeRange(CharAtom.left, CharAtom.left)));
111         if (head.equals("\\}")) return new DropNode(new AtomNode(new AtomNodeRange(CharAtom.right, CharAtom.right)));
112         if (head.equals("~")) return new TildeNode((ElementNode)walk(t.child(0)));
113         if (head.equals("Range") && t.size()==1) return new AtomNodeRange(unescape(t).charAt(0));
114         if (head.equals("Range")) return new AtomNodeRange(unescape(t).charAt(0), unescape(t).charAt(1));
115         if (head.equals("\"\"")) return "";
116         if (head.equals("\n")) return "\n";
117         if (head.equals("\r")) return "\r";
118         if (head.equals("grammar.Grammar")) return walkChildren(t);
119         if (head.equals("SubGrammar")) return Grammar.create(t.child(0), "s");
120         if (head.equals("NonTerminal"))
121             return new NonTerminalNode((String)walk(t.child(0)),
122                                        (Seq[][])walkChildren(t.child(1)), false, null, false);
123         if (head.equals("Colons")) {
124             String tag = (String)walk(t.child(0));
125             Seq[][] seqs = (Seq[][])walk(t.child(1));
126             for(Seq[] seq : seqs)
127                 for(int i=0; i<seq.length; i++)
128                     seq[i] = tag(tag, seq[i]);
129             return new NonTerminalNode(tag, seqs, false, null, false);
130         }
131         if (head.equals("TestCase"))
132             return new RegressionTests.TestCase((String)walk(t.child(0)),
133                                                 (String)walk(t.child(1)),
134                                                 (String[])Reflection.coerce(walkChildren(t.child(2)), String[].class),
135                                                 (Union)walk(t.child(3)),
136                                                 false,
137                                                 false);
138         if (head.equals("#import")) {
139             String fileName = path+(String)stringifyChildren(t.child(0));
140             try {
141                 String newPrefix = (String)walk(t.child(1));
142                 if (newPrefix.length() > 0) newPrefix += ".";
143                 FileInputStream fis = new FileInputStream(fileName);
144                 Tree tr = new CharParser(MetaGrammar.newInstance()).parse(fis).expand1();
145                 return (GrammarNode)new GrammarBuilder(path, newPrefix).walk(tr);
146             } catch (Exception e) {
147                 throw new RuntimeException("while parsing " + fileName, e);
148             }
149         }
150         throw new RuntimeException("unknown head: " + head + "\n"+t);
151     }
152     
153     /** A grammar (a set of nonterminals) */
154     public class GrammarNode extends HashMap<String,NonTerminalNode> {
155         public GrammarNode(NonTerminalNode[] nonterminals) {
156             for(NonTerminalNode nt : nonterminals) {
157                 if (nt==null) continue;
158                 if (this.get(nt.name)!=null)
159                     throw new RuntimeException("duplicate definition of nonterminal \""+nt.name+"\"");
160                 this.put(nt.name, nt);
161             }
162         }
163         public  GrammarNode(Object[] nt) { add(nt); }
164         private void add(Object o) {
165             if (o==null) return;
166             else if (o instanceof Object[]) for(Object o2 : (Object[])o) add(o2);
167             else if (o instanceof NonTerminalNode) {
168                 NonTerminalNode nt = (NonTerminalNode)o;
169                 if (this.get(nt.name)!=null)
170                     throw new RuntimeException("duplicate definition of nonterminal \""+nt.name+"\"");
171                 this.put(nt.name, nt);
172             }
173             else if (o instanceof GrammarNode)
174                 for(NonTerminalNode n : ((GrammarNode)o).values())
175                     add(n);
176         }
177         public String toString() {
178             String ret = "[ ";
179             for(NonTerminalNode nt : values()) ret += nt + ", ";
180             return ret + " ]";
181         }
182         public Union build(String rootNonterminal) {
183             Context cx = new Context(this);
184             Union u = null;
185             for(GrammarBuilder.NonTerminalNode nt : values())
186                 if (nt.name.equals(rootNonterminal))
187                     return (Union)cx.get(nt.name);
188             return null;
189         }
190     }
191
192     public class UnionNode extends ElementNode {
193         public Seq[][] sequences;
194         public String  sep = null;
195         public boolean rep;
196         public UnionNode(Seq[][] sequences) { this(sequences, false, null); }
197         public UnionNode(Seq[][] sequences, boolean rep, String sep) {
198             this.sequences = sequences;
199             this.rep = rep;
200             this.sep = sep;
201         }
202         public Atom toAtom(Context cx) {
203             Atom ret = null;
204             for(Seq[] ss : sequences)
205                 for(Seq s : ss)
206                     ret = ret==null ? s.toAtom(cx) : (Atom)ret.union(s.toAtom(cx));
207             return ret;
208         }
209         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
210             return buildIntoPreallocatedUnion(cx, cnt, dropall, new Union(null, false)); }
211         public Element buildIntoPreallocatedUnion(Context cx, NonTerminalNode cnt, boolean dropall, Union u) {
212             Union urep = null;
213             if (rep) {
214                 urep = new Union(null, false);
215                 urep.add(Sequence.create(new Element[0], cnt.name));
216                 urep.add(sep==null
217                          ? Sequence.create(new Element[] { u }, 0)
218                          : Sequence.create(new Element[] { cx.get(sep), u }, 1));
219             }
220             HashSet<Sequence> bad2 = new HashSet<Sequence>();
221             for(int i=0; i<sequences.length; i++) {
222                 Seq[] group = sequences[i];
223                 Union u2 = new Union(null, false);
224                 if (sequences.length==1) u2 = u;
225                 for(int j=0; j<group.length; j++)
226                     if (!rep)
227                         group[j].build(cx, u2, cnt, dropall);
228                     else {
229                         Union u3 = new Union(null, false);
230                         group[j].build(cx, u3, cnt, dropall);
231                         Sequence s = Sequence.create(cnt.name,
232                                                      new Element[] { u3, urep },
233                                                      new boolean[] { false, false },
234                                                      true);
235                         u2.add(s);
236                     }
237                 if (sequences.length==1) break;
238                 Sequence seq = Sequence.create(u2);
239                 for(Sequence s : bad2) seq = seq.andnot(s);
240                 u.add(seq);
241                 bad2.add(Sequence.create(u2));
242             }
243             return u;
244         }
245     }
246
247     public class NonTerminalNode extends UnionNode {
248         public boolean alwaysDrop;
249         public String  name = null;
250         public boolean drop() { return alwaysDrop; }
251         public NonTerminalNode(String name, Seq[][] sequences, boolean rep, String sep, boolean alwaysDrop) {
252             super(sequences, rep, sep==null?null:(prefix + sep));
253             this.name = prefix + name;
254             this.alwaysDrop = alwaysDrop;
255         }
256         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return cx.get(name); }
257     }
258
259     public class Seq {
260         public boolean alwaysDrop = false;
261         public boolean drop() { return alwaysDrop; }
262         HashSet<Seq> and = new HashSet<Seq>();
263         HashSet<Seq> not = new HashSet<Seq>();
264         ElementNode[] elements;
265         ElementNode follow;
266         String tag = null;
267         public Seq(ElementNode e) { this(new ElementNode[] { e }); }
268         public Seq(ElementNode[] elements) {
269             this.elements = elements;
270             for(int i=0; i<elements.length; i++)
271                 if (elements[i]==null)
272                     throw new RuntimeException();
273         }
274         public Atom toAtom(Context cx) {
275             if (elements.length != 1)
276                 throw new Error("you attempted to use ->, **, ++, or a similar character-class"+
277                                 " operator on a [potentially] multicharacter production");
278             return elements[0].toAtom(cx);
279         }
280         public Seq tag(String tag) { this.tag = tag; return this; }
281         public Seq follow(ElementNode follow) { this.follow = follow; return this; }
282         public Seq and(Seq s) { and.add(s); return this; }
283         public Seq andnot(Seq s) { not.add(s); return this; }
284         public Seq dup() {
285             Seq ret = new Seq(elements);
286             ret.and.addAll(and);
287             ret.not.addAll(not);
288             ret.follow = follow;
289             ret.tag = tag;
290             return ret;
291         }
292         public Seq separate(ElementNode sep) {
293             ElementNode[] elements = new ElementNode[this.elements.length * 2 - 1];
294             for(int i=0; i<this.elements.length; i++) {
295                 elements[i*2] = this.elements[i];
296                 if (i<this.elements.length-1)
297                     elements[i*2+1] = new DropNode(sep);
298             }
299             this.elements = elements;
300             return this;
301         }
302         public Sequence build(Context cx, Union u, NonTerminalNode cnt, boolean dropall) {
303             Sequence ret = build0(cx, cnt, dropall);
304             for(Seq s : and) ret = ret.and(s.build(cx, null, cnt, true));
305             for(Seq s : not) ret = ret.andnot(s.build(cx, null, cnt, true));
306             if (u!=null) u.add(ret);
307             return ret;
308         }
309         public Sequence build0(Context cx, NonTerminalNode cnt, boolean dropall) {
310             boolean[] drops = new boolean[elements.length];
311             Element[] els = new Element[elements.length];
312             dropall |= drop();
313             for(int i=0; i<elements.length; i++) {
314                 if (dropall) drops[i] = true;
315                 else         drops[i] = elements[i].drop();
316                 if (elements[i] instanceof ReferenceNode)
317                     if (((ReferenceNode)elements[i]).resolve(cx).drop())
318                         drops[i] = true;
319                 if (elements[i].getOwnerTag() != null)
320                     tag = elements[i].getOwnerTag();
321             }
322             Sequence ret = null;
323             int idx = -1;
324             boolean multiNonDrop = false;
325             for(int i=0; i<drops.length; i++)
326                 if (!drops[i])
327                     if (idx==-1) idx = i;
328                     else multiNonDrop = true;
329             if (tag==null && multiNonDrop)
330                 throw new Error("multiple non-dropped elements in sequence: " + Sequence.create(els, ""));
331             for(int i=0; i<elements.length; i++) {
332                 if (!multiNonDrop && i==idx && tag!=null && elements[i] instanceof RepeatNode) {
333                     els[i] = ((RepeatNode)elements[i]).build(cx, cnt, dropall, tag);
334                     tag = null;
335                 } else
336                     els[i] = elements[i].build(cx, cnt, dropall);
337             }
338             if (!multiNonDrop) {
339                 if (idx == -1) 
340                     ret = tag==null
341                         ? Sequence.create(els, illegalTag)
342                         : Sequence.create(tag, els, drops, false);
343                 else if (tag==null) ret = Sequence.create(els, idx);
344                 else ret = Sequence.create(tag, els, drops, false);
345             }
346             if (multiNonDrop)
347                 ret = Sequence.create(tag, els, drops, false);
348             if (this.follow != null)
349                 ret = ret.followedBy(this.follow.toAtom(cx));
350             return ret;
351         }
352     }
353
354     public class ReferenceNode extends ElementNode {
355         public String nonTerminal;
356         public ReferenceNode() { }
357         public NonTerminalNode resolve(Context cx) { return cx.grammar.get(nonTerminal); }
358         public ReferenceNode(String nonTerminal) { this.nonTerminal = prefix + nonTerminal; }
359         public Atom toAtom(Context cx) { return cx.grammar.get(nonTerminal).toAtom(cx); }
360         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
361             if (!this.nonTerminal.startsWith(prefix)) nonTerminal = prefix + nonTerminal;
362             Element ret = cx.get(nonTerminal);
363             if (ret == null) throw new RuntimeException("unknown nonterminal \""+nonTerminal+"\"");
364             return ret;
365         }
366     }
367
368     public class LiteralNode extends ElementNode {
369         private String string;
370         private final String thePrefix = prefix;
371         private boolean caret;
372         public LiteralNode(String string) { this(string, false); }
373         public LiteralNode(String string, boolean caret) {
374             this.string = string;
375             this.caret = caret;
376         }
377         public String getOwnerTag() { return caret ? thePrefix+string : super.getOwnerTag(); }
378         public String toString() { return "\""+string+"\""; }
379         public boolean drop() { return true; }
380         public Atom toAtom(Context cx) {
381             if (string.length()!=1) return super.toAtom(cx);
382             edu.berkeley.sbp.util.Range.Set set = new edu.berkeley.sbp.util.Range.Set();
383             set.add(string.charAt(0), string.charAt(0));
384             return CharAtom.set(set);
385         }
386         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return CharAtom.string(string); }
387     }
388
389     public class AtomNode extends ElementNode {
390         AtomNodeRange[] ranges;
391         public AtomNode(AtomNodeRange[] ranges) { this.ranges = ranges; }
392         public AtomNode(AtomNodeRange range) { this.ranges = new AtomNodeRange[] { range }; }
393         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return toAtom(cx); }
394         public Atom toAtom(Context cx) {
395             edu.berkeley.sbp.util.Range.Set set = new edu.berkeley.sbp.util.Range.Set();
396             for(AtomNodeRange r : ranges) set.add(r.first, r.last);
397             return CharAtom.set(set);
398         }
399     }
400     public class AtomNodeRange {
401         public char first;
402         public char last;
403         public AtomNodeRange(char only) { first = only; last = only; }
404         public AtomNodeRange(char first, char last) { this.first = first; this.last = last; }
405     }
406
407     public class RepeatNode extends ElementNode {
408         public ElementNode e, sep;
409         public boolean zero, many, max;
410         public RepeatNode(ElementNode e, ElementNode sep, boolean zero, boolean many, boolean max) {
411             this.e = e; this.sep = sep; this.zero = zero; this.many = many; this.max = max;}
412         public Atom toAtom(Context cx) { return sep==null ? e.toAtom(cx) : super.toAtom(cx); }
413         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
414             if (!dropall && !drop() && !e.drop())
415                 throw new Error("you need a tag on this repetition: " + build(cx, cnt, dropall, ""));
416             return build(cx, cnt, dropall, illegalTag);
417         }
418         public Element build(Context cx, NonTerminalNode cnt, boolean dropall, Object repeatTag) {
419             return (!max)
420                 ? Repeat.repeat(e.build(cx, null, dropall), zero, many, sep==null ? null : sep.build(cx, null, dropall), repeatTag)
421                 : sep==null
422                 ? Repeat.repeatMaximal(e.toAtom(cx), zero, many, repeatTag)
423                 : Repeat.repeatMaximal(e.build(cx, null, dropall), zero, many, sep.toAtom(cx), repeatTag);
424         }
425     }
426
427     public abstract class ElementNode {
428         public String getOwnerTag() { return null; }
429         public boolean drop() { return false; }
430         public Atom toAtom(Context cx) { throw new Error("can't convert a " + this.getClass().getName() + " to an atom: " + this); }
431         public abstract Element build(Context cx, NonTerminalNode cnt, boolean dropall);
432     }
433
434     public abstract class ElementNodeWrapper extends ElementNode {
435         protected ElementNode _e;
436         public ElementNodeWrapper(ElementNode e) { this._e = e; }
437         public String getOwnerTag() { return _e.getOwnerTag(); }
438         public boolean drop() { return _e.drop(); }
439         public Atom toAtom(Context cx) { return _e.toAtom(cx); }
440         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return _e.build(cx, cnt, dropall); }
441     }
442
443     public class TildeNode extends ElementNodeWrapper {
444         public TildeNode(ElementNode e) { super(e); }
445         public Atom toAtom(Context cx) { return (Atom)((Topology<Character>)_e.toAtom(cx).complement()); }
446         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return toAtom(cx); }
447     }
448
449     public class DropNode extends ElementNodeWrapper {
450         public DropNode(ElementNode e) { super(e); }
451         public boolean drop() { return true; }
452     }
453
454     public    Seq  and2(Seq s,        Seq a)   { a.alwaysDrop = true;  return s.and(a); }
455     public   Seq  andnot2(Seq s,     Seq a)   { a.alwaysDrop = true; return s.andnot(a); }
456     public   Seq  arrow(Seq s, ElementNode e) { return s.follow(e); }
457     public   Seq  tag(String tagname, Seq s)  { return s.tag(tagname); }
458     public Seq  seq(ElementNode[] elements)               { return new Seq(elements); }
459     public   Seq  seq2(ElementNode[] elements)               { return new Seq(elements); }
460     public   ElementNode plusmax(final ElementNode e)                         { return new RepeatNode(e, null, false, true, true); }
461     public    ElementNode plus(final ElementNode e)                            { return new RepeatNode(e, null, false, true, false); }
462     public  ElementNode plusmaxfollow(final ElementNode e, final ElementNode sep)     { return new RepeatNode(e, sep,  false, true, true); }
463     public   ElementNode plusfollow(final ElementNode e, final ElementNode sep)        { return new RepeatNode(e, sep,  false, true, false); }
464     public   ElementNode starmax(final ElementNode e)                         { return new RepeatNode(e, null, true,  true, true); }
465     public    ElementNode star(final ElementNode e)                            { return new RepeatNode(e, null, true,  true, false); }
466     public  ElementNode starmaxfollow(final ElementNode e, final ElementNode sep)     { return new RepeatNode(e, sep,  true,  true, true); }
467     public   ElementNode starfollow(final ElementNode e, final ElementNode sep)        { return new RepeatNode(e, sep,  true,  true, false); }
468     public    ElementNode question(final ElementNode e)                        { return new RepeatNode(e, null, true,  true, false); }
469
470     //////////////////////////////////////////////////////////////////////////////
471
472     public class Context {
473         public HashMap<String,Union> map = new HashMap<String,Union>();
474         public GrammarNode grammar;
475         public Context(Tree t) { }
476         public Context(GrammarNode g) { this.grammar = g; }
477         public Union build() {
478             Union ret = null;
479             for(NonTerminalNode nt : grammar.values()) {
480                 Union u = get(nt.name);
481                 if ("s".equals(nt.name))
482                     ret = u;
483             }
484             return ret;
485         }
486         public Union peek(String name) { return map.get(name); }
487         public void  put(String name, Union u) { map.put(name, u); }
488         public Union get(String name) {
489             Union ret = map.get(name);
490             if (ret != null) return ret;
491             NonTerminalNode nt = grammar.get(name);
492             if (nt==null) {
493                 throw new Error("warning could not find " + name);
494             } else {
495                 ret = new Union(name, false);
496                 map.put(name, ret);
497                 nt.buildIntoPreallocatedUnion(this, nt, nt.drop(), ret);
498             }
499             return ret;
500         }
501
502     }
503
504     /*
505     public            class XTree                 extends ElementNode {
506         public  Seq body;
507         public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
508             Union u = new Union(null, false);
509             Sequence s = body.build(cx, u, null, dropall);
510             Union u2 = new Union(null, false);
511             u2.add(Sequence.create(new Element[] {
512                 CharAtom.leftBrace,
513                 cx.get("ws"),
514                 u,
515                 cx.get("ws"),
516                 CharAtom.rightBrace
517             }, 2));
518             return u2;
519         }
520     }
521     */
522 }