1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 23:27:42 +00:00

LibJS: Add numeric literal parsing for different bases and exponents

This commit is contained in:
Stephan Unverwerth 2020-04-05 14:20:58 +02:00 committed by Andreas Kling
parent b82a2239c6
commit 500f6d9e3a
4 changed files with 107 additions and 5 deletions

View file

@ -156,6 +156,16 @@ void Lexer::consume()
m_current_char = m_source[m_position++];
}
void Lexer::consume_exponent()
{
consume();
if (m_current_char == '-' || m_current_char == '+')
consume();
while (isdigit(m_current_char)) {
consume();
}
}
bool Lexer::is_eof() const
{
return m_current_char == EOF;
@ -186,6 +196,11 @@ bool Lexer::is_block_comment_end() const
return m_current_char == '*' && m_position < m_source.length() && m_source[m_position] == '/';
}
bool Lexer::is_numeric_literal_start() const
{
return isdigit(m_current_char) || (m_current_char == '.' && m_position < m_source.length() && isdigit(m_source[m_position]));
}
void Lexer::syntax_error(const char* msg)
{
m_has_errors = true;
@ -235,11 +250,60 @@ Token Lexer::next()
} else {
token_type = it->value;
}
} else if (is_numeric_literal_start()) {
if (m_current_char == '0') {
consume();
if (m_current_char == '.') {
// decimal
consume();
while (isdigit(m_current_char)) {
consume();
}
if (m_current_char == 'e' || m_current_char == 'E') {
consume_exponent();
}
} else if (m_current_char == 'e' || m_current_char == 'E') {
consume_exponent();
} else if (m_current_char == 'o' || m_current_char == 'O') {
// octal
consume();
while (m_current_char >= '0' && m_current_char <= '7') {
consume();
}
} else if (m_current_char == 'b' || m_current_char == 'B') {
// binary
consume();
while (m_current_char == '0' || m_current_char == '1') {
consume();
}
} else if (m_current_char == 'x' || m_current_char == 'X') {
// hexadecimal
consume();
while (isxdigit(m_current_char)) {
consume();
}
} else if (isdigit(m_current_char)) {
// octal without 'O' prefix. Forbidden in 'strict mode'
// FIXME: We need to make sure this produces a syntax error when in strict mode
do {
consume();
while (m_current_char == '.' || isdigit(m_current_char)) {
} while (isdigit(m_current_char));
}
} else {
// 1...9 or period
while (isdigit(m_current_char)) {
consume();
}
if (m_current_char == '.') {
consume();
while (isdigit(m_current_char)) {
consume();
}
}
if (m_current_char == 'e' || m_current_char == 'E') {
consume_exponent();
}
}
token_type = TokenType::NumericLiteral;
} else if (m_current_char == '"' || m_current_char == '\'') {
char stop_char = m_current_char;
@ -330,5 +394,4 @@ Token Lexer::next()
return m_current_token;
}
}

View file

@ -42,12 +42,14 @@ public:
private:
void consume();
void consume_exponent();
bool is_eof() const;
bool is_identifier_start() const;
bool is_identifier_middle() const;
bool is_line_comment_start() const;
bool is_block_comment_start() const;
bool is_block_comment_end() const;
bool is_numeric_literal_start() const;
void syntax_error(const char*);

View file

@ -0,0 +1,20 @@
try {
assert(0xff === 255);
assert(0XFF === 255);
assert(0o10 === 8);
assert(0O10 === 8);
assert(0b10 === 2);
assert(0B10 === 2);
assert(1e3 === 1000);
assert(1e+3 === 1000);
assert(1e-3 === 0.001);
assert(.1 === 0.1);
assert(.1e1 === 1);
assert(0.1e1 === 1);
assert(.1e+1 === 1);
assert(0.1e+1 === 1);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}

View file

@ -27,6 +27,7 @@
#include "Token.h"
#include <AK/Assertions.h>
#include <AK/StringBuilder.h>
#include <ctype.h>
namespace JS {
@ -52,7 +53,23 @@ const char* Token::name() const
double Token::double_value() const
{
ASSERT(type() == TokenType::NumericLiteral);
return strtod(String(m_value).characters(), nullptr);
String value_string(m_value);
if (value_string[0] == '0' && value_string.length() >= 2) {
if (value_string[1] == 'x' || value_string[1] == 'X') {
// hexadecimal
return static_cast<double>(strtoul(value_string.characters() + 2, nullptr, 16));
} else if (value_string[1] == 'o' || value_string[1] == 'O') {
// octal
return static_cast<double>(strtoul(value_string.characters() + 2, nullptr, 8));
} else if (value_string[1] == 'b' || value_string[1] == 'B') {
// binary
return static_cast<double>(strtoul(value_string.characters() + 2, nullptr, 2));
} else if (isdigit(value_string[1])) {
// also octal, but syntax error in strict mode
return static_cast<double>(strtoul(value_string.characters() + 1, nullptr, 8));
}
}
return strtod(value_string.characters(), nullptr);
}
String Token::string_value() const