srctree

Robin Linden parent f4d8a851 f4ab2d12
js: Set up a new library with a basic executable JS AST

inlinesplit
filename was Deleted added: 180, removed: 3, total 177
@@ -0,0 +1,17 @@
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 
cc_library(
name = "js",
hdrs = glob(["*.h"]),
visibility = ["//visibility:public"],
)
 
[cc_test(
name = src[:-4],
size = "small",
srcs = [src],
deps = [
":js",
"//etest",
],
) for src in glob(["*_test.cpp"])]
 
filename was Deleted added: 180, removed: 3, total 177
@@ -0,0 +1,118 @@
// SPDX-FileCopyrightText: 2022 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#ifndef JS_AST_H_
#define JS_AST_H_
 
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>
 
// Based on
// https://github.com/estree/estree/blob/a965082b24524196232232ac75e3f80b17b28bc4/es5.md
 
namespace js {
namespace ast {
 
// TODO(robinlinden): This needs to support more values.
class Value {
public:
explicit Value() : value_{} {}
explicit Value(double value) : value_{value} {}
explicit Value(std::string value) : value_{std::move(value)} {}
 
// std::monostate -> undefined
using ValueT = std::variant<std::monostate, std::string, double>;
[[nodiscard]] ValueT const &value() const { return value_; }
 
[[nodiscard]] bool operator==(Value const &) const = default;
 
private:
ValueT value_;
};
 
struct Context {
[[nodiscard]] bool operator==(Context const &) const = default;
std::map<std::string, Value> variables;
};
 
class Node {
public:
virtual ~Node() = default;
virtual Value execute(Context &) const = 0;
};
 
class Expression : public Node {};
class Statement : public Node {};
class Pattern : public Node {};
class Declaration : public Statement {};
 
class Identifier : public Expression, public Pattern {
public:
explicit Identifier(std::string name) : name_{std::move(name)} {}
Value execute(Context &) const override { return Value{name_}; }
 
private:
std::string name_;
};
 
class Literal : public Expression {
public:
explicit Literal(Value value) : value_{std::move(value)} {}
Value execute(Context &) const override { return value_; }
 
private:
Value value_;
};
 
class Program : public Node {
public:
Value execute(Context &ctx) const override {
Value ret{};
for (auto const &statement : body) {
ret = statement->execute(ctx);
}
return ret;
}
 
std::vector<std::unique_ptr<Statement>> body;
};
 
class VariableDeclarator : public Node {
public:
Value execute(Context &ctx) const override {
auto name = std::get<std::string>(id->execute(ctx).value());
ctx.variables[std::move(name)] = init ? (*init)->execute(ctx) : Value{};
return Value{};
}
 
std::unique_ptr<Pattern> id;
std::optional<std::unique_ptr<Expression>> init;
};
 
class VariableDeclaration : public Declaration {
public:
Value execute(Context &ctx) const override {
for (auto const &declaration : declarations) {
declaration.execute(ctx);
}
 
return Value{};
}
 
std::vector<VariableDeclarator> declarations;
enum class Kind {
Var,
};
Kind kind{Kind::Var};
};
 
} // namespace ast
} // namespace js
 
#endif
 
filename was Deleted added: 180, removed: 3, total 177
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2022 Robin Lindén <dev@robinlinden.eu>
//
// SPDX-License-Identifier: BSD-2-Clause
 
#include "js/ast.h"
 
#include "etest/etest.h"
 
#include <string>
 
using namespace js::ast;
using namespace std::literals;
using etest::expect_eq;
 
int main() {
etest::test("literals", [] {
js::ast::Context ctx;
expect_eq(Literal{Value{5.}}.execute(ctx), Value{5.});
expect_eq(Literal{Value{"hello"s}}.execute(ctx), Value{"hello"s});
});
 
etest::test("variable declaration", [] {
// AST for `var a = 1`
auto init = std::make_unique<Literal>(Value{1.});
auto id = std::make_unique<Identifier>("a");
auto declarator = VariableDeclarator{};
declarator.id = std::move(id);
declarator.init = std::move(init);
 
auto declaration = std::make_unique<VariableDeclaration>();
declaration->declarations.push_back(std::move(declarator));
 
Program program;
program.body.push_back(std::move(declaration));
 
Context ctx;
expect_eq(program.execute(ctx), Value{});
expect_eq(ctx, Context{.variables = {{"a", Value{1.}}}});
});
 
return etest::run_all_tests();
}