8134873: Implement support for ES6 numeric literals

Reviewed-by: attila, sundar
This commit is contained in:
Andreas Woess 2015-09-01 16:11:09 +02:00
parent a81574746d
commit 2af0ac7441
4 changed files with 103 additions and 16 deletions

View File

@ -26,6 +26,7 @@
package jdk.nashorn.internal.parser;
import static jdk.nashorn.internal.parser.TokenType.ADD;
import static jdk.nashorn.internal.parser.TokenType.BINARY_NUMBER;
import static jdk.nashorn.internal.parser.TokenType.COMMENT;
import static jdk.nashorn.internal.parser.TokenType.DECIMAL;
import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT;
@ -40,6 +41,7 @@ import static jdk.nashorn.internal.parser.TokenType.HEXADECIMAL;
import static jdk.nashorn.internal.parser.TokenType.LBRACE;
import static jdk.nashorn.internal.parser.TokenType.LPAREN;
import static jdk.nashorn.internal.parser.TokenType.OCTAL;
import static jdk.nashorn.internal.parser.TokenType.OCTAL_LEGACY;
import static jdk.nashorn.internal.parser.TokenType.RBRACE;
import static jdk.nashorn.internal.parser.TokenType.REGEX;
import static jdk.nashorn.internal.parser.TokenType.RPAREN;
@ -47,6 +49,7 @@ import static jdk.nashorn.internal.parser.TokenType.STRING;
import static jdk.nashorn.internal.parser.TokenType.XML;
import java.io.Serializable;
import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
@ -75,6 +78,9 @@ public class Lexer extends Scanner {
/** True if here and edit strings are supported. */
private final boolean scripting;
/** True if parsing in ECMAScript 6 mode. */
private final boolean es6;
/** True if a nested scan. (scan to completion, no EOF.) */
private final boolean nested;
@ -173,7 +179,7 @@ public class Lexer extends Scanner {
* @param stream the token stream to lex
*/
public Lexer(final Source source, final TokenStream stream) {
this(source, stream, false);
this(source, stream, false, false);
}
/**
@ -182,9 +188,10 @@ public class Lexer extends Scanner {
* @param source the source
* @param stream the token stream to lex
* @param scripting are we in scripting mode
* @param es6 are we in ECMAScript 6 mode
*/
public Lexer(final Source source, final TokenStream stream, final boolean scripting) {
this(source, 0, source.getLength(), stream, scripting, false);
public Lexer(final Source source, final TokenStream stream, final boolean scripting, final boolean es6) {
this(source, 0, source.getLength(), stream, scripting, es6, false);
}
/**
@ -195,16 +202,18 @@ public class Lexer extends Scanner {
* @param len length of source segment to lex
* @param stream token stream to lex
* @param scripting are we in scripting mode
* @param es6 are we in ECMAScript 6 mode
* @param pauseOnFunctionBody if true, lexer will return from {@link #lexify()} when it encounters a
* function body. This is used with the feature where the parser is skipping nested function bodies to
* avoid reading ahead unnecessarily when we skip the function bodies.
*/
public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean pauseOnFunctionBody) {
public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean es6, final boolean pauseOnFunctionBody) {
super(source.getContent(), 1, start, len);
this.source = source;
this.stream = stream;
this.scripting = scripting;
this.es6 = es6;
this.nested = false;
this.pendingLine = 1;
this.last = EOL;
@ -218,6 +227,7 @@ public class Lexer extends Scanner {
source = lexer.source;
stream = lexer.stream;
scripting = lexer.scripting;
es6 = lexer.es6;
nested = true;
pendingLine = state.pendingLine;
@ -1088,6 +1098,24 @@ public class Lexer extends Scanner {
}
type = HEXADECIMAL;
} else if (digit == 0 && es6 && (ch1 == 'o' || ch1 == 'O') && convertDigit(ch2, 8) != -1) {
// Skip over 0oN.
skip(3);
// Skip over remaining digits.
while (convertDigit(ch0, 8) != -1) {
skip(1);
}
type = OCTAL;
} else if (digit == 0 && es6 && (ch1 == 'b' || ch1 == 'B') && convertDigit(ch2, 2) != -1) {
// Skip over 0bN.
skip(3);
// Skip over remaining digits.
while (convertDigit(ch0, 2) != -1) {
skip(1);
}
type = BINARY_NUMBER;
} else {
// Check for possible octal constant.
boolean octal = digit == 0;
@ -1105,7 +1133,7 @@ public class Lexer extends Scanner {
}
if (octal && position - start > 1) {
type = OCTAL;
type = OCTAL_LEGACY;
} else if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') {
// Must be a double.
if (ch0 == '.') {
@ -1637,10 +1665,14 @@ public class Lexer extends Scanner {
switch (Token.descType(token)) {
case DECIMAL:
return Lexer.valueOf(source.getString(start, len), 10); // number
case OCTAL:
return Lexer.valueOf(source.getString(start, len), 8); // number
case HEXADECIMAL:
return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number
case OCTAL_LEGACY:
return Lexer.valueOf(source.getString(start, len), 8); // number
case OCTAL:
return Lexer.valueOf(source.getString(start + 2, len - 2), 8); // number
case BINARY_NUMBER:
return Lexer.valueOf(source.getString(start + 2, len - 2), 2); // number
case FLOATING:
final String str = source.getString(start, len);
final double value = Double.valueOf(str);

View File

@ -273,7 +273,7 @@ public class Parser extends AbstractParser implements Loggable {
try {
stream = new TokenStream();
lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, reparsedFunction != null);
lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null);
lexer.line = lexer.pendingLine = lineOffset + 1;
line = lineOffset;
@ -309,7 +309,7 @@ public class Parser extends AbstractParser implements Loggable {
public List<IdentNode> parseFormalParameterList() {
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6);
// Set up first token (skips opening EOL.)
k = -1;
@ -333,7 +333,7 @@ public class Parser extends AbstractParser implements Loggable {
public FunctionNode parseFunctionBody() {
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6);
final int functionLine = line;
// Set up first token (skips opening EOL.)
@ -1955,7 +1955,7 @@ loop:
}
detectSpecialProperty(ident);
return ident;
case OCTAL:
case OCTAL_LEGACY:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token);
}
@ -1963,6 +1963,8 @@ loop:
case ESCSTRING:
case DECIMAL:
case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING:
case REGEX:
case XML:
@ -2224,7 +2226,7 @@ loop:
switch (type) {
case IDENT:
return getIdent().setIsPropertyName();
case OCTAL:
case OCTAL_LEGACY:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token);
}
@ -2232,6 +2234,8 @@ loop:
case ESCSTRING:
case DECIMAL:
case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING:
return getLiteral();
default:
@ -3035,7 +3039,7 @@ loop:
assert parserState != null;
stream.reset();
lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions);
lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions, env._es6);
line = parserState.line;
linePosition = parserState.linePosition;
// Doesn't really matter, but it's safe to treat it as if there were a semicolon before
@ -3064,8 +3068,8 @@ loop:
this.linePosition = linePosition;
}
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, true);
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting, final boolean es6) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, es6, true);
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
return newLexer;
}

View File

@ -170,8 +170,10 @@ public enum TokenType {
YIELD (FUTURESTRICT, "yield"),
DECIMAL (LITERAL, null),
OCTAL (LITERAL, null),
HEXADECIMAL (LITERAL, null),
OCTAL_LEGACY (LITERAL, null),
OCTAL (LITERAL, null),
BINARY_NUMBER (LITERAL, null),
FLOATING (LITERAL, null),
STRING (LITERAL, null),
ESCSTRING (LITERAL, null),

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8134873: ECMAScript 6 Numeric Literals
*
* @test
* @option --language=es6
*/
function assertEquals(expected, actual) {
if (expected !== actual) {
throw new Error("expected: " + expected + ", actual: " + actual);
}
}
assertEquals(0b0, 0);
assertEquals(0B0, 0);
assertEquals(0b01, 1);
assertEquals(0B10, 2);
assertEquals(0b11111111, 255);
assertEquals(0b11111111111111111111111111111111, 4294967295);
assertEquals(0o0, 0);
assertEquals(0O0, 0);
assertEquals(0o01, 1);
assertEquals(0O10, 8);
assertEquals(0o777, 511);