diff --git a/Userland/Libraries/LibJS/Lexer.cpp b/Userland/Libraries/LibJS/Lexer.cpp index 907e4920ddd..30e00eb6367 100644 --- a/Userland/Libraries/LibJS/Lexer.cpp +++ b/Userland/Libraries/LibJS/Lexer.cpp @@ -654,7 +654,7 @@ Token Lexer::next() if (m_current_char == '.') { // decimal consume(); - while (is_ascii_digit(m_current_char) || match_numeric_literal_separator_followed_by(is_ascii_digit)) + while (is_ascii_digit(m_current_char)) consume(); if (m_current_char == 'e' || m_current_char == 'E') is_invalid_numeric_literal = !consume_exponent(); @@ -688,7 +688,7 @@ Token Lexer::next() // octal without '0o' prefix. Forbidden in 'strict mode' do { consume(); - } while (is_ascii_digit(m_current_char) || match_numeric_literal_separator_followed_by(is_ascii_digit)); + } while (is_ascii_digit(m_current_char)); } } else { // 1...9 or period @@ -700,11 +700,15 @@ Token Lexer::next() } else { if (m_current_char == '.') { consume(); - while (is_ascii_digit(m_current_char) || match_numeric_literal_separator_followed_by(is_ascii_digit)) + if (m_current_char == '_') + is_invalid_numeric_literal = true; + + while (is_ascii_digit(m_current_char) || match_numeric_literal_separator_followed_by(is_ascii_digit)) { consume(); + } } if (m_current_char == 'e' || m_current_char == 'E') - is_invalid_numeric_literal = !consume_exponent(); + is_invalid_numeric_literal = is_invalid_numeric_literal || !consume_exponent(); } } if (is_invalid_numeric_literal) { diff --git a/Userland/Libraries/LibJS/Tests/syntax/numeric-separator.js b/Userland/Libraries/LibJS/Tests/syntax/numeric-separator.js new file mode 100644 index 00000000000..589b744b676 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/syntax/numeric-separator.js @@ -0,0 +1,26 @@ +describe("numeric separators", () => { + test("numeric separator works for 'normal' number", () => { + expect("1_2").toEvalTo(12); + expect("4_2.4_2").toEvalTo(42.42); + expect("1_2e0_2").toEvalTo(1200); + + expect("1_2E+_1").not.toEval(); + expect("1_2E+0_1").toEvalTo(120); + }); + + test("cannot use numeric separator after .", () => { + expect("4._3").not.toEval(); + expect("0._3").not.toEval(); + expect("1_.3._3").not.toEval(); + + // Actually a valid attempt to get property '_3' on 1.3 which fails but does parse. + expect("1.3._3").toEval(); + }); + + test("cannot use numeric separator in octal escaped number", () => { + expect("00_1").not.toEval(); + expect("01_1").not.toEval(); + expect("07_3").not.toEval(); + expect("00_1").not.toEval(); + }); +});