Package uk.co.badgersinfoil.metaas.impl

Source Code of uk.co.badgersinfoil.metaas.impl.ASTUtils

/*
* ASTUtils.java
*
* Copyright (c) 2006-2007 David Holroyd
*
* Licensed 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 uk.co.badgersinfoil.metaas.impl;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.MismatchedTokenException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.asdt.core.internal.antlr.AS3Lexer;
import org.asdt.core.internal.antlr.AS3Parser;
import uk.co.badgersinfoil.metaas.ActionScriptFactory;
import uk.co.badgersinfoil.metaas.SyntaxException;
import uk.co.badgersinfoil.metaas.dom.Expression;
import uk.co.badgersinfoil.metaas.impl.antlr.BasicListUpdateDelegate;
import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListToken;
import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTokenSource;
import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTokenStream;
import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTree;
import uk.co.badgersinfoil.metaas.impl.antlr.LinkedListTreeAdaptor;
import uk.co.badgersinfoil.metaas.impl.antlr.TreeTokenListUpdateDelegate;


/**
* A collection of helper methods for dealing with AST nodes.
*/
public class ASTUtils {
 
  public static final LinkedListTreeAdaptor TREE_ADAPTOR = new LinkedListTreeAdaptor();

  static {
    TREE_ADAPTOR.setFactory(new LinkedListTreeAdaptor.Factory() {
      private BasicListUpdateDelegate basicDelegate = new BasicListUpdateDelegate();
      private ParentheticListUpdateDelegate blockDelegate = new ParentheticListUpdateDelegate(AS3Parser.LCURLY, AS3Parser.RCURLY);
      private ParentheticListUpdateDelegate metadataTagDelegate = new ParentheticListUpdateDelegate(AS3Parser.LBRACK, AS3Parser.RBRACK);
      private ParentheticListUpdateDelegate paramsDelegate = new ParentheticListUpdateDelegate(AS3Parser.LPAREN, AS3Parser.RPAREN);
      public TreeTokenListUpdateDelegate create(LinkedListTree payload) {
        if (payload.isNil()) {
          return basicDelegate;
        }
        switch (payload.getType()) {
            case AS3Parser.BLOCK:
            case AS3Parser.TYPE_BLOCK:
            case AS3Parser.OBJECT_LITERAL:
          return blockDelegate;
            case AS3Parser.ANNOTATION:
            case AS3Parser.ARRAY_LITERAL:
          return metadataTagDelegate;
            case AS3Parser.PARAMS:
            case AS3Parser.ANNOTATION_PARAMS:
            case AS3Parser.ARGUMENTS:
            case AS3Parser.ENCPS_EXPR:
            case AS3Parser.CONDITION:
          return paramsDelegate;
            default:
          return basicDelegate;
        }
      }
    });
  }

  /**
   * Stringifies the given IDENTIFIER node.
   */
  public static String identText(LinkedListTree ast) {
    if (ast.getType() != AS3Parser.IDENTIFIER) {
      throw new IllegalArgumentException("expected IDENTIFIER, but token was a " + tokenName(ast));
    }
    return stringifyIdentAux((LinkedListTree)ast.getChild(0));
  }
 
  private static String stringifyIdentAux(LinkedListTree ast) {
    StringBuffer result = new StringBuffer();
    if (ast.getType()==AS3Parser.DBL_COLON) {
      result.append(ast.getFirstChild());
      result.append("::");
      result.append(ast.getLastChild());
    } else if (ast.getType()==AS3Parser.PROPERTY_OR_IDENTIFIER
               || ast.getType()==AS3Parser.DOT)
    {
      result.append(stringifyIdentAux(ast.getFirstChild()));
      result.append(".");
      result.append(stringifyIdentAux(ast.getLastChild()));
    } else {
      result.append(ast.getText());
    }
    return result.toString();
  }

  public static String qualifiedIdentText(LinkedListTree ast) {
    if (ast.getType()==AS3Parser.DBL_COLON) {
      return ast.getFirstChild() + "::" + ast.getLastChild();
    }
    return ast.getText();
  }

