src/jdk/nashorn/internal/parser/AbstractParser.java

Mon, 11 Feb 2013 21:26:06 +0530

author
sundar
date
Mon, 11 Feb 2013 21:26:06 +0530
changeset 82
abea4ba28901
parent 57
59970b70ebb5
child 97
757a49aaad02
permissions
-rw-r--r--

8007915: Nashorn IR, codegen, parser packages and Context instance should be inaccessible to user code
Reviewed-by: lagergren, jlaskey, attila

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.parser;
    28 import static jdk.nashorn.internal.parser.TokenType.EOF;
    29 import static jdk.nashorn.internal.parser.TokenType.EOL;
    30 import static jdk.nashorn.internal.parser.TokenType.IDENT;
    32 import jdk.nashorn.internal.ir.IdentNode;
    33 import jdk.nashorn.internal.ir.LiteralNode;
    34 import jdk.nashorn.internal.parser.Lexer.LexerToken;
    35 import jdk.nashorn.internal.parser.Lexer.RegexToken;
    36 import jdk.nashorn.internal.runtime.ECMAErrors;
    37 import jdk.nashorn.internal.runtime.ErrorManager;
    38 import jdk.nashorn.internal.runtime.JSErrorType;
    39 import jdk.nashorn.internal.runtime.ParserException;
    40 import jdk.nashorn.internal.runtime.RegExp;
    41 import jdk.nashorn.internal.runtime.Source;
    43 /**
    44  * Base class for parsers.
    45  */
    46 public abstract class AbstractParser {
    47     /** Source to parse. */
    48     protected final Source source;
    50     /** Error manager to report errors. */
    51     protected final ErrorManager errors;
    53     /** Stream of lex tokens to parse. */
    54     protected TokenStream stream;
    56     /** Index of current token. */
    57     protected int k;
    59     /** Descriptor of current token. */
    60     protected long token;
    62     /** Type of current token. */
    63     protected TokenType type;
    65     /** Type of last token. */
    66     protected TokenType last;
    68     /** Start position of current token. */
    69     protected int start;
    71     /** Finish position of previous token. */
    72     protected int finish;
    74     /** Current line number. */
    75     protected int line;
    77     /** Position of last EOL + 1. */
    78     protected int linePosition;
    80     /** Lexer used to scan source content. */
    81     protected Lexer lexer;
    83     /** Is this parser running under strict mode? */
    84     protected boolean isStrictMode;
    86     /**
    87      * Construct a parser.
    88      *
    89      * @param source  Source to parse.
    90      * @param errors  Error reporting manager.
    91      * @param strict  True if we are in strict mode
    92      */
    93     protected AbstractParser(final Source source, final ErrorManager errors, final boolean strict) {
    94         this.source       = source;
    95         this.errors       = errors;
    96         this.k            = -1;
    97         this.token        = Token.toDesc(EOL, 0, 1);
    98         this.type         = EOL;
    99         this.last         = EOL;
   100         this.isStrictMode = strict;
   101     }
   103     /**
   104      * Get the Source
   105      *
   106      * @return the Source
   107      */
   108     public Source getSource() {
   109         return source;
   110     }
   112     /**
   113      * Get the ith token.
   114      *
   115      * @param i Index of token.
   116      *
   117      * @return  the token
   118      */
   119     protected final long getToken(final int i) {
   120         // Make sure there are enough tokens available.
   121         while (i > stream.last()) {
   122             // If we need to buffer more for lookahead.
   123             if (stream.isFull()) {
   124                 stream.grow();
   125             }
   127             // Get more tokens.
   128             lexer.lexify();
   129         }
   131         return stream.get(i);
   132     }
   134     /**
   135      * Return the tokenType of the ith token.
   136      *
   137      * @param i Index of token
   138      *
   139      * @return the token type
   140      */
   141     protected final TokenType T(final int i) {
   142         // Get token descriptor and extract tokenType.
   143         return Token.descType(getToken(i));
   144     }
   146     /**
   147      * Seek next token that is not an EOL.
   148      *
   149      * @return tokenType of next token.
   150      */
   151     protected final TokenType next() {
   152         do {
   153             nextOrEOL();
   154         } while (type == EOL);
   156         return type;
   157     }
   159     /**
   160      * Seek next token.
   161      *
   162      * @return tokenType of next token.
   163      */
   164     protected final TokenType nextOrEOL() {
   165         // Capture last token tokenType.
   166         last = type;
   167         if (type != EOF) {
   169             // Set up next token.
   170             k++;
   171             final long lastToken = token;
   172             token = getToken(k);
   173             type = Token.descType(token);
   175             // do this before the start is changed below
   176             if (last != EOL) {
   177                 finish = start + Token.descLength(lastToken);
   178             }
   180             if (type == EOL) {
   181                 line = Token.descLength(token);
   182                 linePosition = Token.descPosition(token);
   183             } else {
   184                 start = Token.descPosition(token);
   185             }
   187         }
   189         return type;
   190     }
   192     /**
   193      * Get the message string for a message ID and arguments
   194      *
   195      * @param msgId The Message ID
   196      * @param args  The arguments
   197      *
   198      * @return The message string
   199      */
   200     protected static String message(final String msgId, final String... args) {
   201         return ECMAErrors.getMessage("parser.error." + msgId, args);
   202     }
   204     /**
   205      * Report an error.
   206      *
   207      * @param message    Error message.
   208      * @param errorToken Offending token.
   209      */
   210     protected final void error(final String message, final long errorToken) {
   211         error(JSErrorType.SYNTAX_ERROR, message, errorToken);
   212     }
   214     /**
   215      * Report an error.
   216      *
   217      * @param errorType  The error type
   218      * @param message    Error message.
   219      * @param errorToken Offending token.
   220      */
   221     protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
   222         final int position  = Token.descPosition(errorToken);
   223         final int lineNum   = source.getLine(position);
   224         final int columnNum = source.getColumn(position);
   225         final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
   226         final ParserException exp = new ParserException(formatted, source, lineNum, columnNum, errorToken);
   227         exp.setErrorType(errorType);
   228         throw exp;
   229     }
   231     /**
   232      * Report an error.
   233      *
   234      * @param message Error message.
   235      */
   236     protected final void error(final String message) {
   237         error(JSErrorType.SYNTAX_ERROR, message);
   238     }
   240     /**
   241      * Report an error.
   242      *
   243      * @param errorType  The error type
   244      * @param message    Error message.
   245      */
   246     protected final void error(final JSErrorType errorType, final String message) {
   247         // TODO - column needs to account for tabs.
   248         final int position = Token.descPosition(token);
   249         final int column = position - linePosition;
   250         final String formatted = ErrorManager.format(message, source, line, column, token);
   251         final ParserException exp = new ParserException(formatted, source, line, column, token);
   252         exp.setErrorType(errorType);
   253         throw exp;
   254     }
   256     /**
   257      * Generate 'expected' message.
   258      *
   259      * @param expected Expected tokenType.
   260      *
   261      * @return the message string
   262      */
   263     protected final String expectMessage(final TokenType expected) {
   264         final String tokenString = Token.toString(source, token, false);
   265         String msg;
   267         if (expected == null) {
   268             msg = AbstractParser.message("expected.stmt", tokenString);
   269         } else {
   270             final String expectedName = expected.getNameOrType();
   271             msg = AbstractParser.message("expected", expectedName, tokenString);
   272         }
   274         return msg;
   275     }
   277     /**
   278      * Check next token and advance.
   279      *
   280      * @param expected Expected tokenType.
   281      *
   282      * @throws ParserException on unexpected token type
   283      */
   284     protected final void expect(final TokenType expected) throws ParserException {
   285         if (type != expected) {
   286             error(expectMessage(expected));
   287         }
   289         next();
   290     }
   292     /**
   293      * Check next token, get its value and advance.
   294      *
   295      * @param  expected Expected tokenType.
   296      * @return The JavaScript value of the token
   297      * @throws ParserException on unexpected token type
   298      */
   299     protected final Object expectValue(final TokenType expected) throws ParserException {
   300         if (type != expected) {
   301             error(expectMessage(expected));
   302         }
   304         final Object value = getValue();
   306         next();
   308         return value;
   309     }
   311     /**
   312      * Get the value of the current token.
   313      *
   314      * @return JavaScript value of the token.
   315      */
   316     protected final Object getValue() {
   317         return getValue(token);
   318     }
   320     /**
   321      * Get the value of a specific token
   322      *
   323      * @param valueToken the token
   324      *
   325      * @return JavaScript value of the token
   326      */
   327     protected final Object getValue(final long valueToken) {
   328         try {
   329             return lexer.getValueOf(valueToken, isStrictMode);
   330         } catch (final ParserException e) {
   331             errors.error(e);
   332         }
   334         return null;
   335     }
   337     /**
   338      * Certain future reserved words can be used as identifiers in
   339      * non-strict mode. Check if the current token is one such.
   340      *
   341      * @return true if non strict mode identifier
   342      */
   343     protected final boolean isNonStrictModeIdent() {
   344         return !isStrictMode && type.getKind() == TokenKind.FUTURESTRICT;
   345     }
   347     /**
   348      * Get ident.
   349      *
   350      * @return Ident node.
   351      */
   352     protected final IdentNode getIdent() {
   353         // Capture IDENT token.
   354         long identToken = token;
   356         if (isNonStrictModeIdent()) {
   357             // Fake out identifier.
   358             identToken = Token.recast(token, IDENT);
   359             // Get IDENT.
   360             final String ident = (String)getValue(identToken);
   362             next();
   364             // Create IDENT node.
   365             return new IdentNode(source, identToken, finish, ident);
   366         }
   368         // Get IDENT.
   369         final String ident = (String)expectValue(IDENT);
   370         if (ident == null) {
   371             return null;
   372         }
   373         // Create IDENT node.
   374         return new IdentNode(source, identToken, finish, ident);
   375     }
   377     /**
   378      * Check if current token is in identifier name
   379      *
   380      * @return true if current token is an identifier name
   381      */
   382     protected final boolean isIdentifierName() {
   383         final TokenKind kind = type.getKind();
   384         if (kind == TokenKind.KEYWORD || kind == TokenKind.FUTURE || kind == TokenKind.FUTURESTRICT) {
   385             return true;
   386         }
   387         // Fake out identifier.
   388         final long identToken = Token.recast(token, IDENT);
   389         // Get IDENT.
   390         final String ident = (String)getValue(identToken);
   391         return !ident.isEmpty() && Character.isJavaIdentifierStart(ident.charAt(0));
   392     }
   394     /**
   395      * Create an IdentNode from the current token
   396      *
   397      * @return an IdentNode representing the current token
   398      */
   399     protected final IdentNode getIdentifierName() {
   400         if (type == IDENT) {
   401             return getIdent();
   402         } else if (isIdentifierName()) {
   403             // Fake out identifier.
   404             final long identToken = Token.recast(token, IDENT);
   405             // Get IDENT.
   406             final String ident = (String)getValue(identToken);
   407             next();
   408             // Create IDENT node.
   409             return new IdentNode(source, identToken, finish, ident);
   410         } else {
   411             expect(IDENT);
   412             return null;
   413         }
   414     }
   416     /**
   417      * Create a LiteralNode from the current token
   418      *
   419      * @return LiteralNode representing the current token
   420      * @throws ParserException if any literals fails to parse
   421      */
   422     protected final LiteralNode<?> getLiteral() throws ParserException {
   423         // Capture LITERAL token.
   424         final long literalToken = token;
   426         // Create literal node.
   427         final Object value = getValue();
   428         // Advance to have a correct finish
   429         next();
   431         LiteralNode<?> node = null;
   433         if (value == null) {
   434             node = LiteralNode.newInstance(source, literalToken, finish);
   435         } else if (value instanceof Number) {
   436             node = LiteralNode.newInstance(source, literalToken, finish, (Number)value);
   437         } else if (value instanceof String) {
   438             node = LiteralNode.newInstance(source, literalToken, finish, (String)value);
   439         } else if (value instanceof LexerToken) {
   440             if (value instanceof RegexToken) {
   441                 final RegexToken regex = (RegexToken)value;
   442                 try {
   443                     RegExp.validate(regex.getExpression(), regex.getOptions());
   444                 } catch (final ParserException e) {
   445                     error(e.getMessage());
   446                 }
   447             }
   448             node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
   449         } else {
   450             assert false : "unknown type for LiteralNode: " + value.getClass();
   451         }
   453         return node;
   454     }
   455 }

mercurial