Package de.bokelberg.flex.parser

Source Code of de.bokelberg.flex.parser.AS3Parser

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.bokelberg.flex.parser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.adobe.ac.pmd.files.impl.FileUtils;
import com.adobe.ac.pmd.parser.IAS3Parser;
import com.adobe.ac.pmd.parser.IParserNode;
import com.adobe.ac.pmd.parser.KeyWords;
import com.adobe.ac.pmd.parser.NodeKind;
import com.adobe.ac.pmd.parser.Operators;
import com.adobe.ac.pmd.parser.exceptions.NullTokenException;
import com.adobe.ac.pmd.parser.exceptions.TokenException;
import com.adobe.ac.pmd.parser.exceptions.UnExpectedTokenException;
import com.adobe.ac.pmd.parser.exceptions.UnExpectedTokenException.Position;

import de.bokelberg.flex.parser.AS3Scanner.Token;

/**
* @author xagnetti
*/
public class AS3Parser implements IAS3Parser
{
   /**
    *
    */
   public static final String  ASDOC_COMMENT          = "/**";
   /**
    *
    */
   public static final String  MULTIPLE_LINES_COMMENT = "/*";
   /**
    *
    */
   public static final String  NEW_LINE               = "\n";
   /**
    *
    */
   public static final String  SINGLE_LINE_COMMENT    = "//";
   private static final String VECTOR                 = "Vector";
   private Node                currentAsDoc;
   private Node                currentFunctionNode;
   private Node                currentMultiLineComment;
   private String              fileName;
   private boolean             isInFor;
   private AS3Scanner          scn;
   private Token               tok;

   /**
    *
    */
   public AS3Parser()
   {
      this.scn = new AS3Scanner();
      isInFor = false;
   }

   /*
    * (non-Javadoc)
    * @see de.bokelberg.flex.parser.IAS3Parser#buildAst(java.lang.String)
    */
   public final IParserNode buildAst( final String filePath ) throws IOException,
                                                             TokenException
   {
      return parseLines( filePath,
                         FileUtils.readLines( new File( filePath ) ) );
   }

   /*
    * (non-Javadoc)
    * @see com.adobe.ac.pmd.parser.IAS3Parser#buildAst(java.lang.String,
    * java.lang.String[])
    */
   public final IParserNode buildAst( final String filePath,
                                      final String[] scriptBlockLines ) throws TokenException
   {
      return parseLines( filePath,
                         scriptBlockLines );
   }

   /**
    * @return
    */
   final AS3Scanner getScn()
   {
      return scn;
   }

   final void nextToken() throws TokenException
   {
      nextToken( false );
   }

   /**
    * Get the next token Skip comments and newlines for now In the end we want
    * to keep them though.
    *
    * @throws TokenException
    */
   final void nextToken( final boolean ignoreDocumentation ) throws TokenException
   {
      do
      {
         if ( ignoreDocumentation )
         {
            nextTokenIgnoringDocumentation();
         }
         else
         {
            nextTokenAllowNewLine();
         }
      }
      while ( tok.getText().equals( NEW_LINE ) );
   }

   /**
    * tok is first content token
    *
    * @throws TokenException
    */
   final Node parseClassContent() throws TokenException
   {
      final Node result = Node.create( NodeKind.CONTENT,
                                       tok.getLine(),
                                       tok.getColumn() );
      final List< Token > modifiers = new ArrayList< Token >();
      final List< Node > meta = new ArrayList< Node >();

      while ( !tokIs( Operators.RIGHT_CURLY_BRACKET ) )
      {
         if ( tokIs( Operators.LEFT_CURLY_BRACKET ) )
         {
            result.addChild( parseBlock() );
         }
         if ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
         {
            meta.add( parseMetaData() );
         }
         else if ( tokIs( KeyWords.VAR ) )
         {
            parseClassField( result,
                             modifiers,
                             meta );
         }
         else if ( tokIs( KeyWords.CONST ) )
         {
            parseClassConstant( result,
                                modifiers,
                                meta );
         }
         else if ( tokIs( KeyWords.IMPORT ) )
         {
            result.addChild( parseImport() );
         }
         else if ( tokIs( KeyWords.FUNCTION ) )
         {
            parseClassFunctions( result,
                                 modifiers,
                                 meta );
         }
         else
         {
            tryToParseCommentNode( result,
                                   modifiers );
         }
      }
      return result;
   }

   /**
    * tok is empty, since nextToken has not been called before
    *
    * @throws UnExpectedTokenException
    */
   final Node parseCompilationUnit() throws TokenException
   {
      final Node result = Node.create( NodeKind.COMPILATION_UNIT,
                                       -1,
                                       -1 );

      nextTokenIgnoringDocumentation();
      if ( tokIs( KeyWords.PACKAGE ) )
      {
         result.addChild( parsePackage() );
      }
      result.addChild( parsePackageContent() );
      return result;
   }

   /**
    * @return
    * @throws TokenException
    */
   final IParserNode parseExpression() throws TokenException
   {
      return parseAssignmentExpression();
   }

   /**
    * tok is first content token
    *
    * @throws TokenException
    */
   final Node parseInterfaceContent() throws TokenException
   {
      final Node result = Node.create( NodeKind.CONTENT,
                                       tok.getLine(),
                                       tok.getColumn() );
      while ( !tokIs( Operators.RIGHT_CURLY_BRACKET ) )
      {
         if ( tokIs( KeyWords.IMPORT ) )
         {
            result.addChild( parseImport() );
         }
         else if ( tokIs( KeyWords.FUNCTION ) )
         {
            result.addChild( parseFunctionSignature() );
         }
         else if ( tokIs( KeyWords.INCLUDE )
               || tokIs( KeyWords.INCLUDE_AS2 ) )
         {
            result.addChild( parseIncludeExpression() );
         }
         else if ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
         {
            while ( !tokIs( Operators.RIGHT_SQUARE_BRACKET ) )
            {
               nextToken();
            }
            nextToken();
         }
         else
         {
            tryToParseCommentNode( result,
                                   null );
         }
      }
      return result;
   }

