From 1923051c5bb2add119e2c90a7a52e44c240813c6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 29 Mar 2020 13:09:54 +0200 Subject: [PATCH] LibJS: Lexer and parser support for "switch" statements --- Libraries/LibJS/AST.cpp | 47 +++++++++++++++++++++++++++++++++++ Libraries/LibJS/AST.h | 48 +++++++++++++++++++++++++++++++++++- Libraries/LibJS/Lexer.cpp | 6 ++++- Libraries/LibJS/Parser.cpp | 50 ++++++++++++++++++++++++++++++++++++++ Libraries/LibJS/Parser.h | 3 +++ Libraries/LibJS/Token.cpp | 8 ++++++ Libraries/LibJS/Token.h | 4 +++ 7 files changed, 164 insertions(+), 2 deletions(-) diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index bc933cabb0c..81b224ccf24 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -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); + } +} + } diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 819ab4b1c92..4372fbe1852 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -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 m_argument; }; +class SwitchCase final : public ASTNode { +public: + SwitchCase(RefPtr test, NonnullRefPtrVector 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 m_test; + NonnullRefPtrVector m_consequent; +}; + +class SwitchStatement final : public Statement { +public: + SwitchStatement(NonnullRefPtr discriminant, NonnullRefPtrVector 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 m_discriminant; + NonnullRefPtrVector 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"; } +}; + } diff --git a/Libraries/LibJS/Lexer.cpp b/Libraries/LibJS/Lexer.cpp index 97ee8ec901e..04ea59ef5a5 100644 --- a/Libraries/LibJS/Lexer.cpp +++ b/Libraries/LibJS/Lexer.cpp @@ -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); diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 9bb4001f84d..8defbef004c 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -202,6 +202,10 @@ NonnullRefPtr 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 Parser::parse_throw_statement() return create_ast_node(parse_expression(0)); } +NonnullRefPtr Parser::parse_break_statement() +{ + consume(TokenType::Break); + // FIXME: Handle labels. + return create_ast_node(); +} + NonnullRefPtr Parser::parse_try_statement() { consume(TokenType::Try); @@ -578,6 +589,43 @@ NonnullRefPtr Parser::parse_try_statement() return create_ast_node(move(block), move(handler), move(finalizer)); } +NonnullRefPtr Parser::parse_switch_statement() +{ + consume(TokenType::Switch); + + consume(TokenType::ParenOpen); + auto determinant = parse_expression(0); + consume(TokenType::ParenClose); + + consume(TokenType::CurlyOpen); + + NonnullRefPtrVector cases; + + while (match(TokenType::Case) || match(TokenType::Default)) + cases.append(parse_switch_case()); + + consume(TokenType::CurlyClose); + + return create_ast_node(move(determinant), move(cases)); +} + +NonnullRefPtr Parser::parse_switch_case() +{ + RefPtr test; + + if (consume().type() == TokenType::Case) { + test = parse_expression(0); + } + + consume(TokenType::Colon); + + NonnullRefPtrVector consequent; + while (match_statement()) + consequent.append(parse_statement()); + + return create_ast_node(move(test), move(consequent)); +} + NonnullRefPtr 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; } diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index 25bc408da4a..1ef482f2e2a 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -55,6 +55,9 @@ public: NonnullRefPtr parse_throw_statement(); NonnullRefPtr parse_try_statement(); NonnullRefPtr parse_catch_clause(); + NonnullRefPtr parse_switch_statement(); + NonnullRefPtr parse_switch_case(); + NonnullRefPtr parse_break_statement(); NonnullRefPtr parse_expression(int min_precedence, Associativity associate = Associativity::Right); NonnullRefPtr parse_primary_expression(); diff --git a/Libraries/LibJS/Token.cpp b/Libraries/LibJS/Token.cpp index c1902d4e13e..37938396681 100644 --- a/Libraries/LibJS/Token.cpp +++ b/Libraries/LibJS/Token.cpp @@ -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: diff --git a/Libraries/LibJS/Token.h b/Libraries/LibJS/Token.h index 880834d3015..472d835974a 100644 --- a/Libraries/LibJS/Token.h +++ b/Libraries/LibJS/Token.h @@ -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,