  public static String identStarText(LinkedListTree ast) {
    if (ast.getType() != AS3Parser.IDENTIFIER_STAR) {
      throw new IllegalArgumentException("expected IDENTIFIER_STAR, but token was a " + tokenName(ast));
    }
    StringBuffer result = new StringBuffer();
    for (int i=0; i<ast.getChildCount(); i++) {
      LinkedListTree part = (LinkedListTree)ast.getChild(i);
      if (result.length() > 0) {
        result.append(".");
      }
      result.append(part.getText());
    }
    return result.toString();
  }

  /**
   * Stringifies the type name from the given TYPE_SPEC node.
   */
  public static String typeSpecText(LinkedListTree ast) {
    if (ast.getType() != AS3Parser.TYPE_SPEC) {
      throw new IllegalArgumentException("expected TYPE_SPEC, but token was a " + tokenName(ast));
    }
    LinkedListTree type = ast.getFirstChild();
    if (type.getType() == AS3Parser.IDENTIFIER) {
      return identText(type);
    }
    // handle e.g. "void",
    return type.getText();
  }

  /**
   * Helper for constructing error messages
   */
  public static String tokenName(LinkedListTree ast) {
    return tokenName(ast.getType());
  }

  /**
   * Helper for constructing error messages
   */
  public static String tokenName(int type) {
    if (type == -1) {
      return "<EOF>";
    }
    return AS3Parser.tokenNames[type];
  }

  /**
   * Returns the first child of the given AST node which has the given
   * type, or null, if no such node exists.
   */
  public static LinkedListTree findChildByType(LinkedListTree ast, int type) {
    return (LinkedListTree)ast.getFirstChildWithType(type);
  }

  /**
   * Returns an ActionScript3 parser which will recognise input from the
   * given string.
   */
  public static AS3Parser parse(String text) {
    return parse(new StringReader(text));
  }

  /**
   * Returns an ActionScript3 parser which will recognise input from the
   * given reader.
   */
  public static AS3Parser parse(Reader in) {
    ANTLRReaderStream cs;
    try {
      cs = new ANTLRReaderStream(in);
    } catch (IOException e) {
      throw new SyntaxException(e);
    }
    AS3Lexer lexer = new AS3Lexer(cs);
    LinkedListTokenSource linker = new LinkedListTokenSource(lexer);
    LinkedListTokenStream tokenStream = new LinkedListTokenStream(linker);
    AS3Parser parser = new AS3Parser(tokenStream);
    parser.setInput(lexer, cs);
    parser.setTreeAdaptor(TREE_ADAPTOR);
    return parser;
  }


  public static SyntaxException buildSyntaxException(String statement,
                                                     AS3Parser parser,
                                                     RecognitionException e)
  {
    String msg;
    if (e instanceof NoViableAltException) {
      NoViableAltException ex = (NoViableAltException)e;
      msg = "Unexpected token "+tokenName(parser, ex.getUnexpectedType());
      if (statement != null) {
        msg += " in "+ActionScriptFactory.str(statement);
      }
    } else if (e instanceof MismatchedTokenException) {
      MismatchedTokenException ex = (MismatchedTokenException)e;
      msg = "Unexpected token "+tokenName(parser, ex.getUnexpectedType())+" (expecting "+tokenName(parser, ex.expecting)+")";
      if (statement != null) {
        msg += " in "+ActionScriptFactory.str(statement);
      }
    } else {
      if (statement == null) {
        msg = "";
      } else {
        msg = "Problem parsing "+ActionScriptFactory.str(statement);
      }
    }
    msg += " at line " + e.line;
    return new SyntaxException(msg, e);
  }

  private static String tokenName(AS3Parser parser, int type) {
    if (type == -1) {
      return "<end-of-file>";
    }
    return parser.getTokenNames()[type];
  }

  /**
   * Constructs a new AST of the given type, initialized to contain
   * text matching the token's name (use for non-literals only).
   * @deprecated
   */
  public static LinkedListTree newAST(int type) {
    return newImaginaryAST(type);
  }

  /**
   * Constructs a new AST of the given type, initialized to contain
   * text matching the token's name (use for non-literals only).
   */
  public static LinkedListTree newImaginaryAST(int type) {
    return (LinkedListTree)TREE_ADAPTOR.create(type, tokenName(type));
  }

