浏览代码

LibJS: Start building a JavaScript engine for SerenityOS :^)

I always tell people to start building things by working on the thing
that seems the most interesting right now. The most interesting thing
here was an AST + simple interpreter, so that's where we start!

There is no lexer or parser yet, we build an AST directly and then
execute it in the interpreter, producing a return value.

This seems like the start of something interesting. :^)
Andreas Kling 5 年之前
父节点
当前提交
f5476be702

+ 144 - 0
Libraries/LibJS/AST.cpp

@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/AST.h>
+#include <LibJS/Function.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Value.h>
+#include <stdio.h>
+
+namespace JS {
+
+Value ScopeNode::execute(Interpreter& interpreter) const
+{
+    return interpreter.run(*this);
+}
+
+Value FunctionDeclaration::execute(Interpreter& interpreter) const
+{
+    auto* function = new Function(name(), body());
+    interpreter.global_object().put(m_name, Value(function));
+    return Value(function);
+}
+
+Value CallExpression::execute(Interpreter& interpreter) const
+{
+    auto callee = interpreter.global_object().get(name());
+    ASSERT(callee.is_object());
+    auto* callee_object = callee.as_object();
+    ASSERT(callee_object->is_function());
+    auto& function = static_cast<Function&>(*callee_object);
+    return interpreter.run(function.body());
+}
+
+Value ReturnStatement::execute(Interpreter& interpreter) const
+{
+    auto value = argument().execute(interpreter);
+    interpreter.do_return();
+    return value;
+}
+
+Value add(Value lhs, Value rhs)
+{
+    ASSERT(lhs.is_number());
+    ASSERT(rhs.is_number());
+    return Value(lhs.as_double() + rhs.as_double());
+}
+
+Value sub(Value lhs, Value rhs)
+{
+    ASSERT(lhs.is_number());
+    ASSERT(rhs.is_number());
+    return Value(lhs.as_double() - rhs.as_double());
+}
+
+Value BinaryExpression::execute(Interpreter& interpreter) const
+{
+    auto lhs_result = m_lhs->execute(interpreter);
+    auto rhs_result = m_rhs->execute(interpreter);
+
+    switch (m_op) {
+    case BinaryOp::Plus:
+        return add(lhs_result, rhs_result);
+    case BinaryOp::Minus:
+        return sub(lhs_result, rhs_result);
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+static void print_indent(int indent)
+{
+    for (int i = 0; i < indent * 2; ++i)
+        putchar(' ');
+}
+
+void ASTNode::dump(int indent) const
+{
+    print_indent(indent);
+    printf("%s\n", class_name());
+}
+
+void ScopeNode::dump(int indent) const
+{
+    ASTNode::dump(indent);
+    for (auto& child : children())
+        child.dump(indent + 1);
+}
+
+void BinaryExpression::dump(int indent) const
+{
+    ASTNode::dump(indent);
+    m_lhs->dump(indent + 1);
+    m_rhs->dump(indent + 1);
+}
+
+void CallExpression::dump(int indent) const
+{
+    print_indent(indent);
+    printf("%s '%s'\n", class_name(), name().characters());
+}
+
+void Literal::dump(int indent) const
+{
+    print_indent(indent);
+    printf("%d\n", (i32)m_value.as_double());
+}
+
+void FunctionDeclaration::dump(int indent) const
+{
+    print_indent(indent);
+    printf("%s '%s'\n", class_name(), name().characters());
+    body().dump(indent + 1);
+}
+
+void ReturnStatement::dump(int indent) const
+{
+    ASTNode::dump(indent);
+    argument().dump(indent + 1);
+}
+
+}

+ 188 - 0
Libraries/LibJS/AST.h

@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullOwnPtrVector.h>
+#include <AK/String.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+class ASTNode {
+public:
+    virtual ~ASTNode() {}
+    virtual const char* class_name() const = 0;
+    virtual Value execute(Interpreter&) const = 0;
+    virtual void dump(int indent) const;
+
+protected:
+    ASTNode() {}
+
+private:
+};
+
+class ScopeNode : public ASTNode {
+public:
+    template<typename T, typename... Args>
+    T& append(Args&&... args)
+    {
+        auto child = make<T>(forward<Args>(args)...);
+        m_children.append(move(child));
+        return static_cast<T&>(m_children.last());
+    }
+
+    const NonnullOwnPtrVector<ASTNode>& children() const { return m_children; }
+    virtual Value execute(Interpreter&) const override;
+    virtual void dump(int indent) const override;
+
+protected:
+    ScopeNode() {}
+
+private:
+    NonnullOwnPtrVector<ASTNode> m_children;
+};
+
+class Program : public ScopeNode {
+public:
+    Program() {}
+
+private:
+    virtual const char* class_name() const override { return "Program"; }
+};
+
+class BlockStatement : public ScopeNode {
+public:
+    BlockStatement() {}
+
+private:
+    virtual const char* class_name() const override { return "BlockStatement"; }
+};
+
+class FunctionDeclaration : public ASTNode {
+public:
+    FunctionDeclaration(String name, NonnullOwnPtr<ScopeNode> body)
+        : m_name(move(name))
+        , m_body(move(body))
+    {
+    }
+
+    String name() const { return m_name; }
+    const ScopeNode& body() const { return *m_body; }
+
+    virtual Value execute(Interpreter&) const override;
+    virtual void dump(int indent) const override;
+
+private:
+    virtual const char* class_name() const override { return "FunctionDeclaration"; }
+
+    String m_name;
+    NonnullOwnPtr<ScopeNode> m_body;
+};
+
+class Expression : public ASTNode {
+public:
+};
+
+class ReturnStatement : public ASTNode {
+public:
+    explicit ReturnStatement(NonnullOwnPtr<Expression> argument)
+        : m_argument(move(argument))
+    {
+    }
+
+    const Expression& argument() const { return *m_argument; }
+
+    virtual Value execute(Interpreter&) const override;
+    virtual void dump(int indent) const override;
+
+private:
+    virtual const char* class_name() const override { return "ReturnStatement"; }
+
+    NonnullOwnPtr<Expression> m_argument;
+};
+
+enum class BinaryOp {
+    Plus,
+    Minus,
+};
+
+class BinaryExpression : public Expression {
+public:
+    BinaryExpression(BinaryOp op, NonnullOwnPtr<Expression> lhs, NonnullOwnPtr<Expression> 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 "BinaryExpression"; }
+
+    BinaryOp m_op;
+    NonnullOwnPtr<Expression> m_lhs;
+    NonnullOwnPtr<Expression> m_rhs;
+};
+
+class Literal : public Expression {
+public:
+    explicit Literal(Value value)
+        : m_value(move(value))
+    {
+    }
+
+    virtual Value execute(Interpreter&) const override { return m_value; }
+    virtual void dump(int indent) const override;
+
+private:
+    virtual const char* class_name() const override { return "Literal"; }
+
+    Value m_value;
+};
+
+class CallExpression : public Expression {
+public:
+    explicit CallExpression(String name)
+        : m_name(move(name))
+    {
+    }
+
+    virtual Value execute(Interpreter&) const override;
+    virtual void dump(int indent) const override;
+
+    const String& name() const { return m_name; }
+
+private:
+    virtual const char* class_name() const override { return "CallExpression"; }
+
+    String m_name;
+};
+
+}

