View Javadoc

1   /*
2    * ASTBuilder.java
3    * 
4    * Copyright (c) 2006-2007 David Holroyd
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package uk.co.badgersinfoil.metaas.impl;
20  
21  import java.util.List;
22  import org.asdt.core.internal.antlr.AS3Parser;
23  import uk.co.badgersinfoil.metaas.ActionScriptFactory;
24  import uk.co.badgersinfoil.metaas.SyntaxException;
25  import uk.co.badgersinfoil.metaas.dom.ASAssignmentExpression;
26  import uk.co.badgersinfoil.metaas.dom.ASBinaryExpression;
27  import uk.co.badgersinfoil.metaas.dom.ASCompilationUnit;
28  import uk.co.badgersinfoil.metaas.dom.Expression;
29  import uk.co.badgersinfoil.metaas.dom.Visibility;
30  import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListToken;
31  import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTree;
32  
33  
34  /**
35   * Utilities to build and assemble Abstract Syntax Tree fragments for inclusion
36   * into the compilation unit being generated.
37   */
38  public class ASTBuilder {
39  
40  	private ASTBuilder() {
41  		// hide default ctor
42  	}
43  
44  	public static LinkedListTree newExprStmt(LinkedListTree expr) {
45  		LinkedListTree exprStmt = ASTUtils.newImaginaryAST(AS3Parser.EXPR_STMNT);
46  		exprStmt.addChildWithTokens(expr);
47  		exprStmt.appendToken(TokenBuilder.newSemi());
48  		return exprStmt;
49  	}
50  
51  	public static AS3ASTCompilationUnit synthesizeClass(String qualifiedName) {
52  		LinkedListTree unit = ASTUtils.newImaginaryAST(AS3Parser.COMPILATION_UNIT);
53  		LinkedListTree pkg = ASTUtils.newAST(AS3Parser.PACKAGE, "package");
54  		pkg.appendToken(TokenBuilder.newSpace());
55  		unit.addChildWithTokens(pkg);
56  		pkg.appendToken(TokenBuilder.newSpace());
57  		String packageName = packageNameFrom(qualifiedName);
58  		if (packageName != null) {
59  			pkg.addChildWithTokens(AS3FragmentParser.parseIdent(packageName));
60  		}
61  		LinkedListTree packageBlock = newBlock();
62  		pkg.addChildWithTokens(packageBlock);
63  		String className = typeNameFrom(qualifiedName);
64  		
65  		LinkedListTree clazz = synthesizeAS3Class(className);
66  		ASTUtils.addChildWithIndentation(packageBlock, clazz);
67  		return new AS3ASTCompilationUnit(unit);
68  	}
69  
70  	public static ASCompilationUnit synthesizeInterface(String qualifiedName) {
71  		LinkedListTree unit = ASTUtils.newImaginaryAST(AS3Parser.COMPILATION_UNIT);
72  		LinkedListTree pkg = ASTUtils.newAST(AS3Parser.PACKAGE, "package");
73  		unit.addChildWithTokens(pkg);
74  		pkg.appendToken(TokenBuilder.newSpace());
75  		String packageName = packageNameFrom(qualifiedName);
76  		if (packageName != null) {
77  			pkg.addChildWithTokens(AS3FragmentParser.parseIdent(packageName));
78  		}
79  		LinkedListTree packageBlock = newBlock();
80  		pkg.addChildWithTokens(packageBlock);
81  		
82  		LinkedListTree iface = synthesizeAS3Interface(qualifiedName);
83  		ASTUtils.addChildWithIndentation(packageBlock, iface);
84  		return new AS3ASTCompilationUnit(unit);
85  	}
86  
87  	private static LinkedListTree synthesizeAS3Interface(String qualifiedName) {
88  		LinkedListTree iface = ASTUtils.newImaginaryAST(AS3Parser.INTERFACE_DEF);
89  		LinkedListTree modifiers = ASTUtils.newImaginaryAST(AS3Parser.MODIFIERS);
90  		iface.addChildWithTokens(modifiers);
91  		modifiers.addChildWithTokens(ASTUtils.newAST(AS3Parser.PUBLIC, "public"));
92  		modifiers.appendToken(TokenBuilder.newSpace());
93  		iface.appendToken(TokenBuilder.newInterface());
94  		iface.appendToken(TokenBuilder.newSpace());
95  		iface.addChildWithTokens(ASTUtils.newAST(AS3Parser.IDENT, typeNameFrom(qualifiedName)));
96  		iface.appendToken(TokenBuilder.newSpace());
97  		iface.addChildWithTokens(newTypeBlock());
98  		LinkedListTree annos = ASTUtils.newPlaceholderAST(AS3Parser.ANNOTATIONS);
99  		iface.addChildWithTokens(0, annos);
100 		return iface;
101 	}
102 
103 	private static String typeNameFrom(String qualifiedName) {
104 		int p = qualifiedName.lastIndexOf('.');
105 		if (p == -1) {
106 			return qualifiedName;
107 		}
108 		return qualifiedName.substring(p+1);
109 	}
110 
111 	private static LinkedListTree synthesizeAS3Class(String className) {
112 		LinkedListTree clazz = ASTUtils.newImaginaryAST(AS3Parser.CLASS_DEF);
113 		LinkedListTree modifiers = ASTUtils.newImaginaryAST(AS3Parser.MODIFIERS);
114 		clazz.addChildWithTokens(modifiers);
115 		LinkedListTree modPublic = ASTUtils.newAST(AS3Parser.PUBLIC, "public");
116 		modifiers.addChildWithTokens(modPublic);
117 		modifiers.appendToken(TokenBuilder.newSpace());
118 		clazz.appendToken(TokenBuilder.newClass());
119 		clazz.appendToken(TokenBuilder.newSpace());
120 		clazz.addChildWithTokens(ASTUtils.newAST(AS3Parser.IDENT, className));
121 		clazz.appendToken(TokenBuilder.newSpace());
122 		clazz.addChildWithTokens(newTypeBlock());
123 		LinkedListTree annos = ASTUtils.newPlaceholderAST(AS3Parser.ANNOTATIONS);
124 		clazz.addChildWithTokens(0, annos);
125 		return clazz;
126 	}
127 
128 	private static String packageNameFrom(String qualifiedName) {
129 		int p = qualifiedName.lastIndexOf('.');
130 		if (p == -1) {
131 			return null;
132 		}
133 		return qualifiedName.substring(0, p);
134 	}
135 
136 	public static ASTASMethod newClassMethod(String name, Visibility visibility, String returnType) {
137 		LinkedListTree def = ASTUtils.newImaginaryAST(AS3Parser.METHOD_DEF);
138 		LinkedListTree annos = ASTUtils.newPlaceholderAST(AS3Parser.ANNOTATIONS);
139 		def.addChildWithTokens(annos);
140 		def.addChildWithTokens(ModifierUtils.toModifiers(visibility));
141 		def.appendToken(TokenBuilder.newFunction());
142 		def.appendToken(TokenBuilder.newSpace());
143 		LinkedListTree acc = ASTUtils.newPlaceholderAST(AS3Parser.ACCESSOR_ROLE);
144 		def.addChildWithTokens(acc);
145 		LinkedListTree methName = ASTUtils.newAST(AS3Parser.IDENT, name);
146 		def.addChildWithTokens(methName);
147 		def.addChildWithTokens(ASTUtils.newParentheticAST(AS3Parser.PARAMS, AS3Parser.LPAREN, "(", AS3Parser.RPAREN, ")"));
148 		if (returnType != null) {
149 			def.addChildWithTokens(AS3FragmentParser.parseTypeSpec(returnType));
150 		}
151 		def.appendToken(TokenBuilder.newSpace());
152 		LinkedListTree block = newBlock();
153 		def.addChildWithTokens(block);
154 
155 		return new ASTASMethod(def);
156 	}
157 
158 	public static ASTASField newField(String name, Visibility visibility, String type) {
159 		if (name.indexOf('.') != -1) {
160 			throw new SyntaxException("field name must not contain '.'");
161 		}
162 		LinkedListTree decl = ASTUtils.newImaginaryAST(AS3Parser.VAR_DEF);
163 		LinkedListTree annos = ASTUtils.newPlaceholderAST(AS3Parser.ANNOTATIONS);
164 		decl.addChildWithTokens(annos);
165 		decl.addChildWithTokens(ModifierUtils.toModifiers(visibility));
166 		decl.addChildWithTokens(ASTUtils.newAST(AS3Parser.VAR, "var"));
167 		decl.appendToken(TokenBuilder.newSpace());
168 		LinkedListTree def = ASTUtils.newAST(AS3Parser.IDENT, name);
169 		decl.addChildWithTokens(def);
170 		if (type != null) {
171 			def.addChildWithTokens(AS3FragmentParser.parseTypeSpec(type));
172 		}
173 		decl.appendToken(TokenBuilder.newSemi());
174 		return new ASTASField(decl);
175 	}
176 
177 	public static ASTASMethod newInterfaceMethod(String name, Visibility visibility, String returnType) {
178 		LinkedListTree def = ASTUtils.newImaginaryAST(AS3Parser.METHOD_DEF);
179 		LinkedListTree annos = ASTUtils.newPlaceholderAST(AS3Parser.ANNOTATIONS);
180 		def.addChildWithTokens(annos);
181 		def.addChildWithTokens(ModifierUtils.toModifiers(visibility));
182 		def.appendToken(TokenBuilder.newFunction());
183 		def.appendToken(TokenBuilder.newSpace());
184 		LinkedListTree acc = ASTUtils.newPlaceholderAST(AS3Parser.ACCESSOR_ROLE);
185 		def.addChildWithTokens(acc);
186 		LinkedListTree methName = ASTUtils.newAST(AS3Parser.IDENT, name);
187 		def.addChildWithTokens(methName);
188 		def.addChildWithTokens(ASTUtils.newParentheticAST(AS3Parser.PARAMS, AS3Parser.LPAREN, "(", AS3Parser.RPAREN, ")"));
189 		if (returnType != null) {
190 			def.addChildWithTokens(AS3FragmentParser.parseTypeSpec(returnType));
191 		}
192 		def.appendToken(TokenBuilder.newSemi());
193 
194 		return new ASTASMethod(def);
195 	}
196 
197 	public static LinkedListTree newBlock() {
198 		return newBlock(AS3Parser.BLOCK);
199 	}
200 
201 	public static LinkedListTree newTypeBlock() {
202 		return newBlock(AS3Parser.TYPE_BLOCK);
203 	}
204 
205 	private static LinkedListTree newBlock(int type) {
206 		LinkedListTree ast = ASTUtils.newParentheticAST(type,
207 		                                                AS3Parser.LCURLY, "{",
208 		                                                AS3Parser.RCURLY, "}");
209 		LinkedListToken nl = TokenBuilder.newNewline();
210 		ast.getInitialInsertionAfter().afterInsert(nl);
211 		ast.setInitialInsertionAfter(nl);
212 		return ast;
213 	}
214 
215 	public static LinkedListTree newMetadataTag(String name) {
216 		LinkedListTree ast = ASTUtils.newParentheticAST(AS3Parser.ANNOTATION,
217 		                                                AS3Parser.LBRACK, "[",
218 		                                                AS3Parser.RBRACK, "]");
219 		ast.addChildWithTokens(ASTUtils.newAST(AS3Parser.IDENT, name));
220 		return ast;
221 	}
222 
223 	/**
224 	 * returns a CONDITION node with the given expression as its child
225 	 */
226 	private static LinkedListTree condition(LinkedListTree expr) {
227 		LinkedListTree cond =  ASTUtils.newParentheticAST(AS3Parser.CONDITION,
228 		                                                  AS3Parser.LPAREN, "(",
229 		                                                  AS3Parser.RPAREN, ")");
230 		cond.addChildWithTokens(expr);
231 		return cond;
232 	}
233 
234 	public static LinkedListTree newIf(String condition) {
235 		return newIf(AS3FragmentParser.parseExpr(condition));
236 	}
237 	public static LinkedListTree newIf(LinkedListTree condition) {
238 		LinkedListTree ifStmt = ASTUtils.newAST(AS3Parser.IF, "if");
239 		ifStmt.appendToken(TokenBuilder.newSpace());
240 		ifStmt.addChildWithTokens(condition(condition));
241 		ifStmt.appendToken(TokenBuilder.newSpace());
242 		ifStmt.addChildWithTokens(ASTBuilder.newBlock());
243 		return ifStmt;
244 	}
245 
246 	public static LinkedListTree newFor(String init, String condition, String iterate) {
247 		return newFor(init==null      ? null : AS3FragmentParser.parseForInit(init),
248 		              condition==null ? null : AS3FragmentParser.parseForCond(condition),
249 		              iterate==null   ? null : AS3FragmentParser.parseForIter(iterate));
250 	}
251 	public static LinkedListTree newFor(LinkedListTree init, LinkedListTree condition, LinkedListTree iterate) {
252 		LinkedListTree forStmt = ASTUtils.newAST(AS3Parser.FOR, "for");
253 		forStmt.appendToken(TokenBuilder.newSpace());
254 		forStmt.appendToken(TokenBuilder.newLParen());
255 		if (init != null) {
256 			forStmt.addChildWithTokens(init);
257 		} else {
258 			LinkedListTree initStmt = ASTUtils.newPlaceholderAST(AS3Parser.FOR_INIT);
259 			forStmt.addChildWithTokens(initStmt);
260 		}
261 		forStmt.appendToken(TokenBuilder.newSemi());
262 		forStmt.appendToken(TokenBuilder.newSpace());
263 		if (condition != null) {
264 			forStmt.addChildWithTokens(condition);
265 		} else {
266 			LinkedListTree condStmt = ASTUtils.newPlaceholderAST(AS3Parser.FOR_CONDITION);
267 			forStmt.addChildWithTokens(condStmt);
268 		}
269 		forStmt.appendToken(TokenBuilder.newSemi());
270 		forStmt.appendToken(TokenBuilder.newSpace());
271 		if (iterate != null) {
272 			forStmt.addChildWithTokens(iterate);
273 		} else {
274 			LinkedListTree iterStmt = ASTUtils.newPlaceholderAST(AS3Parser.FOR_ITERATOR);
275 			forStmt.addChildWithTokens(iterStmt);
276 		}
277 		forStmt.appendToken(TokenBuilder.newRParen());
278 		return forStmt;
279 	}
280 
281 	public static LinkedListTree newForIn(String declaration, String expression) {
282 		return newForIn(AS3FragmentParser.parseForInVar(declaration),
283 		                AS3FragmentParser.parseExpr(expression));
284 	}
285 	public static LinkedListTree newForIn(LinkedListTree declaration, LinkedListTree expression) {
286 		LinkedListTree forStmt = ASTUtils.newAST(AS3Parser.FOR_IN, "for");
287 		forStmt.appendToken(TokenBuilder.newSpace());
288 		genForInSetup(forStmt, declaration, expression);
289 		return forStmt;
290 	}
291 
292 	public static LinkedListTree newForEachIn(String declaration, String expression) {
293 		return newForEachIn(AS3FragmentParser.parseForInVar(declaration),
294 		                    AS3FragmentParser.parseExpr(expression));
295 	}
296 	public static LinkedListTree newForEachIn(LinkedListTree declaration, LinkedListTree expression) {
297 		LinkedListTree forStmt = ASTUtils.newAST(AS3Parser.FOR_EACH, "for");
298 		forStmt.appendToken(TokenBuilder.newSpace());
299 		forStmt.appendToken(TokenBuilder.newEach());
300 		genForInSetup(forStmt, declaration, expression);
301 		return forStmt;
302 	}
303 
304 	/**
305 	 * Common code for both for-in and for-each-in loop setup
306 	 */
307 	private static void genForInSetup(LinkedListTree forStmt,
308 	                                  LinkedListTree declaration,
309 	                                  LinkedListTree expression)
310 	{
311 		forStmt.appendToken(TokenBuilder.newLParen());
312 		forStmt.addChildWithTokens(declaration);
313 		forStmt.appendToken(TokenBuilder.newSpace());
314 		forStmt.appendToken(TokenBuilder.newIn());
315 		forStmt.appendToken(TokenBuilder.newSpace());
316 		forStmt.addChildWithTokens(expression);
317 		forStmt.appendToken(TokenBuilder.newRParen());
318 	}
319 
320 	public static LinkedListTree newWhile(String condition) {
321 		return newWhile(AS3FragmentParser.parseExpr(condition));
322 	}
323 	public static LinkedListTree newWhile(LinkedListTree condition) {
324 		LinkedListTree whileStmt = ASTUtils.newAST(AS3Parser.WHILE, "while");
325 		whileStmt.appendToken(TokenBuilder.newSpace());
326 		whileStmt.addChildWithTokens(condition(condition));
327 		return whileStmt;
328 	}
329 
330 	public static LinkedListTree newDoWhile(String condition) {
331 		return newDoWhile(AS3FragmentParser.parseExpr(condition));
332 	}
333 	public static LinkedListTree newDoWhile(LinkedListTree condition) {
334 		LinkedListTree doWhileStmt = ASTUtils.newAST(AS3Parser.DO, "do");
335 		doWhileStmt.appendToken(TokenBuilder.newSpace());
336 		LinkedListTree block = ASTBuilder.newBlock();
337 		doWhileStmt.addChildWithTokens(block);
338 		doWhileStmt.appendToken(TokenBuilder.newSpace());
339 		doWhileStmt.appendToken(TokenBuilder.newWhile());
340 		doWhileStmt.appendToken(TokenBuilder.newSpace());
341 		doWhileStmt.addChildWithTokens(condition(condition));
342 		doWhileStmt.appendToken(TokenBuilder.newSemi());
343 		return doWhileStmt;
344 	}
345 
346 	public static LinkedListTree newSwitch(String condition) {
347 		return newSwitch(AS3FragmentParser.parseExpr(condition));
348 	}
349 	public static LinkedListTree newSwitch(LinkedListTree condition) {
350 		LinkedListTree switchStmt = ASTUtils.newAST(AS3Parser.SWITCH, "switch");
351 		switchStmt.appendToken(TokenBuilder.newSpace());
352 		switchStmt.addChildWithTokens(condition(condition));
353 		switchStmt.appendToken(TokenBuilder.newSpace());
354 		LinkedListTree block = ASTBuilder.newBlock();
355 		switchStmt.addChildWithTokens(block);
356 		return switchStmt;
357 	}
358 
359 	public static LinkedListTree newWith(String expr) {
360 		return newWith(AS3FragmentParser.parseExpr(expr));
361 	}
362 	public static LinkedListTree newWith(LinkedListTree expr) {
363 		LinkedListTree withStmt = ASTUtils.newAST(AS3Parser.WITH, "with");
364 		withStmt.appendToken(TokenBuilder.newSpace());
365 		withStmt.addChildWithTokens(condition(expr));
366 		return withStmt;
367 	}
368 
369 	public static LinkedListTree newDeclaration(String assignment) {
370 		return newDeclaration(AS3FragmentParser.parseVariableDeclarator(assignment));
371 	}
372 	public static LinkedListTree newDeclaration(LinkedListTree assignment) {
373 		LinkedListTree declStmt = ASTUtils.newAST(AS3Parser.VAR, "var");
374 		declStmt.appendToken(TokenBuilder.newSpace());
375 		declStmt.addChildWithTokens(assignment);
376 		declStmt.appendToken(TokenBuilder.newSemi());
377 		return declStmt;
378 	}
379 
380 	public static LinkedListTree newReturn(String expr) {
381 		return newReturn(expr==null ? null : AS3FragmentParser.parseExpr(expr));
382 	}
383 	public static LinkedListTree newReturn(LinkedListTree expr) {
384 		LinkedListTree returnStmt = ASTUtils.newAST(AS3Parser.RETURN, "return");
385 		if (expr != null) {
386 			returnStmt.appendToken(TokenBuilder.newSpace());
387 			returnStmt.addChildWithTokens(expr);
388 		}
389 		returnStmt.appendToken(TokenBuilder.newSemi());
390 		return returnStmt;
391 	}
392 
393 	/**
394 	 * @param args a list of ASExpression objects
395 	 */
396 	public static LinkedListTree newSuperStatement(List args) {
397 		LinkedListTree superStmt = ASTUtils.newAST(AS3Parser.SUPER, "super");
398 		LinkedListTree argumentsNode = ASTUtils.newParentheticAST(AS3Parser.ARGUMENTS, AS3Parser.LPAREN, "(", AS3Parser.RPAREN, ")");
399 		ArgumentUtils.overwriteArgsWithExpressionList(argumentsNode, args);
400 		superStmt.addChildWithTokens(argumentsNode);
401 		superStmt.appendToken(TokenBuilder.newSemi());
402 		return superStmt;
403 	}
404 
405 	public static LinkedListTree newBreakStatement() {
406 		LinkedListTree breakStmt = ASTUtils.newAST(AS3Parser.BREAK, "break");
407 		breakStmt.appendToken(TokenBuilder.newSemi());
408 		return breakStmt;
409 	}
410 
411 	public static LinkedListTree newTryStatement() {
412 		LinkedListTree tryStmt = ASTUtils.newAST(AS3Parser.TRY, "try");
413 		tryStmt.appendToken(TokenBuilder.newSpace());
414 		tryStmt.addChildWithTokens(newBlock());
415 		return tryStmt;
416 	}
417 
418 	public static LinkedListTree newCatchClause(String var, String type) {
419 		LinkedListTree tryStmt = ASTUtils.newAST(AS3Parser.CATCH, "catch");
420 		tryStmt.appendToken(TokenBuilder.newSpace());
421 		tryStmt.appendToken(TokenBuilder.newLParen());
422 		tryStmt.addChildWithTokens(AS3FragmentParser.parseSimpleIdent(var));
423 		if (type != null) {
424 			tryStmt.addChildWithTokens(AS3FragmentParser.parseTypeSpec(type));
425 		}
426 		tryStmt.appendToken(TokenBuilder.newRParen());
427 		tryStmt.appendToken(TokenBuilder.newSpace());
428 		tryStmt.addChildWithTokens(newBlock());
429 		return tryStmt;
430 	}
431 
432 	public static LinkedListTree newFinallyClause() {
433 		LinkedListTree tryStmt = ASTUtils.newAST(AS3Parser.FINALLY, "finally");
434 		tryStmt.appendToken(TokenBuilder.newSpace());
435 		tryStmt.addChildWithTokens(newBlock());
436 		return tryStmt;
437 	}
438 
439 	public static LinkedListTree newContinueStatement() {
440 		LinkedListTree continueStmt = ASTUtils.newAST(AS3Parser.CONTINUE, "continue");
441 		continueStmt.appendToken(TokenBuilder.newSemi());
442 		return continueStmt;
443 	}
444 
445 	public static ASBinaryExpression newBinaryExpression(LinkedListToken op, Expression left, Expression right) {
446 		LinkedListTree ast = ASTUtils.newAST(op);
447 		LinkedListTree leftExpr = ((ASTExpression)left).getAST();
448 		assertNoParent("left-hand expression", leftExpr);
449 		LinkedListTree rightExpr = ((ASTExpression)right).getAST();
450 		if (precidence(ast) < precidence(leftExpr)) {
451 			leftExpr = parenthise(leftExpr);
452 		}
453 		if (precidence(ast) < precidence(rightExpr)) {
454 			rightExpr = parenthise(rightExpr);
455 		}
456 		// don't use addChildWithTokens(); special handling below,
457 		ast.addChild(leftExpr);
458 		ast.addChild(rightExpr);
459 		leftExpr.getStopToken().setNext(op);
460 		rightExpr.getStartToken().setPrev(op);
461 		ast.setStartToken(leftExpr.getStartToken());
462 		ast.setStopToken(rightExpr.getStopToken());
463 		spaceEitherSide(op);
464 		return new ASTASBinaryExpression(ast);
465 	}
466 
467 	public static ASAssignmentExpression newAssignExpression(LinkedListToken op, Expression left, Expression right) {
468 		LinkedListTree ast = ASTUtils.newAST(op);
469 		LinkedListTree leftExpr = ((ASTExpression)left).getAST();
470 		assertNoParent("left-hand expression", leftExpr);
471 		LinkedListTree rightExpr = ((ASTExpression)right).getAST();
472 		if (precidence(ast) < precidence(leftExpr)) {
473 			leftExpr = parenthise(leftExpr);
474 		}
475 		if (precidence(ast) < precidence(rightExpr)) {
476 			rightExpr = parenthise(rightExpr);
477 		}
478 		// don't use addChildWithTokens(); special handling below,
479 		ast.addChild(leftExpr);
480 		ast.addChild(rightExpr);
481 		leftExpr.getStopToken().setNext(op);
482 		rightExpr.getStartToken().setPrev(op);
483 		ast.setStartToken(leftExpr.getStartToken());
484 		ast.setStopToken(rightExpr.getStopToken());
485 		spaceEitherSide(op);
486 		return new ASTASAssignmentExpression(ast);
487 	}
488 
489 	public static void assertNoParent(String astDescription, LinkedListTree ast)
490 	{
491 		if (ast.getParent() != null) {
492 			throw new SyntaxException(astDescription+" cannot be added to a second parent node");
493 		}
494 	}
495 
496 	private static LinkedListTree parenthise(LinkedListTree expr) {
497 		LinkedListTree result = ASTUtils.newParentheticAST(AS3Parser.ENCPS_EXPR, AS3Parser.LPAREN, "(", AS3Parser.RPAREN, ")");
498 		result.addChildWithTokens(expr);
499 		return result;
500 	}
501 
502 	private static int precidence(LinkedListTree ast) {
503 		switch (ast.getType()) {
504 			case AS3Parser.ASSIGN:
505 			case AS3Parser.STAR_ASSIGN:
506 			case AS3Parser.DIV_ASSIGN:
507 			case AS3Parser.MOD_ASSIGN:
508 			case AS3Parser.PLUS_ASSIGN:
509 			case AS3Parser.MINUS_ASSIGN:
510 			case AS3Parser.SL_ASSIGN:
511 			case AS3Parser.SR_ASSIGN:
512 			case AS3Parser.BSR_ASSIGN:
513 			case AS3Parser.BAND_ASSIGN:
514 			case AS3Parser.BXOR_ASSIGN:
515 			case AS3Parser.BOR_ASSIGN:
516 			case AS3Parser.LAND_ASSIGN:
517 			case AS3Parser.LOR_ASSIGN:
518 				return 13;
519 			case AS3Parser.QUESTION:
520 				return 12;
521 			case AS3Parser.LOR:
522 				return 11;
523 			case AS3Parser.LAND:
524 				return 10;
525 			case AS3Parser.BOR:
526 				return 9;
527 			case AS3Parser.BXOR:
528 				return 8;
529 			case AS3Parser.BAND:
530 				return 7;
531 			case AS3Parser.STRICT_EQUAL:
532 			case AS3Parser.STRICT_NOT_EQUAL:
533 			case AS3Parser.NOT_EQUAL:
534 			case AS3Parser.EQUAL:
535 				return 6;
536 			case AS3Parser.IN:
537 			case AS3Parser.LT:
538 			case AS3Parser.GT:
539 			case AS3Parser.LE:
540 			case AS3Parser.GE:
541 			case AS3Parser.IS:
542 			case AS3Parser.AS:
543 			case AS3Parser.INSTANCEOF:
544 				return 5;
545 			case AS3Parser.SL:
546 			case AS3Parser.SR:
547 			case AS3Parser.BSR:
548 				return 4;
549 			case AS3Parser.PLUS:
550 			case AS3Parser.MINUS:
551 				return 3;
552 			case AS3Parser.STAR:
553 			case AS3Parser.DIV:
554 			case AS3Parser.MOD:
555 				return 2;
556 			default:
557 				return 1;
558 		}
559 	}
560 
561 	public static LinkedListTree newFieldAccessExpression(LinkedListTree target, LinkedListTree name) {
562 		LinkedListToken op = TokenBuilder.newDot();
563 		LinkedListTree ast = ASTUtils.newAST(op);
564 		assertNoParent("target expression", target);
565 		// don't use addChildWithTokens(); special handling below,
566 		ast.addChild(target);
567 		ast.addChild(name);
568 		target.getStopToken().setNext(op);
569 		name.getStartToken().setPrev(op);
570 		ast.setStartToken(target.getStartToken());
571 		ast.setStopToken(name.getStopToken());
572 		return ast;
573 	}
574 
575 	public static LinkedListTree newConditionalExpression(LinkedListTree conditionExpr,
576 	                                                      LinkedListTree thenExpr,
577 	                                                      LinkedListTree elseExpr)
578 	{
579 		LinkedListToken op = TokenBuilder.newQuestion();
580 		LinkedListToken colon = TokenBuilder.newColon();
581 		LinkedListTree ast = ASTUtils.newAST(op);
582 		// don't use addChildWithTokens(); special handling below,
583 		ast.addChild(conditionExpr);
584 		conditionExpr.getStopToken().setNext(op);
585 		ast.addChild(thenExpr);
586 		thenExpr.getStartToken().setPrev(op);
587 		thenExpr.getStopToken().setNext(colon);
588 		ast.addChild(elseExpr);
589 		elseExpr.getStartToken().setPrev(colon);
590 		ast.setStartToken(conditionExpr.getStartToken());
591 		ast.setStopToken(elseExpr.getStopToken());
592 		spaceEitherSide(op);
593 		spaceEitherSide(colon);
594 		return ast;
595 	}
596 
597 	private static void spaceEitherSide(LinkedListToken token) {
598 		token.beforeInsert(TokenBuilder.newSpace());
599 		token.afterInsert(TokenBuilder.newSpace());
600 	}
601 
602 	public static LinkedListTree newFunctionExpression() {
603 		LinkedListTree def = ASTUtils.newImaginaryAST(AS3Parser.FUNC_DEF);
604 		def.appendToken(TokenBuilder.newFunction());
605 		def.appendToken(TokenBuilder.newSpace());
606 		// TODO: placeholder for name?
607 		def.addChildWithTokens(ASTUtils.newParentheticAST(AS3Parser.PARAMS, AS3Parser.LPAREN, "(", AS3Parser.RPAREN, ")"));
608 		def.appendToken(TokenBuilder.newSpace());
609 		LinkedListTree block = newBlock();
610 		def.addChildWithTokens(block);
611 		return def;
612 	}
613 
614 	public static LinkedListTree newArrayLiteral() {
615 		LinkedListTree lit = ASTUtils.newParentheticAST(AS3Parser.ARRAY_LITERAL, AS3Parser.LBRACK, "[", AS3Parser.RBRACK, "]");
616 		return lit;
617 	}
618 
619 	public static LinkedListTree newObjectLiteral() {
620 		LinkedListTree lit = newBlock(AS3Parser.OBJECT_LITERAL);
621 		return lit;
622 	}
623 
624 	public static LinkedListTree newObjectField(String name,
625 	                                            LinkedListTree value)
626 	{
627 		LinkedListTree field = ASTUtils.newImaginaryAST(AS3Parser.OBJECT_FIELD);
628 		field.addChildWithTokens(AS3FragmentParser.parseSimpleIdent(name));
629 		field.appendToken(TokenBuilder.newColon());
630 		field.appendToken(TokenBuilder.newSpace());
631 		field.addChildWithTokens(value);
632 		return field;
633 	}
634 
635 	public static LinkedListTree newThrowStatement(LinkedListTree ast) {
636 		LinkedListTree thrw = ASTUtils.newAST(AS3Parser.THROW, "throw");
637 		thrw.appendToken(TokenBuilder.newSpace());
638 		thrw.addChildWithTokens(ast);
639 		thrw.appendToken(TokenBuilder.newSemi());
640 		return thrw;
641 	}
642 
643 	public static LinkedListTree newDescendantExpression(LinkedListTree target,
644 	                                                     LinkedListTree selector)
645 	{
646 		LinkedListToken op = TokenBuilder.newE4XDescendant();
647 		LinkedListTree ast = ASTUtils.newAST(op);
648 		assertNoParent("left-hand expression", target);
649 		// TODO: is this needed..?
650 		if (precidence(ast) < precidence(target)) {
651 			target = parenthise(target);
652 		}
653 		if (precidence(ast) < precidence(selector)) {
654 			selector = parenthise(selector);
655 		}
656 		// don't use addChildWithTokens(); special handling below,
657 		ast.addChild(target);
658 		ast.addChild(selector);
659 		target.getStopToken().setNext(op);
660 		selector.getStartToken().setPrev(op);
661 		ast.setStartToken(target.getStartToken());
662 		ast.setStopToken(selector.getStopToken());
663 		return ast;
664 	}
665 
666 	public static LinkedListTree newFilterExpression(LinkedListTree target,
667 	                                                 LinkedListTree selector)
668 	{
669 		LinkedListTree ast = ASTUtils.newImaginaryAST(AS3Parser.E4X_FILTER);
670 		assertNoParent("target expression", target);
671 		assertNoParent("query expression", target);
672 		// don't use addChildWithTokens(); special handling below,
673 		ast.addChildWithTokens(target);
674 		ast.appendToken(TokenBuilder.newDot());
675 		ast.appendToken(TokenBuilder.newLParen());
676 		ast.addChildWithTokens(selector);
677 		ast.appendToken(TokenBuilder.newRParen());
678 		return ast;
679 	}
680 
681 	public static LinkedListTree newStarAttribute() {
682 		LinkedListTree ast = ASTUtils.newImaginaryAST(AS3Parser.E4X_ATTRI_STAR);
683 		ast.appendToken(TokenBuilder.newAt());
684 		ast.appendToken(TokenBuilder.newStar());
685 		return ast;
686 	}
687 
688 	public static LinkedListTree newPropertyAttribute(String propertyName) {
689 		LinkedListTree ast = ASTUtils.newImaginaryAST(AS3Parser.E4X_ATTRI_PROPERTY);
690 		ast.appendToken(TokenBuilder.newAt());
691 		LinkedListTree prop = AS3FragmentParser.parseQualifiedIdent(propertyName);
692 		ast.addChildWithTokens(prop);
693 		return ast;
694 	}
695 
696 	public static LinkedListTree newExpressionAttribute(LinkedListTree expr) {
697 		LinkedListTree ast = ASTUtils.newImaginaryAST(AS3Parser.E4X_ATTRI_EXPR);
698 		ast.appendToken(TokenBuilder.newAt());
699 		ast.appendToken(TokenBuilder.newLBrack());
700 		ast.addChildWithTokens(expr);
701 		ast.appendToken(TokenBuilder.newRBrack());
702 		return ast;
703 	}
704 
705 	public static LinkedListTree newString(String value) {
706 		return ASTUtils.newAST(AS3Parser.STRING_LITERAL, ActionScriptFactory.str(value));
707 	}
708 
709 	public static LinkedListTree newDefaultXMLNamespace(LinkedListTree namespace) {
710 		LinkedListTree ast = ASTUtils.newImaginaryAST(AS3Parser.DEFAULT_XML_NAMESPACE);
711 		ast.appendToken(TokenBuilder.newDefault());
712 		ast.appendToken(TokenBuilder.newSpace());
713 		ast.appendToken(TokenBuilder.newXml());
714 		ast.appendToken(TokenBuilder.newSpace());
715 		ast.appendToken(TokenBuilder.newNamespace());
716 		ast.appendToken(TokenBuilder.newSpace());
717 		ast.appendToken(TokenBuilder.newAssign());
718 		ast.appendToken(TokenBuilder.newSpace());
719 		ast.addChildWithTokens(namespace);
720 		ast.appendToken(TokenBuilder.newSemi());
721 		return ast;
722 	}
723 }