summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-03-23 16:46:41 +0100
committerAndreas Kling <kling@serenityos.org>2020-03-23 16:52:58 +0100
commitfbb9e1b71507acefec54f8fbee68fa6b6d8a4512 (patch)
treea8cfda6e367a0f913d8817fe0e76bea8444b075f
parentb2f005125debe74051bf74d773c15efa39bbb949 (diff)
downloadserenity-fbb9e1b71507acefec54f8fbee68fa6b6d8a4512.zip
LibJS: Implement "else" parsing
We can now handle scripts with if/else in LibJS. Most of the changes are about fixing IfStatement to store the consequent and alternate node as Statements. Interpreter now also runs Statements, rather than running ScopeNodes.
-rw-r--r--Libraries/LibJS/AST.h14
-rw-r--r--Libraries/LibJS/Forward.h1
-rw-r--r--Libraries/LibJS/Interpreter.cpp12
-rw-r--r--Libraries/LibJS/Interpreter.h2
-rw-r--r--Libraries/LibJS/Parser.cpp18
5 files changed, 31 insertions, 16 deletions
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index 3d58f0b395..3f5e58bec5 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -26,11 +26,11 @@
#pragma once
+#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/RefPtr.h>
#include <AK/String.h>
-#include <AK/FlyString.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Value.h>
@@ -52,6 +52,7 @@ public:
virtual void dump(int indent) const;
virtual bool is_identifier() const { return false; }
virtual bool is_member_expression() const { return false; }
+ virtual bool is_scope_node() const { return false; }
protected:
ASTNode() {}
@@ -107,6 +108,7 @@ protected:
ScopeNode() {}
private:
+ virtual bool is_scope_node() const final { return true; }
NonnullRefPtrVector<Statement> m_children;
};
@@ -212,7 +214,7 @@ private:
class IfStatement : public Statement {
public:
- IfStatement(NonnullRefPtr<Expression> predicate, NonnullRefPtr<ScopeNode> consequent, RefPtr<ScopeNode> alternate)
+ IfStatement(NonnullRefPtr<Expression> predicate, NonnullRefPtr<Statement> consequent, RefPtr<Statement> alternate)
: m_predicate(move(predicate))
, m_consequent(move(consequent))
, m_alternate(move(alternate))
@@ -220,8 +222,8 @@ public:
}
const Expression& predicate() const { return *m_predicate; }
- const ScopeNode& consequent() const { return *m_consequent; }
- const ScopeNode* alternate() const { return m_alternate; }
+ const Statement& consequent() const { return *m_consequent; }
+ const Statement* alternate() const { return m_alternate; }
virtual Value execute(Interpreter&) const override;
virtual void dump(int indent) const override;
@@ -230,8 +232,8 @@ private:
virtual const char* class_name() const override { return "IfStatement"; }
NonnullRefPtr<Expression> m_predicate;
- NonnullRefPtr<ScopeNode> m_consequent;
- RefPtr<ScopeNode> m_alternate;
+ NonnullRefPtr<Statement> m_consequent;
+ RefPtr<Statement> m_alternate;
};
class WhileStatement : public Statement {
diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h
index 8e1febcfcd..359d6f6ab4 100644
--- a/Libraries/LibJS/Forward.h
+++ b/Libraries/LibJS/Forward.h
@@ -40,6 +40,7 @@ class Interpreter;
class Object;
class PrimitiveString;
class ScopeNode;
+class Statement;
class Value;
enum class DeclarationType;
diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp
index 47a63646d1..ac80f688b0 100644
--- a/Libraries/LibJS/Interpreter.cpp
+++ b/Libraries/LibJS/Interpreter.cpp
@@ -50,16 +50,20 @@ Interpreter::~Interpreter()
{
}
-Value Interpreter::run(const ScopeNode& scope_node, Vector<Argument> arguments, ScopeType scope_type)
+Value Interpreter::run(const Statement& statement, Vector<Argument> arguments, ScopeType scope_type)
{
- enter_scope(scope_node, move(arguments), scope_type);
+ if (!statement.is_scope_node())
+ return statement.execute(*this);
+
+ auto& block = static_cast<const BlockStatement&>(statement);
+ enter_scope(block, move(arguments), scope_type);
Value last_value = js_undefined();
- for (auto& node : scope_node.children()) {
+ for (auto& node : block.children()) {
last_value = node.execute(*this);
}
- exit_scope(scope_node);
+ exit_scope(block);
return last_value;
}
diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h
index 4c749fcd0e..1aa7b87f05 100644
--- a/Libraries/LibJS/Interpreter.h
+++ b/Libraries/LibJS/Interpreter.h
@@ -67,7 +67,7 @@ public:
Interpreter();
~Interpreter();
- Value run(const ScopeNode&, Vector<Argument> = {}, ScopeType = ScopeType::Block);
+ Value run(const Statement&, Vector<Argument> = {}, ScopeType = ScopeType::Block);
Object& global_object() { return *m_global_object; }
const Object& global_object() const { return *m_global_object; }
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index 6b5a09cb18..a6532fb775 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -197,8 +197,12 @@ NonnullRefPtr<Statement> Parser::parse_statement()
case TokenType::If:
return parse_if_statement();
default:
- if (match_expression())
- return adopt(*new ExpressionStatement(parse_expression(0)));
+ if (match_expression()) {
+ auto statement = adopt(*new ExpressionStatement(parse_expression(0)));
+ if (match(TokenType::Semicolon))
+ consume();
+ return statement;
+ }
m_has_errors = true;
expected("statement (missing switch case)");
consume();
@@ -520,9 +524,13 @@ NonnullRefPtr<IfStatement> Parser::parse_if_statement()
consume(TokenType::ParenOpen);
auto predicate = parse_expression(0);
consume(TokenType::ParenClose);
- auto consequent = parse_block_statement();
- // FIXME: Parse "else"
- return create_ast_node<IfStatement>(move(predicate), move(consequent), nullptr);
+ auto consequent = parse_statement();
+ RefPtr<Statement> alternate;
+ if (match(TokenType::Else)) {
+ consume(TokenType::Else);
+ alternate = parse_statement();
+ }
+ return create_ast_node<IfStatement>(move(predicate), move(consequent), move(alternate));
}
NonnullRefPtr<ForStatement> Parser::parse_for_statement()