1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package org.codehaus.groovy.syntax;
48
49 import org.codehaus.groovy.GroovyBugError;
50 import org.codehaus.groovy.syntax.Token;
51 import org.codehaus.groovy.syntax.Types;
52 import org.codehaus.groovy.syntax.Reduction;
53
54 import java.io.StringWriter;
55 import java.io.PrintWriter;
56
57
58 /***
59 * An abstract base class for nodes in the concrete syntax tree that is
60 * the result of parsing. Note that the CSTNode is inextricably linked
61 * with the Token in that every CSTNode has a Token as it's root.
62 *
63 * @see org.codehaus.groovy.syntax.parser.Parser
64 * @see Token
65 * @see org.codehaus.groovy.syntax.Reduction
66 * @see org.codehaus.groovy.syntax.Types
67 *
68 * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
69 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
70 *
71 * @version $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 jstrachan Exp $
72 */
73
74 public abstract class CSTNode
75 {
76
77
78
79
80
81 /***
82 * Returns the meaning of this node. If the node isEmpty(), returns
83 * the type of Token.NULL.
84 */
85
86 public int getMeaning()
87 {
88 return getRoot( true ).getMeaning();
89 }
90
91
92
93 /***
94 * Sets the meaning for this node (and it's root Token). Not
95 * valid if the node isEmpty(). Returns the node, for convenience.
96 */
97
98 public CSTNode setMeaning( int meaning )
99 {
100 getRoot().setMeaning( meaning );
101 return this;
102 }
103
104
105
106 /***
107 * Returns the actual type of the node. If the node isEmpty(), returns
108 * the type of Token.NULL.
109 */
110
111 public int getType()
112 {
113 return getRoot( true ).getType();
114 }
115
116
117
118 /***
119 * Returns true if the node can be coerced to the specified type.
120 */
121
122 public boolean canMean( int type )
123 {
124 return Types.canMean( getMeaning(), type );
125 }
126
127
128
129 /***
130 * Returns true if the node's meaning matches the specified type.
131 */
132
133 public boolean isA( int type )
134 {
135 return Types.ofType( getMeaning(), type );
136 }
137
138
139
140 /***
141 * Returns true if the node's meaning matches any of the specified types.
142 */
143
144 public boolean isOneOf( int[] types )
145 {
146 int meaning = getMeaning();
147 for( int i = 0; i < types.length; i++ )
148 {
149 if( Types.ofType(meaning, types[i]) )
150 {
151 return true;
152 }
153 }
154
155 return false;
156 }
157
158
159
160 /***
161 * Returns true if the node's meaning matches all of the specified types.
162 */
163
164 public boolean isAllOf( int[] types )
165 {
166 int meaning = getMeaning();
167 for( int i = 0; i < types.length; i++ )
168 {
169 if( !Types.ofType(meaning, types[i]) )
170 {
171 return false;
172 }
173 }
174
175 return true;
176 }
177
178
179
180 /***
181 * Returns the first matching meaning of the specified types.
182 * Returns Types.UNKNOWN if there are no matches.
183 */
184
185 public int getMeaningAs( int[] types )
186 {
187
188 for( int i = 0; i < types.length; i++ )
189 {
190 if( isA(types[i]) )
191 {
192 return types[i];
193 }
194 }
195
196 return Types.UNKNOWN;
197 }
198
199
200
201
202
203
204
205
206 /***
207 * Returns true if the node matches the specified type. Effectively
208 * a synonym for <code>isA()</code>. Missing nodes are Token.NULL.
209 */
210
211 boolean matches( int type )
212 {
213 return isA(type);
214 }
215
216
217
218 /***
219 * Returns true if the node and it's first child match the specified
220 * types. Missing nodes are Token.NULL.
221 */
222
223 boolean matches( int type, int child1 )
224 {
225 return isA(type) && get(1, true).isA(child1);
226 }
227
228
229
230 /***
231 * Returns true if the node and it's first and second child match the
232 * specified types. Missing nodes are Token.NULL.
233 */
234
235 boolean matches( int type, int child1, int child2 )
236 {
237 return matches( type, child1 ) && get(2, true).isA(child2);
238 }
239
240
241
242 /***
243 * Returns true if the node and it's first three children match the
244 * specified types. Missing nodes are Token.NULL.
245 */
246
247 boolean matches( int type, int child1, int child2, int child3 )
248 {
249 return matches( type, child1, child2 ) && get(3, true).isA(child3);
250 }
251
252
253
254 /***
255 * Returns true if the node an it's first four children match the
256 * specified types. Missing nodes have type Types.NULL.
257 */
258
259 boolean matches( int type, int child1, int child2, int child3, int child4 )
260 {
261 return matches( type, child1, child2, child3 ) && get(4, true).isA(child4);
262 }
263
264
265
266
267
268
269
270
271
272 /***
273 * Returns true if the node is completely empty (no root, even).
274 */
275
276 public boolean isEmpty()
277 {
278 return false;
279 }
280
281
282
283 /***
284 * Returns the number of elements in the node (including root).
285 */
286
287 public abstract int size();
288
289
290
291 /***
292 * Returns true if the node has any non-root elements.
293 */
294
295 public boolean hasChildren()
296 {
297 return children() > 0;
298 }
299
300
301
302 /***
303 * Returns the number of non-root elements in the node.
304 */
305
306 public int children()
307 {
308 int size = size();
309 if( size > 1 )
310 {
311 return size - 1;
312 }
313 return 0;
314 }
315
316
317
318 /***
319 * Returns the specified element, or null.
320 */
321
322 public abstract CSTNode get( int index );
323
324
325
326 /***
327 * Returns the specified element, or Token.NULL if
328 * safe is set and the specified element is null (or doesn't
329 * exist).
330 */
331
332 public CSTNode get( int index, boolean safe )
333 {
334 CSTNode element = get( index );
335
336 if( element == null && safe )
337 {
338 element = Token.NULL;
339 }
340
341 return element;
342 }
343
344
345
346 /***
347 * Returns the root of the node. By convention, all nodes have
348 * a Token as the first element (or root), which indicates the type
349 * of the node. May return null if the node <code>isEmpty()</code>.
350 */
351
352 public abstract Token getRoot();
353
354
355
356 /***
357 * Returns the root of the node, the Token that indicates it's
358 * type. Returns a Token.NULL if safe and the actual root is null.
359 */
360
361 public Token getRoot( boolean safe )
362 {
363 Token root = getRoot();
364
365 if( root == null && safe )
366 {
367 root = Token.NULL;
368 }
369
370 return root;
371 }
372
373
374
375 /***
376 * Returns the text of the root. Uses <code>getRoot(true)</code>
377 * to get the root, so you will only receive null in return if the
378 * root token returns it.
379 */
380
381 public String getRootText()
382 {
383 Token root = getRoot( true );
384 return root.getText();
385 }
386
387
388
389 /***
390 * Returns a description of the node.
391 */
392
393 public String getDescription()
394 {
395 return Types.getDescription( getMeaning() );
396 }
397
398
399
400 /***
401 * Returns the starting line of the node. Returns -1
402 * if not known.
403 */
404
405 public int getStartLine()
406 {
407 return getRoot(true).getStartLine();
408 }
409
410
411
412 /***
413 * Returns the starting column of the node. Returns -1
414 * if not known.
415 */
416
417 public int getStartColumn()
418 {
419 return getRoot(true).getStartColumn();
420 }
421
422
423
424 /***
425 * Marks the node a complete expression. Not all nodes support
426 * this operation!
427 */
428
429 public void markAsExpression()
430 {
431 throw new GroovyBugError( "markAsExpression() not supported for this CSTNode type" );
432 }
433
434
435
436 /***
437 * Returns true if the node is a complete expression.
438 */
439
440 public boolean isAnExpression()
441 {
442 return isA(Types.SIMPLE_EXPRESSION);
443 }
444
445
446
447
448
449
450
451
452
453 /***
454 * Adds an element to the node. Returns the element for convenience.
455 * Not all nodes support this operation!
456 */
457
458 public CSTNode add( CSTNode element )
459 {
460 throw new GroovyBugError( "add() not supported for this CSTNode type" );
461 }
462
463
464
465 /***
466 * Adds all children of the specified node to this one. Not all
467 * nodes support this operation!
468 */
469
470 public void addChildrenOf( CSTNode of )
471 {
472 for( int i = 1; i < of.size(); i++ )
473 {
474 add( of.get(i) );
475 }
476 }
477
478
479
480 /***
481 * Sets an element node in at the specified index. Returns the element
482 * for convenience. Not all nodes support this operation!
483 */
484
485 public CSTNode set( int index, CSTNode element )
486 {
487 throw new GroovyBugError( "set() not supported for this CSTNode type" );
488 }
489
490
491
492 /***
493 * Creates a <code>Reduction</code> from this node. Returns self if the
494 * node is already a <code>Reduction</code>.
495 */
496
497 public abstract Reduction asReduction();
498
499
500
501
502
503
504
505
506 /***
507 * Formats the node as a <code>String</code> and returns it.
508 */
509
510 public String toString()
511 {
512 StringWriter string = new StringWriter();
513 write( new PrintWriter(string) );
514
515 string.flush();
516 return string.toString();
517 }
518
519
520 /***
521 * Formats the node and writes it to the specified <code>Writer</code>.
522 */
523
524 public void write( PrintWriter writer )
525 {
526 write( writer, "" );
527 }
528
529
530 /***
531 * Formats the node and writes it to the specified <code>Writer</code>.
532 * The indent is prepended to each output line, and is increased for each
533 * recursion.
534 */
535
536 protected void write( PrintWriter writer, String indent )
537 {
538 writer.print( "(" );
539
540 if( !isEmpty() )
541 {
542 Token root = getRoot( true );
543 int type = root.getType();
544 int meaning = root.getMeaning();
545
546
547
548
549
550 writer.print( Types.getDescription(type) );
551
552 if( meaning != type )
553 {
554 writer.print( " as " );
555 writer.print( Types.getDescription(meaning) );
556 }
557
558 if( getStartLine() > -1 )
559 {
560 writer.print( " at " + getStartLine() + ":" + getStartColumn() );
561 }
562
563 String text = root.getText();
564 int length = text.length();
565 if( length > 0 )
566 {
567 writer.print( ": " );
568 if( length > 40 )
569 {
570 text = text.substring( 0, 17 ) + "..." + text.substring( length - 17, length );
571 }
572
573 writer.print( " \"" );
574 writer.print( text );
575 writer.print( "\" " );
576 }
577 else if( children() > 0 )
578 {
579 writer.print( ": " );
580 }
581
582
583
584
585
586
587 int count = size();
588 if( count > 1 )
589 {
590 writer.println( "" );
591
592 String indent1 = indent + " ";
593 String indent2 = indent + " ";
594 for( int i = 1; i < count; i++ )
595 {
596 writer.print( indent1 );
597 writer.print( i );
598 writer.print( ": " );
599
600 get( i, true ).write( writer, indent2 );
601 }
602
603 writer.print( indent );
604 }
605 }
606
607 if( indent.length() > 0 )
608 {
609 writer.println( ")" );
610 }
611 else
612 {
613 writer.print( ")" );
614 }
615 }
616 }