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. :^)
This commit is contained in:
Andreas Kling 2020-03-07 19:42:11 +01:00
parent f2f16e1c24
commit f5476be702
Notes: sideshowbarker 2024-07-19 08:51:14 +09:00
14 changed files with 957 additions and 1 deletions

144
Libraries/LibJS/AST.cpp Normal file
View file

@ -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
Libraries/LibJS/AST.h Normal file
View file

@ -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
Libraries/LibJS/Forward.h Normal file
View file

@ -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;
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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()";
}
}

View file

@ -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
Libraries/LibJS/Makefile Normal file
View file

@ -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

View file

@ -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
Libraries/LibJS/Object.h Normal file
View file

@ -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
Libraries/LibJS/Value.cpp Normal file
View file

@ -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
Libraries/LibJS/Value.h Normal file
View file

@ -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;
}
}
}

View file

@ -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
Userland/js.cpp Normal file
View file

@ -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;
}