+ 37 - 0
Libraries/LibJS/Forward.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace JS {
+
+class ASTNode;
+class Interpreter;
+class Object;
+class ScopeNode;
+class Value;
+
+}

+ 42 - 0
Libraries/LibJS/Function.cpp

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Function.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+Function::Function(String name, const ScopeNode& body)
+    : m_name(move(name))
+    , m_body(body)
+{
+}
+
+Function::~Function()
+{
+}
+
+}

+ 52 - 0
Libraries/LibJS/Function.h

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <LibJS/Object.h>
+
+namespace JS {
+
+class Function : public Object {
+public:
+    Function(String name, const ScopeNode& body);
+    virtual ~Function();
+
+    const String& name() const { return m_name; }
+    const ScopeNode& body() const { return m_body; }
+
+protected:
+    virtual const char* class_name() const override { return "Function"; }
+
+private:
+    virtual bool is_function() const final { return true; }
+
+    String m_name;
+    const ScopeNode& m_body;
+};
+
+}

+ 72 - 0
Libraries/LibJS/Interpreter.cpp

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/AST.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Object.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+Interpreter::Interpreter()
+{
+    m_global_object = new Object;
+}
+
+Interpreter::~Interpreter()
+{
+}
+
+Value Interpreter::run(const ScopeNode& scope_node)
+{
+    enter_scope(scope_node);
+
+    Value last_value = js_undefined();
+    for (auto& node : scope_node.children()) {
+        last_value = node.execute(*this);
+    }
+
+    exit_scope(scope_node);
+    return last_value;
+}
+
+void Interpreter::enter_scope(const ScopeNode& scope_node)
+{
+    m_scope_stack.append({ scope_node });
+}
+
+void Interpreter::exit_scope(const ScopeNode& scope_node)
+{
+    ASSERT(&m_scope_stack.last().scope_node == &scope_node);
+    m_scope_stack.take_last();
+}
+
+void Interpreter::do_return()
+{
+    dbg() << "FIXME: Implement Interpreter::do_return()";
+}
+
+}