  public static LinkedListTree newPlaceholderAST(int type) {
    LinkedListTree node = newImaginaryAST(type);
    LinkedListToken placeholder = TokenBuilder.newPlaceholder(node);
    return node;
  }

  /**
   * Constructs a new AST of the given type, initialized to contain the
   * given text.
   */
  public static LinkedListTree newAST(int type, String text) {
    LinkedListToken tok = TokenBuilder.newToken(type, text);
    LinkedListTree ast = (LinkedListTree)TREE_ADAPTOR.create(tok);
    return ast;
  }

  public static LinkedListTree newAST(LinkedListToken tok) {
    return (LinkedListTree)TREE_ADAPTOR.create(tok);
  }

  public static LinkedListTree newParentheticAST(int type,
                                                  int startType,
                                                  String startText,
                                                  int endType,
                                                  String endText) {
    LinkedListTree ast = newImaginaryAST(type);
    LinkedListToken start = TokenBuilder.newToken(startType, startText);
    ast.setStartToken(start);
    LinkedListToken stop = TokenBuilder.newToken(endType, endText);
    ast.setStopToken(stop);
    start.setNext(stop);
    ast.setInitialInsertionAfter(start);
    return ast;
  }

  public static void increaseIndent(LinkedListTree node, String indent) {
    LinkedListToken newStart = increaseIndentAt(node.getStartToken(), indent);
    node.setStartToken(newStart);
    increaseIndentAfterFirstLine(node, indent);
  }

  public static void increaseIndentAfterFirstLine(LinkedListTree node, String indent) {
    for (LinkedListToken tok=node.getStartToken() ; tok!=node.getStopToken(); tok=tok.getNext()) {
      switch (tok.getType()) {
      case AS3Parser.NL:
        tok = increaseIndentAt(tok.getNext(), indent);
        break;
      case AS3Parser.ML_COMMENT:
        DocCommentUtils.increaseCommentIndent(tok, indent);
        break;
      }
    }
  }

  private static LinkedListToken increaseIndentAt(LinkedListToken tok, String indentStr) {
    if (tok.getType() == AS3Parser.WS) {
      tok.setText(indentStr + tok.getText());
      return tok;
    }
    LinkedListToken indent = TokenBuilder.newWhiteSpace(indentStr);
    tok.beforeInsert(indent);
    return indent;
  }

  /**
   * Inspects the whitespace preceeding the given tree node to try and
   * determine its level of indentation.  Scans backwards from the
   * startToken and returns the contents of the first whitespace token
   * following a newline token, or the empty string if no such pattern
   * is found.
   */
  public static String findIndent(LinkedListTree node) {
    LinkedListToken tok = node.getStartToken();
    if (tok == null) {
      return findIndent(node.getParent());
    }
    // the start-token of this AST node is actually whitespace, so
    // scan forward until we hit a non-WS token,
    while (tok.getType()==AS3Parser.NL || tok.getType()==AS3Parser.WS) {
      if (tok.getNext() == null) {
        break;
      }
      tok = tok.getNext();
    }
    // search backwards though the tokens, looking for the start of
    // the line,
    for (; tok.getPrev()!=null; tok=tok.getPrev()) {
      if (tok.getType() == AS3Parser.NL) {
        break;
      }
    }
    if (tok.getType() == AS3Parser.WS) {
      return tok.getText();
    }
    if (tok.getType()!=AS3Parser.NL) {
      return "";
    }
    LinkedListToken startOfLine = tok.getNext();
    if (startOfLine.getType() == AS3Parser.WS) {
      return startOfLine.getText();
    }
    return "";
  }

  public static void addChildWithIndentation(LinkedListTree ast, LinkedListTree stmt) {
    LinkedListTree last = ast.getLastChild();
    String indent;
    if (last == null) {
      indent = "\t" + ASTUtils.findIndent(ast);
    } else {
      indent = ASTUtils.findIndent(last);
    }
    ASTUtils.increaseIndent(stmt, indent);
    stmt.addToken(0, TokenBuilder.newNewline());
    ast.addChildWithTokens(stmt);
  }

