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 }