View Javadoc

    1   /*
    2    $Id: CSTNode.java,v 1.2 2005/04/12 15:04:59 jstrachan 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 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    // NODE IDENTIFICATION AND MEANING
    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   // TYPE SUGAR
    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   // MEMBER ACCESS
    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   // OPERATIONS
    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   // STRING CONVERSION
    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             // Display our type, text, and (optional) meaning
    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             // Recurse to display the children.
    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 }