LibJS: Lexer and parser support for "switch" statements
This commit is contained in:
parent
70dc80fa47
commit
1923051c5b
Notes:
sideshowbarker
2024-07-19 08:05:00 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/1923051c5bb
7 changed files with 164 additions and 2 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue