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