001: package persistence.antlr;
002:
003: /* ANTLR Translator Generator
004: * Project led by Terence Parr at http://www.jGuru.com
005: * Software rights: http://www.antlr.org/license.html
006: *
007: */
008:
009: import persistence.antlr.collections.AST;
010: import persistence.antlr.collections.impl.ASTArray;
011:
012: import java.util.Hashtable;
013: import java.lang.reflect.Constructor;
014:
015: /** AST Support code shared by TreeParser and Parser.
016: * We use delegation to share code (and have only one
017: * bit of code to maintain) rather than subclassing
018: * or superclassing (forces AST support code to be
019: * loaded even when you don't want to do AST stuff).
020: *
021: * Typically, setASTNodeType is used to specify the
022: * homogeneous type of node to create, but you can override
023: * create to make heterogeneous nodes etc...
024: */
025: public class ASTFactory {
026: /** Name of AST class to create during tree construction.
027: * Null implies that the create method should create
028: * a default AST type such as CommonAST. This is for
029: * homogeneous nodes.
030: */
031: protected String theASTNodeType = null;
032: protected Class theASTNodeTypeClass = null;
033:
034: /** How to specify the classname to create for a particular
035: * token type. Note that ANTLR allows you to say, for example,
036: *
037: tokens {
038: PLUS<AST=PLUSNode>;
039: ...
040: }
041: *
042: * and it tracks everything statically. #[PLUS] will make you
043: * a PLUSNode w/o use of this table.
044: *
045: * For tokens that ANTLR cannot track statically like #[i],
046: * you can use this table to map PLUS (Integer) -> PLUSNode (Class)
047: * etc... ANTLR sets the class map from the tokens {...} section
048: * via the ASTFactory(Hashtable) ctor in persistence.antlr.Parser.
049: */
050: protected Hashtable tokenTypeToASTClassMap = null;
051:
052: public ASTFactory() {
053: }
054:
055: /** Create factory with a specific mapping from token type
056: * to Java AST node type. Your subclasses of ASTFactory
057: * can override and reuse the map stuff.
058: */
059: public ASTFactory(Hashtable tokenTypeToClassMap) {
060: setTokenTypeToASTClassMap(tokenTypeToClassMap);
061: }
062:
063: /** Specify an "override" for the Java AST object created for a
064: * specific token. It is provided as a convenience so
065: * you can specify node types dynamically. ANTLR sets
066: * the token type mapping automatically from the tokens{...}
067: * section, but you can change that mapping with this method.
068: * ANTLR does it's best to statically determine the node
069: * type for generating parsers, but it cannot deal with
070: * dynamic values like #[LT(1)]. In this case, it relies
071: * on the mapping. Beware differences in the tokens{...}
072: * section and what you set via this method. Make sure
073: * they are the same.
074: *
075: * Set className to null to remove the mapping.
076: *
077: * @since 2.7.2
078: */
079: public void setTokenTypeASTNodeType(int tokenType, String className)
080: throws IllegalArgumentException {
081: if (tokenTypeToASTClassMap == null) {
082: tokenTypeToASTClassMap = new Hashtable();
083: }
084: if (className == null) {
085: tokenTypeToASTClassMap.remove(new Integer(tokenType));
086: return;
087: }
088: Class c = null;
089: try {
090: c = Class.forName(className);
091: tokenTypeToASTClassMap.put(new Integer(tokenType), c);
092: } catch (Exception e) {
093: throw new IllegalArgumentException("Invalid class, "
094: + className);
095: }
096: }
097:
098: /** For a given token type, what is the AST node object type to create
099: * for it?
100: * @since 2.7.2
101: */
102: public Class getASTNodeType(int tokenType) {
103: // try node specific class
104: if (tokenTypeToASTClassMap != null) {
105: Class c = (Class) tokenTypeToASTClassMap.get(new Integer(
106: tokenType));
107: if (c != null) {
108: return c;
109: }
110: }
111:
112: // try a global specified class
113: if (theASTNodeTypeClass != null) {
114: return theASTNodeTypeClass;
115: }
116:
117: // default to the common type
118: return CommonAST.class;
119: }
120:
121: /** Add a child to the current AST */
122: public void addASTChild(ASTPair currentAST, AST child) {
123: if (child != null) {
124: if (currentAST.root == null) {
125: // Make new child the current root
126: currentAST.root = child;
127: } else {
128: if (currentAST.child == null) {
129: // Add new child to current root
130: currentAST.root.setFirstChild(child);
131: } else {
132: currentAST.child.setNextSibling(child);
133: }
134: }
135: // Make new child the current child
136: currentAST.child = child;
137: currentAST.advanceChildToEnd();
138: }
139: }
140:
141: /** Create a new empty AST node; if the user did not specify
142: * an AST node type, then create a default one: CommonAST.
143: */
144: public AST create() {
145: return create(Token.INVALID_TYPE);
146: }
147:
148: public AST create(int type) {
149: Class c = getASTNodeType(type);
150: AST t = create(c);
151: if (t != null) {
152: t.initialize(type, "");
153: }
154: return t;
155: }
156:
157: public AST create(int type, String txt) {
158: AST t = create(type);
159: if (t != null) {
160: t.initialize(type, txt);
161: }
162: return t;
163: }
164:
165: /** Create an AST node with the token type and text passed in, but
166: * with a specific Java object type. Typically called when you
167: * say @[PLUS,"+",PLUSNode] in an antlr action.
168: * @since 2.7.2
169: */
170: public AST create(int type, String txt, String className) {
171: AST t = create(className);
172: if (t != null) {
173: t.initialize(type, txt);
174: }
175: return t;
176: }
177:
178: /** Create a new empty AST node; if the user did not specify
179: * an AST node type, then create a default one: CommonAST.
180: */
181: public AST create(AST tr) {
182: if (tr == null)
183: return null; // create(null) == null
184: AST t = create(tr.getType());
185: if (t != null) {
186: t.initialize(tr);
187: }
188: return t;
189: }
190:
191: public AST create(Token tok) {
192: AST t = create(tok.getType());
193: if (t != null) {
194: t.initialize(tok);
195: }
196: return t;
197: }
198:
199: /** ANTLR generates reference to this when you reference a token
200: * that has a specified heterogeneous AST node type. This is
201: * also a special case node creation routine for backward
202: * compatibility. Before, ANTLR generated "new T(tokenObject)"
203: * and so I must call the appropriate constructor not T().
204: *
205: * @since 2.7.2
206: */
207: public AST create(Token tok, String className) {
208: AST t = createUsingCtor(tok, className);
209: return t;
210: }
211:
212: /**
213: * @since 2.7.2
214: */
215: public AST create(String className) {
216: Class c = null;
217: try {
218: c = Class.forName(className);
219: } catch (Exception e) {
220: throw new IllegalArgumentException("Invalid class, "
221: + className);
222: }
223: return create(c);
224: }
225:
226: /**
227: * @since 2.7.2
228: */
229: protected AST createUsingCtor(Token token, String className) {
230: Class c = null;
231: AST t = null;
232: try {
233: c = Class.forName(className);
234: Class[] tokenArgType = new Class[] { persistence.antlr.Token.class };
235: try {
236: Constructor ctor = c.getConstructor(tokenArgType);
237: t = (AST) ctor.newInstance(new Object[] { token }); // make a new one
238: } catch (NoSuchMethodException e) {
239: // just do the regular thing if you can't find the ctor
240: // Your AST must have default ctor to use this.
241: t = create(c);
242: if (t != null) {
243: t.initialize(token);
244: }
245: }
246: } catch (Exception e) {
247: throw new IllegalArgumentException(
248: "Invalid class or can't make instance, "
249: + className);
250: }
251: return t;
252: }
253:
254: /**
255: * @since 2.7.2
256: */
257: protected AST create(Class c) {
258: AST t = null;
259: try {
260: t = (AST) c.newInstance(); // make a new one
261: } catch (Exception e) {
262: error("Can't create AST Node " + c.getName());
263: return null;
264: }
265: return t;
266: }
267:
268: /** Copy a single node with same Java AST objec type.
269: * Ignore the tokenType->Class mapping since you know
270: * the type of the node, t.getClass(), and doing a dup.
271: *
272: * clone() is not used because we want all AST creation
273: * to go thru the factory so creation can be
274: * tracked. Returns null if t is null.
275: */
276: public AST dup(AST t) {
277: if (t == null) {
278: return null;
279: }
280: AST dup_t = create(t.getClass());
281: dup_t.initialize(t);
282: return dup_t;
283: }
284:
285: /** Duplicate tree including siblings of root. */
286: public AST dupList(AST t) {
287: AST result = dupTree(t); // if t == null, then result==null
288: AST nt = result;
289: while (t != null) { // for each sibling of the root
290: t = t.getNextSibling();
291: nt.setNextSibling(dupTree(t)); // dup each subtree, building new tree
292: nt = nt.getNextSibling();
293: }
294: return result;
295: }
296:
297: /**Duplicate a tree, assuming this is a root node of a tree--
298: * duplicate that node and what's below; ignore siblings of root node.
299: */
300: public AST dupTree(AST t) {
301: AST result = dup(t); // make copy of root
302: // copy all children of root.
303: if (t != null) {
304: result.setFirstChild(dupList(t.getFirstChild()));
305: }
306: return result;
307: }
308:
309: /** Make a tree from a list of nodes. The first element in the
310: * array is the root. If the root is null, then the tree is
311: * a simple list not a tree. Handles null children nodes correctly.
312: * For example, build(a, b, null, c) yields tree (a b c). build(null,a,b)
313: * yields tree (nil a b).
314: */
315: public AST make(AST[] nodes) {
316: if (nodes == null || nodes.length == 0)
317: return null;
318: AST root = nodes[0];
319: AST tail = null;
320: if (root != null) {
321: root.setFirstChild(null); // don't leave any old pointers set
322: }
323: // link in children;
324: for (int i = 1; i < nodes.length; i++) {
325: if (nodes[i] == null)
326: continue; // ignore null nodes
327: if (root == null) {
328: // Set the root and set it up for a flat list
329: root = tail = nodes[i];
330: } else if (tail == null) {
331: root.setFirstChild(nodes[i]);
332: tail = root.getFirstChild();
333: } else {
334: tail.setNextSibling(nodes[i]);
335: tail = tail.getNextSibling();
336: }
337: // Chase tail to last sibling
338: while (tail.getNextSibling() != null) {
339: tail = tail.getNextSibling();
340: }
341: }
342: return root;
343: }
344:
345: /** Make a tree from a list of nodes, where the nodes are contained
346: * in an ASTArray object
347: */
348: public AST make(ASTArray nodes) {
349: return make(nodes.array);
350: }
351:
352: /** Make an AST the root of current AST */
353: public void makeASTRoot(ASTPair currentAST, AST root) {
354: if (root != null) {
355: // Add the current root as a child of new root
356: root.addChild(currentAST.root);
357: // The new current child is the last sibling of the old root
358: currentAST.child = currentAST.root;
359: currentAST.advanceChildToEnd();
360: // Set the new root
361: currentAST.root = root;
362: }
363: }
364:
365: public void setASTNodeClass(String t) {
366: theASTNodeType = t;
367: try {
368: theASTNodeTypeClass = Class.forName(t); // get class def
369: } catch (Exception e) {
370: // either class not found,
371: // class is interface/abstract, or
372: // class or initializer is not accessible.
373: error("Can't find/access AST Node type" + t);
374: }
375: }
376:
377: /** Specify the type of node to create during tree building.
378: * @deprecated since 2.7.1
379: */
380: public void setASTNodeType(String t) {
381: setASTNodeClass(t);
382: }
383:
384: public Hashtable getTokenTypeToASTClassMap() {
385: return tokenTypeToASTClassMap;
386: }
387:
388: public void setTokenTypeToASTClassMap(Hashtable tokenTypeToClassMap) {
389: this .tokenTypeToASTClassMap = tokenTypeToClassMap;
390: }
391:
392: /** To change where error messages go, can subclass/override this method
393: * and then setASTFactory in Parser and TreeParser. This method removes
394: * a prior dependency on class persistence.antlr.Tool.
395: */
396: public void error(String e) {
397: System.err.println(e);
398: }
399: }
|