LibJS: Lexer and parser support for "switch" statements

This commit is contained in:
Andreas Kling 2020-03-29 13:09:54 +02:00
parent 70dc80fa47
commit 1923051c5b
7 changed files with 164 additions and 2 deletions

View file

@ -871,4 +871,51 @@ Value ThrowStatement::execute(Interpreter& interpreter) const
return interpreter.throw_exception(value);
}
Value SwitchStatement::execute(Interpreter& interpreter) const
{
(void)interpreter;
return {};
}
Value SwitchCase::execute(Interpreter& interpreter) const
{
(void)interpreter;
return {};
}
Value BreakStatement::execute(Interpreter& interpreter) const
{
(void)interpreter;
return {};
}
void SwitchStatement::dump(int indent) const
{
ASTNode::dump(indent);
m_discriminant->dump(indent + 1);
for (auto& switch_case : m_cases) {
switch_case.dump(indent + 1);
}
}
void SwitchCase::dump(int indent) const
{
ASTNode::dump(indent);
print_indent(indent);
if (m_test) {
printf("(Test)\n");
m_test->dump(indent + 1);
} else {
printf("(Default)\n");
}
print_indent(indent);
printf("(Consequent)\n");
int i = 0;
for (auto& statement : m_consequent) {
print_indent(indent);
printf("[%d]\n", i++);
statement.dump(indent + 1);
}
}
}

View file

@ -496,7 +496,7 @@ public:
}
private:
virtual const char * class_name() const override { return "NewExpression"; }
virtual const char* class_name() const override { return "NewExpression"; }
virtual bool is_new_expression() const override { return true; }
};
@ -707,4 +707,50 @@ private:
NonnullRefPtr<Expression> m_argument;
};
class SwitchCase final : public ASTNode {
public:
SwitchCase(RefPtr<Expression> test, NonnullRefPtrVector<Statement> consequent)
: m_test(move(test))
, m_consequent(move(consequent))
{
}
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&) const override;
private:
virtual const char* class_name() const override { return "SwitchCase"; }
RefPtr<Expression> m_test;
NonnullRefPtrVector<Statement> m_consequent;
};
class SwitchStatement final : public Statement {
public:
SwitchStatement(NonnullRefPtr<Expression> discriminant, NonnullRefPtrVector<SwitchCase> cases)
: m_discriminant(move(discriminant))
, m_cases(move(cases))
{
}
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&) const override;
private:
virtual const char* class_name() const override { return "SwitchStatement"; }
NonnullRefPtr<Expression> m_discriminant;
NonnullRefPtrVector<SwitchCase> m_cases;
};
class BreakStatement final : public Statement {
public:
BreakStatement() {}
virtual Value execute(Interpreter&) const override;
private:
virtual const char* class_name() const override { return "BreakStatement"; }
};
}

View file