   /**
    * tok is first token of content
    *
    * @throws UnExpectedTokenException
    */
   final Node parsePackageContent() throws TokenException
   {
      final Node result = Node.create( NodeKind.CONTENT,
                                       tok.getLine(),
                                       tok.getColumn() );
      final List< Token > modifiers = new ArrayList< Token >();
      final List< Node > meta = new ArrayList< Node >();

      while ( !tokIs( Operators.RIGHT_CURLY_BRACKET )
            && !tokIs( KeyWords.EOF ) )
      {
         if ( tokIs( KeyWords.IMPORT ) )
         {
            result.addChild( parseImport() );
         }
         else if ( tokIs( KeyWords.USE ) )
         {
            result.addChild( parseUse() );
         }
         else if ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
         {
            meta.add( parseMetaData() );
         }
         else if ( tokIs( KeyWords.CLASS ) )
         {
            result.addChild( parseClass( meta,
                                         modifiers ) );

            modifiers.clear();
            meta.clear();
         }
         else if ( tokIs( KeyWords.INTERFACE ) )
         {
            result.addChild( parseInterface( meta,
                                             modifiers ) );
            modifiers.clear();
            meta.clear();
         }
         else if ( tokIs( KeyWords.FUNCTION ) )
         {
            parseClassFunctions( result,
                                 modifiers,
                                 meta );
         }
         else if ( tok.getText().startsWith( ASDOC_COMMENT ) )
         {
            currentAsDoc = Node.create( NodeKind.AS_DOC,
                                        tok.getLine(),
                                        tok.getColumn(),
                                        tok.getText() );
            nextToken();
         }
         else if ( tok.getText().startsWith( MULTIPLE_LINES_COMMENT ) )
         {
            currentMultiLineComment = Node.create( NodeKind.MULTI_LINE_COMMENT,
                                                   tok.getLine(),
                                                   tok.getColumn(),
                                                   tok.getText() );
            nextToken();
         }
         else
         {
            modifiers.add( tok );
            nextTokenIgnoringDocumentation();
         }
      }
      return result;
   }

   /**
    * @return
    * @throws TokenException
    */
   final Node parsePrimaryExpression() throws TokenException
   {
      Node result = Node.create( NodeKind.PRIMARY,
                                 tok.getLine(),
                                 tok.getColumn(),
                                 tok.getText() );

      if ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
      {
         result.addChild( parseArrayLiteral() );
      }
      else if ( tokIs( Operators.LEFT_CURLY_BRACKET ) )
      {
         result.addChild( parseObjectLiteral() );
      }
      else if ( tokIs( KeyWords.FUNCTION ) )
      {
         result.addChild( parseLambdaExpression() );
      }
      else if ( tokIs( KeyWords.NEW ) )
      {
         result = parseNewExpression();
      }
      else if ( tokIs( Operators.LEFT_PARENTHESIS ) )
      {
         result.addChild( parseEncapsulatedExpression() );
      }
      else
      {
         nextToken( true );
      }
      return result;
   }

   /**
    * tok is the first token of a statement
    *
    * @throws TokenException
    */
   final IParserNode parseStatement() throws TokenException
   {
      IParserNode result;

      if ( tokIs( KeyWords.FOR ) )
      {
         result = parseFor();
      }
      else if ( tokIs( KeyWords.IF ) )
      {
         result = parseIf();
      }
      else if ( tokIs( KeyWords.SWITCH ) )
      {
         result = parseSwitch();
      }
      else if ( tokIs( KeyWords.DO ) )
      {
         result = parseDo();
      }
      else if ( tokIs( KeyWords.WHILE ) )
      {
         result = parseWhile();
      }
      else if ( tokIs( KeyWords.TRY ) )
      {
         result = parseTry();
      }
      else if ( tokIs( KeyWords.CATCH ) )
      {
         result = parseCatch();
      }
      else if ( tokIs( KeyWords.FINALLY ) )
      {
         result = parseFinally();
      }
      else if ( tokIs( Operators.LEFT_CURLY_BRACKET ) )
      {
         result = parseBlock();
      }
      else if ( tokIs( KeyWords.VAR ) )
      {
         result = parseVar();
      }
      else if ( tokIs( KeyWords.CONST ) )
      {
         result = parseConst();
      }
      else if ( tokIs( KeyWords.RETURN ) )
      {
         result = parseReturnStatement();
      }
      else if ( tokIs( Operators.SEMI_COLUMN ) )
      {
         result = parseEmptyStatement();
      }
      else
      {
         result = parseExpressionList();
         skip( Operators.SEMI_COLUMN );
      }
      return result;
   }

   /**
    * @return
    * @throws TokenException
    */
   final Node parseUnaryExpression() throws TokenException
   {
      Node result;
      if ( tokIs( Operators.INCREMENT ) )
      {
         nextToken();
         result = Node.create( NodeKind.PRE_INC,
                               tok.getLine(),
                               tok.getColumn(),
                               parseUnaryExpression() );
      }
      else if ( tokIs( Operators.DECREMENT ) )
      {
         nextToken();
         result = Node.create( NodeKind.PRE_DEC,
                               tok.getLine(),
                               tok.getColumn(),
                               parseUnaryExpression() );
      }
      else if ( tokIs( Operators.MINUS ) )
      {
         nextToken();
         result = Node.create( NodeKind.MINUS,
                               tok.getLine(),
                               tok.getColumn(),
                               parseUnaryExpression() );
      }
      else if ( tokIs( Operators.PLUS )
            || tokIs( Operators.PLUS_AS2 ) )
      {
         nextToken();
         result = Node.create( NodeKind.PLUS,
                               tok.getLine(),
                               tok.getColumn(),
                               parseUnaryExpression() );
      }
      else
      {
         result = parseUnaryExpressionNotPlusMinus();
      }
      return result;
   }

   private IParserNode collectVarListContent( final Node result ) throws TokenException
   {
      result.addChild( parseNameTypeInit() );
      while ( tokIs( Operators.COMMA ) )
      {
         nextToken( true );
         result.addChild( parseNameTypeInit() );
      }
      return result;
   }

   private void consume( final KeyWords keyword ) throws TokenException
   {
      consume( keyword.toString() );
   }

   private void consume( final Operators operator ) throws TokenException
   {
      consume( operator.toString() );
   }

   /**
    * Compare the current token to the parameter. If it equals, get the next
    * token. If not, throw a runtime exception.
    *
    * @param text
    * @throws UnExpectedTokenException
    */
   private void consume( final String text ) throws TokenException
   {
      while ( tok.getText().startsWith( "//" ) )
      {
         nextToken();
      }

      if ( !tokIs( text ) )
      {
         throw new UnExpectedTokenException( tok.getText(),
                                             new Position( tok.getLine(), tok.getColumn() ),
                                             fileName,
                                             text );
      }
      nextToken();
   }

   private Node convertMeta( final List< Node > metadataList )
   {
      if ( metadataList == null
            || metadataList.isEmpty() )
      {
         return null;
      }

      final Node result = Node.create( NodeKind.META_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );

      for ( final Node metadataNode : metadataList )
      {
         result.addChild( metadataNode );
      }
      return result;
   }

   private Node convertModifiers( final List< Token > modifierList )
   {
      if ( modifierList == null )
      {
         return null;
      }

      final Node result = Node.create( NodeKind.MOD_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );

      for ( final Token modifierToken : modifierList )
      {
         result.addChild( NodeKind.MODIFIER,
                          tok.getLine(),
                          tok.getColumn(),
                          modifierToken.getText() );
      }
      return result;
   }

