diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 2e6acb947c..acac0b3273 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -393,4 +393,64 @@ void WhileStatement::dump(int indent) const body().dump(indent + 1); } +Value Identifier::execute(Interpreter& interpreter) const +{ + return interpreter.get_variable(string()); +} + +void Identifier::dump(int indent) const +{ + print_indent(indent); + printf("Identifier \"%s\"\n", m_string.characters()); +} + +Value AssignmentExpression::execute(Interpreter& interpreter) const +{ + ASSERT(m_lhs->is_identifier()); + auto name = static_cast(*m_lhs).string(); + auto rhs_result = m_rhs->execute(interpreter); + + switch (m_op) { + case AssignmentOp::Assign: + interpreter.set_variable(name, rhs_result); + break; + } + return rhs_result; +} + +void AssignmentExpression::dump(int indent) const +{ + const char* op_string = nullptr; + switch (m_op) { + case AssignmentOp::Assign: + op_string = "="; + break; + } + + ASTNode::dump(indent); + print_indent(indent + 1); + printf("%s\n", op_string); + m_lhs->dump(indent + 1); + m_rhs->dump(indent + 1); +} + +Value VariableDeclaration::execute(Interpreter& interpreter) const +{ + interpreter.declare_variable(name().string()); + if (m_initializer) { + auto initalizer_result = m_initializer->execute(interpreter); + interpreter.set_variable(name().string(), initalizer_result); + } + return js_undefined(); +} + + +void VariableDeclaration::dump(int indent) const +{ + ASTNode::dump(indent); + m_name->dump(indent + 1); + if (m_initializer) + m_initializer->dump(indent + 1); +} + } diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 23a2dcbb3b..9e51124a30 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -40,6 +40,7 @@ public: virtual const char* class_name() const = 0; virtual Value execute(Interpreter&) const = 0; virtual void dump(int indent) const; + virtual bool is_identifier() const { return false; } protected: ASTNode() {} @@ -188,7 +189,7 @@ enum class BinaryOp { class BinaryExpression : public Expression { public: - BinaryExpression(BinaryOp op, NonnullOwnPtr lhs, NonnullOwnPtr rhs) + BinaryExpression(BinaryOp op, NonnullOwnPtr lhs, NonnullOwnPtr rhs) : m_op(op) , m_lhs(move(lhs)) , m_rhs(move(rhs)) @@ -202,8 +203,8 @@ private: virtual const char* class_name() const override { return "BinaryExpression"; } BinaryOp m_op; - NonnullOwnPtr m_lhs; - NonnullOwnPtr m_rhs; + NonnullOwnPtr m_lhs; + NonnullOwnPtr m_rhs; }; enum class LogicalOp { @@ -270,6 +271,25 @@ private: Value m_value; }; +class Identifier final : public ASTNode { +public: + explicit Identifier(String string) + : m_string(move(string)) + { + } + + const String& string() const { return m_string; } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + virtual bool is_identifier() const override { return true; } + +private: + virtual const char* class_name() const override { return "Identifier"; } + + String m_string; +}; + class CallExpression : public Expression { public: explicit CallExpression(String name) @@ -288,4 +308,48 @@ private: String m_name; }; +enum class AssignmentOp { + Assign, +}; + +class AssignmentExpression : public Expression { +public: + AssignmentExpression(AssignmentOp op, NonnullOwnPtr lhs, NonnullOwnPtr rhs) + : m_op(op) + , m_lhs(move(lhs)) + , m_rhs(move(rhs)) + { + } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + +private: + virtual const char* class_name() const override { return "AssignmentExpression"; } + + AssignmentOp m_op; + NonnullOwnPtr m_lhs; + NonnullOwnPtr m_rhs; +}; + +class VariableDeclaration : public ASTNode { +public: + VariableDeclaration(NonnullOwnPtr name, OwnPtr initializer) + : m_name(move(name)) + , m_initializer(move(initializer)) + { + } + + const Identifier& name() const { return *m_name; } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + +private: + virtual const char* class_name() const override { return "VariableDeclaration"; } + + NonnullOwnPtr m_name; + OwnPtr m_initializer; +}; + } diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 102ee10ed1..3c8f155825 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -56,7 +56,7 @@ Value Interpreter::run(const ScopeNode& scope_node) void Interpreter::enter_scope(const ScopeNode& scope_node) { - m_scope_stack.append({ scope_node }); + m_scope_stack.append({ scope_node, {} }); } void Interpreter::exit_scope(const ScopeNode& scope_node) @@ -70,4 +70,34 @@ void Interpreter::do_return() dbg() << "FIXME: Implement Interpreter::do_return()"; } +void Interpreter::declare_variable(String name) +{ + m_scope_stack.last().variables.set(move(name), js_undefined()); +} + +void Interpreter::set_variable(String name, Value value) +{ + for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { + auto& scope = m_scope_stack.at(i); + if (scope.variables.contains(name)) { + scope.variables.set(move(name), move(value)); + return; + } + } + + global_object().put(move(name), move(value)); +} + +Value Interpreter::get_variable(const String& name) +{ + for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { + auto& scope = m_scope_stack.at(i); + auto value = scope.variables.get(name); + if (value.has_value()) + return value.value(); + } + + return global_object().get(name); +} + } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 3de8e0a99e..e13dc8e518 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -34,6 +35,7 @@ namespace JS { struct ScopeFrame { const ScopeNode& scope_node; + HashMap variables; }; class Interpreter { @@ -50,6 +52,10 @@ public: void do_return(); + Value get_variable(const String& name); + void set_variable(String name, Value); + void declare_variable(String name); + private: void enter_scope(const ScopeNode&); void exit_scope(const ScopeNode&); diff --git a/Userland/js.cpp b/Userland/js.cpp index 2787ddedd4..419f4b7ced 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -31,24 +31,13 @@ #include #include +//static void build_program_1(JS::Program&); +static void build_program_2(JS::Program&); + int main() { - // function foo() { return (1 + 2) + 3; } - // foo(); auto program = make(); - - auto block = make(); - block->append( - make( - JS::BinaryOp::Plus, - make( - JS::BinaryOp::Plus, - make(JS::Value(1)), - make(JS::Value(2))), - make(JS::Value(3)))); - - program->append("foo", move(block)); - program->append("foo"); + build_program_2(*program); program->dump(0); @@ -68,3 +57,59 @@ int main() interpreter.heap().collect_garbage(); return 0; } + +#if 0 +void build_program_1(JS::Program& program) +{ + // function foo() { return (1 + 2) + 3; } + // foo(); + + auto block = make(); + block->append( + make( + JS::BinaryOp::Plus, + make( + JS::BinaryOp::Plus, + make(JS::Value(1)), + make(JS::Value(2))), + make(JS::Value(3)))); + + program.append("foo", move(block)); + program.append("foo"); +} +#endif + +void build_program_2(JS::Program& program) +{ + // c = 1; + // function foo() { + // var a = 5; + // var b = 7; + // return a + b + c; + // } + // foo(); + + program.append( + JS::AssignmentOp::Assign, + make("c"), + make(JS::Value(1))); + + auto block = make(); + block->append( + make("a"), + make(JS::Value(5))); + block->append( + make("b"), + make(JS::Value(7))); + + block->append( + make( + JS::BinaryOp::Plus, + make( + JS::BinaryOp::Plus, + make("a"), + make("b")), + make("c"))); + program.append("foo", move(block)); + program.append("foo"); +}