View Javadoc

1   /*
2    $Id: CSTNode.java,v 1.1 2004/04/01 06:21:53 cpoirier Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
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    // NODE IDENTIFICATION AND MEANING
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   // TYPE SUGAR
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   // MEMBER ACCESS
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   // OPERATIONS
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   // STRING CONVERSION
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             // Display our type, text, and (optional) meaning
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             // Recurse to display the children.
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 }