+ 59 - 0
Libraries/LibJS/Interpreter.h

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Vector.h>
+#include <LibJS/Forward.h>
+
+namespace JS {
+
+struct ScopeFrame {
+    const ScopeNode& scope_node;
+};
+
+class Interpreter {
+public:
+    Interpreter();
+    ~Interpreter();
+
+    Value run(const ScopeNode&);
+
+    Object& global_object() { return *m_global_object; }
+    const Object& global_object() const { return *m_global_object; }
+
+    void do_return();
+
+private:
+    void enter_scope(const ScopeNode&);
+    void exit_scope(const ScopeNode&);
+
+    Vector<ScopeFrame> m_scope_stack;
+
+    Object* m_global_object { nullptr };
+};
+
+}

+ 15 - 0
Libraries/LibJS/Makefile

@@ -0,0 +1,15 @@
+OBJS = \
+    AST.o \
+    Function.o \
+    Interpreter.o \
+    Object.o \
+    Value.o
+
+LIBRARY = libjs.a
+
+install:
+	mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/LibJS/
+	cp *.h $(SERENITY_BASE_DIR)/Root/usr/include/LibJS/
+	cp $(LIBRARY) $(SERENITY_BASE_DIR)/Root/usr/lib/
+
+include ../../Makefile.common

+ 43 - 0
Libraries/LibJS/Object.cpp

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/String.h>
+#include <LibJS/Object.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+Value Object::get(String property_name) const
+{
+    return m_properties.get(property_name).value_or(js_undefined());
+}
+
+void Object::put(String property_name, Value value)
+{
+    m_properties.set(property_name, move(value));
+}
+
+}

+ 50 - 0
Libraries/LibJS/Object.h

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/HashMap.h>
+#include <LibJS/Forward.h>
+
+namespace JS {
+
+class Object {
+public:
+    Object() {}
+    virtual ~Object() {}
+
+    Value get(String property_name) const;
+    void put(String property_name, Value);
+
+    virtual bool is_function() const { return false; }
+
+    virtual const char* class_name() const { return "Object"; }
+
+private:
+    HashMap<String, Value> m_properties;
+};
+
+}

