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

Tue, 12 Mar 2013 15:30:53 +0100

author
lagergren
date
Tue, 12 Mar 2013 15:30:53 +0100
changeset 137
e15806b9d716
parent 7
5a1b0714df0e
child 211
3a209cbd1d8f
permissions
-rw-r--r--

8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey

     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.COLON;
    29 import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
    30 import static jdk.nashorn.internal.parser.TokenType.EOF;
    31 import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
    32 import static jdk.nashorn.internal.parser.TokenType.RBRACE;
    33 import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
    34 import static jdk.nashorn.internal.parser.TokenType.STRING;
    36 import java.util.ArrayList;
    37 import java.util.List;
    38 import jdk.nashorn.internal.ir.LiteralNode;
    39 import jdk.nashorn.internal.ir.Node;
    40 import jdk.nashorn.internal.ir.ObjectNode;
    41 import jdk.nashorn.internal.ir.PropertyNode;
    42 import jdk.nashorn.internal.ir.UnaryNode;
    43 import jdk.nashorn.internal.runtime.ErrorManager;
    44 import jdk.nashorn.internal.runtime.Source;
    46 /**
    47  * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
    48  *
    49  * See: 15.12.1.2 The JSON Syntactic Grammar
    50  */
    51 public class JSONParser extends AbstractParser {
    53     /**
    54      * Constructor
    55      * @param source  the source
    56      * @param errors  the error manager
    57      * @param strict  are we in strict mode
    58      */
    59     public JSONParser(final Source source, final ErrorManager errors, final boolean strict) {
    60         super(source, errors, strict);
    61     }
    63     /**
    64      * Implementation of the Quote(value) operation as defined in the ECMA script spec
    65      * It wraps a String value in double quotes and escapes characters within in
    66      *
    67      * @param value string to quote
    68      *
    69      * @return quoted and escaped string
    70      */
    71     public static String quote(final String value) {
    73         final StringBuilder product = new StringBuilder();
    75         product.append("\"");
    77         for (final char ch : value.toCharArray()) {
    78             // TODO: should use a table?
    79             switch (ch) {
    80             case '\\':
    81                 product.append("\\\\");
    82                 break;
    83             case '"':
    84                 product.append("\\\"");
    85                 break;
    86             case '\b':
    87                 product.append("\\b");
    88                 break;
    89             case '\f':
    90                 product.append("\\f");
    91                 break;
    92             case '\n':
    93                 product.append("\\n");
    94                 break;
    95             case '\r':
    96                 product.append("\\r");
    97                 break;
    98             case '\t':
    99                 product.append("\\t");
   100                 break;
   101             default:
   102                 if (ch < ' ') {
   103                     product.append(Lexer.unicodeEscape(ch));
   104                     break;
   105                 }
   107                 product.append(ch);
   108                 break;
   109             }
   110         }
   112         product.append("\"");
   114         return product.toString();
   115     }
   117     /**
   118      * Public parsed method - start lexing a new token stream for
   119      * a JSON script
   120      *
   121      * @return the JSON literal
   122      */
   123     public Node parse() {
   124         stream = new TokenStream();
   126         lexer = new Lexer(source, stream) {
   128             @Override
   129             protected boolean skipComments() {
   130                 return false;
   131             }
   133             @Override
   134             protected boolean isStringDelimiter(final char ch) {
   135                 return ch == '\"';
   136             }
   138             @Override
   139             protected boolean isWhitespace(final char ch) {
   140                 return Lexer.isJsonWhitespace(ch);
   141             }
   143             @Override
   144             protected boolean isEOL(final char ch) {
   145                 return Lexer.isJsonEOL(ch);
   146             }
   147         };
   149         k = -1;
   151         next();
   153         final Node resultNode = jsonLiteral();
   154         expect(EOF);
   156         return resultNode;
   157     }
   159     @SuppressWarnings("fallthrough")
   160     private LiteralNode<?> getStringLiteral() {
   161         final LiteralNode<?> literal = getLiteral();
   162         final String         str     = (String)literal.getValue();
   164         for (int i = 0; i < str.length(); i++) {
   165             final char ch = str.charAt(i);
   166             switch (ch) {
   167             default:
   168                 if (ch > 0x001f) {
   169                     break;
   170                 }
   171             case '"':
   172             case '\\':
   173                 error(AbstractParser.message("unexpected.token", str));
   174                 break;
   175             }
   176         }
   178         return literal;
   179     }
   181     /**
   182      * Parse a JSON literal from the token stream
   183      * @return the JSON literal as a Node
   184      */
   185     private Node jsonLiteral() {
   186         final long literalToken = token;
   188         switch (type) {
   189         case STRING:
   190             return getStringLiteral();
   191         case ESCSTRING:
   192         case DECIMAL:
   193         case FLOATING:
   194             return getLiteral();
   195         case FALSE:
   196             next();
   197             return LiteralNode.newInstance(source, literalToken, finish, false);
   198         case TRUE:
   199             next();
   200             return LiteralNode.newInstance(source, literalToken, finish, true);
   201         case NULL:
   202             next();
   203             return LiteralNode.newInstance(source, literalToken, finish);
   204         case LBRACKET:
   205             return arrayLiteral();
   206         case LBRACE:
   207             return objectLiteral();
   208         /*
   209          * A.8.1 JSON Lexical Grammar
   210          *
   211          * JSONNumber :: See 15.12.1.1
   212          *    -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt
   213          */
   214         case SUB:
   215             next();
   217             final long realToken = token;
   218             final Object value = getValue();
   220             if (value instanceof Number) {
   221                 next();
   222                 return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
   223             }
   225             error(AbstractParser.message("expected", "number", type.getNameOrType()));
   226             break;
   227         default:
   228             break;
   229         }
   231         error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
   232         return null;
   233     }
   235     /**
   236      * Parse an array literal from the token stream
   237      * @return the array literal as a Node
   238      */
   239     private Node arrayLiteral() {
   240         // Unlike JavaScript array literals, elison is not permitted in JSON.
   242         // Capture LBRACKET token.
   243         final long arrayToken = token;
   244         // LBRACKET tested in caller.
   245         next();
   247         Node result = null;
   248         // Prepare to accummulating elements.
   249         final List<Node> elements = new ArrayList<>();
   251 loop:
   252         while (true) {
   253             switch (type) {
   254             case RBRACKET:
   255                 next();
   256                 result = LiteralNode.newInstance(source, arrayToken, finish, elements);
   257                 break loop;
   259             case COMMARIGHT:
   260                 next();
   261                 break;
   263             default:
   264                 // Add expression element.
   265                 elements.add(jsonLiteral());
   266                 // Comma between array elements is mandatory in JSON.
   267                 if (type != COMMARIGHT && type != RBRACKET) {
   268                     error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
   269                 }
   270                 break;
   271             }
   272         }
   274         return result;
   275     }
   277     /**
   278      * Parse an object literal from the token stream
   279      * @return the object literal as a Node
   280      */
   281     private Node objectLiteral() {
   282         // Capture LBRACE token.
   283         final long objectToken = token;
   284         // LBRACE tested in caller.
   285         next();
   287         // Prepare to accumulate elements.
   288         final List<Node> elements = new ArrayList<>();
   290         // Create a block for the object literal.
   291 loop:
   292         while (true) {
   293             switch (type) {
   294             case RBRACE:
   295                 next();
   296                 break loop;
   298             case COMMARIGHT:
   299                 next();
   300                 break;
   302             default:
   303                 // Get and add the next property.
   304                 final Node property = propertyAssignment();
   305                 elements.add(property);
   307                 // Comma between property assigments is mandatory in JSON.
   308                 if (type != RBRACE && type != COMMARIGHT) {
   309                     error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
   310                 }
   311                 break;
   312             }
   313         }
   315         // Construct new object literal.
   316         return new ObjectNode(source, objectToken, finish, elements);
   317     }
   319     /**
   320      * Parse a property assignment from the token stream
   321      * @return the property assignment as a Node
   322      */
   323     private Node propertyAssignment() {
   324         // Capture firstToken.
   325         final long propertyToken = token;
   326         LiteralNode<?> name = null;
   328         if (type == STRING) {
   329             name = getStringLiteral();
   330         } else if (type == ESCSTRING) {
   331             name = getLiteral();
   332         }
   334         if (name != null) {
   335             expect(COLON);
   336             final Node value = jsonLiteral();
   337             return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
   338         }
   340         // Raise an error.
   341         error(AbstractParser.message("expected", "string", type.getNameOrType()));
   343         return null;
   344     }
   346 }

mercurial