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