// Copyright 2006-2007 all rights reserved; see LICENSE file for BSD-style license
package edu.berkeley.sbp.meta;
import edu.berkeley.sbp.util.*;
import edu.berkeley.sbp.*;
import edu.berkeley.sbp.chr.*;
import edu.berkeley.sbp.misc.*;
import java.util.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.io.*;
/**
* The inner classes of this class represent nodes in the Abstract
* Syntax Tree of a grammar.
*/
public class GrammarAST {
public static interface ImportResolver {
public InputStream getImportStream(String importname);
}
/**
* Returns a Union representing the metagrammar (meta.g); the Tree produced by
* parsing with this Union should be provided to buildFromAST
*/
public static Union getMetaGrammar() {
return buildFromAST(MetaGrammar.meta, "s", null);
}
/**
* Create a grammar from a parse tree and binding resolver
*
* @param t a tree produced by parsing a grammar using the metagrammar
* @param s the name of the "start symbol"
* @param gbr a GrammarBindingResolver that resolves grammatical reductions into tree-node-heads
*/
public static Union buildFromAST(Tree grammarAST, String startingNonterminal, ImportResolver resolver) {
return new GrammarAST(resolver, "").buildGrammar(grammarAST, startingNonterminal);
}
private static Object illegalTag = ""; // this is the tag that should never appear in the non-dropped output FIXME
// Instance //////////////////////////////////////////////////////////////////////////////
private final String prefix;
private final ImportResolver resolver;
public GrammarAST(ImportResolver resolver, String prefix) {
this.prefix = prefix;
this.resolver = resolver;
}
// Methods //////////////////////////////////////////////////////////////////////////////
private Union buildGrammar(Tree t, String rootNonTerminal) {
Object o = walk(t);
if (o instanceof Union) return (Union)o;
return ((GrammarAST.GrammarNode)o).build(rootNonTerminal);
}
private Object[] walkChildren(Tree t) {
Object[] ret = new Object[t.size()];
for(int i=0; i 0)
head = head.substring(head.indexOf('.')+1);
if (head==null) throw new RuntimeException("head is null: " + t);
if (head.equals("|")) return walkChildren(t);
if (head.equals("RHS")) return walkChildren(t);
if (head.equals("Grammar")) return new GrammarNode(walkChildren(t));
if (head.equals("(")) return new UnionNode((Seq[][])walkChildren(t.child(0)));
if (head.equals("Word")) return stringifyChildren(t);
if (head.equals("Elements")) return new Seq((ElementNode[])Reflection.rebuild(walkChildren(t), ElementNode[].class));
if (head.equals("NonTerminalReference")) return new ReferenceNode(stringifyChildren(t.child(0)));
if (head.equals(")")) return new ReferenceNode(stringifyChildren(t.child(0)), true);
if (head.equals(":")) return new LabelNode(stringifyChildren(t.child(0)), walkElement(t.child(1)));
if (head.equals("::")) return walkSeq(t.child(1)).tag(walkString(t.child(0)));
if (head.equals("...")) return new DropNode(new RepeatNode(new TildeNode(new AtomNode()), null, true, true, false));
if (head.equals("++")) return new RepeatNode(walkElement(t.child(0)), null, false, true, true);
if (head.equals("+")) return new RepeatNode(walkElement(t.child(0)), null, false, true, false);
if (head.equals("++/")) return new RepeatNode(walkElement(t.child(0)), walkElement(t.child(1)), false, true, true);
if (head.equals("+/")) return new RepeatNode(walkElement(t.child(0)), walkElement(t.child(1)), false, true, false);
if (head.equals("**")) return new RepeatNode(walkElement(t.child(0)), null, true, true, true);
if (head.equals("*")) return new RepeatNode(walkElement(t.child(0)), null, true, true, false);
if (head.equals("**/")) return new RepeatNode(walkElement(t.child(0)), walkElement(t.child(1)), true, true, true);
if (head.equals("*/")) return new RepeatNode(walkElement(t.child(0)), walkElement(t.child(1)), true, true, false);
if (head.equals("?")) return new RepeatNode(walkElement(t.child(0)), null, true, false, false);
if (head.equals("!")) return new DropNode(walkElement(t.child(0)));
if (head.equals("^")) return new LiteralNode(walkString(t.child(0)), true);
if (head.equals("`")) return new BacktickNode(walkElement(t.child(0)));
if (head.equals("Quoted")) return stringifyChildren(t);
if (head.equals("Literal")) return new LiteralNode(walkString(t.child(0)));
if (head.equals("->")) return walkSeq(t.child(0)).follow(walkElement(t.child(1)));
if (head.equals("DropNT")) return new NonTerminalNode(walkString(t.child(0)), (Seq[][])walkChildren(t.child(1)), false, null, true);
if (head.equals("=")) return new NonTerminalNode(walkString(t.child(0)), (Seq[][])walk(t.child(2)),
true, t.size()==2 ? null : walkString(t.child(1)), false);
if (head.equals("&")) return walkSeq(t.child(0)).and(walkSeq(t.child(1)));
if (head.equals("&~")) return walkSeq(t.child(0)).andnot(walkSeq(t.child(1)));
if (head.equals("/")) return (walkSeq(t.child(0))).separate(walkElement(t.child(1)));
if (head.equals("()")) return new LiteralNode("");
if (head.equals("[")) return new AtomNode((char[][])Reflection.rebuild(walkChildren(t), char[][].class));
if (head.equals("\\{")) return new DropNode(new AtomNode(new char[] { CharAtom.left, CharAtom.left }));
if (head.equals("\\}")) return new DropNode(new AtomNode(new char[] { CharAtom.right, CharAtom.right }));
if (head.equals(">>")) return new DropNode(new AtomNode(new char[] { CharAtom.left, CharAtom.left }));
if (head.equals("<<")) return new DropNode(new AtomNode(new char[] { CharAtom.right, CharAtom.right }));
if (head.equals("~")) return new TildeNode(walkElement(t.child(0)));
if (head.equals("~~")) return new Seq(new RepeatNode(new TildeNode(new AtomNode()), null, true, true, false)).andnot(walkSeq(t.child(0)));
if (head.equals("Range")) {
if (t.size()==2 && ">".equals(t.child(0).head())) return new char[] { CharAtom.left, CharAtom.left };
if (t.size()==2 && "<".equals(t.child(0).head())) return new char[] { CharAtom.right, CharAtom.right };
if (t.size()==1) return new char[] { unescape(t).charAt(0), unescape(t).charAt(0) };
return new char[] { unescape(t).charAt(0), unescape(t).charAt(1) };
}
if (head.equals("\"\"")) return "";
if (head.equals("\n")) return "\n";
if (head.equals("\t")) return "\t";
if (head.equals("\r")) return "\r";
if (head.equals("SubGrammar")) return GrammarAST.buildFromAST(t.child(0), "s", resolver);
if (head.equals("NonTerminal"))
return new NonTerminalNode(walkString(t.child(0)),
(Seq[][])walkChildren(t.child(1)), false, null, false);
if (head.equals("Colons")) {
String tag = walkString(t.child(0));
Seq[][] seqs = (Seq[][])walk(t.child(1));
for(Seq[] seq : seqs)
for(int i=0; i " + (head.equals("...")));
}
// Nodes //////////////////////////////////////////////////////////////////////////////
/** Root node of a grammar's AST; a set of named nonterminals */
private class GrammarNode extends HashMap {
public GrammarNode(NonTerminalNode[] nonterminals) {
for(NonTerminalNode nt : nonterminals) {
if (nt==null) continue;
if (this.get(nt.name)!=null)
throw new RuntimeException("duplicate definition of nonterminal \""+nt.name+"\"");
this.put(nt.name, nt);
}
}
public GrammarNode(Object[] nt) { add(nt); }
private void add(Object o) {
if (o==null) return;
else if (o instanceof Object[]) for(Object o2 : (Object[])o) add(o2);
else if (o instanceof NonTerminalNode) {
NonTerminalNode nt = (NonTerminalNode)o;
if (this.get(nt.name)!=null)
throw new RuntimeException("duplicate definition of nonterminal \""+nt.name+"\"");
this.put(nt.name, nt);
}
else if (o instanceof GrammarNode)
for(NonTerminalNode n : ((GrammarNode)o).values())
add(n);
}
public String toString() {
String ret = "[ ";
for(NonTerminalNode nt : values()) ret += nt + ", ";
return ret + " ]";
}
public Union build(String rootNonterminal) {
Context cx = new Context(this);
Union u = null;
for(GrammarAST.NonTerminalNode nt : values())
if (nt.name.equals(rootNonterminal))
return (Union)cx.get(nt.name);
return null;
}
}
/** a node in the AST which is resolved into an Element */
private abstract class ElementNode {
public boolean isLifted() { return false; }
public boolean isDropped(Context cx) { return false; }
public Atom toAtom(Context cx) { throw new Error("can't convert a " + this.getClass().getName() + " to an atom: " + this); }
public abstract Element build(Context cx, NonTerminalNode cnt, boolean dropall);
}
/** a union, produced by a ( .. | .. | .. ) construct */
private class UnionNode extends ElementNode {
/** each component of a union is a sequence */
public Seq[][] sequences;
/** if the union is a NonTerminal specified as Foo*=..., this is true */
public boolean rep;
/** if the union is a NonTerminal specified as Foo* /ws=..., then this is "ws" */
public String sep = null;
public UnionNode(Seq seq) { this(new Seq[][] { new Seq[] { seq } }); }
public UnionNode(Seq[][] sequences) { this(sequences, false, null); }
public UnionNode(Seq[][] sequences, boolean rep, String sep) {
this.sequences = sequences;
this.rep = rep;
this.sep = sep;
}
public boolean isDropped(Context cx) {
for(Seq[] seqs : sequences)
for(Seq seq : seqs)
if (!seq.isDropped(cx))
return false;
return true;
}
public Atom toAtom(Context cx) {
Atom ret = null;
for(Seq[] ss : sequences)
for(Seq s : ss)
ret = ret==null ? s.toAtom(cx) : (Atom)ret.union(s.toAtom(cx));
return ret;
}
public Element build(Context cx, NonTerminalNode cnt, boolean dropall) {
return buildIntoPreallocatedUnion(cx, cnt, dropall, new Union(null, false)); }
public Element buildIntoPreallocatedUnion(Context cx, NonTerminalNode cnt, boolean dropall, Union u) {
Union urep = null;
if (rep) {
urep = new Union(null, false);
urep.add(Sequence.create(cnt.name, new Element[0]));
urep.add(sep==null
? Sequence.create(new Element[] { u }, 0)
: Sequence.create(new Element[] { cx.get(sep), u }, 1));
}
HashSet bad2 = new HashSet();
for(int i=0; i and = new HashSet();
/** negative conjuncts */
HashSet not = new HashSet();
public boolean alwaysDrop = false;
public boolean isDropped(Context cx) {
if (alwaysDrop) return true;
if (tag!=null) return false;
for(int i=0; i, **, ++, or a similar character-class"+
" operator on a [potentially] multicharacter production");
return elements[0].toAtom(cx);
}
public Seq tag(String tag) { this.tag = tag; return this; }
public Seq follow(ElementNode follow) { this.follow = follow; return this; }
public Seq and(Seq s) { and.add(s); s.alwaysDrop = true; return this; }
public Seq andnot(Seq s) { not.add(s); s.alwaysDrop = true; return this; }
public Seq separate(ElementNode sep) {
ElementNode[] elements = new ElementNode[this.elements.length * 2 - 1];
for(int i=0; i)_e.toAtom(cx).complement()); }
public Element build(Context cx, NonTerminalNode cnt, boolean dropall) { return toAtom(cx); }
}
private class DropNode extends ElementNodeWrapper {
public DropNode(ElementNode e) { super(e); }
public boolean isDropped(Context cx) { return true; }
}
/** provides a label on the fields of a Seq */
private class LabelNode extends ElementNodeWrapper {
public final String label;
public LabelNode(String label, ElementNode e) { super(e); this.label = label; }
}
//////////////////////////////////////////////////////////////////////////////
public class Context {
public HashMap map = new HashMap();
public GrammarNode grammar;
public Context(Tree t) { }
public Context(GrammarNode g) { this.grammar = g; }
public Union build() {
Union ret = null;
for(NonTerminalNode nt : grammar.values()) {
Union u = get(nt.name);
if ("s".equals(nt.name))
ret = u;
}
return ret;
}
public Union peek(String name) { return map.get(name); }
public void put(String name, Union u) { map.put(name, u); }
public Union get(String name) {
Union ret = map.get(name);
if (ret != null) return ret;
NonTerminalNode nt = grammar.get(name);
if (nt==null) {
throw new Error("warning could not find " + name);
} else {
ret = new Union(name, false);
map.put(name, ret);
nt.buildIntoPreallocatedUnion(this, nt, nt.isDropped(this), ret);
}
return ret;
}
}
}