diff --git a/src/evaluator/evaluator.cpp b/src/evaluator/evaluator.cpp new file mode 100644 index 0000000..78aeaee --- /dev/null +++ b/src/evaluator/evaluator.cpp @@ -0,0 +1,41 @@ + +#include "evaluator.hpp" + +#include "ast/ast.hpp" +#include "ast/expressions/boolean.hpp" +#include "ast/expressions/integer.hpp" +#include "ast/program.hpp" +#include "ast/statements/expression.hpp" +#include "object/boolean.hpp" +#include "object/integers.hpp" +#include "object/null.hpp" + +#include + +namespace eval { + static object::null* null = new object::null; + + object::object* eval(std::vector statements) { + object::object* ret; + for (auto& stmt : statements) + ret = eval(stmt); + return ret; + } + + object::object* eval(ast::node* node) { + if (ast::integer_literal* integer = + dynamic_cast(node)) { + return new object::integer(integer->value); + } else if (ast::boolean_literal* boolean = + dynamic_cast(node)) { + return new object::boolean(boolean->value); + } else if (ast::program* program = dynamic_cast(node)) { + return eval(program->statements); + } else if (ast::expression_stmt* expression_stmt = + dynamic_cast(node)) { + return eval(expression_stmt->expression); + } + return null; + } + +} // namespace eval diff --git a/src/evaluator/evaluator.hpp b/src/evaluator/evaluator.hpp new file mode 100644 index 0000000..6cf3abd --- /dev/null +++ b/src/evaluator/evaluator.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "ast/ast.hpp" +#include "object/object.hpp" + +namespace eval { + object::object* eval(ast::node*); +} // namespace eval diff --git a/src/object/boolean.hpp b/src/object/boolean.hpp index 0ba3e06..14f9624 100644 --- a/src/object/boolean.hpp +++ b/src/object/boolean.hpp @@ -6,6 +6,8 @@ namespace object { bool value; ::object::type type = type::BOOLEAN_OBJ; + explicit boolean(bool value): value(value) {} + std::string inspect(); }; } // namespace object diff --git a/src/object/integers.hpp b/src/object/integers.hpp index e2de585..8982a58 100644 --- a/src/object/integers.hpp +++ b/src/object/integers.hpp @@ -6,6 +6,8 @@ namespace object { int value; ::object::type type = type::INTEGER_OBJ; + explicit integer(int value): value(value) {} + std::string inspect(); }; } // namespace object diff --git a/test/evaluator/integer.cpp b/test/evaluator/integer.cpp new file mode 100644 index 0000000..f36ae57 --- /dev/null +++ b/test/evaluator/integer.cpp @@ -0,0 +1,20 @@ +#include "doctest.h" +#include "utils.hpp" + +TEST_SUITE("Eval: integers") { + TEST_CASE_FIXTURE( + test::utils::EvalFixture, + "Simple integer expression statements" + ) { + struct test_struct { + std::string input; + int expected; + }; + struct test_struct tests[]{{"5", 5}, {"10", 10}}; + + for (auto& t : tests) { + setup(t.input); + test::utils::test_integer_object(result.get(), t.expected); + } + } +} diff --git a/test/utils/fixtures.cpp b/test/utils/fixtures.cpp index af11622..d0dcc17 100644 --- a/test/utils/fixtures.cpp +++ b/test/utils/fixtures.cpp @@ -1,3 +1,5 @@ +#include "evaluator/evaluator.hpp" +#include "object/object.hpp" #include "utils.hpp" #include @@ -30,4 +32,9 @@ namespace test::utils { "parse_program() returned a null pointer" ); } + + void EvalFixture::setup(std::string source) { + ParserFixture::setup(source); + result = std::unique_ptr(eval::eval(program.get())); + } } // namespace test::utils diff --git a/test/utils/test_eval.cpp b/test/utils/test_eval.cpp new file mode 100644 index 0000000..1de5de5 --- /dev/null +++ b/test/utils/test_eval.cpp @@ -0,0 +1,10 @@ +#include "object/integers.hpp" +#include "object/object.hpp" +#include "utils.hpp" + +namespace test::utils { + void test_integer_object(object::object* obj, int expected) { + object::integer* i = cast(obj); + CHECK(i->value == expected); + } +} // namespace test::utils diff --git a/test/utils/test.cpp b/test/utils/test_parser.cpp similarity index 100% rename from test/utils/test.cpp rename to test/utils/test_parser.cpp diff --git a/test/utils/utils.hpp b/test/utils/utils.hpp index d0c54af..115d795 100644 --- a/test/utils/utils.hpp +++ b/test/utils/utils.hpp @@ -2,6 +2,7 @@ #include "ast/ast.hpp" #include "lexer/lexer.hpp" +#include "object/object.hpp" #include "parser/parser.hpp" #include @@ -21,9 +22,15 @@ namespace test::utils { ParserFixture() = default; + virtual void setup(std::string); + }; + + struct EvalFixture : ParserFixture { + std::unique_ptr result; void setup(std::string); }; + // parser tests void test_identifier(ast::expression*, std::string); void test_integer_literal(ast::expression*, int); void test_boolean_literal(ast::expression*, bool); @@ -32,6 +39,9 @@ namespace test::utils { test_infix_expression(ast::expression*, std::any, std::string, std::any); void test_failing_parsing(std::string, std::vector, int = 0); + // eval tests + void test_integer_object(object::object*, int); + namespace { std::string demangle(const char* name) { @@ -73,4 +83,9 @@ namespace test::utils { return cast_impl(err); } + template + T* cast(object::object* err) { + return cast_impl(err); + } + } // namespace test::utils