8134873: Implement support for ES6 numeric literals
Reviewed-by: attila, sundar
This commit is contained in:
parent
a81574746d
commit
2af0ac7441
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
49
nashorn/test/script/basic/es6/numeric-literals.js
Normal file
49
nashorn/test/script/basic/es6/numeric-literals.js
Normal 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);
|
||||
|
Loading…
Reference in New Issue
Block a user