@ -43,9 +43,12 @@ Lexer::Lexer(StringView source)
{
if (s_keywords.is_empty()) {
s_keywords.set("await", TokenType::Await);
s_keywords.set("break", TokenType::Break);
s_keywords.set("case", TokenType::Case);
s_keywords.set("catch", TokenType::Catch);
s_keywords.set("class", TokenType::Class);
s_keywords.set("const", TokenType::Const);
s_keywords.set("default", TokenType::Default);
s_keywords.set("delete", TokenType::Delete);
s_keywords.set("do", TokenType::Do);
s_keywords.set("else", TokenType::Else);
@ -60,12 +63,13 @@ Lexer::Lexer(StringView source)
s_keywords.set("let", TokenType::Let);
s_keywords.set("new", TokenType::New);
s_keywords.set("null", TokenType::NullLiteral);
s_keywords.set("undefined", TokenType::UndefinedLiteral);
s_keywords.set("return", TokenType::Return);
s_keywords.set("switch", TokenType::Switch);
s_keywords.set("throw", TokenType::Throw);
s_keywords.set("true", TokenType::BoolLiteral);
s_keywords.set("try", TokenType::Try);
s_keywords.set("typeof", TokenType::Typeof);
s_keywords.set("undefined", TokenType::UndefinedLiteral);
s_keywords.set("var", TokenType::Var);
s_keywords.set("void", TokenType::Void);
s_keywords.set("while", TokenType::While);

View file

@ -202,6 +202,10 @@ NonnullRefPtr<Statement> Parser::parse_statement()
return parse_throw_statement();
case TokenType::Try:
return parse_try_statement();
case TokenType::Break:
return parse_break_statement();
case TokenType::Switch:
return parse_switch_statement();
default:
if (match_expression())
return adopt(*new ExpressionStatement(parse_expression(0)));
@ -559,6 +563,13 @@ NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement()
return create_ast_node<ThrowStatement>(parse_expression(0));
}
NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
{
consume(TokenType::Break);
// FIXME: Handle labels.
return create_ast_node<BreakStatement>();
}
NonnullRefPtr<TryStatement> Parser::parse_try_statement()
{
consume(TokenType::Try);
@ -578,6 +589,43 @@ NonnullRefPtr<TryStatement> Parser::parse_try_statement()
return create_ast_node<TryStatement>(move(block), move(handler), move(finalizer));
}
NonnullRefPtr<SwitchStatement> Parser::parse_switch_statement()
{
consume(TokenType::Switch);
consume(TokenType::ParenOpen);
auto determinant = parse_expression(0);
consume(TokenType::ParenClose);
consume(TokenType::CurlyOpen);
NonnullRefPtrVector<SwitchCase> cases;
while (match(TokenType::Case) || match(TokenType::Default))
cases.append(parse_switch_case());
consume(TokenType::CurlyClose);
return create_ast_node<SwitchStatement>(move(determinant), move(cases));
}
NonnullRefPtr<SwitchCase> Parser::parse_switch_case()
{
RefPtr<Expression> test;
if (consume().type() == TokenType::Case) {
test = parse_expression(0);
}
consume(TokenType::Colon);
NonnullRefPtrVector<Statement> consequent;
while (match_statement())
consequent.append(parse_statement());
return create_ast_node<SwitchCase>(move(test), move(consequent));
}
NonnullRefPtr<CatchClause> Parser::parse_catch_clause()
{
consume(TokenType::Catch);
@ -746,6 +794,8 @@ bool Parser::match_statement() const
|| type == TokenType::For
|| type == TokenType::Const
|| type == TokenType::CurlyOpen
|| type == TokenType::Switch
|| type == TokenType::Break
|| type == TokenType::Var;
}

View file

@ -55,6 +55,9 @@ public:
NonnullRefPtr<ThrowStatement> parse_throw_statement();
NonnullRefPtr<TryStatement> parse_try_statement();
NonnullRefPtr<CatchClause> parse_catch_clause();
NonnullRefPtr<SwitchStatement> parse_switch_statement();
NonnullRefPtr<SwitchCase> parse_switch_case();
NonnullRefPtr<BreakStatement> parse_break_statement();
NonnullRefPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<Expression> parse_primary_expression();

View file

@ -51,8 +51,12 @@ const char* Token::name(TokenType type)
return "BracketOpen";
case TokenType::BracketClose:
return "BracketClose";
case TokenType::Break:
return "Break";
case TokenType::Caret:
return "Caret";
case TokenType::Case:
return "Case";
case TokenType::Catch:
return "Catch";
case TokenType::Class:
@ -67,6 +71,8 @@ const char* Token::name(TokenType type)
return "CurlyClose";
case TokenType::CurlyOpen:
return "CurlyOpen";
case TokenType::Default:
return "Default";
case TokenType::Delete:
return "Delete";
case TokenType::Do:
@ -179,6 +185,8 @@ const char* Token::name(TokenType type)
return "SlashEquals";
case TokenType::StringLiteral:
return "StringLiteral";
case TokenType::Switch:
return "Switch";
case TokenType::Tilde:
return "Tilde";
case TokenType::Try:

View file

@ -41,7 +41,9 @@ enum class TokenType {
BoolLiteral,
BracketClose,
BracketOpen,
Break,
Caret,
Case,
Catch,
Class,
Colon,
@ -49,6 +51,7 @@ enum class TokenType {
Const,
CurlyClose,
CurlyOpen,
Default,
Delete,
Do,
DoubleAmpersand,
@ -105,6 +108,7 @@ enum class TokenType {
Slash,
SlashEquals,
StringLiteral,
Switch,
Throw,
Tilde,
Try,