1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package uk.co.badgersinfoil.metaas.impl;
20
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import org.antlr.runtime.ANTLRReaderStream;
25 import org.antlr.runtime.MismatchedTokenException;
26 import org.antlr.runtime.NoViableAltException;
27 import org.antlr.runtime.RecognitionException;
28 import org.asdt.core.internal.antlr.AS3Lexer;
29 import org.asdt.core.internal.antlr.AS3Parser;
30 import uk.co.badgersinfoil.metaas.ActionScriptFactory;
31 import uk.co.badgersinfoil.metaas.SyntaxException;
32 import uk.co.badgersinfoil.metaas.dom.Expression;
33 import uk.co.badgersinfoil.metaas.impl.antlr.BasicListUpdateDelegate;
34 import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListToken;
35 import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTokenSource;
36 import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTokenStream;
37 import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTree;
38 import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTreeAdaptor;
39 import uk.co.badgersinfoil.metaas.impl.antlr.TreeTokenListUpdateDelegate;
40
41
42
43
44
45 public class ASTUtils {
46
47 public static final LinkedListTreeAdaptor TREE_ADAPTOR = new LinkedListTreeAdaptor();
48
49 static {
50 TREE_ADAPTOR.setFactory(new LinkedListTreeAdaptor.Factory() {
51 private BasicListUpdateDelegate basicDelegate = new BasicListUpdateDelegate();
52 private ParentheticListUpdateDelegate blockDelegate = new ParentheticListUpdateDelegate(AS3Parser.LCURLY, AS3Parser.RCURLY);
53 private ParentheticListUpdateDelegate metadataTagDelegate = new ParentheticListUpdateDelegate(AS3Parser.LBRACK, AS3Parser.RBRACK);
54 private ParentheticListUpdateDelegate paramsDelegate = new ParentheticListUpdateDelegate(AS3Parser.LPAREN, AS3Parser.RPAREN);
55 public TreeTokenListUpdateDelegate create(LinkedListTree payload) {
56 if (payload.isNil()) {
57 return basicDelegate;
58 }
59 switch (payload.getType()) {
60 case AS3Parser.BLOCK:
61 case AS3Parser.TYPE_BLOCK:
62 case AS3Parser.OBJECT_LITERAL:
63 return blockDelegate;
64 case AS3Parser.ANNOTATION:
65 case AS3Parser.ARRAY_LITERAL:
66 return metadataTagDelegate;
67 case AS3Parser.PARAMS:
68 case AS3Parser.ANNOTATION_PARAMS:
69 case AS3Parser.ARGUMENTS:
70 case AS3Parser.ENCPS_EXPR:
71 case AS3Parser.CONDITION:
72 return paramsDelegate;
73 default:
74 return basicDelegate;
75 }
76 }
77 });
78 }
79
80
81
82
83 public static String identText(LinkedListTree ast) {
84 if (ast.getType() != AS3Parser.IDENTIFIER) {
85 throw new IllegalArgumentException("expected IDENTIFIER, but token was a " + tokenName(ast));
86 }
87 return stringifyIdentAux((LinkedListTree)ast.getChild(0));
88 }
89
90 private static String stringifyIdentAux(LinkedListTree ast) {
91 StringBuffer result = new StringBuffer();
92 if (ast.getType()==AS3Parser.DBL_COLON) {
93 result.append(ast.getFirstChild());
94 result.append("::");
95 result.append(ast.getLastChild());
96 } else if (ast.getType()==AS3Parser.PROPERTY_OR_IDENTIFIER
97 || ast.getType()==AS3Parser.DOT)
98 {
99 result.append(stringifyIdentAux(ast.getFirstChild()));
100 result.append(".");
101 result.append(stringifyIdentAux(ast.getLastChild()));
102 } else {
103 result.append(ast.getText());
104 }
105 return result.toString();
106 }
107
108 public static String qualifiedIdentText(LinkedListTree ast) {
109 if (ast.getType()==AS3Parser.DBL_COLON) {
110 return ast.getFirstChild() + "::" + ast.getLastChild();
111 }
112 return ast.getText();
113 }
114
115 public static String identStarText(LinkedListTree ast) {
116 if (ast.getType() != AS3Parser.IDENTIFIER_STAR) {
117 throw new IllegalArgumentException("expected IDENTIFIER_STAR, but token was a " + tokenName(ast));
118 }
119 StringBuffer result = new StringBuffer();
120 for (int i=0; i<ast.getChildCount(); i++) {
121 LinkedListTree part = (LinkedListTree)ast.getChild(i);
122 if (result.length() > 0) {
123 result.append(".");
124 }
125 result.append(part.getText());
126 }
127 return result.toString();
128 }
129
130
131
132
133 public static String typeSpecText(LinkedListTree ast) {
134 if (ast.getType() != AS3Parser.TYPE_SPEC) {
135 throw new IllegalArgumentException("expected TYPE_SPEC, but token was a " + tokenName(ast));
136 }
137 LinkedListTree type = ast.getFirstChild();
138 if (type.getType() == AS3Parser.IDENTIFIER) {
139 return identText(type);
140 }
141
142 return type.getText();
143 }
144
145
146
147
148 public static String tokenName(LinkedListTree ast) {
149 return tokenName(ast.getType());
150 }
151
152
153
154
155 public static String tokenName(int type) {
156 if (type == -1) {
157 return "<EOF>";
158 }
159 return AS3Parser.tokenNames[type];
160 }
161
162
163
164
165
166 public static LinkedListTree findChildByType(LinkedListTree ast, int type) {
167 return (LinkedListTree)ast.getFirstChildWithType(type);
168 }
169
170
171
172
173
174 public static AS3Parser parse(String text) {
175 return parse(new StringReader(text));
176 }
177
178
179
180
181
182 public static AS3Parser parse(Reader in) {
183 ANTLRReaderStream cs;
184 try {
185 cs = new ANTLRReaderStream(in);
186 } catch (IOException e) {
187 throw new SyntaxException(e);
188 }
189 AS3Lexer lexer = new AS3Lexer(cs);
190 LinkedListTokenSource linker = new LinkedListTokenSource(lexer);
191 LinkedListTokenStream tokenStream = new LinkedListTokenStream(linker);
192 AS3Parser parser = new AS3Parser(tokenStream);
193 parser.setInput(lexer, cs);
194 parser.setTreeAdaptor(TREE_ADAPTOR);
195 return parser;
196 }
197
198
199 public static SyntaxException buildSyntaxException(String statement,
200 AS3Parser parser,
201 RecognitionException e)
202 {
203 String msg;
204 if (e instanceof NoViableAltException) {
205 NoViableAltException ex = (NoViableAltException)e;
206 msg = "Unexpected token "+tokenName(parser, ex.getUnexpectedType());
207 if (statement != null) {
208 msg += " in "+ActionScriptFactory.str(statement);
209 }
210 } else if (e instanceof MismatchedTokenException) {
211 MismatchedTokenException ex = (MismatchedTokenException)e;
212 msg = "Unexpected token "+tokenName(parser, ex.getUnexpectedType())+" (expecting "+tokenName(parser, ex.expecting)+")";
213 if (statement != null) {
214 msg += " in "+ActionScriptFactory.str(statement);
215 }
216 } else {
217 if (statement == null) {
218 msg = "";
219 } else {
220 msg = "Problem parsing "+ActionScriptFactory.str(statement);
221 }
222 }
223 msg += " at line " + e.line;
224 return new SyntaxException(msg, e);
225 }
226
227 private static String tokenName(AS3Parser parser, int type) {
228 if (type == -1) {
229 return "<end-of-file>";
230 }
231 return parser.getTokenNames()[type];
232 }
233
234
235
236
237
238
239 public static LinkedListTree newAST(int type) {
240 return newImaginaryAST(type);
241 }
242
243
244
245
246
247 public static LinkedListTree newImaginaryAST(int type) {
248 return (LinkedListTree)TREE_ADAPTOR.create(type, tokenName(type));
249 }
250
251 public static LinkedListTree newPlaceholderAST(int type) {
252 LinkedListTree node = newImaginaryAST(type);
253 LinkedListToken placeholder = TokenBuilder.newPlaceholder(node);
254 return node;
255 }
256
257
258
259
260
261 public static LinkedListTree newAST(int type, String text) {
262 LinkedListToken tok = TokenBuilder.newToken(type, text);
263 LinkedListTree ast = (LinkedListTree)TREE_ADAPTOR.create(tok);
264 return ast;
265 }
266
267 public static LinkedListTree newAST(LinkedListToken tok) {
268 return (LinkedListTree)TREE_ADAPTOR.create(tok);
269 }
270
271 public static LinkedListTree newParentheticAST(int type,
272 int startType,
273 String startText,
274 int endType,
275 String endText) {
276 LinkedListTree ast = newImaginaryAST(type);
277 LinkedListToken start = TokenBuilder.newToken(startType, startText);
278 ast.setStartToken(start);
279 LinkedListToken stop = TokenBuilder.newToken(endType, endText);
280 ast.setStopToken(stop);
281 start.setNext(stop);
282 ast.setInitialInsertionAfter(start);
283 return ast;
284 }
285
286 public static void increaseIndent(LinkedListTree node, String indent) {
287 LinkedListToken newStart = increaseIndentAt(node.getStartToken(), indent);
288 node.setStartToken(newStart);
289 increaseIndentAfterFirstLine(node, indent);
290 }
291
292 public static void increaseIndentAfterFirstLine(LinkedListTree node, String indent) {
293 for (LinkedListToken tok=node.getStartToken() ; tok!=node.getStopToken(); tok=tok.getNext()) {
294 switch (tok.getType()) {
295 case AS3Parser.NL:
296 tok = increaseIndentAt(tok.getNext(), indent);
297 break;
298 case AS3Parser.ML_COMMENT:
299 DocCommentUtils.increaseCommentIndent(tok, indent);
300 break;
301 }
302 }
303 }
304
305 private static LinkedListToken increaseIndentAt(LinkedListToken tok, String indentStr) {
306 if (tok.getType() == AS3Parser.WS) {
307 tok.setText(indentStr + tok.getText());
308 return tok;
309 }
310 LinkedListToken indent = TokenBuilder.newWhiteSpace(indentStr);
311 tok.beforeInsert(indent);
312 return indent;
313 }
314
315
316
317
318
319
320
321
322 public static String findIndent(LinkedListTree node) {
323 LinkedListToken tok = node.getStartToken();
324 if (tok == null) {
325 return findIndent(node.getParent());
326 }
327
328
329 while (tok.getType()==AS3Parser.NL || tok.getType()==AS3Parser.WS) {
330 if (tok.getNext() == null) {
331 break;
332 }
333 tok = tok.getNext();
334 }
335
336
337 for (; tok.getPrev()!=null; tok=tok.getPrev()) {
338 if (tok.getType() == AS3Parser.NL) {
339 break;
340 }
341 }
342 if (tok.getType() == AS3Parser.WS) {
343 return tok.getText();
344 }
345 if (tok.getType()!=AS3Parser.NL) {
346 return "";
347 }
348 LinkedListToken startOfLine = tok.getNext();
349 if (startOfLine.getType() == AS3Parser.WS) {
350 return startOfLine.getText();
351 }
352 return "";
353 }
354
355 public static void addChildWithIndentation(LinkedListTree ast, LinkedListTree stmt) {
356 LinkedListTree last = ast.getLastChild();
357 String indent;
358 if (last == null) {
359 indent = "\t" + ASTUtils.findIndent(ast);
360 } else {
361 indent = ASTUtils.findIndent(last);
362 }
363 ASTUtils.increaseIndent(stmt, indent);
364 stmt.addToken(0, TokenBuilder.newNewline());
365 ast.addChildWithTokens(stmt);
366 }
367
368 public static void addChildWithIndentation(LinkedListTree ast, int index, LinkedListTree stmt) {
369 LinkedListTree last = (LinkedListTree)ast.getChild(index);
370 String indent;
371 if (last == null) {
372 indent = "\t" + ASTUtils.findIndent(ast);
373 } else {
374 indent = ASTUtils.findIndent(last);
375 }
376 ASTUtils.increaseIndent(stmt, indent);
377 stmt.addToken(0, TokenBuilder.newNewline());
378 ast.addChildWithTokens(index, stmt);
379 }
380
381 public static String decodeStringLiteral(String str) {
382 StringBuffer result = new StringBuffer();
383 char[] s = str.toCharArray();
384 int end = s.length - 1;
385 if (s[0] != '"' && s[0] != '\'') {
386 throw new SyntaxException("Invalid delimiter at position 0: "+s[0]);
387 }
388 char delimiter = s[0];
389 for (int i=1; i<end; i++) {
390 char c = s[i];
391 switch (c) {
392 case '\\':
393 c = s[++i];
394 switch (c) {
395 case 'n':
396 result.append('\n');
397 break;
398 case 't':
399 result.append('\t');
400 break;
401 case '\\':
402 result.append('\\');
403 break;
404 default:
405 result.append(c);
406 }
407 break;
408 default:
409 result.append(c);
410 }
411 }
412 if (s[end] != delimiter) {
413 throw new SyntaxException("End delimiter doesn't match "+delimiter+" at position "+end);
414 }
415 return result.toString();
416 }
417
418 public static LinkedListToken newStringLiteral(String constant) {
419 return new LinkedListToken(AS3Parser.STRING_LITERAL, ActionScriptFactory.str(constant));
420 }
421
422
423
424
425
426
427
428 public static void removeTrailingWhitespaceAndComma(LinkedListToken stopToken) {
429 for (LinkedListToken tok = stopToken.getNext(); tok!=null; tok=tok.getNext()) {
430 if (tok.getChannel() == AS3Parser.HIDDEN) {
431
432 tok.delete();
433 } else if (tok.getType() == AS3Parser.COMMA) {
434 tok.delete();
435 break;
436 } else {
437 throw new SyntaxException("Unexpected token: "+tok);
438 }
439 }
440 }
441
442 public static void removePreceedingWhitespaceAndComma(LinkedListToken startToken) {
443 for (LinkedListToken tok = startToken.getPrev(); tok!=null; tok=tok.getPrev()) {
444 if (tok.getChannel() == AS3Parser.HIDDEN) {
445 LinkedListToken del = tok;
446 tok = tok.getNext();
447 del.delete();
448 continue;
449 } else if (tok.getType() == AS3Parser.COMMA) {
450 tok.delete();
451 break;
452 } else {
453 throw new SyntaxException("Unexpected token: "+tok);
454 }
455 }
456 }
457
458 public static void assertAS3TokenTypeIs(int expected, int actual) {
459 if (expected != actual) {
460 throw new SyntaxException("Expected "+tokenName(expected)+", got "+tokenName(actual));
461 }
462 }
463
464 public static void assertAS3TokenTypeIs(String msg, int expected, int actual) {
465 if (expected != actual) {
466 throw new SyntaxException(msg+" Expected "+tokenName(expected)+", got "+tokenName(actual));
467 }
468 }
469
470 public static String stringifyNode(LinkedListTree ast) {
471 StringBuffer result = new StringBuffer();
472 for (LinkedListToken tok=ast.getStartToken(); tok!=null&&tok.getType()!=-1; tok=tok.getNext()) {
473 result.append(tok.getText());
474 if (tok == ast.getStopToken()) {
475 break;
476 }
477 }
478 return result.toString();
479 }
480
481 public static void deleteAllChildren(LinkedListTree ast) {
482 while (ast.getChildCount() > 0) {
483 ast.deleteChild(0);
484 }
485 }
486
487 public static LinkedListTree ast(Expression expr) {
488 return ((ASTExpression)expr).getAST();
489 }
490 }