  public static void addChildWithIndentation(LinkedListTree ast, int index, LinkedListTree stmt) {
    LinkedListTree last = (LinkedListTree)ast.getChild(index);
    String indent;
    if (last == null) {
      indent = "\t" + ASTUtils.findIndent(ast);
    } else {
      indent = ASTUtils.findIndent(last);
    }
    ASTUtils.increaseIndent(stmt, indent);
    stmt.addToken(0, TokenBuilder.newNewline());
    ast.addChildWithTokens(index, stmt);
  }

  public static String decodeStringLiteral(String str) {
    StringBuffer result = new StringBuffer();
    char[] s = str.toCharArray();
    int end = s.length - 1;
    if (s[0] != '"' && s[0] != '\'') {
      throw new SyntaxException("Invalid delimiter at position 0: "+s[0]);
    }
    char delimiter = s[0];
    for (int i=1; i<end; i++) {
      char c = s[i];
      switch (c) {
      case '\\':
        c = s[++i];
        switch (c) {
        case 'n':
          result.append('\n');
          break;
        case 't':
          result.append('\t');
          break;
        case '\\':
          result.append('\\');
          break;
        default:
          result.append(c);
        }
        break;
      default:
        result.append(c);
      }
    }
    if (s[end] != delimiter) {
      throw new SyntaxException("End delimiter doesn't match "+delimiter+" at position "+end);
    }
    return result.toString();
  }

  public static LinkedListToken newStringLiteral(String constant) {
    return new LinkedListToken(AS3Parser.STRING_LITERAL, ActionScriptFactory.str(constant));
  }

  /**
   * Deletes any whitespace tokens following (but not including) the given
   * token up to a comma token, and then deletes the comma token too.
   *
   * Used when removing elements from comma-seperated lists.
   */
  public static void removeTrailingWhitespaceAndComma(LinkedListToken stopToken) {
    for (LinkedListToken tok = stopToken.getNext(); tok!=null; tok=tok.getNext()) {
      if (tok.getChannel() == AS3Parser.HIDDEN) {
        // TODO: this might be incorrect (but never called?) see code in removePreceeding...
        tok.delete();
      } else if (tok.getType() == AS3Parser.COMMA) {
        tok.delete();
        break;
      } else {
        throw new SyntaxException("Unexpected token: "+tok);
      }
    }
  }

  public static void removePreceedingWhitespaceAndComma(LinkedListToken startToken) {
    for (LinkedListToken tok = startToken.getPrev(); tok!=null; tok=tok.getPrev()) {
      if (tok.getChannel() == AS3Parser.HIDDEN) {
        LinkedListToken del = tok;
        tok = tok.getNext();
        del.delete();
        continue;
      } else if (tok.getType() == AS3Parser.COMMA) {
        tok.delete();
        break;
      } else {
        throw new SyntaxException("Unexpected token: "+tok);
      }
    }
  }
 
  public static void assertAS3TokenTypeIs(int expected, int actual) {
    if (expected != actual) {
      throw new SyntaxException("Expected "+tokenName(expected)+", got "+tokenName(actual));
    }
  }
 
  public static void assertAS3TokenTypeIs(String msg, int expected, int actual) {
    if (expected != actual) {
      throw new SyntaxException(msg+" Expected "+tokenName(expected)+", got "+tokenName(actual));
    }
  }

  public static String stringifyNode(LinkedListTree ast) {
    StringBuffer result = new StringBuffer();
    for (LinkedListToken tok=ast.getStartToken(); tok!=null&&tok.getType()!=-1; tok=tok.getNext()) {
      result.append(tok.getText());
      if (tok == ast.getStopToken()) {
        break;
      }
    }
    return result.toString();
  }

  public static void deleteAllChildren(LinkedListTree ast) {
    while (ast.getChildCount() > 0) {
      ast.deleteChild(0);
    }
  }

  public static LinkedListTree ast(Expression expr) {
    return ((ASTExpression)expr).getAST();
  }
}
TOP

Related Classes of uk.co.badgersinfoil.metaas.impl.ASTUtils

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.