   private Node[] doParseSignature() throws TokenException
   {
      consume( KeyWords.FUNCTION );

      Node type = Node.create( NodeKind.TYPE,
                               tok.getLine(),
                               tok.getColumn(),
                               KeyWords.FUNCTION.toString() );
      if ( tokIs( KeyWords.SET )
            || tokIs( KeyWords.GET ) )
      {
         type = Node.create( NodeKind.TYPE,
                             tok.getLine(),
                             tok.getColumn(),
                             tok.getText() );
         nextToken(); // set or get
      }
      final Node name = Node.create( NodeKind.NAME,
                                     tok.getLine(),
                                     tok.getColumn(),
                                     tok.getText() );
      nextToken(); // name
      final Node params = parseParameterList();
      final Node returnType = parseOptionalType();
      return new Node[]
      { type,
                  name,
                  params,
                  returnType };
   }

   private NodeKind findFunctionTypeFromSignature( final Node[] signature )
   {
      for ( final Node node : signature )
      {
         if ( node.is( NodeKind.TYPE ) )
         {
            if ( node.getStringValue().equals( "set" ) )
            {
               return NodeKind.SET;
            }
            if ( node.getStringValue().equals( "get" ) )
            {
               return NodeKind.GET;
            }
            return NodeKind.FUNCTION;
         }
      }
      return NodeKind.FUNCTION;
   }

   /**
    * Get the next token Skip comments but keep newlines We need this method for
    * beeing able to decide if a returnStatement has an expression
    *
    * @throws UnExpectedTokenException
    */
   private void nextTokenAllowNewLine() throws TokenException
   {
      do
      {
         tok = scn.nextToken();

         if ( tok == null )
         {
            throw new NullTokenException( fileName );

         }
         if ( tok.getText() == null )
         {
            throw new NullTokenException( fileName );
         }
      }
      while ( tok.getText().startsWith( SINGLE_LINE_COMMENT ) );
   }

   private void nextTokenIgnoringDocumentation() throws TokenException
   {
      do
      {
         nextToken();
      }
      while ( tok.getText().startsWith( MULTIPLE_LINES_COMMENT ) );
   }