+ 56 - 0
Libraries/LibJS/Value.cpp

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/String.h>
+#include <LibJS/Object.h>
+#include <LibJS/Value.h>
+
+namespace JS {
+
+String Value::to_string() const
+{
+    if (is_boolean())
+        return as_bool() ? "true" : "false";
+
+    if (is_null())
+        return "null";
+
+    if (is_undefined())
+        return "null";
+
+    if (is_number()) {
+        // FIXME: This needs improvement.
+        return String::number((i32)as_double());
+    }
+
+    if (is_object()) {
+        return String::format("{%s}", as_object()->class_name());
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+}

+ 139 - 0
Libraries/LibJS/Value.h

@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Assertions.h>
+#include <AK/Forward.h>
+#include <AK/LogStream.h>
+#include <LibJS/Forward.h>
+
+namespace JS {
+
+class Value {
+public:
+    enum class Type {
+        Undefined,
+        Null,
+        Number,
+        String,
+        Object,
+        Boolean,
+    };
+
+    bool is_undefined() const { return m_type == Type::Undefined; }
+    bool is_null() const { return m_type == Type::Null; }
+    bool is_number() const { return m_type == Type::Number; }
+    bool is_string() const { return m_type == Type::String; }
+    bool is_object() const { return m_type == Type::Object; }
+    bool is_boolean() const { return m_type == Type::Boolean; }
+
+    explicit Value(double value)
+        : m_type(Type::Number)
+    {
+        m_value.as_double = value;
+    }
+
+    explicit Value(i32 value)
+        : m_type(Type::Number)
+    {
+        m_value.as_double = value;
+    }
+
+    explicit Value(Object* object)
+        : m_type(Type::Object)
+    {
+        m_value.as_object = object;
+    }
+
+    explicit Value(Type type)
+        : m_type(type)
+    {
+    }
+
+    Type type() const { return m_type; }
+
+    double as_double() const
+    {
+        ASSERT(type() == Type::Number);
+        return m_value.as_double;
+    }
+
+    bool as_bool() const
+    {
+        ASSERT(type() == Type::Boolean);
+        return m_value.as_bool;
+    }
+
+    Object* as_object()
+    {
+        ASSERT(type() == Type::Object);
+        return m_value.as_object;
+    }
+
+    const Object* as_object() const
+    {
+        ASSERT(type() == Type::Object);
+        return m_value.as_object;
+    }
+
+    String to_string() const;
+
+private:
+    Type m_type { Type::Undefined };
+
+    union {
+        bool as_bool;
+        double as_double;
+        StringImpl* as_string;
+        Object* as_object;
+    } m_value;
+};
+
+inline Value js_undefined()
+{
+    return Value(Value::Type::Undefined);
+}
+
+inline Value js_null()
+{
+    return Value(Value::Type::Null);
+}
+
+inline const LogStream& operator<<(const LogStream& stream, const Value& value)
+{
+    switch (value.type()) {
+    case Value::Type::Boolean:
+        return stream << value.as_bool();
+    case Value::Type::Number:
+        return stream << (i32)value.as_double();
+    default:
+        ASSERT_NOT_REACHED();
+        return stream;
+    }
+}
+
+}

+ 1 - 1
Userland/Makefile

@@ -4,7 +4,7 @@ APPS = ${SRCS:.cpp=}
 
 EXTRA_CLEAN = $(APPS)
 
-LIB_DEPS = Web GUI Gfx Audio Protocol IPC Thread Pthread Core PCIDB Markdown
+LIB_DEPS = Web GUI Gfx Audio Protocol IPC Thread Pthread Core PCIDB Markdown JS
 
 include ../Makefile.common
 

+ 59 - 0
Userland/js.cpp

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/NonnullOwnPtr.h>
+#include <LibJS/AST.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Object.h>
+#include <LibJS/Value.h>
+#include <stdio.h>
+
+int main()
+{
+    auto program = make<JS::Program>();
+
+    auto block = make<JS::BlockStatement>();
+    block->append<JS::ReturnStatement>(
+        make<JS::BinaryExpression>(
+            JS::BinaryOp::Plus,
+            make<JS::BinaryExpression>(
+                JS::BinaryOp::Plus,
+                make<JS::Literal>(JS::Value(1)),
+                make<JS::Literal>(JS::Value(2))),
+            make<JS::Literal>(JS::Value(3))));
+
+    program->append<JS::FunctionDeclaration>("foo", move(block));
+    program->append<JS::CallExpression>("foo");
+
+    program->dump(0);
+
+    JS::Interpreter interpreter;
+    auto result = interpreter.run(*program);
+    dbg() << "Interpreter returned " << result;
+
+    printf("%s\n", result.to_string().characters());
+    return 0;
+}