   private IParserNode parseAdditiveExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.ADD,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseMultiplicativeExpression() );
      while ( tokIs( Operators.PLUS )
            || tokIs( Operators.PLUS_AS2 ) || tokIs( Operators.MINUS ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseMultiplicativeExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   // ------------------------------------------------------------------------
   // language specific recursive descent parsing
   // ------------------------------------------------------------------------

   private IParserNode parseAndExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.AND,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseBitwiseOrExpression() );
      while ( tokIs( Operators.AND )
            || tokIs( Operators.AND_AS2 ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseBitwiseOrExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   /**
    * tok is ( exit tok is first token after )
    */
   private Node parseArgumentList() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );
      final Node result = Node.create( NodeKind.ARGUMENTS,
                                       tok.getLine(),
                                       tok.getColumn() );
      while ( !tokIs( Operators.RIGHT_PARENTHESIS ) )
      {
         result.addChild( parseExpression() );
         skip( Operators.COMMA );
      }
      consume( Operators.RIGHT_PARENTHESIS );
      return result;
   }

   private Node parseArrayAccessor( final Node node ) throws TokenException
   {
      final Node result = Node.create( NodeKind.ARRAY_ACCESSOR,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( node );
      while ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
      {
         nextToken( true );
         result.addChild( parseExpression() );
         consume( Operators.RIGHT_SQUARE_BRACKET );
      }
      return result;
   }

   /**
    * tok is [
    */
   private IParserNode parseArrayLiteral() throws TokenException
   {
      final Node result = Node.create( NodeKind.ARRAY,
                                       tok.getLine(),
                                       tok.getColumn() );
      consume( Operators.LEFT_SQUARE_BRACKET );
      while ( !tokIs( Operators.RIGHT_SQUARE_BRACKET ) )
      {
         result.addChild( parseExpression() );
         skip( Operators.COMMA );
      }
      consume( Operators.RIGHT_SQUARE_BRACKET );
      return result;
   }

   private IParserNode parseAssignmentExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.ASSIGN,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseConditionalExpression() );
      while ( tokIs( Operators.EQUAL )
            || tokIs( Operators.PLUS_EQUAL ) || tokIs( Operators.MINUS_EQUAL )
            || tokIs( Operators.TIMES_EQUAL ) || tokIs( Operators.DIVIDED_EQUAL )
            || tokIs( Operators.MODULO_EQUAL ) || tokIs( Operators.AND_EQUAL ) || tokIs( Operators.OR_EQUAL )
            || tokIs( Operators.XOR_EQUAL ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private IParserNode parseBitwiseAndExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.B_AND,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseEqualityExpression() );
      while ( tokIs( Operators.B_AND ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseEqualityExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private IParserNode parseBitwiseOrExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.B_OR,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseBitwiseXorExpression() );
      while ( tokIs( Operators.B_OR ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseBitwiseXorExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private IParserNode parseBitwiseXorExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.B_XOR,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseBitwiseAndExpression() );
      while ( tokIs( Operators.B_XOR ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseBitwiseAndExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private Node parseBlock() throws TokenException
   {
      return parseBlock( Node.create( NodeKind.BLOCK,
                                      tok.getLine(),
                                      tok.getColumn() ) );
   }

   private Node parseBlock( final Node result ) throws TokenException
   {
      consume( Operators.LEFT_CURLY_BRACKET );

      while ( !tokIs( Operators.RIGHT_CURLY_BRACKET ) )
      {
         if ( tok.getText().startsWith( MULTIPLE_LINES_COMMENT ) )
         {
            currentFunctionNode.addChild( Node.create( NodeKind.MULTI_LINE_COMMENT,
                                                       tok.getLine(),
                                                       tok.getColumn(),
                                                       tok.getText() ) );
            nextToken();
         }
         else
         {
            result.addChild( parseStatement() );
         }
      }
      consume( Operators.RIGHT_CURLY_BRACKET );
      return result;
   }

   /**
    * tok is catch
    *
    * @throws TokenException
    */
   private Node parseCatch() throws TokenException
   {
      consume( KeyWords.CATCH );
      consume( Operators.LEFT_PARENTHESIS );
      final Node result = Node.create( NodeKind.CATCH,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       Node.create( NodeKind.NAME,
                                                    tok.getLine(),
                                                    tok.getColumn(),
                                                    tok.getText() ) );
      nextToken( true ); // name
      if ( tokIs( Operators.COLUMN ) )
      {
         nextToken( true ); // :
         result.addChild( Node.create( NodeKind.TYPE,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true ); // type
      }
      consume( Operators.RIGHT_PARENTHESIS );
      result.addChild( parseBlock() );
      return result;
   }

   /**
    * tok is class
    *
    * @param meta
    * @param modifier
    * @throws TokenException
    */
   private Node parseClass( final List< Node > meta,
                            final List< Token > modifier ) throws TokenException
   {
      consume( KeyWords.CLASS );

      final Node result = Node.create( NodeKind.CLASS,
                                       tok.getLine(),
                                       tok.getColumn() );

      if ( currentAsDoc != null )
      {
         result.addChild( currentAsDoc );
         currentAsDoc = null;
      }
      if ( currentMultiLineComment != null )
      {
         result.addChild( currentMultiLineComment );
         currentMultiLineComment = null;
      }

      result.addChild( NodeKind.NAME,
                       tok.getLine(),
                       tok.getColumn(),
                       parseQualifiedName( true ) );

      result.addChild( convertMeta( meta ) );
      result.addChild( convertModifiers( modifier ) );

      // nextToken( true ); // name

      do
      {
         if ( tokIs( KeyWords.EXTENDS ) )
         {
            nextToken( true ); // extends
            result.addChild( NodeKind.EXTENDS,
                             tok.getLine(),
                             tok.getColumn(),
                             parseQualifiedName( false ) );
         }
         else if ( tokIs( KeyWords.IMPLEMENTS ) )
         {
            result.addChild( parseImplementsList() );
         }
      }
      while ( !tokIs( Operators.LEFT_CURLY_BRACKET ) );
      consume( Operators.LEFT_CURLY_BRACKET );
      result.addChild( parseClassContent() );
      consume( Operators.RIGHT_CURLY_BRACKET );
      return result;
   }

   private void parseClassConstant( final Node result,
                                    final List< Token > modifiers,
                                    final List< Node > meta ) throws TokenException
   {
      result.addChild( parseConstList( meta,
                                       modifiers ) );
      if ( tokIs( Operators.SEMI_COLUMN ) )
      {
         nextToken();
      }
      meta.clear();
      modifiers.clear();
   }

   private void parseClassField( final Node result,
                                 final List< Token > modifiers,
                                 final List< Node > meta ) throws TokenException
   {
      final Node varList = parseVarList( meta,
                                         modifiers );
      result.addChild( varList );
      if ( currentAsDoc != null )
      {
         varList.addChild( currentAsDoc );
         currentAsDoc = null;
      }
      if ( currentMultiLineComment != null )
      {
         result.addChild( currentMultiLineComment );
         currentMultiLineComment = null;
      }
      if ( tokIs( Operators.SEMI_COLUMN ) )
      {
         nextToken();
      }
      meta.clear();
      modifiers.clear();
   }

   private void parseClassFunctions( final Node result,
                                     final List< Token > modifiers,
                                     final List< Node > meta ) throws TokenException
   {
      result.addChild( parseFunction( meta,
                                      modifiers ) );
      meta.clear();
      modifiers.clear();
   }

   /**
    * tok is (
    *
    * @throws TokenException
    */
   private Node parseCondition() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );
      final Node result = Node.create( NodeKind.CONDITION,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseExpression() );
      consume( Operators.RIGHT_PARENTHESIS );
      return result;
   }

   private IParserNode parseConditionalExpression() throws TokenException
   {
      final IParserNode result = parseOrExpression();
      if ( tokIs( Operators.QUESTION_MARK ) )
      {
         final Node conditional = Node.create( NodeKind.CONDITIONAL,
                                               tok.getLine(),
                                               tok.getColumn(),
                                               result );
         nextToken( true ); // ?
         conditional.addChild( parseExpression() );
         nextToken( true ); // :
         conditional.addChild( parseExpression() );

         return conditional;
      }
      return result;
   }

   private Node parseConst() throws TokenException
   {
      Node result;
      result = parseConstList( null,
                               null );
      skip( Operators.SEMI_COLUMN );
      return result;
   }

   /**
    * tok is const
    *
    * @param modifiers
    * @param meta
    * @throws TokenException
    */
   private Node parseConstList( final List< Node > meta,
                                final List< Token > modifiers ) throws TokenException
   {
      consume( KeyWords.CONST );
      final Node result = Node.create( NodeKind.CONST_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( convertMeta( meta ) );
      result.addChild( convertModifiers( modifiers ) );
      collectVarListContent( result );
      return result;
   }

   private Node parseDecrement( final Node node ) throws TokenException
   {
      nextToken( true );
      final Node result = Node.create( NodeKind.POST_DEC,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( node );
      return result;
   }

   /**
    * tok is do
    *
    * @throws TokenException
    */
   private Node parseDo() throws TokenException
   {
      consume( KeyWords.DO );
      final Node result = Node.create( NodeKind.DO,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseStatement() );
      consume( KeyWords.WHILE );
      result.addChild( parseCondition() );
      if ( tokIs( Operators.SEMI_COLUMN ) )
      {
         nextToken( true );
      }
      return result;
   }

   private Node parseDot( final Node node ) throws TokenException
   {
      nextToken();
      if ( tokIs( Operators.LEFT_PARENTHESIS ) )
      {
         nextToken();
         final Node result = Node.create( NodeKind.E4X_FILTER,
                                          tok.getLine(),
                                          tok.getColumn() );
         result.addChild( node );
         result.addChild( parseExpression() );
         consume( Operators.RIGHT_PARENTHESIS );
         return result;
      }
      else if ( tokIs( Operators.TIMES ) )
      {
         final Node result = Node.create( NodeKind.E4X_STAR,
                                          tok.getLine(),
                                          tok.getColumn() );
         result.addChild( node );
         return result;
      }
      final Node result = Node.create( NodeKind.DOT,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( node );
      result.addChild( parseExpression() );
      return result;
   }

   private Node parseEmptyStatement() throws TokenException
   {
      Node result;
      result = Node.create( NodeKind.STMT_EMPTY,
                            tok.getLine(),
                            tok.getColumn(),
                            Operators.SEMI_COLUMN.toString() );
      nextToken( true );
      return result;
   }

   private Node parseEncapsulatedExpression() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );
      final Node result = Node.create( NodeKind.ENCAPSULATED,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( parseExpressionList() );

      consume( Operators.RIGHT_PARENTHESIS );

      return result;
   }

   private IParserNode parseEqualityExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.EQUALITY,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseRelationalExpression() );
      while ( tokIs( Operators.DOUBLE_EQUAL )
            || tokIs( Operators.DOUBLE_EQUAL_AS2 ) || tokIs( Operators.STRICTLY_EQUAL )
            || tokIs( Operators.NON_EQUAL ) || tokIs( Operators.NON_EQUAL_AS2_1 )
            || tokIs( Operators.NON_EQUAL_AS2_2 ) || tokIs( Operators.NON_STRICTLY_EQUAL ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseRelationalExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private IParserNode parseExpressionList() throws TokenException
   {
      final Node result = Node.create( NodeKind.EXPR_LIST,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseAssignmentExpression() );
      while ( tokIs( Operators.COMMA ) )
      {
         nextToken( true );
         result.addChild( parseAssignmentExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private Node parseFinally() throws TokenException
   {
      Node result;
      nextToken( true );
      result = Node.create( NodeKind.FINALLY,
                            tok.getLine(),
                            tok.getColumn(),
                            parseBlock() );
      return result;
   }

   /**
    * tok is for
    *
    * @throws TokenException
    */
   private Node parseFor() throws TokenException
   {
      consume( KeyWords.FOR );

      if ( tokIs( KeyWords.EACH ) )
      {
         nextToken();
         return parseForEach();
      }
      else
      {
         return parseTraditionalFor();
      }
   }

   /**
    * tok is ( for each( var obj : Type in List )
    *
    * @throws TokenException
    */
   private Node parseForEach() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );

      final Node result = Node.create( NodeKind.FOREACH,
                                       tok.getLine(),
                                       tok.getColumn() );
      if ( tokIs( KeyWords.VAR ) )
      {
         final Node var = Node.create( NodeKind.VAR,
                                       tok.getLine(),
                                       tok.getColumn() );
         nextToken();
         var.addChild( parseNameTypeInit() );
         result.addChild( var );
      }
      else
      {
         result.addChild( NodeKind.NAME,
                          tok.getLine(),
                          tok.getColumn(),
                          tok.getText() );
         // names allowed?
         nextToken();
      }
      nextToken(); // in
      result.addChild( NodeKind.IN,
                       tok.getLine(),
                       tok.getColumn(),
                       parseExpression() );
      consume( Operators.RIGHT_PARENTHESIS );
      result.addChild( parseStatement() );
      return result;
   }

   private Node parseForIn( final Node result ) throws TokenException
   {
      nextToken();
      result.addChild( NodeKind.IN,
                       tok.getLine(),
                       tok.getColumn(),
                       parseExpression() );
      result.setId( NodeKind.FORIN );
      return result;
   }

   /**
    * tok is function
    *
    * @param modifiers
    * @param meta
    * @throws TokenException
    */
   private Node parseFunction( final List< Node > meta,
                               final List< Token > modifiers ) throws TokenException
   {
      final Node[] signature = doParseSignature();
      final Node result = Node.create( findFunctionTypeFromSignature( signature ),
                                       tok.getLine(),
                                       tok.getColumn(),
                                       signature[ 0 ].getStringValue() );

      if ( currentAsDoc != null )
      {
         result.addChild( currentAsDoc );
         currentAsDoc = null;
      }
      if ( currentMultiLineComment != null )
      {
         result.addChild( currentMultiLineComment );
         currentMultiLineComment = null;
      }
      result.addChild( convertMeta( meta ) );
      result.addChild( convertModifiers( modifiers ) );
      result.addChild( signature[ 1 ] );
      result.addChild( signature[ 2 ] );
      result.addChild( signature[ 3 ] );
      if ( tokIs( Operators.SEMI_COLUMN ) )
      {
         consume( Operators.SEMI_COLUMN );
      }
      else
      {
         result.addChild( parseFunctionBlock() );
      }
      currentFunctionNode = null;
      return result;
   }

   /**
    * tok is { exit tok is the first tok after }
    *
    * @throws TokenException
    * @throws TokenException
    */

   private Node parseFunctionBlock() throws TokenException
   {
      final Node block = Node.create( NodeKind.BLOCK,
                                      tok.getLine(),
                                      tok.getColumn() );

      currentFunctionNode = block;

      parseBlock( block );

      return block;
   }

   private Node parseFunctionCall( final Node node ) throws TokenException
   {
      final Node result = Node.create( NodeKind.CALL,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( node );
      while ( tokIs( Operators.LEFT_PARENTHESIS ) )
      {
         result.addChild( parseArgumentList() );
      }
      while ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
      {
         result.addChild( parseArrayLiteral() );
      }

      return result;
   }

   /**
    * tok is function exit tok is the first token after the optional ;
    *
    * @throws TokenException
    */
   private Node parseFunctionSignature() throws TokenException
   {
      final Node[] signature = doParseSignature();
      skip( Operators.SEMI_COLUMN );
      final Node result = Node.create( findFunctionTypeFromSignature( signature ),
                                       tok.getLine(),
                                       tok.getColumn(),
                                       signature[ 0 ].getStringValue() );
      result.addChild( signature[ 1 ] );
      result.addChild( signature[ 2 ] );
      result.addChild( signature[ 3 ] );
      return result;
   }

   /**
    * tok is if
    *
    * @throws TokenException
    */
   private Node parseIf() throws TokenException
   {
      consume( KeyWords.IF );
      final Node result = Node.create( NodeKind.IF,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseCondition() );
      result.addChild( parseStatement() );
      if ( tokIs( KeyWords.ELSE ) )
      {
         nextToken( true );
         result.addChild( parseStatement() );
      }
      return result;
   }

   /**
    * tok is implements implements a,b,c exit tok is the first token after the
    * list of qualfied names
    *
    * @throws TokenException
    */
   private Node parseImplementsList() throws TokenException
   {
      consume( KeyWords.IMPLEMENTS );

      final Node result = Node.create( NodeKind.IMPLEMENTS_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( NodeKind.IMPLEMENTS,
                       tok.getLine(),
                       tok.getColumn(),
                       parseQualifiedName( true ) );
      while ( tokIs( Operators.COMMA ) )
      {
         nextToken( true );
         result.addChild( NodeKind.IMPLEMENTS,
                          tok.getLine(),
                          tok.getColumn(),
                          parseQualifiedName( false ) );
      }
      return result;
   }

   /**
    * tok is import
    *
    * @throws TokenException
    */
   private Node parseImport() throws TokenException
   {
      consume( KeyWords.IMPORT );
      final Node result = Node.create( NodeKind.IMPORT,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseImportName() );
      skip( Operators.SEMI_COLUMN );
      return result;
   }

   /**
    * tok is the first part of a name the last part can be a star exit tok is
    * the first token, which doesn't belong to the name
    *
    * @throws TokenException
    */
   private String parseImportName() throws TokenException
   {
      final StringBuffer result = new StringBuffer();

      result.append( tok.getText() );
      nextToken();
      while ( tokIs( Operators.DOT ) )
      {
         result.append( Operators.DOT );
         nextToken(); // .
         result.append( tok.getText() );
         nextToken(); // part of name
      }
      return result.toString();
   }

   private IParserNode parseIncludeExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.INCLUDE,
                                       tok.getLine(),
                                       tok.getColumn() );
      if ( tokIs( KeyWords.INCLUDE ) )
      {
         consume( KeyWords.INCLUDE );
      }
      else if ( tokIs( KeyWords.INCLUDE_AS2 ) )
      {
         consume( KeyWords.INCLUDE_AS2 );
      }
      result.addChild( parseExpression() );
      return result;
   }

   private Node parseIncrement( final Node node ) throws TokenException
   {
      nextToken( true );
      final Node result = Node.create( NodeKind.POST_INC,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( node );
      return result;
   }

   /**
    * tok is interface
    *
    * @param meta
    * @param modifier
    * @throws TokenException
    */
   private Node parseInterface( final List< Node > meta,
                                final List< Token > modifier ) throws TokenException
   {
      consume( KeyWords.INTERFACE );
      final Node result = Node.create( NodeKind.INTERFACE,
                                       tok.getLine(),
                                       tok.getColumn() );

      if ( currentAsDoc != null )
      {
         result.addChild( currentAsDoc );
         currentAsDoc = null;
      }
      if ( currentMultiLineComment != null )
      {
         result.addChild( currentMultiLineComment );
         currentMultiLineComment = null;
      }
      result.addChild( NodeKind.NAME,
                       tok.getLine(),
                       tok.getColumn(),
                       parseQualifiedName( true ) );

      result.addChild( convertMeta( meta ) );
      result.addChild( convertModifiers( modifier ) );

      if ( tokIs( KeyWords.EXTENDS ) )
      {
         nextToken(); // extends
         result.addChild( NodeKind.EXTENDS,
                          tok.getLine(),
                          tok.getColumn(),
                          parseQualifiedName( false ) );
      }
      while ( tokIs( Operators.COMMA ) )
      {
         nextToken(); // comma
         result.addChild( NodeKind.EXTENDS,
                          tok.getLine(),
                          tok.getColumn(),
                          parseQualifiedName( false ) );
      }
      consume( Operators.LEFT_CURLY_BRACKET );
      result.addChild( parseInterfaceContent() );
      consume( Operators.RIGHT_CURLY_BRACKET );
      return result;
   }

   /**
    * tok is function
    *
    * @throws TokenException
    */
   private Node parseLambdaExpression() throws TokenException
   {
      consume( KeyWords.FUNCTION );
      Node result;

      if ( tok.getText().compareTo( "(" ) == 0 )
      {
         result = Node.create( NodeKind.LAMBDA,
                               tok.getLine(),
                               tok.getColumn() );
      }
      else
      {
         result = Node.create( NodeKind.FUNCTION,
                               tok.getLine(),
                               tok.getColumn(),
                               tok.getText() );
         nextToken( true );
      }
      result.addChild( parseParameterList() );
      result.addChild( parseOptionalType() );
      result.addChild( parseBlock() );
      return result;
   }

   private IParserNode parseLines( final String filePath,
                                   final String[] lines ) throws TokenException
   {
      setFileName( filePath );
      scn = new AS3Scanner();
      scn.setLines( lines );
      return parseCompilationUnit();
   }

   /**
    * tok is [ [id] [id ("test")] [id (name="test",type="a.b.c.Event")] exit
    * token is the first token after ]
    *
    * @throws TokenException
    */
   private Node parseMetaData() throws TokenException
   {
      final StringBuffer buffer = new StringBuffer();

      final int line = tok.getLine();
      final int column = tok.getColumn();

      consume( Operators.LEFT_SQUARE_BRACKET );
      while ( !tokIs( Operators.RIGHT_SQUARE_BRACKET ) )
      {
         if ( buffer.length() > 0 )
         {
            buffer.append( ' ' );
         }
         buffer.append( tok.getText() );
         nextToken();
      }
      skip( Operators.RIGHT_SQUARE_BRACKET );
      final Node metaDataNode = Node.create( NodeKind.META,
                                             line,
                                             column,
                                             buffer.toString() );

      return metaDataNode;
   }

   private IParserNode parseMultiplicativeExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.MULTIPLICATION,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseUnaryExpression() );
      while ( tokIs( Operators.TIMES )
            || tokIs( Operators.SLASH ) || tokIs( Operators.MODULO ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseUnaryExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private String parseNamespaceName() throws TokenException
   {
      final String name = tok.getText();
      nextToken(); // simple name for now
      return name;
   }

   private Node parseNameTypeInit() throws TokenException
   {
      final Node result = Node.create( NodeKind.NAME_TYPE_INIT,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( NodeKind.NAME,
                       tok.getLine(),
                       tok.getColumn(),
                       tok.getText() );
      nextToken( true ); // name
      result.addChild( parseOptionalType() );
      result.addChild( parseOptionalInit() );
      return result;
   }

   private Node parseNewExpression() throws TokenException
   {
      consume( KeyWords.NEW );

      final Node result = Node.create( NodeKind.NEW,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( parseExpression() ); // name
      if ( tokIs( Operators.VECTOR_START ) )
      {
         result.addChild( Node.create( NodeKind.VECTOR,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseVector() ) );
      }
      if ( tokIs( Operators.LEFT_PARENTHESIS ) )
      {
         result.addChild( parseArgumentList() );
      }
      return result;
   }

   /**
    * tok is {
    */
   private Node parseObjectLiteral() throws TokenException
   {
      final Node result = Node.create( NodeKind.OBJECT,
                                       tok.getLine(),
                                       tok.getColumn() );
      consume( Operators.LEFT_CURLY_BRACKET );
      while ( !tokIs( Operators.RIGHT_CURLY_BRACKET ) )
      {
         result.addChild( parseObjectLiteralPropertyDeclaration() );
         skip( Operators.COMMA );
      }
      consume( Operators.RIGHT_CURLY_BRACKET );
      return result;
   }

   /*
    * tok is name
    */
   private Node parseObjectLiteralPropertyDeclaration() throws TokenException
   {
      final Node result = Node.create( NodeKind.PROP,
                                       tok.getLine(),
                                       tok.getColumn() );
      final Node name = Node.create( NodeKind.NAME,
                                     tok.getLine(),
                                     tok.getColumn(),
                                     tok.getText() );
      result.addChild( name );
      nextToken(); // name
      consume( Operators.COLUMN );
      result.addChild( NodeKind.VALUE,
                       tok.getLine(),
                       tok.getColumn(),
                       parseExpression() );
      return result;
   }

   /**
    * if tok is "=" parse the expression otherwise do nothing
    *
    * @return
    */
   private Node parseOptionalInit() throws TokenException
   {
      Node result = null;
      if ( tokIs( Operators.EQUAL ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.INIT,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      return result;
   }

   /**
    * if tok is ":" parse the type otherwise do nothing
    *
    * @return
    * @throws TokenException
    */
   private Node parseOptionalType() throws TokenException
   {
      Node result = Node.create( NodeKind.TYPE,
                                 tok.getLine(),
                                 tok.getColumn(),
                                 "" );
      if ( tokIs( Operators.COLUMN ) )
      {
         nextToken( true );
         result = parseType();
      }
      return result;
   }

   private IParserNode parseOrExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.OR,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseAndExpression() );
      while ( tokIs( Operators.LOGICAL_OR )
            || tokIs( Operators.LOGICAL_OR_AS2 ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseAndExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   /**
    * tok is package
    *
    * @throws UnExpectedTokenException
    */
   private Node parsePackage() throws TokenException
   {
      consume( KeyWords.PACKAGE );

      final Node result = Node.create( NodeKind.PACKAGE,
                                       tok.getLine(),
                                       tok.getColumn() );
      final StringBuffer nameBuffer = new StringBuffer();

      while ( !tokIs( Operators.LEFT_CURLY_BRACKET ) )
      {
         nameBuffer.append( tok.getText() );
         nextToken();
      }
      result.addChild( NodeKind.NAME,
                       tok.getLine(),
                       tok.getColumn(),
                       nameBuffer.toString() );
      consume( Operators.LEFT_CURLY_BRACKET );
      result.addChild( parsePackageContent() );
      consume( Operators.RIGHT_CURLY_BRACKET );
      return result;
   }

   /**
    * tok is the name of a parameter or ...
    */
   private Node parseParameter() throws TokenException
   {
      final Node result = Node.create( NodeKind.PARAMETER,
                                       tok.getLine(),
                                       tok.getColumn() );
      if ( tokIs( Operators.REST_PARAMETERS ) )
      {
         nextToken( true ); // ...
         final Node rest = Node.create( NodeKind.REST,
                                        tok.getLine(),
                                        tok.getColumn(),
                                        tok.getText() );
         nextToken( true ); // rest
         result.addChild( rest );
      }
      else
      {
         result.addChild( parseNameTypeInit() );
      }
      return result;
   }

   /**
    * tok is (
    *
    * @throws TokenException
    */
   private Node parseParameterList() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );

      final Node result = Node.create( NodeKind.PARAMETER_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );
      while ( !tokIs( Operators.RIGHT_PARENTHESIS ) )
      {
         result.addChild( parseParameter() );
         if ( tokIs( Operators.COMMA ) )
         {
            nextToken( true );
         }
         else
         {
            break;
         }
      }
      consume( Operators.RIGHT_PARENTHESIS );
      return result;
   }

   /**
    * tok is first part of the name exit tok is the first token after the name
    *
    * @throws TokenException
    */
   private String parseQualifiedName( final boolean skipPackage ) throws TokenException
   {
      final StringBuffer buffer = new StringBuffer();

      buffer.append( tok.getText() );
      nextToken();
      while ( tokIs( Operators.DOT )
            || tokIs( Operators.DOUBLE_COLUMN ) )
      {
         buffer.append( tok.getText() );
         nextToken();
         buffer.append( tok.getText() );
         nextToken(); // name
      }

      if ( skipPackage )
      {
         return buffer.substring( buffer.lastIndexOf( Operators.DOT.toString() ) + 1 );
      }
      return buffer.toString();
   }

   private IParserNode parseRelationalExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.RELATION,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseShiftExpression() );
      while ( tokIs( Operators.INFERIOR )
            || tokIs( Operators.INFERIOR_AS2 ) || tokIs( Operators.INFERIOR_OR_EQUAL )
            || tokIs( Operators.INFERIOR_OR_EQUAL_AS2 ) || tokIs( Operators.SUPERIOR )
            || tokIs( Operators.SUPERIOR_AS2 ) || tokIs( Operators.SUPERIOR_OR_EQUAL )
            || tokIs( Operators.SUPERIOR_OR_EQUAL_AS2 ) || tokIs( KeyWords.IS ) || tokIs( KeyWords.IN )
            && !isInFor || tokIs( KeyWords.AS ) || tokIs( KeyWords.INSTANCE_OF ) )
      {
         if ( !tokIs( KeyWords.AS ) )
         {
            result.addChild( Node.create( NodeKind.OP,
                                          tok.getLine(),
                                          tok.getColumn(),
                                          tok.getText() ) );
         }
         else
         {
            result.addChild( Node.create( NodeKind.AS,
                                          tok.getLine(),
                                          tok.getColumn(),
                                          tok.getText() ) );
         }
         nextToken( true );
         result.addChild( parseShiftExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   private IParserNode parseReturnStatement() throws TokenException
   {
      Node result;

      nextTokenAllowNewLine();
      if ( tokIs( NEW_LINE )
            || tokIs( Operators.SEMI_COLUMN ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.RETURN,
                               tok.getLine(),
                               tok.getColumn(),
                               "" );
      }
      else
      {
         result = Node.create( NodeKind.RETURN,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
         skip( Operators.SEMI_COLUMN );
      }
      return result;
   }

   private IParserNode parseShiftExpression() throws TokenException
   {
      final Node result = Node.create( NodeKind.SHIFT,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseAdditiveExpression() );
      while ( tokIs( Operators.DOUBLE_SHIFT_LEFT )
            || tokIs( Operators.TRIPLE_SHIFT_LEFT ) || tokIs( Operators.DOUBLE_SHIFT_RIGHT )
            || tokIs( Operators.TRIPLE_SHIFT_RIGHT ) )
      {
         result.addChild( Node.create( NodeKind.OP,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken( true );
         result.addChild( parseAdditiveExpression() );
      }
      return result.numChildren() > 1 ? result
                                     : result.getChild( 0 );
   }

   /**
    * tok is switch
    *
    * @throws TokenException
    */
   private Node parseSwitch() throws TokenException
   {
      consume( KeyWords.SWITCH );
      final Node result = Node.create( NodeKind.SWITCH,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseCondition() );
      if ( tokIs( Operators.LEFT_CURLY_BRACKET ) )
      {
         nextToken();
         result.addChild( parseSwitchCases() );
         consume( Operators.RIGHT_CURLY_BRACKET );
      }
      return result;
   }

   /**
    * tok is case, default or the first token of the first statement
    *
    * @throws TokenException
    */
   private Node parseSwitchBlock() throws TokenException
   {
      final Node result = Node.create( NodeKind.SWITCH_BLOCK,
                                       tok.getLine(),
                                       tok.getColumn() );
      while ( !tokIs( KeyWords.CASE )
            && !tokIs( KeyWords.DEFAULT ) && !tokIs( Operators.RIGHT_CURLY_BRACKET ) )
      {
         result.addChild( parseStatement() );
      }
      return result;
   }

   /**
    * tok is { exit tok is }
    *
    * @throws TokenException
    */
   private Node parseSwitchCases() throws TokenException
   {
      final Node result = Node.create( NodeKind.CASES,
                                       tok.getLine(),
                                       tok.getColumn() );
      for ( ;; )
      {
         if ( tokIs( Operators.RIGHT_CURLY_BRACKET ) )
         {
            break;
         }
         else if ( tokIs( KeyWords.CASE ) )
         {
            nextToken( true ); // case
            final Node caseNode = Node.create( NodeKind.CASE,
                                               tok.getLine(),
                                               tok.getColumn(),
                                               parseExpression() );
            consume( Operators.COLUMN );
            caseNode.addChild( parseSwitchBlock() );
            result.addChild( caseNode );
         }
         else if ( tokIs( KeyWords.DEFAULT ) )
         {
            nextToken( true ); // default
            consume( Operators.COLUMN );
            final Node caseNode = Node.create( NodeKind.CASE,
                                               tok.getLine(),
                                               tok.getColumn(),
                                               Node.create( NodeKind.DEFAULT,
                                                            tok.getLine(),
                                                            tok.getColumn(),
                                                            KeyWords.DEFAULT.toString() ) );
            caseNode.addChild( parseSwitchBlock() );
            result.addChild( caseNode );
         }
      }
      return result;
   }

   /**
    * tok is ( for( var x : int = 0; i < length; i++ ) for( var s : String in
    * Object )
    *
    * @throws TokenException
    */
   private Node parseTraditionalFor() throws TokenException
   {
      consume( Operators.LEFT_PARENTHESIS );

      final Node result = Node.create( NodeKind.FOR,
                                       tok.getLine(),
                                       tok.getColumn() );
      if ( !tokIs( Operators.SEMI_COLUMN ) )
      {
         if ( tokIs( KeyWords.VAR ) )
         {
            result.addChild( NodeKind.INIT,
                             tok.getLine(),
                             tok.getColumn(),
                             parseVarList( null,
                                           null ) );
         }
         else
         {
            isInFor = true;
            result.addChild( NodeKind.INIT,
                             tok.getLine(),
                             tok.getColumn(),
                             parseExpression() );
            isInFor = false;
         }
         if ( tokIs( NodeKind.IN.toString() ) )
         {
            return parseForIn( result );
         }
      }
      consume( Operators.SEMI_COLUMN );
      if ( !tokIs( Operators.SEMI_COLUMN ) )
      {
         result.addChild( NodeKind.COND,
                          tok.getLine(),
                          tok.getColumn(),
                          parseExpression() );
      }
      consume( Operators.SEMI_COLUMN );
      if ( !tokIs( Operators.RIGHT_PARENTHESIS ) )
      {
         result.addChild( NodeKind.ITER,
                          tok.getLine(),
                          tok.getColumn(),
                          parseExpressionList() );
      }
      consume( Operators.RIGHT_PARENTHESIS );
      result.addChild( parseStatement() );
      return result;
   }

   private Node parseTry() throws TokenException
   {
      Node result;
      nextToken( true );
      result = Node.create( NodeKind.TRY,
                            tok.getLine(),
                            tok.getColumn(),
                            parseBlock() );
      return result;
   }

   private Node parseType() throws TokenException
   {
      Node result;
      if ( tok.getText().equals( VECTOR ) )
      {
         result = parseVector();
      }
      else
      {
         result = Node.create( NodeKind.TYPE,
                               tok.getLine(),
                               tok.getColumn(),
                               parseQualifiedName( true ) );
         // nextToken( true );
      }
      return result;
   }

   private Node parseUnaryExpressionNotPlusMinus() throws TokenException
   {
      Node result;
      if ( tokIs( KeyWords.DELETE ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.DELETE,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      else if ( tokIs( KeyWords.VOID ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.VOID,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      else if ( tokIs( KeyWords.TYPEOF ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.TYPEOF,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      else if ( tokIs( "!" )
            || tokIs( "not" ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.NOT,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      else if ( tokIs( "~" ) )
      {
         nextToken( true );
         result = Node.create( NodeKind.B_NOT,
                               tok.getLine(),
                               tok.getColumn(),
                               parseExpression() );
      }
      else
      {
         result = parseUnaryPostfixExpression();
      }
      return result;
   }

   private Node parseUnaryPostfixExpression() throws TokenException
   {
      Node node = parsePrimaryExpression();

      if ( tokIs( Operators.LEFT_SQUARE_BRACKET ) )
      {
         node = parseArrayAccessor( node );
      }
      else if ( tokIs( Operators.LEFT_PARENTHESIS ) )
      {
         node = parseFunctionCall( node );
      }
      if ( tokIs( Operators.INCREMENT ) )
      {
         node = parseIncrement( node );
      }
      else if ( tokIs( Operators.DECREMENT ) )
      {
         node = parseDecrement( node );
      }
      else if ( tokIs( Operators.DOT )
            || tokIs( Operators.DOUBLE_COLUMN ) )
      {
         node = parseDot( node );
      }
      return node;
   }

   private Node parseUse() throws TokenException
   {
      consume( KeyWords.USE );
      consume( KeyWords.NAMESPACE );
      final Node result = Node.create( NodeKind.USE,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       parseNamespaceName() );
      skip( Operators.SEMI_COLUMN );
      return result;
   }

   private Node parseVar() throws TokenException
   {
      Node result;
      result = parseVarList( null,
                             null );
      skip( Operators.SEMI_COLUMN );
      return result;
   }

   /**
    * tok is var var x, y : String, z : int = 0;
    *
    * @param modifiers
    * @param meta
    * @throws TokenException
    */
   private Node parseVarList( final List< Node > meta,
                              final List< Token > modifiers ) throws TokenException
   {
      consume( KeyWords.VAR );
      final Node result = Node.create( NodeKind.VAR_LIST,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( convertMeta( meta ) );
      result.addChild( convertModifiers( modifiers ) );
      collectVarListContent( result );
      return result;
   }

   private Node parseVector() throws TokenException
   {
      final Node result = Node.create( NodeKind.VECTOR,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       "" );
      if ( tok.getText().compareTo( "Vector" ) == 0 )
      {
         nextToken();
      }
      consume( Operators.VECTOR_START );

      result.addChild( parseType() );

      consume( Operators.SUPERIOR );

      return result;
   }

   /**
    * tok is while
    *
    * @throws TokenException
    */
   private Node parseWhile() throws TokenException
   {
      consume( KeyWords.WHILE );
      final Node result = Node.create( NodeKind.WHILE,
                                       tok.getLine(),
                                       tok.getColumn() );
      result.addChild( parseCondition() );
      result.addChild( parseStatement() );
      return result;
   }

   private void setFileName( final String fileNameToParse )
   {
      fileName = fileNameToParse;
   }

   private void skip( final Operators operator ) throws TokenException
   {
      skip( operator.toString() );
   }

   /**
    * Skip the current token, if it equals to the parameter
    *
    * @param text
    * @throws UnExpectedTokenException
    */
   private void skip( final String text ) throws TokenException
   {
      if ( tokIs( text ) )
      {
         nextToken();
      }
   }

   private boolean tokIs( final KeyWords keyword )
   {
      return tok.getText().compareTo( keyword.toString() ) == 0;
   }

   private boolean tokIs( final Operators operator )
   {
      return tok.getText().compareTo( operator.toString() ) == 0;
   }

   /**
    * Compare the current token to the parameter
    *
    * @param text
    * @return true, if tok's text property equals the parameter
    */
   private boolean tokIs( final String text )
   {
      return tok.getText().equals( text );
   }

   private void tryToParseCommentNode( final Node result,
                                       final List< Token > modifiers ) throws TokenException
   {
      if ( tok.getText().startsWith( ASDOC_COMMENT ) )
      {
         currentAsDoc = Node.create( NodeKind.AS_DOC,
                                     tok.getLine(),
                                     tok.getColumn(),
                                     tok.getText() );
         nextToken();
      }
      else if ( tok.getText().startsWith( MULTIPLE_LINES_COMMENT ) )
      {
         result.addChild( Node.create( NodeKind.MULTI_LINE_COMMENT,
                                       tok.getLine(),
                                       tok.getColumn(),
                                       tok.getText() ) );
         nextToken();
      }
      else
      {
         if ( modifiers != null )
         {
            modifiers.add( tok );
         }
         nextTokenIgnoringDocumentation();
      }
   }
}
TOP

Related Classes of de.bokelberg.flex.parser.AS3Parser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.