From 580522cae1a484dd8d5e174d98b498967ac0f31f Mon Sep 17 00:00:00 2001 From: hrzlgnm Date: Fri, 13 Dec 2024 23:41:16 +0100 Subject: [PATCH] feat: Use custom allocator instead of smart pointers (#100) --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 1 + source/ast/array_expression.hpp | 4 +- source/ast/binary_expression.hpp | 4 +- source/ast/boolean.hpp | 2 +- source/ast/builtin_function_expression.cpp | 6 +- source/ast/builtin_function_expression.hpp | 16 +- source/ast/call_expression.hpp | 6 +- source/ast/callable_expression.cpp | 2 +- source/ast/callable_expression.hpp | 15 +- source/ast/expression.hpp | 15 +- source/ast/function_expression.cpp | 4 +- source/ast/function_expression.hpp | 12 +- source/ast/hash_literal_expression.hpp | 4 +- source/ast/identifier.hpp | 4 +- source/ast/if_expression.hpp | 8 +- source/ast/index_expression.hpp | 6 +- source/ast/integer_literal.hpp | 2 +- source/ast/program.hpp | 6 +- source/ast/statements.cpp | 10 +- source/ast/statements.hpp | 21 +- source/ast/string_literal.hpp | 2 +- source/ast/unary_expression.hpp | 4 +- source/ast/util.hpp | 2 +- source/chungus.hpp | 39 ++ source/code/code.cpp | 6 +- source/code/code.hpp | 76 ++-- source/compiler/ast_compile.cpp | 11 +- source/compiler/compiler.cpp | 31 +- source/compiler/compiler.hpp | 29 +- source/compiler/symbol_table.cpp | 2 +- source/compiler/symbol_table.hpp | 24 +- source/eval/ast_eval.cpp | 479 +++++++++++---------- source/eval/environment.cpp | 20 +- source/eval/environment.hpp | 17 +- source/eval/environment_fwd.hpp | 6 - source/eval/object.cpp | 23 +- source/eval/object.hpp | 156 +++---- source/lexer/lexer.cpp | 98 +++-- source/main.cpp | 46 +- source/parser/parser.cpp | 219 +++++----- source/parser/parser.hpp | 46 +- source/vm/vm.cpp | 170 ++++---- source/vm/vm.hpp | 41 +- test/benchmark/source/main.cpp | 9 +- 45 files changed, 882 insertions(+), 824 deletions(-) create mode 100644 source/chungus.hpp delete mode 100644 source/eval/environment_fwd.hpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f13dd17..7d1175b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: matrix: toolchain: - { - runs_on: macos-12, + runs_on: macos-13, name: "macOS Clang", CC: clang, CXX: clang++, diff --git a/CMakeLists.txt b/CMakeLists.txt index b502e8a..955d617 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ target_sources(monkey_lib source/ast/statements.cpp source/ast/string_literal.cpp source/ast/unary_expression.cpp + source/chungus.hpp source/code/code.cpp source/compiler/ast_compile.cpp source/compiler/compiler.cpp diff --git a/source/ast/array_expression.hpp b/source/ast/array_expression.hpp index 2f53372..b4e60f0 100644 --- a/source/ast/array_expression.hpp +++ b/source/ast/array_expression.hpp @@ -6,8 +6,8 @@ struct array_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - std::vector elements; + std::vector elements; }; diff --git a/source/ast/binary_expression.hpp b/source/ast/binary_expression.hpp index 6c0e168..c38c4fa 100644 --- a/source/ast/binary_expression.hpp +++ b/source/ast/binary_expression.hpp @@ -5,8 +5,8 @@ struct binary_expression : unary_expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr left {}; + expression* left {}; }; diff --git a/source/ast/boolean.hpp b/source/ast/boolean.hpp index 6ac1aec..7ba6923 100644 --- a/source/ast/boolean.hpp +++ b/source/ast/boolean.hpp @@ -6,7 +6,7 @@ struct boolean : expression { explicit boolean(bool val); [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; bool value {}; diff --git a/source/ast/builtin_function_expression.cpp b/source/ast/builtin_function_expression.cpp index 59b6542..6cfe294 100644 --- a/source/ast/builtin_function_expression.cpp +++ b/source/ast/builtin_function_expression.cpp @@ -3,9 +3,9 @@ #include "eval/object.hpp" builtin_function_expression::builtin_function_expression( - std::string&& name, - std::vector&& params, - std::function&& bod) + std::string name, + std::vector params, + std::function bod) : callable_expression {std::move(params)} , name {std::move(name)} , body {std::move(bod)} diff --git a/source/ast/builtin_function_expression.hpp b/source/ast/builtin_function_expression.hpp index 34741b5..c8d3fde 100644 --- a/source/ast/builtin_function_expression.hpp +++ b/source/ast/builtin_function_expression.hpp @@ -8,18 +8,18 @@ struct builtin_function_expression : callable_expression { - builtin_function_expression(std::string&& name, - std::vector&& params, - std::function&& arguments)>&& bod); + builtin_function_expression(std::string name, + std::vector params, + std::function&& arguments)> bod); - [[nodiscard]] auto call(environment_ptr closure_env, - environment_ptr caller_env, - const std::vector& arguments) const -> object_ptr override; + [[nodiscard]] auto call(environment* closure_env, + environment* caller_env, + const std::vector& arguments) const -> const object* override; [[nodiscard]] auto string() const -> std::string override; auto compile(compiler& comp) const -> void override; - static const std::vector builtins; + static const std::vector builtins; std::string name; - std::function body; + std::function body; }; diff --git a/source/ast/call_expression.hpp b/source/ast/call_expression.hpp index 2500c54..3f3fae5 100644 --- a/source/ast/call_expression.hpp +++ b/source/ast/call_expression.hpp @@ -7,9 +7,9 @@ struct call_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr function {}; - std::vector arguments; + expression* function {}; + std::vector arguments; }; diff --git a/source/ast/callable_expression.cpp b/source/ast/callable_expression.cpp index 5eaeb39..67da00d 100644 --- a/source/ast/callable_expression.cpp +++ b/source/ast/callable_expression.cpp @@ -2,7 +2,7 @@ #include -callable_expression::callable_expression(std::vector&& params) +callable_expression::callable_expression(std::vector params) : parameters {std::move(params)} { } diff --git a/source/ast/callable_expression.hpp b/source/ast/callable_expression.hpp index 6e324cf..a0e0a1d 100644 --- a/source/ast/callable_expression.hpp +++ b/source/ast/callable_expression.hpp @@ -5,18 +5,13 @@ struct callable_expression : expression { - callable_expression(callable_expression&&) = delete; - auto operator=(callable_expression&&) -> callable_expression& = default; - callable_expression(const callable_expression&) = default; - auto operator=(const callable_expression&) -> callable_expression& = default; - explicit callable_expression(std::vector&& params); - ~callable_expression() override = default; + explicit callable_expression(std::vector params); - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] virtual auto call(environment_ptr closure_env, - environment_ptr caller_env, - const std::vector& arguments) const -> object_ptr = 0; + [[nodiscard]] virtual auto call(environment* closure_env, + environment* caller_env, + const std::vector& arguments) const -> const object* = 0; std::vector parameters; }; diff --git a/source/ast/expression.hpp b/source/ast/expression.hpp index 114b48f..8619e03 100644 --- a/source/ast/expression.hpp +++ b/source/ast/expression.hpp @@ -1,8 +1,5 @@ #pragma once -#include - -#include #include struct compiler; @@ -10,15 +7,13 @@ struct compiler; struct expression { expression() = default; - expression(const expression&) = default; - expression(expression&&) = default; - auto operator=(const expression&) -> expression& = default; - auto operator=(expression&&) -> expression& = default; virtual ~expression() = default; + expression(const expression&) = delete; + expression(expression&&) = delete; + auto operator=(const expression&) -> expression& = delete; + auto operator=(expression&&) -> expression& = delete; [[nodiscard]] virtual auto string() const -> std::string = 0; - [[nodiscard]] virtual auto eval(environment_ptr) const -> object_ptr = 0; + [[nodiscard]] virtual auto eval(environment*) const -> const object* = 0; virtual inline auto compile(compiler& /*comp*/) const -> void = 0; }; - -using expression_ptr = std::unique_ptr; diff --git a/source/ast/function_expression.cpp b/source/ast/function_expression.cpp index 92eb233..c316a43 100644 --- a/source/ast/function_expression.cpp +++ b/source/ast/function_expression.cpp @@ -2,9 +2,9 @@ #include -function_expression::function_expression(std::vector&& parameters, statement_ptr&& body) +function_expression::function_expression(std::vector&& parameters, statement* body) : callable_expression(std::move(parameters)) - , body(std::move(body)) + , body {body} { } diff --git a/source/ast/function_expression.hpp b/source/ast/function_expression.hpp index 49f342b..f9c3686 100644 --- a/source/ast/function_expression.hpp +++ b/source/ast/function_expression.hpp @@ -7,14 +7,14 @@ struct function_expression : callable_expression { - function_expression(std::vector&& parameters, statement_ptr&& body); + function_expression(std::vector&& parameters, statement* body); [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto call(environment_ptr closure_env, - environment_ptr caller_env, - const std::vector& arguments) const -> object_ptr override; + [[nodiscard]] auto call(environment* closure_env, + environment* caller_env, + const std::vector& arguments) const -> const object* override; auto compile(compiler& comp) const -> void override; - statement_ptr body; - std::string name; + const statement* body {}; + mutable std::string name; }; diff --git a/source/ast/hash_literal_expression.hpp b/source/ast/hash_literal_expression.hpp index 18f2a08..67d7859 100644 --- a/source/ast/hash_literal_expression.hpp +++ b/source/ast/hash_literal_expression.hpp @@ -8,8 +8,8 @@ struct hash_literal_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - std::vector> pairs; + std::vector> pairs; }; diff --git a/source/ast/identifier.hpp b/source/ast/identifier.hpp index d5e1dd0..19d5faa 100644 --- a/source/ast/identifier.hpp +++ b/source/ast/identifier.hpp @@ -6,10 +6,8 @@ struct identifier : expression { explicit identifier(std::string val); [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; std::string value; }; - -using identifier_ptr = std::unique_ptr; diff --git a/source/ast/if_expression.hpp b/source/ast/if_expression.hpp index 4ade7ee..ad79a32 100644 --- a/source/ast/if_expression.hpp +++ b/source/ast/if_expression.hpp @@ -6,10 +6,10 @@ struct if_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr condition {}; - block_statement_ptr consequence {}; - block_statement_ptr alternative {}; + expression* condition {}; + block_statement* consequence {}; + block_statement* alternative {}; }; diff --git a/source/ast/index_expression.hpp b/source/ast/index_expression.hpp index 103306c..022930c 100644 --- a/source/ast/index_expression.hpp +++ b/source/ast/index_expression.hpp @@ -5,9 +5,9 @@ struct index_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr left; - expression_ptr index; + expression* left {}; + expression* index {}; }; diff --git a/source/ast/integer_literal.hpp b/source/ast/integer_literal.hpp index abd8036..bb9e7b6 100644 --- a/source/ast/integer_literal.hpp +++ b/source/ast/integer_literal.hpp @@ -5,7 +5,7 @@ struct integer_literal : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> object* override; auto compile(compiler& comp) const -> void override; int64_t value {}; diff --git a/source/ast/program.hpp b/source/ast/program.hpp index b6a815e..a3518ad 100644 --- a/source/ast/program.hpp +++ b/source/ast/program.hpp @@ -8,10 +8,8 @@ struct program : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - std::vector statements {}; + std::vector statements; }; - -using program_ptr = std::unique_ptr; diff --git a/source/ast/statements.cpp b/source/ast/statements.cpp index 391105c..e01268d 100644 --- a/source/ast/statements.cpp +++ b/source/ast/statements.cpp @@ -1,7 +1,3 @@ -#include -#include -#include - #include "statements.hpp" #include @@ -10,17 +6,17 @@ auto let_statement::string() const -> std::string { - return fmt::format("let {} = {};", name->string(), value ? value->string() : std::string()); + return fmt::format("let {} = {};", name->string(), (value != nullptr) ? value->string() : std::string()); } auto return_statement::string() const -> std::string { - return fmt::format("return {};", value ? value->string() : std::string()); + return fmt::format("return {};", (value != nullptr) ? value->string() : std::string()); } auto expression_statement::string() const -> std::string { - if (expr) { + if (expr != nullptr) { return expr->string(); } return {}; diff --git a/source/ast/statements.hpp b/source/ast/statements.hpp index 83d0eef..ebb65e3 100644 --- a/source/ast/statements.hpp +++ b/source/ast/statements.hpp @@ -5,43 +5,40 @@ #include "identifier.hpp" using statement = expression; -using statement_ptr = expression_ptr; struct let_statement : statement { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - identifier_ptr name {}; - expression_ptr value {}; + const identifier* name {}; + const expression* value {}; }; struct return_statement : statement { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr value {}; + const expression* value {}; }; struct expression_statement : statement { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - expression_ptr expr {}; + const expression* expr {}; }; struct block_statement : statement { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; - std::vector statements {}; + std::vector statements; }; - -using block_statement_ptr = std::unique_ptr; diff --git a/source/ast/string_literal.hpp b/source/ast/string_literal.hpp index 9fdbc05..6c98e67 100644 --- a/source/ast/string_literal.hpp +++ b/source/ast/string_literal.hpp @@ -6,6 +6,6 @@ struct string_literal : identifier { using identifier::identifier; [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; }; diff --git a/source/ast/unary_expression.hpp b/source/ast/unary_expression.hpp index 5a75662..8364012 100644 --- a/source/ast/unary_expression.hpp +++ b/source/ast/unary_expression.hpp @@ -7,9 +7,9 @@ struct unary_expression : expression { [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto eval(environment_ptr env) const -> object_ptr override; + [[nodiscard]] auto eval(environment* env) const -> const object* override; auto compile(compiler& comp) const -> void override; token_type op {}; - expression_ptr right {}; + expression* right {}; }; diff --git a/source/ast/util.hpp b/source/ast/util.hpp index 98c2d46..2fce9cc 100644 --- a/source/ast/util.hpp +++ b/source/ast/util.hpp @@ -8,7 +8,7 @@ #include template -auto join(const std::vector>& nodes, std::string_view sep = {}) -> std::string +auto join(const std::vector& nodes, std::string_view sep = {}) -> std::string { auto strs = std::vector(); std::transform( diff --git a/source/chungus.hpp b/source/chungus.hpp new file mode 100644 index 0000000..dd99e4a --- /dev/null +++ b/source/chungus.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include + +template +class chungus +{ + public: + static void track(T* obj) { get_allocation_list().push_back(obj); } + + static void cleanup() + { + for (T* obj : get_allocation_list()) { + delete obj; // Properly call destructor + } + get_allocation_list().clear(); + } + + private: + static auto get_allocation_list() -> std::vector& + { + static std::vector allocations; + static bool registered = []() + { + return std::atexit(cleanup); // Register cleanup at exit + }(); + (void)registered; // Avoid unused variable warning + return allocations; + } +}; + +template +auto make(Args&&... args) -> T* +{ + T* p = new T(std::forward(args)...); + chungus::track(p); + return p; +} diff --git a/source/code/code.cpp b/source/code/code.cpp index d496605..f6a2227 100644 --- a/source/code/code.cpp +++ b/source/code/code.cpp @@ -136,6 +136,8 @@ auto lookup(opcodes opcode) -> std::optional return definitions.at(opcode); } +namespace +{ auto fmt_instruction(const definition& def, const operands& operands) -> std::string { auto count = def.operand_widths.size(); @@ -144,7 +146,7 @@ auto fmt_instruction(const definition& def, const operands& operands) -> std::st } switch (count) { case 0: - return def.name; + return std::string(def.name); case 1: return fmt::format("{} {}", def.name, operands.at(0)); case 2: @@ -154,6 +156,8 @@ auto fmt_instruction(const definition& def, const operands& operands) -> std::st } } +} // namespace + auto to_string(const instructions& code) -> std::string { std::string result; diff --git a/source/code/code.hpp b/source/code/code.hpp index 1569b30..d245728 100644 --- a/source/code/code.hpp +++ b/source/code/code.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -52,49 +51,50 @@ using instructions = std::vector; struct definition { - std::string name; - operands operand_widths {}; + std::string_view name; + operands operand_widths; }; using definition_type = std::map; const definition_type definitions { - {opcodes::constant, definition {"OpConstant", {2}}}, - {opcodes::add, definition {"OpAdd"}}, - {opcodes::sub, definition {"OpSub"}}, - {opcodes::mul, definition {"OpMul"}}, - {opcodes::div, definition {"OpDiv"}}, - {opcodes::pop, definition {"OpPop"}}, - {opcodes::tru, definition {"OpTrue"}}, - {opcodes::fals, definition {"OpFalse"}}, - {opcodes::equal, definition {"OpEqual"}}, - {opcodes::not_equal, definition {"OpNotEquql"}}, - {opcodes::greater_than, definition {"OpGreaterThan"}}, - {opcodes::minus, definition {"OpMinus"}}, - {opcodes::bang, definition {"OpBang"}}, - {opcodes::jump_not_truthy, definition {"OpJumpNotTruthy", {2}}}, - {opcodes::jump, definition {"OpJump", {2}}}, - {opcodes::null, definition {"OpNull"}}, - {opcodes::get_global, definition {"OpGetGlobal", {2}}}, - {opcodes::set_global, definition {"OpSetGlobal", {2}}}, - {opcodes::array, definition {"OpArray", {2}}}, - {opcodes::hash, definition {"OpHash", {2}}}, - {opcodes::index, definition {"OpIndex"}}, - {opcodes::call, definition {"OpCall", {1}}}, - {opcodes::return_value, definition {"OpReturnValue"}}, - {opcodes::ret, definition {"OpReturn"}}, - {opcodes::get_local, definition {"OpGetLocal", {1}}}, - {opcodes::set_local, definition {"OpSetLocal", {1}}}, - {opcodes::get_builtin, definition {"OpGetBuiltin", {1}}}, - {opcodes::closure, definition {"OpClosure", {2, 1}}}, - {opcodes::get_free, definition {"OpGetFree", {1}}}, - {opcodes::current_closure, definition {"OpCurrentClosure"}}, + {opcodes::constant, definition {.name = "OpConstant", .operand_widths = {2}}}, + {opcodes::add, definition {.name = "OpAdd"}}, + {opcodes::sub, definition {.name = "OpSub"}}, + {opcodes::mul, definition {.name = "OpMul"}}, + {opcodes::div, definition {.name = "OpDiv"}}, + {opcodes::pop, definition {.name = "OpPop"}}, + {opcodes::tru, definition {.name = "OpTrue"}}, + {opcodes::fals, definition {.name = "OpFalse"}}, + {opcodes::equal, definition {.name = "OpEqual"}}, + {opcodes::not_equal, definition {.name = "OpNotEquql"}}, + {opcodes::greater_than, definition {.name = "OpGreaterThan"}}, + {opcodes::minus, definition {.name = "OpMinus"}}, + {opcodes::bang, definition {.name = "OpBang"}}, + {opcodes::jump_not_truthy, definition {.name = "OpJumpNotTruthy", .operand_widths = {2}}}, + {opcodes::jump, definition {.name = "OpJump", .operand_widths = {2}}}, + {opcodes::null, definition {.name = "OpNull"}}, + {opcodes::get_global, definition {.name = "OpGetGlobal", .operand_widths = {2}}}, + {opcodes::set_global, definition {.name = "OpSetGlobal", .operand_widths = {2}}}, + {opcodes::array, definition {.name = "OpArray", .operand_widths = {2}}}, + {opcodes::hash, definition {.name = "OpHash", .operand_widths = {2}}}, + {opcodes::index, definition {.name = "OpIndex"}}, + {opcodes::call, definition {.name = "OpCall", .operand_widths = {1}}}, + {opcodes::return_value, definition {.name = "OpReturnValue"}}, + {opcodes::ret, definition {.name = "OpReturn"}}, + {opcodes::get_local, definition {.name = "OpGetLocal", .operand_widths = {1}}}, + {opcodes::set_local, definition {.name = "OpSetLocal", .operand_widths = {1}}}, + {opcodes::get_builtin, definition {.name = "OpGetBuiltin", .operand_widths = {1}}}, + {opcodes::closure, definition {.name = "OpClosure", .operand_widths = {2, 1}}}, + {opcodes::get_free, definition {.name = "OpGetFree", .operand_widths = {1}}}, + {opcodes::current_closure, definition {.name = "OpCurrentClosure"}}, }; -auto make(opcodes opcode, const operands& operands = {}) -> instructions; -auto make(opcodes opcode, size_t operand) -> instructions; -auto lookup(opcodes opcode) -> std::optional; -auto read_operands(const definition& def, const instructions& instr) -> std::pair; -auto to_string(const instructions& code) -> std::string; +[[nodiscard]] auto make(opcodes opcode, const operands& operands = {}) -> instructions; +[[nodiscard]] auto make(opcodes opcode, size_t operand) -> instructions; +[[nodiscard]] auto lookup(opcodes opcode) -> std::optional; +[[nodiscard]] auto read_operands(const definition& def, const instructions& instr) + -> std::pair; +[[nodiscard]] auto to_string(const instructions& code) -> std::string; [[nodiscard]] auto read_uint16_big_endian(const instructions& bytes, size_t offset) -> uint16_t; void write_uint16_big_endian(instructions& bytes, size_t offset, uint16_t value); diff --git a/source/compiler/ast_compile.cpp b/source/compiler/ast_compile.cpp index 76c4c96..d5a2853 100644 --- a/source/compiler/ast_compile.cpp +++ b/source/compiler/ast_compile.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -13,6 +11,7 @@ #include #include #include +#include #include #include @@ -101,7 +100,7 @@ auto if_expression::compile(compiler& comp) const -> void auto after_consequence = comp.current_instrs().size(); comp.change_operand(jump_not_truthy_pos, after_consequence); - if (!alternative) { + if (alternative == nullptr) { comp.emit(null); } else { alternative->compile(comp); @@ -122,7 +121,7 @@ auto index_expression::compile(compiler& comp) const -> void auto integer_literal::compile(compiler& comp) const -> void { - comp.emit(opcodes::constant, comp.add_constant(std::make_shared(value))); + comp.emit(opcodes::constant, comp.add_constant(make(value))); } auto program::compile(compiler& comp) const -> void @@ -165,7 +164,7 @@ auto block_statement::compile(compiler& comp) const -> void auto string_literal::compile(compiler& comp) const -> void { - comp.emit(opcodes::constant, comp.add_constant(std::make_shared(value))); + comp.emit(opcodes::constant, comp.add_constant(make(value))); } auto unary_expression::compile(compiler& comp) const -> void @@ -208,7 +207,7 @@ auto function_expression::compile(compiler& comp) const -> void comp.load_symbol(sym); } auto function_index = - comp.add_constant(std::make_shared(std::move(instrs), num_locals, parameters.size())); + comp.add_constant(make(std::move(instrs), num_locals, parameters.size())); comp.emit(closure, {function_index, free.size()}); } diff --git a/source/compiler/compiler.cpp b/source/compiler/compiler.cpp index f569491..3f80906 100644 --- a/source/compiler/compiler.cpp +++ b/source/compiler/compiler.cpp @@ -1,11 +1,12 @@ +#include #include -#include #include #include #include "compiler.hpp" #include +#include #include #include #include @@ -15,36 +16,36 @@ auto compiler::create() -> compiler { - auto symbols = symbol_table::create(); + auto* symbols = symbol_table::create(); for (size_t idx = 0; const auto& builtin : builtin_function_expression::builtins) { - symbols->define_builtin(idx++, builtin.name); + symbols->define_builtin(idx++, builtin->name); } - return {std::make_shared(), std::move(symbols)}; + return {make(), symbols}; } -compiler::compiler(constants_ptr&& consts, symbol_table_ptr symbols) - : m_consts {std::move(consts)} - , m_symbols {std::move(symbols)} +compiler::compiler(constants* consts, symbol_table* symbols) + : m_consts {consts} + , m_symbols {symbols} , m_scopes {1} { } -auto compiler::compile(const program_ptr& program) -> void +auto compiler::compile(const program* program) -> void { program->compile(*this); } -auto compiler::add_constant(object_ptr&& obj) -> size_t +auto compiler::add_constant(object* obj) -> size_t { - m_consts->push_back(std::move(obj)); + m_consts->push_back(obj); return m_consts->size() - 1; } -auto compiler::add_instructions(instructions&& ins) -> size_t +auto compiler::add_instructions(const instructions& ins) -> size_t { auto& scope = m_scopes[m_scope_index]; auto pos = scope.instrs.size(); - std::copy(ins.begin(), ins.end(), std::back_inserter(scope.instrs)); + std::copy(ins.cbegin(), ins.cend(), std::back_inserter(scope.instrs)); return pos; } @@ -107,7 +108,7 @@ auto compiler::current_instrs() const -> const instructions& auto compiler::byte_code() const -> bytecode { - return {m_scopes[m_scope_index].instrs, m_consts}; + return {.instrs = m_scopes[m_scope_index].instrs, .consts = m_consts}; } auto compiler::enter_scope() -> void @@ -174,7 +175,7 @@ auto compiler::number_symbol_definitions() const -> size_t return m_symbols->num_definitions(); } -auto compiler::consts() const -> constants_ptr +auto compiler::consts() const -> constants* { return m_consts; } @@ -205,7 +206,7 @@ auto check_no_parse_errors(const parser& prsr) -> bool return prsr.errors().empty(); } -using parsed_program = std::pair; +using parsed_program = std::pair; auto check_program(std::string_view input) -> parsed_program { diff --git a/source/compiler/compiler.hpp b/source/compiler/compiler.hpp index e81fc7e..974ccb4 100644 --- a/source/compiler/compiler.hpp +++ b/source/compiler/compiler.hpp @@ -8,13 +8,12 @@ #include "symbol_table.hpp" -using constants = std::vector; -using constants_ptr = std::shared_ptr; +using constants = std::vector; struct bytecode { - instructions instrs {}; - constants_ptr consts; + instructions instrs; + const constants* consts {}; }; struct emitted_instruction @@ -32,19 +31,19 @@ struct compilation_scope struct compiler { - auto compile(const program_ptr& program) -> void; - static auto create() -> compiler; + auto compile(const program* program) -> void; + [[nodiscard]] static auto create() -> compiler; - static inline auto create_with_state(constants_ptr constants, symbol_table_ptr symbols) -> compiler + [[nodiscard]] static auto create_with_state(constants* constants, symbol_table* symbols) -> compiler { - return compiler {std::move(constants), std::move(symbols)}; + return compiler {constants, symbols}; } - auto add_constant(object_ptr&& obj) -> size_t; - auto add_instructions(instructions&& ins) -> size_t; + [[nodiscard]] auto add_constant(object* obj) -> size_t; + [[nodiscard]] auto add_instructions(const instructions& ins) -> size_t; auto emit(opcodes opcode, operands&& operands = {}) -> size_t; - inline auto emit(opcodes opcode, size_t operand) -> size_t { return emit(opcode, std::vector {operand}); } + auto emit(opcodes opcode, size_t operand) -> size_t { return emit(opcode, std::vector {operand}); } [[nodiscard]] auto last_instruction_is(opcodes opcode) const -> bool; auto remove_last_pop() -> void; @@ -61,12 +60,12 @@ struct compiler [[nodiscard]] auto resolve_symbol(const std::string& name) const -> std::optional; [[nodiscard]] auto free_symbols() const -> std::vector; [[nodiscard]] auto number_symbol_definitions() const -> size_t; - [[nodiscard]] auto consts() const -> constants_ptr; + [[nodiscard]] auto consts() const -> constants*; private: - constants_ptr m_consts; - symbol_table_ptr m_symbols; + constants* m_consts {}; + symbol_table* m_symbols; std::vector m_scopes; size_t m_scope_index {0}; - compiler(constants_ptr&& consts, symbol_table_ptr symbols); + compiler(constants* consts, symbol_table* symbols); }; diff --git a/source/compiler/symbol_table.cpp b/source/compiler/symbol_table.cpp index e6f6f43..0dc5a71 100644 --- a/source/compiler/symbol_table.cpp +++ b/source/compiler/symbol_table.cpp @@ -34,7 +34,7 @@ auto operator<<(std::ostream& ost, const symbol& sym) -> std::ostream& return ost << fmt::format("symbol{{{}, {}, {}}}", sym.name, sym.scope, sym.index); } -symbol_table::symbol_table(symbol_table_ptr outer) +symbol_table::symbol_table(symbol_table* outer) : m_parent(std::move(outer)) { } diff --git a/source/compiler/symbol_table.hpp b/source/compiler/symbol_table.hpp index 4b71af4..62a0ffa 100644 --- a/source/compiler/symbol_table.hpp +++ b/source/compiler/symbol_table.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,12 +8,13 @@ #include #include +#include #include template using string_map = std::map>; -enum class symbol_scope +enum class symbol_scope : std::uint8_t { global, local, @@ -28,41 +30,37 @@ struct symbol symbol_scope scope {}; size_t index {}; - [[nodiscard]] inline auto is_local() const -> bool { return scope == symbol_scope::local; } + [[nodiscard]] auto is_local() const -> bool { return scope == symbol_scope::local; } }; auto operator==(const symbol& lhs, const symbol& rhs) -> bool; auto operator<<(std::ostream& ost, const symbol& sym) -> std::ostream&; struct symbol_table; -using symbol_table_ptr = std::shared_ptr; struct symbol_table : std::enable_shared_from_this { - static inline auto create() -> symbol_table_ptr { return std::make_shared(); } + static auto create() -> symbol_table* { return make(); } - static inline auto create_enclosed(symbol_table_ptr outer) -> symbol_table_ptr - { - return std::make_shared(std::move(outer)); - } + static auto create_enclosed(symbol_table* outer) -> symbol_table* { return make(outer); } - explicit symbol_table(symbol_table_ptr outer = {}); + explicit symbol_table(symbol_table* outer = {}); auto define(const std::string& name) -> symbol; auto define_builtin(size_t index, const std::string& name) -> symbol; auto define_function_name(const std::string& name) -> symbol; auto resolve(const std::string& name) -> std::optional; - inline auto is_global() const -> bool { return !m_parent; } + auto is_global() const -> bool { return m_parent == nullptr; } - inline auto parent() const -> symbol_table_ptr { return m_parent; } + auto parent() const -> symbol_table* { return m_parent; } - inline auto num_definitions() const -> size_t { return m_defs; } + auto num_definitions() const -> size_t { return m_defs; } auto free() const -> std::vector; private: auto define_free(const symbol& sym) -> symbol; - symbol_table_ptr m_parent; + symbol_table* m_parent {}; string_map m_store; size_t m_defs {}; std::vector m_free; diff --git a/source/eval/ast_eval.cpp b/source/eval/ast_eval.cpp index de3a6c7..86b200c 100644 --- a/source/eval/ast_eval.cpp +++ b/source/eval/ast_eval.cpp @@ -1,6 +1,6 @@ +#include #include #include -#include #include #include @@ -16,39 +16,41 @@ #include #include #include +#include #include #include #include -#include "code/code.hpp" #include "environment.hpp" #include "object.hpp" -auto array_expression::eval(environment_ptr env) const -> object_ptr +auto array_expression::eval(environment* env) const -> const object* { array_object::array arr; for (const auto& element : elements) { - auto evaluated = element->eval(env); + auto* evaluated = element->eval(env); if (evaluated->is_error()) { return evaluated; } - arr.push_back(std::move(evaluated)); + arr.push_back(evaluated); } - return std::make_shared(std::move(arr)); + return make(std::move(arr)); } -auto apply_integer_binary_operator(token_type oper, const int64_t left, const int64_t right) -> object_ptr +namespace +{ +auto apply_integer_binary_operator(token_type oper, const int64_t left, const int64_t right) -> const object* { using enum token_type; switch (oper) { case plus: - return std::make_shared(left + right); + return make(left + right); case asterisk: - return std::make_shared(left * right); + return make(left * right); case minus: - return std::make_shared(left - right); + return make(left - right); case slash: - return std::make_shared(left / right); + return make(left / right); case less_than: return native_bool_to_object(left < right); case greater_than: @@ -62,12 +64,12 @@ auto apply_integer_binary_operator(token_type oper, const int64_t left, const in } } -auto apply_string_binary_operator(token_type oper, const std::string& left, const std::string& right) -> object_ptr +auto apply_string_binary_operator(token_type oper, const std::string& left, const std::string& right) -> const object* { using enum token_type; switch (oper) { case plus: - return std::make_shared(left + right); + return make(left + right); case less_than: return native_bool_to_object(left < right); case greater_than: @@ -80,15 +82,16 @@ auto apply_string_binary_operator(token_type oper, const std::string& left, cons return {}; } } +} // namespace -auto binary_expression::eval(environment_ptr env) const -> object_ptr +auto binary_expression::eval(environment* env) const -> const object* { - auto evaluated_left = left->eval(env); + const auto* evaluated_left = left->eval(env); if (evaluated_left->is_error()) { return evaluated_left; } - auto evaluated_right = right->eval(env); + const auto* evaluated_right = right->eval(env); if (evaluated_right->is_error()) { return evaluated_right; } @@ -98,35 +101,35 @@ auto binary_expression::eval(environment_ptr env) const -> object_ptr using enum object::object_type; if (evaluated_left->is(string) && evaluated_right->is(string)) { - auto res = apply_string_binary_operator( + const auto* res = apply_string_binary_operator( op, evaluated_left->as()->value, evaluated_right->as()->value); - if (res) { + if (res != nullptr) { return res; } } if (evaluated_left->is(integer) && evaluated_right->is(integer)) { - auto res = apply_integer_binary_operator( + const auto* res = apply_integer_binary_operator( op, evaluated_left->as()->value, evaluated_right->as()->value); - if (res) { + if (res != nullptr) { return res; } } return make_error("unknown operator: {} {} {}", evaluated_left->type(), op, evaluated_right->type()); } -auto boolean::eval(environment_ptr /*env*/) const -> object_ptr +auto boolean::eval(environment* /*env*/) const -> const object* { return native_bool_to_object(value); } -auto function_expression::call(environment_ptr closure_env, - environment_ptr caller_env, - const std::vector& arguments) const -> object_ptr +auto function_expression::call(environment* closure_env, + environment* caller_env, + const std::vector& arguments) const -> const object* { - auto locals = std::make_shared(closure_env); + auto* locals = make(closure_env); for (auto arg_itr = arguments.begin(); const auto& parameter : parameters) { if (arg_itr != arguments.end()) { - const auto& arg = *(arg_itr++); + const expression* arg = *(arg_itr++); locals->set(parameter, arg->eval(caller_env)); } else { locals->set(parameter, {}); @@ -135,68 +138,68 @@ auto function_expression::call(environment_ptr closure_env, return body->eval(locals); } -auto call_expression::eval(environment_ptr env) const -> object_ptr +auto call_expression::eval(environment* env) const -> const object* { - auto evaluated = function->eval(env); + const auto* evaluated = function->eval(env); if (evaluated->is_error()) { return evaluated; } - auto fn = evaluated->as(); + const auto* fn = evaluated->as(); return fn->callable->call(fn->closure_env, env, arguments); } -auto hash_literal_expression::eval(environment_ptr env) const -> object_ptr +auto hash_literal_expression::eval(environment* env) const -> const object* { hash_object::hash result; for (const auto& [key, value] : pairs) { - auto eval_key = key->eval(env); + const auto* eval_key = key->eval(env); if (eval_key->is_error()) { return eval_key; } if (!eval_key->is_hashable()) { return make_error("unusable as hash key {}", eval_key->type()); } - auto eval_val = value->eval(env); + const auto* eval_val = value->eval(env); if (eval_val->is_error()) { return eval_val; } result.insert({eval_key->as()->hash_key(), eval_val}); } - return std::make_shared(std::move(result)); + return make(std::move(result)); } -auto identifier::eval(environment_ptr env) const -> object_ptr +auto identifier::eval(environment* env) const -> const object* { - auto val = env->get(value); + const auto* val = env->get(value); if (val->is(object::object_type::null)) { return make_error("identifier not found: {}", value); } return val; } -auto if_expression::eval(environment_ptr env) const -> object_ptr +auto if_expression::eval(environment* env) const -> const object* { - auto evaluated_condition = condition->eval(env); + const auto* evaluated_condition = condition->eval(env); if (evaluated_condition->is_error()) { return evaluated_condition; } if (evaluated_condition->is_truthy()) { return consequence->eval(env); } - if (alternative) { + if (alternative != nullptr) { return alternative->eval(env); } - return null; + return native_null(); } -auto index_expression::eval(environment_ptr env) const -> object_ptr +auto index_expression::eval(environment* env) const -> const object* { - auto evaluated_left = left->eval(env); + const auto* evaluated_left = left->eval(env); if (evaluated_left->is_error()) { return evaluated_left; } - auto evaluated_index = index->eval(env); + const auto* evaluated_index = index->eval(env); if (evaluated_index->is_error()) { return evaluated_index; } @@ -206,7 +209,7 @@ auto index_expression::eval(environment_ptr env) const -> object_ptr auto index = evaluated_index->as()->value; auto max = static_cast(arr.size() - 1); if (index < 0 || index > max) { - return ::null; + return native_null(); } return arr.at(static_cast(index)); } @@ -216,9 +219,9 @@ auto index_expression::eval(environment_ptr env) const -> object_ptr auto index = evaluated_index->as()->value; auto max = static_cast(str.size() - 1); if (index < 0 || index > max) { - return ::null; + return native_null(); } - return std::make_shared(str.substr(static_cast(index), 1)); + return make(str.substr(static_cast(index), 1)); } if (evaluated_left->is(hash)) { @@ -228,22 +231,22 @@ auto index_expression::eval(environment_ptr env) const -> object_ptr } const auto hash_key = evaluated_index->as()->hash_key(); if (!hsh.contains(hash_key)) { - return ::null; + return native_null(); } return hsh.at(hash_key); } return make_error("index operator not supported: {}", evaluated_left->type()); } -auto integer_literal::eval(environment_ptr /*env*/) const -> object_ptr +auto integer_literal::eval(environment* /*env*/) const -> object* { - return std::make_shared(value); + return make(value); } -auto program::eval(environment_ptr env) const -> object_ptr +auto program::eval(environment* env) const -> const object* { - object_ptr result; - for (const auto& statement : statements) { + const object* result = nullptr; + for (const auto* statement : statements) { result = statement->eval(env); if (result->is_return_value) { return result; @@ -255,40 +258,40 @@ auto program::eval(environment_ptr env) const -> object_ptr return result; } -auto let_statement::eval(environment_ptr env) const -> object_ptr +auto let_statement::eval(environment* env) const -> const object* { - auto val = value->eval(env); + const auto* val = value->eval(env); if (val->is_error()) { return val; } env->set(name->value, val); - return null; + return native_null(); } -auto return_statement::eval(environment_ptr env) const -> object_ptr +auto return_statement::eval(environment* env) const -> const object* { - if (value) { - auto evaluated = value->eval(env); + if (value != nullptr) { + const auto* evaluated = value->eval(env); if (evaluated->is_error()) { return evaluated; } evaluated->is_return_value = true; return evaluated; } - return null; + return native_null(); } -auto expression_statement::eval(environment_ptr env) const -> object_ptr +auto expression_statement::eval(environment* env) const -> const object* { - if (expr) { + if (expr != nullptr) { return expr->eval(env); } - return null; + return native_null(); } -auto block_statement::eval(environment_ptr env) const -> object_ptr +auto block_statement::eval(environment* env) const -> const object* { - object_ptr result; + const object* result = nullptr; for (const auto& stmt : statements) { result = stmt->eval(env); if (result->is_return_value || result->is_error()) { @@ -298,15 +301,15 @@ auto block_statement::eval(environment_ptr env) const -> object_ptr return result; } -auto string_literal::eval(environment_ptr /*env*/) const -> object_ptr +auto string_literal::eval(environment* /*env*/) const -> const object* { - return std::make_shared(value); + return make(value); } -auto unary_expression::eval(environment_ptr env) const -> object_ptr +auto unary_expression::eval(environment* env) const -> const object* { using enum token_type; - auto evaluated_value = right->eval(env); + const auto* evaluated_value = right->eval(env); if (evaluated_value->is_error()) { return evaluated_value; } @@ -315,10 +318,10 @@ auto unary_expression::eval(environment_ptr env) const -> object_ptr if (!evaluated_value->is(object::object_type::integer)) { return make_error("unknown operator: -{}", evaluated_value->type()); } - return std::make_shared(-evaluated_value->as()->value); + return make(-evaluated_value->as()->value); case exclamation: if (!evaluated_value->is(object::object_type::boolean)) { - return false_object; + return native_bool_to_object(/*val=*/false); } return native_bool_to_object(!evaluated_value->as()->value); default: @@ -326,167 +329,175 @@ auto unary_expression::eval(environment_ptr env) const -> object_ptr } } -auto builtin_function_expression::call(environment_ptr /*closure_env*/, - environment_ptr caller_env, - const std::vector& arguments) const -> object_ptr +auto builtin_function_expression::call(environment* /*closure_env*/, + environment* caller_env, + const std::vector& arguments) const -> const object* { array_object::array args; std::transform(arguments.cbegin(), arguments.cend(), std::back_inserter(args), - [&caller_env](const expression_ptr& expr) { return expr->eval(caller_env); }); + [caller_env](const expression* expr) { return expr->eval(caller_env); }); return body(std::move(args)); } -const std::vector builtin_function_expression::builtins { - {"len", - {"val"}, - [](const array_object::array& arguments) -> object_ptr - { - if (arguments.size() != 1) { - return make_error("wrong number of arguments to len(): expected=1, got={}", arguments.size()); - } - const auto& maybe_string_or_array = arguments.at(0); - using enum object::object_type; - if (maybe_string_or_array->is(string)) { - const auto& str = maybe_string_or_array->as()->value; - return std::make_shared(str.size()); - } - if (maybe_string_or_array->is(array)) { - const auto& arr = maybe_string_or_array->as()->elements; - - return std::make_shared(arr.size()); - } - return make_error("argument of type {} to len() is not supported", maybe_string_or_array->type()); - }}, - {"puts", - {"str"}, - [](const array_object::array& arguments) -> object_ptr - { - using enum object::object_type; - for (bool first = true; const auto& arg : arguments) { - if (!first) { - fmt::print(" "); - } - if (arg->is(string)) { - fmt::print("{}", arg->as()->value); - } else { - fmt::print("{}", arg->inspect()); - } - first = false; - } - fmt::print("\n"); - return ::null; - }}, - {"first", - {"arr"}, - [](const array_object::array& arguments) -> object_ptr - { - if (arguments.size() != 1) { - return make_error("wrong number of arguments to first(): expected=1, got={}", arguments.size()); - } - const auto& maybe_string_or_array = arguments.at(0); - using enum object::object_type; - if (maybe_string_or_array->is(string)) { - const auto& str = maybe_string_or_array->as()->value; - if (!str.empty()) { - return std::make_shared(str.substr(0, 1)); - } - return ::null; - } - if (maybe_string_or_array->is(array)) { - const auto& arr = maybe_string_or_array->as()->elements; - if (!arr.empty()) { - return arr.front(); - } - return ::null; - } - return make_error("argument of type {} to first() is not supported", maybe_string_or_array->type()); - }}, - {"last", - {"arr"}, - [](const array_object::array& arguments) -> object_ptr - { - if (arguments.size() != 1) { - return make_error("wrong number of arguments to last(): expected=1, got={}", arguments.size()); - } - const auto& maybe_string_or_array = arguments.at(0); - using enum object::object_type; - if (maybe_string_or_array->is(string)) { - const auto& str = maybe_string_or_array->as()->value; - if (!str.empty()) { - return std::make_shared(str.substr(str.length() - 1, 1)); - } - return ::null; - } - if (maybe_string_or_array->is(array)) { - const auto& arr = maybe_string_or_array->as()->elements; - if (!arr.empty()) { - return arr.back(); - } - return ::null; - } - return make_error("argument of type {} to last() is not supported", maybe_string_or_array->type()); - }}, - {"rest", - {"arr"}, - [](const array_object::array& arguments) -> object_ptr - { - if (arguments.size() != 1) { - return make_error("wrong number of arguments to rest(): expected=1, got={}", arguments.size()); - } - const auto& maybe_string_or_array = arguments.at(0); - using enum object::object_type; - if (maybe_string_or_array->is(string)) { - const auto& str = maybe_string_or_array->as()->value; - if (str.size() > 1) { - return std::make_shared(str.substr(1)); - } - return ::null; - } - if (maybe_string_or_array->is(array)) { - const auto& arr = maybe_string_or_array->as()->elements; - if (arr.size() > 1) { - array_object::array rest; - std::copy(arr.cbegin() + 1, arr.cend(), std::back_inserter(rest)); - return std::make_shared(std::move(rest)); - } - return ::null; - } - return make_error("argument of type {} to rest() is not supported", maybe_string_or_array->type()); - }}, - {"push", - {"arr", "val"}, - [](const array_object::array& arguments) -> object_ptr - { - if (arguments.size() != 2) { - return make_error("wrong number of arguments to push(): expected=2, got={}", arguments.size()); - } - const auto& lhs = arguments[0]; - const auto& rhs = arguments[1]; - using enum object::object_type; - if (lhs->is(array)) { - auto copy = lhs->as()->elements; - copy.push_back(rhs); - return std::make_shared(std::move(copy)); - } - if (lhs->is(string) && rhs->is(string)) { - auto copy = lhs->as()->value; - copy.append(rhs->as()->value); - return std::make_shared(std::move(copy)); - } - return make_error("argument of type {} and {} to push() are not supported", lhs->type(), rhs->type()); - }}, -}; +namespace +{ +const builtin_function_expression builtin_len { + "len", + {"val"}, + [](const array_object::array& arguments) -> const object* + { + if (arguments.size() != 1) { + return make_error("wrong number of arguments to len(): expected=1, got={}", arguments.size()); + } + const auto& maybe_string_or_array = arguments.at(0); + using enum object::object_type; + if (maybe_string_or_array->is(string)) { + const auto& str = maybe_string_or_array->as()->value; + return make(str.size()); + } + if (maybe_string_or_array->is(array)) { + const auto& arr = maybe_string_or_array->as()->elements; -auto callable_expression::eval(environment_ptr env) const -> object_ptr + return make(arr.size()); + } + return make_error("argument of type {} to len() is not supported", maybe_string_or_array->type()); + }}; +const builtin_function_expression builtin_puts {{"puts"}, + {"str"}, + [](const array_object::array& arguments) -> const object* + { + using enum object::object_type; + for (bool first = true; const auto& arg : arguments) { + if (!first) { + fmt::print(" "); + } + if (arg->is(string)) { + fmt::print("{}", arg->as()->value); + } else { + fmt::print("{}", arg->inspect()); + } + first = false; + } + fmt::print("\n"); + return native_null(); + }}; +const builtin_function_expression builtin_first { + "first", + {"arr"}, + [](const array_object::array& arguments) -> const object* + { + if (arguments.size() != 1) { + return make_error("wrong number of arguments to first(): expected=1, got={}", arguments.size()); + } + const auto& maybe_string_or_array = arguments.at(0); + using enum object::object_type; + if (maybe_string_or_array->is(string)) { + const auto& str = maybe_string_or_array->as()->value; + if (!str.empty()) { + return make(str.substr(0, 1)); + } + return native_null(); + } + if (maybe_string_or_array->is(array)) { + const auto& arr = maybe_string_or_array->as()->elements; + if (!arr.empty()) { + return arr.front(); + } + return native_null(); + } + return make_error("argument of type {} to first() is not supported", maybe_string_or_array->type()); + }}; +const builtin_function_expression builtin_last { + "last", + {"arr"}, + [](const array_object::array& arguments) -> const object* + { + if (arguments.size() != 1) { + return make_error("wrong number of arguments to last(): expected=1, got={}", arguments.size()); + } + const auto& maybe_string_or_array = arguments.at(0); + using enum object::object_type; + if (maybe_string_or_array->is(string)) { + const auto& str = maybe_string_or_array->as()->value; + if (!str.empty()) { + return make(str.substr(str.length() - 1, 1)); + } + return native_null(); + } + if (maybe_string_or_array->is(array)) { + const auto& arr = maybe_string_or_array->as()->elements; + if (!arr.empty()) { + return arr.back(); + } + return native_null(); + } + return make_error("argument of type {} to last() is not supported", maybe_string_or_array->type()); + }}; +const builtin_function_expression builtin_rest { + "rest", + {"arr"}, + [](const array_object::array& arguments) -> const object* + { + if (arguments.size() != 1) { + return make_error("wrong number of arguments to rest(): expected=1, got={}", arguments.size()); + } + const auto& maybe_string_or_array = arguments.at(0); + using enum object::object_type; + if (maybe_string_or_array->is(string)) { + const auto& str = maybe_string_or_array->as()->value; + if (str.size() > 1) { + return make(str.substr(1)); + } + return native_null(); + } + if (maybe_string_or_array->is(array)) { + const auto& arr = maybe_string_or_array->as()->elements; + if (arr.size() > 1) { + array_object::array rest; + std::copy(arr.cbegin() + 1, arr.cend(), std::back_inserter(rest)); + return make(std::move(rest)); + } + return native_null(); + } + return make_error("argument of type {} to rest() is not supported", maybe_string_or_array->type()); + }}; +const builtin_function_expression builtin_push { + "push", + {"arr", "val"}, + [](const array_object::array& arguments) -> const object* + { + if (arguments.size() != 2) { + return make_error("wrong number of arguments to push(): expected=2, got={}", arguments.size()); + } + const auto& lhs = arguments[0]; + const auto& rhs = arguments[1]; + using enum object::object_type; + if (lhs->is(array)) { + auto copy = lhs->as()->elements; + copy.push_back(rhs); + return make(std::move(copy)); + } + if (lhs->is(string) && rhs->is(string)) { + auto copy = lhs->as()->value; + copy.append(rhs->as()->value); + return make(std::move(copy)); + } + return make_error("argument of type {} and {} to push() are not supported", lhs->type(), rhs->type()); + }}; +} // namespace + +const std::vector builtin_function_expression::builtins { + &builtin_len, &builtin_puts, &builtin_first, &builtin_last, &builtin_rest, &builtin_push}; + +auto callable_expression::eval(environment* env) const -> const object* { - return std::make_shared(this, env); + return make(this, env); } namespace { - struct error { std::string message; @@ -497,7 +508,7 @@ using null_type = std::monostate; const null_type null_value {}; // NOLINTBEGIN(*) -auto require_eq(const object_ptr& obj, const int64_t expected, std::string_view input) -> void +auto require_eq(const object* obj, const int64_t expected, std::string_view input) -> void { INFO(input, " expected: integer ", " with: ", expected, " got: ", obj->type(), " with: ", obj->inspect()); REQUIRE(obj->is(object::object_type::integer)); @@ -505,7 +516,7 @@ auto require_eq(const object_ptr& obj, const int64_t expected, std::string_view REQUIRE(actual == expected); } -auto require_eq(const object_ptr& obj, const bool expected, std::string_view input) -> void +auto require_eq(const object* obj, const bool expected, std::string_view input) -> void { INFO(input, " expected: boolean ", " with: ", expected, " got: ", obj->type(), " with: ", obj->inspect()); REQUIRE(obj->is(object::object_type::boolean)); @@ -513,7 +524,7 @@ auto require_eq(const object_ptr& obj, const bool expected, std::string_view inp REQUIRE(actual == expected); } -auto require_eq(const object_ptr& obj, const std::string& expected, std::string_view input) -> void +auto require_eq(const object* obj, const std::string& expected, std::string_view input) -> void { INFO(input, " expected: string with: ", expected, " got: ", obj->type(), " with: ", obj->inspect()); REQUIRE(obj->is(object::object_type::string)); @@ -521,7 +532,7 @@ auto require_eq(const object_ptr& obj, const std::string& expected, std::string_ REQUIRE(actual == expected); } -auto require_array_eq(const object_ptr& obj, const array& expected, std::string_view input) -> void +auto require_array_eq(const object* obj, const array& expected, std::string_view input) -> void { INFO(input, " expected: array with: ", expected.size(), "elements got: ", obj->type(), " with: ", obj->inspect()); REQUIRE(obj->is(object::object_type::array)); @@ -538,7 +549,7 @@ auto require_array_eq(const object_ptr& obj, const array& expected, std::string_ } } -auto require_error_eq(const object_ptr& obj, const std::string& expected, std::string_view input) -> void +auto require_error_eq(const object* obj, const std::string& expected, std::string_view input) -> void { INFO(input, " expected: error with message: ", expected, " got: ", obj->type(), " with: ", obj->inspect()); REQUIRE(obj->is(object::object_type::error)); @@ -553,7 +564,7 @@ auto check_no_parse_errors(const parser& prsr) -> bool return prsr.errors().empty(); } -using parsed_program = std::pair; +using parsed_program = std::pair; auto check_program(std::string_view input) -> parsed_program { @@ -561,35 +572,29 @@ auto check_program(std::string_view input) -> parsed_program auto prgrm = prsr.parse_program(); INFO("while parsing: `", input, "`"); CHECK(check_no_parse_errors(prsr)); - return {std::move(prgrm), std::move(prsr)}; + return {prgrm, std::move(prsr)}; } -auto run(std::string_view input) -> object_ptr +auto run(std::string_view input) -> const object* { auto [prgrm, _] = check_program(input); - auto env = std::make_shared(); + environment env; for (const auto& builtin : builtin_function_expression::builtins) { - env->set(builtin.name, std::make_shared(&builtin, environment_ptr {})); + env.set(builtin->name, make(builtin, nullptr)); } - auto result = prgrm->eval(env); - env->break_cycle(); + auto result = prgrm->eval(&env); return result; } -auto run_multi(std::deque& inputs) -> object_ptr +auto run_multi(std::deque& inputs) -> const object* { - auto globals = std::make_shared(); - auto statements = std::vector(); - object_ptr result; + environment env; + const object* result = nullptr; while (!inputs.empty()) { auto [prgrm, _] = check_program(inputs.front()); - result = prgrm->eval(globals); - for (auto& stmt : prgrm->statements) { - statements.push_back(std::move(stmt)); - } + result = prgrm->eval(&env); inputs.pop_front(); } - globals->break_cycle(); return result; } diff --git a/source/eval/environment.cpp b/source/eval/environment.cpp index 7e87c1b..03e56ab 100644 --- a/source/eval/environment.cpp +++ b/source/eval/environment.cpp @@ -4,27 +4,25 @@ #include "object.hpp" -environment::environment(environment_ptr parent_env) - : m_parent(std::move(parent_env)) +environment::environment(environment* parent_env) + : m_parent(parent_env) { + if (m_parent == this) { + abort(); + } } -auto environment::break_cycle() -> void -{ - m_store.clear(); -} - -auto environment::get(const std::string& name) const -> object_ptr +auto environment::get(const std::string& name) const -> const object* { - for (auto ptr = shared_from_this(); ptr; ptr = ptr->m_parent) { + for (const auto* ptr = this; ptr != nullptr; ptr = ptr->m_parent) { if (const auto itr = ptr->m_store.find(name); itr != ptr->m_store.end()) { return itr->second; } } - return null; + return native_null(); } -auto environment::set(const std::string& name, const object_ptr& val) -> void +auto environment::set(const std::string& name, const object* val) -> void { m_store[name] = val; } diff --git a/source/eval/environment.hpp b/source/eval/environment.hpp index 49448ed..df85d77 100644 --- a/source/eval/environment.hpp +++ b/source/eval/environment.hpp @@ -1,21 +1,18 @@ #pragma once -#include #include -#include "environment_fwd.hpp" -#include "object.hpp" +struct object; -struct environment : std::enable_shared_from_this +struct environment { - explicit environment(environment_ptr parent_env = {}); + explicit environment(environment* parent_env = nullptr); auto debug() const -> void; - auto break_cycle() -> void; - auto get(const std::string& name) const -> object_ptr; - auto set(const std::string& name, const object_ptr& val) -> void; + auto get(const std::string& name) const -> const object*; + auto set(const std::string& name, const object* val) -> void; private: - std::unordered_map m_store; - environment_ptr m_parent; + std::unordered_map m_store; + environment* m_parent {}; }; diff --git a/source/eval/environment_fwd.hpp b/source/eval/environment_fwd.hpp deleted file mode 100644 index 0539a7e..0000000 --- a/source/eval/environment_fwd.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -struct environment; -using environment_ptr = std::shared_ptr; diff --git a/source/eval/object.cpp b/source/eval/object.cpp index 9cb4a07..a83f19d 100644 --- a/source/eval/object.cpp +++ b/source/eval/object.cpp @@ -1,4 +1,3 @@ -#include #include #include "object.hpp" @@ -57,3 +56,25 @@ auto builtin_object::inspect() const -> std::string { return fmt::format("builtin {}(){{...}}", builtin->name); } + +namespace +{ +const boolean_object false_obj {/*val=*/false}; +const boolean_object true_obj {/*val=*/true}; +const null_object null_obj; +} // namespace + +auto native_true() -> const object* +{ + return &true_obj; +} + +auto native_false() -> const object* +{ + return &false_obj; +} + +auto native_null() -> const object* +{ + return &null_obj; +} diff --git a/source/eval/object.hpp b/source/eval/object.hpp index 4c09984..15018c9 100644 --- a/source/eval/object.hpp +++ b/source/eval/object.hpp @@ -1,21 +1,18 @@ #pragma once -#include -#include #include -#include -#include #include #include #include #include #include +#include #include #include #include -#include "environment_fwd.hpp" +#include "eval/environment.hpp" // helper type for std::visit template @@ -26,12 +23,9 @@ struct overloaded : T... template overloaded(T...) -> overloaded; -struct object; -using object_ptr = std::shared_ptr; - -struct object : std::enable_shared_from_this +struct object { - enum class object_type + enum class object_type : std::uint8_t { integer, boolean, @@ -46,21 +40,21 @@ struct object : std::enable_shared_from_this builtin, }; object() = default; + virtual ~object() = default; object(const object&) = delete; object(object&&) = delete; auto operator=(const object&) -> object& = delete; auto operator=(object&&) -> object& = delete; - virtual ~object() = default; - [[nodiscard]] inline auto is(object_type obj_type) const -> bool { return type() == obj_type; } + [[nodiscard]] auto is(object_type obj_type) const -> bool { return type() == obj_type; } template - [[nodiscard]] inline auto as() -> std::shared_ptr + [[nodiscard]] auto as() const -> const T* { - return std::static_pointer_cast(shared_from_this()); + return static_cast(this); } - [[nodiscard]] inline auto is_error() const -> bool { return type() == object_type::error; } + [[nodiscard]] auto is_error() const -> bool { return type() == object_type::error; } [[nodiscard]] virtual auto is_truthy() const -> bool { return true; } @@ -68,8 +62,10 @@ struct object : std::enable_shared_from_this [[nodiscard]] virtual auto type() const -> object_type = 0; [[nodiscard]] virtual auto inspect() const -> std::string = 0; - [[nodiscard]] virtual auto equals_to(const object_ptr& other) const -> bool = 0; - bool is_return_value {}; + + [[nodiscard]] virtual auto equals_to(const object* /*other*/) const -> bool { return false; }; + + mutable bool is_return_value {}; }; auto operator<<(std::ostream& ostrm, object::object_type type) -> std::ostream&; @@ -78,25 +74,25 @@ struct hashable_object : object { using hash_key_type = std::variant; - [[nodiscard]] inline auto is_hashable() const -> bool override { return true; } + [[nodiscard]] auto is_hashable() const -> bool override { return true; } [[nodiscard]] virtual auto hash_key() const -> hash_key_type = 0; }; struct integer_object : hashable_object { - explicit inline integer_object(int64_t val) + explicit integer_object(int64_t val) : value {val} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::integer; } + [[nodiscard]] auto type() const -> object_type override { return object_type::integer; } - [[nodiscard]] inline auto inspect() const -> std::string override { return std::to_string(value); } + [[nodiscard]] auto inspect() const -> std::string override { return std::to_string(value); } [[nodiscard]] auto hash_key() const -> hash_key_type override; - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { return other->is(type()) && other->as()->value == value; } @@ -106,20 +102,20 @@ struct integer_object : hashable_object struct boolean_object : hashable_object { - explicit inline boolean_object(bool val) + explicit boolean_object(bool val) : value {val} { } [[nodiscard]] auto is_truthy() const -> bool override { return value; } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::boolean; } + [[nodiscard]] auto type() const -> object_type override { return object_type::boolean; } - [[nodiscard]] inline auto inspect() const -> std::string override { return value ? "true" : "false"; } + [[nodiscard]] auto inspect() const -> std::string override { return value ? "true" : "false"; } [[nodiscard]] auto hash_key() const -> hash_key_type override; - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { return other->is(type()) && other->as()->value == value; } @@ -127,31 +123,31 @@ struct boolean_object : hashable_object bool value {}; }; -static const object_ptr false_object {std::make_shared(false)}; -static const object_ptr true_object {std::make_shared(true)}; +auto native_true() -> const object*; +auto native_false() -> const object*; -inline auto native_bool_to_object(bool val) -> object_ptr +inline auto native_bool_to_object(bool val) -> const object* { if (val) { - return true_object; + return native_true(); } - return false_object; + return native_false(); } struct string_object : hashable_object { - explicit inline string_object(std::string val) + explicit string_object(std::string val) : value {std::move(val)} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::string; } + [[nodiscard]] auto type() const -> object_type override { return object_type::string; } - [[nodiscard]] inline auto inspect() const -> std::string override { return value; } + [[nodiscard]] auto inspect() const -> std::string override { return value; } [[nodiscard]] auto hash_key() const -> hash_key_type override; - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { return other->is(type()) && other->as()->value == value; } @@ -161,31 +157,29 @@ struct string_object : hashable_object struct null_object : object { - null_object() = default; - [[nodiscard]] auto is_truthy() const -> bool override { return false; } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::null; } + [[nodiscard]] auto type() const -> object_type override { return object_type::null; } - [[nodiscard]] inline auto inspect() const -> std::string override { return "null"; } + [[nodiscard]] auto inspect() const -> std::string override { return "null"; } - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override { return other->is(type()); } + [[nodiscard]] auto equals_to(const object* other) const -> bool override { return other->is(type()); } }; -static const object_ptr null {std::make_shared()}; +auto native_null() -> const object*; struct error_object : object { - explicit inline error_object(std::string msg) + explicit error_object(std::string msg) : message {std::move(msg)} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::error; } + [[nodiscard]] auto type() const -> object_type override { return object_type::error; } - [[nodiscard]] inline auto inspect() const -> std::string override { return "ERROR: " + message; } + [[nodiscard]] auto inspect() const -> std::string override { return "ERROR: " + message; } - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { return other->is(type()) && other->as()->message == message; } @@ -194,25 +188,25 @@ struct error_object : object }; template -auto make_error(fmt::format_string fmt, T&&... args) -> object_ptr +auto make_error(fmt::format_string fmt, T&&... args) -> object* { - return std::make_unique(fmt::format(fmt, std::forward(args)...)); + return make(fmt::format(fmt, std::forward(args)...)); } struct array_object : object { - using array = std::vector; + using array = std::vector; - inline explicit array_object(array&& arr) + explicit array_object(array&& arr) : elements {std::move(arr)} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::array; } + [[nodiscard]] auto type() const -> object_type override { return object_type::array; } - [[nodiscard]] inline auto inspect() const -> std::string override { return "todo"; } + [[nodiscard]] auto inspect() const -> std::string override { return "todo"; } - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { // FIXME: return other->is(type()) && other->as()->elements.size() == elements.size(); @@ -223,18 +217,18 @@ struct array_object : object struct hash_object : object { - using hash = std::unordered_map; + using hash = std::unordered_map; - inline explicit hash_object(hash&& hsh) + explicit hash_object(hash&& hsh) : pairs {std::move(hsh)} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::hash; } + [[nodiscard]] auto type() const -> object_type override { return object_type::hash; } - [[nodiscard]] inline auto inspect() const -> std::string override { return "todo"; } + [[nodiscard]] auto inspect() const -> std::string override { return "todo"; } - [[nodiscard]] inline auto equals_to(const object_ptr& other) const -> bool override + [[nodiscard]] auto equals_to(const object* other) const -> bool override { // FIXME: return other->is(type()) && other->as()->pairs.size() == pairs.size(); @@ -247,38 +241,32 @@ struct callable_expression; struct function_object : object { - inline explicit function_object(const callable_expression* expr, environment_ptr env) + function_object(const callable_expression* expr, environment* env) : callable {expr} - , closure_env {std::move(env)} + , closure_env {env} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::function; } + [[nodiscard]] auto type() const -> object_type override { return object_type::function; } - [[nodiscard]] inline auto inspect() const -> std::string override { return "todo"; } + [[nodiscard]] auto inspect() const -> std::string override { return "todo"; } - [[nodiscard]] inline auto equals_to(const object_ptr& /*other*/) const -> bool override { return false; } - - const callable_expression* callable; - environment_ptr closure_env; + const callable_expression* callable {}; + environment* closure_env {}; }; struct compiled_function_object : object { - using ptr = std::shared_ptr; - - inline compiled_function_object(instructions&& instr, size_t locals, size_t args) + compiled_function_object(instructions&& instr, size_t locals, size_t args) : instrs {std::move(instr)} , num_locals {locals} , num_arguments {args} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::compiled_function; } - - [[nodiscard]] inline auto inspect() const -> std::string override { return "fn(...) {...}"; } + [[nodiscard]] auto type() const -> object_type override { return object_type::compiled_function; } - [[nodiscard]] inline auto equals_to(const object_ptr& /*other*/) const -> bool override { return false; } + [[nodiscard]] auto inspect() const -> std::string override { return "fn(...) {...}"; } instructions instrs; size_t num_locals {}; @@ -287,38 +275,32 @@ struct compiled_function_object : object struct closure_object : object { - using ptr = std::shared_ptr; - - inline explicit closure_object(compiled_function_object::ptr cmpld, std::vector frees = {}) - : fn {std::move(cmpld)} + explicit closure_object(const compiled_function_object* cmpld, std::vector frees = {}) + : fn {cmpld} , free {std::move(frees)} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::closure; } - - [[nodiscard]] inline auto inspect() const -> std::string override { return "closure{...}"; } + [[nodiscard]] auto type() const -> object_type override { return object_type::closure; } - [[nodiscard]] inline auto equals_to(const object_ptr& /*other*/) const -> bool override { return false; } + [[nodiscard]] auto inspect() const -> std::string override { return "closure{...}"; } - compiled_function_object::ptr fn; - std::vector free; + const compiled_function_object* fn {}; + std::vector free; }; struct builtin_function_expression; struct builtin_object : object { - inline explicit builtin_object(const builtin_function_expression* bltn) + explicit builtin_object(const builtin_function_expression* bltn) : builtin {bltn} { } - [[nodiscard]] inline auto type() const -> object_type override { return object_type::builtin; } + [[nodiscard]] auto type() const -> object_type override { return object_type::builtin; } [[nodiscard]] auto inspect() const -> std::string override; - [[nodiscard]] inline auto equals_to(const object_ptr& /*other*/) const -> bool override { return false; } - - const builtin_function_expression* builtin; + const builtin_function_expression* builtin {}; }; diff --git a/source/lexer/lexer.cpp b/source/lexer/lexer.cpp index f5f8b81..92951fc 100644 --- a/source/lexer/lexer.cpp +++ b/source/lexer/lexer.cpp @@ -11,6 +11,8 @@ using char_literal_lookup_table = std::array::max()>; +namespace +{ constexpr auto build_char_to_token_type_map() -> char_literal_lookup_table { auto arr = char_literal_lookup_table {}; @@ -71,6 +73,8 @@ inline auto is_digit(char chr) -> bool return std::isdigit(static_cast(chr)) != 0; } +} // namespace + lexer::lexer(std::string_view input) : m_input {input} { @@ -82,21 +86,21 @@ auto lexer::next_token() -> token using enum token_type; skip_whitespace(); if (m_byte == '\0') { - return token {eof, ""}; + return token {.type = eof, .literal = ""}; } const auto as_uchar = static_cast(m_byte); - const auto char_token_type = char_literal_tokens[as_uchar]; + const auto char_token_type = char_literal_tokens.at(as_uchar); if (char_token_type != illegal) { - const auto peek_token_type = char_literal_tokens[static_cast(peek_char())]; + const auto peek_token_type = char_literal_tokens.at(static_cast(peek_char())); switch (char_token_type) { case assign: if (peek_token_type == assign) { - return read_char(), read_char(), token {equals, "=="}; + return read_char(), read_char(), token {.type = equals, .literal = "=="}; } break; case exclamation: if (peek_token_type == assign) { - return read_char(), read_char(), token {not_equals, "!="}; + return read_char(), read_char(), token {.type = not_equals, .literal = "!="}; } break; default: @@ -104,8 +108,8 @@ auto lexer::next_token() -> token } return read_char(), token { - char_literal_tokens[as_uchar], - std::string_view {&m_input.at(m_position - 1), 1}, + .type = char_literal_tokens.at(as_uchar), + .literal = std::string_view {&m_input.at(m_position - 1), 1}, }; } if (m_byte == '"') { @@ -118,7 +122,7 @@ auto lexer::next_token() -> token return read_integer(); } auto literal = m_byte; - return read_char(), token {token_type::illegal, std::string_view {&literal, 1}}; + return read_char(), token {.type = token_type::illegal, .literal = std::string_view {&literal, 1}}; } auto lexer::read_char() -> void @@ -157,13 +161,13 @@ auto lexer::read_identifier_or_keyword() -> token const auto count = end - position; const auto identifier_or_keyword = m_input.substr(position, count); const auto itr = - std::find_if(keyword_tokens.begin(), - keyword_tokens.end(), + std::find_if(keyword_tokens.cbegin(), + keyword_tokens.cend(), [&identifier_or_keyword](auto pair) -> bool { return pair.first == identifier_or_keyword; }); if (itr != keyword_tokens.end()) { - return token {itr->second, itr->first}; + return token {.type = itr->second, .literal = itr->first}; } - return token {token_type::ident, identifier_or_keyword}; + return token {.type = token_type::ident, .literal = identifier_or_keyword}; } auto lexer::read_integer() -> token @@ -174,7 +178,7 @@ auto lexer::read_integer() -> token } auto end = m_position; auto count = end - position; - return token {token_type::integer, m_input.substr(position, count)}; + return token {.type = token_type::integer, .literal = m_input.substr(position, count)}; } auto lexer::read_string() -> token @@ -188,7 +192,7 @@ auto lexer::read_string() -> token } auto end = m_position; auto count = end - position; - return read_char(), token {token_type::string, m_input.substr(position, count)}; + return read_char(), token {.type = token_type::string, .literal = m_input.substr(position, count)}; } namespace @@ -220,28 +224,50 @@ return false; {"foo": "bar"} )"}; const std::array expected_tokens { - token {let, "let"}, token {ident, "five"}, token {assign, "="}, token {integer, "5"}, - token {semicolon, ";"}, token {let, "let"}, token {ident, "ten"}, token {assign, "="}, - token {integer, "10"}, token {semicolon, ";"}, token {let, "let"}, token {ident, "add"}, - token {assign, "="}, token {function, "fn"}, token {lparen, "("}, token {ident, "x"}, - token {comma, ","}, token {ident, "y"}, token {rparen, ")"}, token {lsquirly, "{"}, - token {ident, "x"}, token {plus, "+"}, token {ident, "y"}, token {semicolon, ";"}, - token {rsquirly, "}"}, token {semicolon, ";"}, token {let, "let"}, token {ident, "result"}, - token {assign, "="}, token {ident, "add"}, token {lparen, "("}, token {ident, "five"}, - token {comma, ","}, token {ident, "ten"}, token {rparen, ")"}, token {semicolon, ";"}, - token {exclamation, "!"}, token {minus, "-"}, token {slash, "/"}, token {asterisk, "*"}, - token {integer, "5"}, token {semicolon, ";"}, token {integer, "5"}, token {less_than, "<"}, - token {integer, "10"}, token {greater_than, ">"}, token {integer, "5"}, token {semicolon, ";"}, - token {eef, "if"}, token {lparen, "("}, token {integer, "5"}, token {less_than, "<"}, - token {integer, "10"}, token {rparen, ")"}, token {lsquirly, "{"}, token {ret, "return"}, - token {tru, "true"}, token {semicolon, ";"}, token {rsquirly, "}"}, token {elze, "else"}, - token {lsquirly, "{"}, token {ret, "return"}, token {fals, "false"}, token {semicolon, ";"}, - token {rsquirly, "}"}, token {integer, "10"}, token {equals, "=="}, token {integer, "10"}, - token {semicolon, ";"}, token {integer, "10"}, token {not_equals, "!="}, token {integer, "9"}, - token {semicolon, ";"}, token {string, "foobar"}, token {string, "foo bar"}, token {string, ""}, - token {lbracket, "["}, token {integer, "1"}, token {comma, ","}, token {integer, "2"}, - token {rbracket, "]"}, token {semicolon, ";"}, token {lsquirly, "{"}, token {string, "foo"}, - token {colon, ":"}, token {string, "bar"}, token {rsquirly, "}"}, token {eof, ""}, + token {.type = let, .literal = "let"}, token {.type = ident, .literal = "five"}, + token {.type = assign, .literal = "="}, token {.type = integer, .literal = "5"}, + token {.type = semicolon, .literal = ";"}, token {.type = let, .literal = "let"}, + token {.type = ident, .literal = "ten"}, token {.type = assign, .literal = "="}, + token {.type = integer, .literal = "10"}, token {.type = semicolon, .literal = ";"}, + token {.type = let, .literal = "let"}, token {.type = ident, .literal = "add"}, + token {.type = assign, .literal = "="}, token {.type = function, .literal = "fn"}, + token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "x"}, + token {.type = comma, .literal = ","}, token {.type = ident, .literal = "y"}, + token {.type = rparen, .literal = ")"}, token {.type = lsquirly, .literal = "{"}, + token {.type = ident, .literal = "x"}, token {.type = plus, .literal = "+"}, + token {.type = ident, .literal = "y"}, token {.type = semicolon, .literal = ";"}, + token {.type = rsquirly, .literal = "}"}, token {.type = semicolon, .literal = ";"}, + token {.type = let, .literal = "let"}, token {.type = ident, .literal = "result"}, + token {.type = assign, .literal = "="}, token {.type = ident, .literal = "add"}, + token {.type = lparen, .literal = "("}, token {.type = ident, .literal = "five"}, + token {.type = comma, .literal = ","}, token {.type = ident, .literal = "ten"}, + token {.type = rparen, .literal = ")"}, token {.type = semicolon, .literal = ";"}, + token {.type = exclamation, .literal = "!"}, token {.type = minus, .literal = "-"}, + token {.type = slash, .literal = "/"}, token {.type = asterisk, .literal = "*"}, + token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"}, + token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"}, + token {.type = integer, .literal = "10"}, token {.type = greater_than, .literal = ">"}, + token {.type = integer, .literal = "5"}, token {.type = semicolon, .literal = ";"}, + token {.type = eef, .literal = "if"}, token {.type = lparen, .literal = "("}, + token {.type = integer, .literal = "5"}, token {.type = less_than, .literal = "<"}, + token {.type = integer, .literal = "10"}, token {.type = rparen, .literal = ")"}, + token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"}, + token {.type = tru, .literal = "true"}, token {.type = semicolon, .literal = ";"}, + token {.type = rsquirly, .literal = "}"}, token {.type = elze, .literal = "else"}, + token {.type = lsquirly, .literal = "{"}, token {.type = ret, .literal = "return"}, + token {.type = fals, .literal = "false"}, token {.type = semicolon, .literal = ";"}, + token {.type = rsquirly, .literal = "}"}, token {.type = integer, .literal = "10"}, + token {.type = equals, .literal = "=="}, token {.type = integer, .literal = "10"}, + token {.type = semicolon, .literal = ";"}, token {.type = integer, .literal = "10"}, + token {.type = not_equals, .literal = "!="}, token {.type = integer, .literal = "9"}, + token {.type = semicolon, .literal = ";"}, token {.type = string, .literal = "foobar"}, + token {.type = string, .literal = "foo bar"}, token {.type = string, .literal = ""}, + token {.type = lbracket, .literal = "["}, token {.type = integer, .literal = "1"}, + token {.type = comma, .literal = ","}, token {.type = integer, .literal = "2"}, + token {.type = rbracket, .literal = "]"}, token {.type = semicolon, .literal = ";"}, + token {.type = lsquirly, .literal = "{"}, token {.type = string, .literal = "foo"}, + token {.type = colon, .literal = ":"}, token {.type = string, .literal = "bar"}, + token {.type = rsquirly, .literal = "}"}, token {.type = eof, .literal = ""}, }; for (const auto& expected_token : expected_tokens) { diff --git a/source/main.cpp b/source/main.cpp index f8019f2..4db2a05 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,21 +1,26 @@ +#include #include #include #include #include #include -#include #include #include #include #include #include +#include #include #include #include #include #include +#include "eval/object.hpp" + +namespace +{ constexpr auto prompt = ">> "; constexpr auto monkey_face = R"r( @@ -47,7 +52,7 @@ auto print_parse_errors(const std::vector& errors) } } -enum class engine +enum class engine : std::uint8_t { vm, eval, @@ -115,7 +120,7 @@ auto run_file(const command_line_args& opts) -> int std::string contents {(std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())}; auto lxr = lexer {contents}; auto prsr = parser {lxr}; - auto prgrm = prsr.parse_program(); + auto* prgrm = prsr.parse_program(); if (!prsr.errors().empty()) { print_parse_errors(prsr.errors()); return 1; @@ -125,70 +130,67 @@ auto run_file(const command_line_args& opts) -> int cmplr.compile(prgrm); auto machine = vm::create(cmplr.byte_code()); machine.run(); - auto result = machine.last_popped(); + const auto* result = machine.last_popped(); if (!result->is(object::object_type::null)) { std::cout << result->inspect() << "\n"; } } else { - auto global_env = std::make_shared(); + auto* global_env = make(); for (const auto& builtin : builtin_function_expression::builtins) { - global_env->set(builtin.name, std::make_shared(&builtin, environment_ptr {})); + global_env->set(builtin->name, make(builtin, nullptr)); } - auto result = prgrm->eval(global_env); + const auto* result = prgrm->eval(global_env); if (!result->is(object::object_type::null)) { std::cout << result->inspect() << "\n"; } - global_env->break_cycle(); } return 0; } auto run_repl(const command_line_args& opts) -> int { - auto global_env = std::make_shared(); - auto symbols = symbol_table::create(); - auto consts = std::make_shared(); - auto globals = std::make_shared(globals_size); + auto* global_env = make(); + auto* symbols = symbol_table::create(); + constants consts; + constants globals(globals_size); for (auto idx = 0UL; const auto& builtin : builtin_function_expression::builtins) { - global_env->set(builtin.name, std::make_shared(&builtin, environment_ptr {})); - symbols->define_builtin(idx, builtin.name); + global_env->set(builtin->name, make(builtin, nullptr)); + symbols->define_builtin(idx, builtin->name); idx++; } - auto cache = std::vector(); auto input = std::string {}; std::cout << prompt; while (getline(std::cin, input)) { auto lxr = lexer {input}; auto prsr = parser {lxr}; - auto prgrm = prsr.parse_program(); + auto* prgrm = prsr.parse_program(); if (!prsr.errors().empty()) { print_parse_errors(prsr.errors()); continue; } if (opts.mode == engine::vm) { - auto cmplr = compiler::create_with_state(consts, symbols); + auto cmplr = compiler::create_with_state(&consts, symbols); cmplr.compile(prgrm); - auto machine = vm::create_with_state(cmplr.byte_code(), globals); + auto machine = vm::create_with_state(cmplr.byte_code(), &globals); machine.run(); - auto result = machine.last_popped(); + const auto* result = machine.last_popped(); if (!result->is(object::object_type::null)) { std::cout << result->inspect() << "\n"; } } else { - auto result = prgrm->eval(global_env); + const auto* result = prgrm->eval(global_env); if (!result->is(object::object_type::null)) { std::cout << result->inspect() << "\n"; } } - std::move(prgrm->statements.begin(), prgrm->statements.end(), std::back_inserter(cache)); if (opts.debug_env) { global_env->debug(); } std::cout << prompt; } - global_env->break_cycle(); return 0; } +} // namespace auto main(int argc, char* argv[]) -> int { diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp index 84d333a..9da7e85 100644 --- a/source/parser/parser.cpp +++ b/source/parser/parser.cpp @@ -1,10 +1,6 @@ -#include #include -#include -#include -#include +#include #include -#include #include "parser.hpp" @@ -22,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -84,24 +81,24 @@ parser::parser(lexer lxr) register_unary(string, [this] { return parse_string_literal(); }); register_unary(lbracket, [this] { return parse_array_expression(); }); register_unary(lsquirly, [this] { return parse_hash_literal(); }); - register_binary(plus, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(minus, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(slash, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(asterisk, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(equals, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(not_equals, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(less_than, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(greater_than, [this](expression_ptr left) { return parse_binary_expression(std::move(left)); }); - register_binary(lparen, [this](expression_ptr left) { return parse_call_expression(std::move(left)); }); - register_binary(lbracket, [this](expression_ptr left) { return parse_index_expression(std::move(left)); }); + register_binary(plus, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(minus, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(slash, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(asterisk, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(equals, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(not_equals, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(less_than, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(greater_than, [this](expression* left) { return parse_binary_expression(left); }); + register_binary(lparen, [this](expression* left) { return parse_call_expression(left); }); + register_binary(lbracket, [this](expression* left) { return parse_index_expression(left); }); } -auto parser::parse_program() -> program_ptr +auto parser::parse_program() -> program* { - auto prog = std::make_unique(); + auto* prog = make(); while (m_current_token.type != token_type::eof) { - auto stmt = parse_statement(); - if (stmt) { + auto* stmt = parse_statement(); + if (stmt != nullptr) { prog->statements.push_back(std::move(stmt)); } next_token(); @@ -120,7 +117,7 @@ auto parser::next_token() -> void m_peek_token = m_lxr.next_token(); } -auto parser::parse_statement() -> statement_ptr +auto parser::parse_statement() -> statement* { using enum token_type; switch (m_current_token.type) { @@ -133,10 +130,10 @@ auto parser::parse_statement() -> statement_ptr } } -auto parser::parse_let_statement() -> statement_ptr +auto parser::parse_let_statement() -> statement* { using enum token_type; - auto stmt = std::make_unique(); + auto* stmt = make(); if (!get(ident)) { return {}; } @@ -148,8 +145,8 @@ auto parser::parse_let_statement() -> statement_ptr next_token(); stmt->value = parse_expression(lowest); - if (auto* func_expr = dynamic_cast(stmt->value.get()); func_expr != nullptr) { - func_expr->name = stmt->name->value; + if (auto* func_expr = dynamic_cast(stmt->value); func_expr != nullptr) { + const_cast(func_expr)->name = stmt->name->value; } if (peek_token_is(semicolon)) { @@ -158,10 +155,10 @@ auto parser::parse_let_statement() -> statement_ptr return stmt; } -auto parser::parse_return_statement() -> statement_ptr +auto parser::parse_return_statement() -> statement* { using enum token_type; - auto stmt = std::make_unique(); + auto stmt = make(); next_token(); stmt->value = parse_expression(lowest); @@ -172,9 +169,9 @@ auto parser::parse_return_statement() -> statement_ptr return stmt; } -auto parser::parse_expression_statement() -> statement_ptr +auto parser::parse_expression_statement() -> statement* { - auto expr_stmt = std::make_unique(); + auto expr_stmt = make(); expr_stmt->expr = parse_expression(lowest); if (peek_token_is(token_type::semicolon)) { next_token(); @@ -182,14 +179,14 @@ auto parser::parse_expression_statement() -> statement_ptr return expr_stmt; } -auto parser::parse_expression(int precedence) -> expression_ptr +auto parser::parse_expression(int precedence) -> expression* { auto unary = m_unary_parsers[m_current_token.type]; if (!unary) { no_unary_expression_error(m_current_token.type); return {}; } - auto left_expr = unary(); + auto* left_expr = unary(); while (!peek_token_is(token_type::semicolon) && precedence < peek_precedence()) { auto binary = m_binary_parsers[m_peek_token.type]; if (!binary) { @@ -197,19 +194,19 @@ auto parser::parse_expression(int precedence) -> expression_ptr } next_token(); - left_expr = binary(std::move(left_expr)); + left_expr = binary(left_expr); } return left_expr; } -auto parser::parse_identifier() const -> identifier_ptr +auto parser::parse_identifier() const -> identifier* { - return std::make_unique(std::string {m_current_token.literal}); + return make(std::string(m_current_token.literal)); } -auto parser::parse_integer_literal() -> expression_ptr +auto parser::parse_integer_literal() -> expression* { - auto lit = std::make_unique(); + auto* lit = make(); try { lit->value = std::stoll(std::string {m_current_token.literal}); } catch (const std::out_of_range&) { @@ -219,9 +216,9 @@ auto parser::parse_integer_literal() -> expression_ptr return lit; } -auto parser::parse_unary_expression() -> expression_ptr +auto parser::parse_unary_expression() -> expression* { - auto unary = std::make_unique(); + auto unary = make(); unary->op = m_current_token.type; next_token(); @@ -229,12 +226,12 @@ auto parser::parse_unary_expression() -> expression_ptr return unary; } -auto parser::parse_boolean() -> expression_ptr +auto parser::parse_boolean() -> expression* { - return std::make_unique(current_token_is(token_type::tru)); + return make(current_token_is(token_type::tru)); } -auto parser::parse_grouped_expression() -> expression_ptr +auto parser::parse_grouped_expression() -> expression* { next_token(); auto exp = parse_expression(lowest); @@ -244,10 +241,10 @@ auto parser::parse_grouped_expression() -> expression_ptr return exp; } -auto parser::parse_if_expression() -> expression_ptr +auto parser::parse_if_expression() -> expression* { using enum token_type; - auto expr = std::make_unique(); + auto* expr = make(); if (!get(lparen)) { return {}; } @@ -275,7 +272,7 @@ auto parser::parse_if_expression() -> expression_ptr return expr; } -auto parser::parse_function_expression() -> expression_ptr +auto parser::parse_function_expression() -> expression* { using enum token_type; if (!get(lparen)) { @@ -285,8 +282,8 @@ auto parser::parse_function_expression() -> expression_ptr if (!get(lsquirly)) { return {}; } - auto body = parse_block_statement(); - return std::make_unique(std::move(parameters), std::move(body)); + auto* body = parse_block_statement(); + return make(std::move(parameters), body); } auto parser::parse_function_parameters() -> std::vector @@ -298,7 +295,7 @@ auto parser::parse_function_parameters() -> std::vector return parameters; } next_token(); - auto param = parse_identifier(); + auto* param = parse_identifier(); parameters.push_back(param->value); while (peek_token_is(comma)) { next_token(); @@ -312,14 +309,14 @@ auto parser::parse_function_parameters() -> std::vector return parameters; } -auto parser::parse_block_statement() -> block_statement_ptr +auto parser::parse_block_statement() -> block_statement* { using enum token_type; - auto block = std::make_unique(); + auto* block = make(); next_token(); while (!current_token_is(rsquirly) && !current_token_is(eof)) { - auto stmt = parse_statement(); - if (stmt) { + auto* stmt = parse_statement(); + if (stmt != nullptr) { block->statements.push_back(std::move(stmt)); } next_token(); @@ -328,19 +325,19 @@ auto parser::parse_block_statement() -> block_statement_ptr return block; } -auto parser::parse_call_expression(expression_ptr function) -> expression_ptr +auto parser::parse_call_expression(expression* function) -> expression* { - auto call = std::make_unique(); - call->function = std::move(function); + auto* call = make(); + call->function = function; call->arguments = parse_expression_list(token_type::rparen); return call; } -auto parser::parse_binary_expression(expression_ptr left) -> expression_ptr +auto parser::parse_binary_expression(expression* left) -> expression* { - auto bin_expr = std::make_unique(); + auto* bin_expr = make(); bin_expr->op = m_current_token.type; - bin_expr->left = std::move(left); + bin_expr->left = left; auto precedence = current_precedence(); next_token(); @@ -349,15 +346,15 @@ auto parser::parse_binary_expression(expression_ptr left) -> expression_ptr return bin_expr; } -auto parser::parse_string_literal() const -> expression_ptr +auto parser::parse_string_literal() const -> expression* { - return std::make_unique(std::string {m_current_token.literal}); + return make(std::string {m_current_token.literal}); } -auto parser::parse_expression_list(token_type end) -> std::vector +auto parser::parse_expression_list(token_type end) -> std::vector { using enum token_type; - auto list = std::vector(); + auto list = std::vector(); if (peek_token_is(end)) { next_token(); return list; @@ -378,17 +375,17 @@ auto parser::parse_expression_list(token_type end) -> std::vector expression_ptr +auto parser::parse_array_expression() -> expression* { - auto array_expr = std::make_unique(); + auto* array_expr = make(); array_expr->elements = parse_expression_list(token_type::rbracket); return array_expr; } -auto parser::parse_index_expression(expression_ptr left) -> expression_ptr +auto parser::parse_index_expression(expression* left) -> expression* { - auto index_expr = std::make_unique(); - index_expr->left = std::move(left); + auto* index_expr = make(); + index_expr->left = left; next_token(); index_expr->index = parse_expression(lowest); @@ -399,19 +396,19 @@ auto parser::parse_index_expression(expression_ptr left) -> expression_ptr return index_expr; } -auto parser::parse_hash_literal() -> expression_ptr +auto parser::parse_hash_literal() -> expression* { - auto hash = std::make_unique(); + auto* hash = make(); using enum token_type; while (!peek_token_is(rsquirly)) { next_token(); - auto key = parse_expression(lowest); + auto* key = parse_expression(lowest); if (!get(colon)) { return {}; } next_token(); - auto value = parse_expression(lowest); - hash->pairs.emplace_back(std::move(key), std::move(value)); + auto* value = parse_expression(lowest); + hash->pairs.emplace_back(key, value); if (!peek_token_is(comma)) { break; } @@ -485,7 +482,7 @@ auto check_no_parse_errors(const parser& prsr) -> bool return prsr.errors().empty(); } -using parsed_program = std::pair; +using parsed_program = std::pair; auto check_program(std::string_view input) -> parsed_program { @@ -496,41 +493,41 @@ auto check_program(std::string_view input) -> parsed_program return {std::move(prgrm), std::move(prsr)}; } -auto require_boolean_literal(const expression_ptr& expr, bool value) -> void +auto require_boolean_literal(const expression* expr, bool value) -> void { - auto* bool_expr = dynamic_cast(expr.get()); - INFO("expected boolean, got:", expr.get()->string()); + auto* bool_expr = dynamic_cast(expr); + INFO("expected boolean, got:", expr->string()); REQUIRE(bool_expr); REQUIRE_EQ(bool_expr->value, value); } -auto require_identifier(const expression_ptr& expr, const std::string& value) -> void +auto require_identifier(const expression* expr, const std::string& value) -> void { - auto* ident = dynamic_cast(expr.get()); - INFO("expected identifier, got:", expr.get()->string()); + auto* ident = dynamic_cast(expr); + INFO("expected identifier, got:", expr->string()); REQUIRE(ident); REQUIRE_EQ(ident->value, value); } -auto require_string_literal(const expression_ptr& expr, const std::string& value) -> void +auto require_string_literal(const expression* expr, const std::string& value) -> void { - auto* string_lit = dynamic_cast(expr.get()); - INFO("expected string_literal, got:", expr.get()->string()); + auto* string_lit = dynamic_cast(expr); + INFO("expected string_literal, got:", expr->string()); REQUIRE(string_lit); REQUIRE_EQ(string_lit->value, value); } -auto require_integer_literal(const expression_ptr& expr, int64_t value) -> void +auto require_integer_literal(const expression* expr, int64_t value) -> void { - auto* integer_lit = dynamic_cast(expr.get()); - INFO("expected integer_literal, got:", expr.get()->string()); + auto* integer_lit = dynamic_cast(expr); + INFO("expected integer_literal, got:", expr->string()); REQUIRE(integer_lit); REQUIRE_EQ(integer_lit->value, value); REQUIRE_EQ(integer_lit->string(), std::to_string(value)); } -auto require_literal_expression(const expression_ptr& expr, const expected_value_type& expected) -> void +auto require_literal_expression(const expression* expr, const expected_value_type& expected) -> void { std::visit( overloaded { @@ -541,12 +538,12 @@ auto require_literal_expression(const expression_ptr& expr, const expected_value expected); } -auto require_binary_expression(const expression_ptr& expr, +auto require_binary_expression(const expression* expr, const expected_value_type& left, const token_type oprtr, const expected_value_type& right) -> void { - auto* binary = dynamic_cast(expr.get()); + auto* binary = dynamic_cast(expr); INFO("expected binary expression, got: ", expr->string()); REQUIRE(binary); require_literal_expression(binary->left, left); @@ -554,12 +551,12 @@ auto require_binary_expression(const expression_ptr& expr, require_literal_expression(binary->right, right); } -auto require_expression_statement(const program_ptr& prgrm) -> expression_statement* +auto require_expression_statement(const program* prgrm) -> const expression_statement* { INFO("expected one statement, got: ", prgrm->statements.size()); REQUIRE_EQ(prgrm->statements.size(), 1); - auto* stmt = prgrm->statements[0].get(); - auto* expr_stmt = dynamic_cast(stmt); + auto* stmt = prgrm->statements[0]; + auto* expr_stmt = dynamic_cast(stmt); INFO("expected expression statement, got: ", stmt->string()); REQUIRE(expr_stmt); INFO("expected an expression statement with an expression"); @@ -568,18 +565,18 @@ auto require_expression_statement(const program_ptr& prgrm) -> expression_statem } template -auto require_expression(const program_ptr& prgrm) -> E* +auto require_expression(const program* prgrm) -> const E* { auto* expr_stmt = require_expression_statement(prgrm); - auto expr = dynamic_cast(expr_stmt->expr.get()); + auto* expr = dynamic_cast(expr_stmt->expr); INFO("expected expression"); REQUIRE(expr); return expr; } -auto require_let_statement(statement* stmt, const std::string& expected_identifier) -> let_statement* +auto require_let_statement(const statement* stmt, const std::string& expected_identifier) -> const let_statement* { - auto* let_stmt = dynamic_cast(stmt); + auto* let_stmt = dynamic_cast(stmt); INFO("expected let statement, got: ", stmt->string()); REQUIRE(let_stmt); REQUIRE_EQ(let_stmt->name->value, expected_identifier); @@ -600,7 +597,7 @@ let foobar = 838383; REQUIRE_EQ(program->statements.size(), 3); auto expected_identifiers = std::vector {"x", "y", "foobar"}; for (size_t i = 0; i < 3; ++i) { - require_let_statement(program->statements[i].get(), expected_identifiers[i]); + require_let_statement(program->statements[i], expected_identifiers[i]); } } @@ -621,7 +618,7 @@ TEST_CASE("letStatements") for (const auto& [input, expected_identifier, expected_value] : tests) { auto [prgrm, _] = check_program(input); - auto* let_stmt = require_let_statement(prgrm->statements[0].get(), expected_identifier); + auto* let_stmt = require_let_statement(prgrm->statements[0], expected_identifier); require_literal_expression(let_stmt->value, expected_value); } } @@ -652,8 +649,8 @@ return 993322; REQUIRE_EQ(prgrm->statements.size(), 3); std::array expected_return_values {5, 10, 993322}; for (size_t i = 0; i < 3; ++i) { - auto* stmt = prgrm->statements[i].get(); - auto* ret_stmt = dynamic_cast(stmt); + auto* stmt = prgrm->statements[i]; + auto* ret_stmt = dynamic_cast(stmt); REQUIRE(ret_stmt); require_literal_expression(ret_stmt->value, expected_return_values.at(i)); } @@ -662,15 +659,15 @@ return 993322; TEST_CASE("string") { using enum token_type; - auto name = std::make_unique("myVar"); - auto value = std::make_unique("anotherVar"); + auto name = make("myVar"); + auto value = make("anotherVar"); program prgrm; - auto let_stmt = std::make_unique(); - let_stmt->name = std::move(name); - let_stmt->value = std::move(value); - prgrm.statements.push_back(std::move(let_stmt)); + auto let_stmt = make(); + let_stmt->name = name; + let_stmt->value = value; + prgrm.statements.push_back(let_stmt); REQUIRE_EQ(prgrm.string(), "let myVar = anotherVar;"); } @@ -710,7 +707,7 @@ TEST_CASE("unaryExpressions") for (const auto& [input, op, val] : tests) { auto [prgrm, _] = check_program(input); - auto* unary = require_expression(prgrm); + auto unary = require_expression(prgrm); REQUIRE(unary); REQUIRE_EQ(op, unary->op); @@ -884,7 +881,7 @@ TEST_CASE("ifExpression") REQUIRE(if_expr->consequence); REQUIRE_EQ(if_expr->consequence->statements.size(), 1); - auto* consequence = dynamic_cast(if_expr->consequence->statements[0].get()); + auto* consequence = dynamic_cast(if_expr->consequence->statements[0]); REQUIRE(consequence); require_identifier(consequence->expr, "x"); CHECK_FALSE(if_expr->alternative); @@ -900,11 +897,11 @@ TEST_CASE("ifElseExpression") REQUIRE(if_expr->consequence); REQUIRE_EQ(if_expr->consequence->statements.size(), 1); - auto* consequence = dynamic_cast(if_expr->consequence->statements[0].get()); + auto* consequence = dynamic_cast(if_expr->consequence->statements[0]); REQUIRE(consequence); require_identifier(consequence->expr, "x"); - auto* alternative = dynamic_cast(if_expr->alternative->statements[0].get()); + auto* alternative = dynamic_cast(if_expr->alternative->statements[0]); REQUIRE(alternative); require_identifier(alternative->expr, "y"); } @@ -920,10 +917,10 @@ TEST_CASE("functionLiteral") REQUIRE_EQ(fn_expr->parameters[0], "x"); REQUIRE_EQ(fn_expr->parameters[1], "y"); - auto* block = dynamic_cast(fn_expr->body.get()); + auto* block = dynamic_cast(fn_expr->body); REQUIRE_EQ(block->statements.size(), 1); - auto* body_stmt = dynamic_cast(block->statements.at(0).get()); + auto* body_stmt = dynamic_cast(block->statements.at(0)); REQUIRE(body_stmt); require_binary_expression(body_stmt->expr, "x", token_type::plus, "y"); } @@ -932,8 +929,8 @@ TEST_CASE("functionLiteralWithName") { const auto* input = R"(let myFunction = fn() { };)"; auto [prgrm, _] = check_program(input); - auto* let = require_let_statement(prgrm->statements[0].get(), "myFunction"); - auto* fnexpr = dynamic_cast(let->value.get()); + auto* let = require_let_statement(prgrm->statements[0], "myFunction"); + auto* fnexpr = dynamic_cast(let->value); REQUIRE(fnexpr); REQUIRE_EQ(fnexpr->name, "myFunction"); } diff --git a/source/parser/parser.hpp b/source/parser/parser.hpp index 76e715c..ddbf44c 100644 --- a/source/parser/parser.hpp +++ b/source/parser/parser.hpp @@ -15,37 +15,37 @@ class parser final { public: explicit parser(lexer lxr); - auto parse_program() -> program_ptr; + auto parse_program() -> program*; auto errors() const -> const std::vector&; private: - using binary_parser = std::function; - using unary_parser = std::function; + using binary_parser = std::function; + using unary_parser = std::function; auto next_token() -> void; - auto parse_statement() -> statement_ptr; - auto parse_let_statement() -> statement_ptr; - auto parse_return_statement() -> statement_ptr; - auto parse_expression_statement() -> statement_ptr; + auto parse_statement() -> statement*; + auto parse_let_statement() -> statement*; + auto parse_return_statement() -> statement*; + auto parse_expression_statement() -> statement*; - auto parse_expression(int precedence) -> expression_ptr; - auto parse_identifier() const -> identifier_ptr; - auto parse_integer_literal() -> expression_ptr; - auto parse_unary_expression() -> expression_ptr; - auto parse_binary_expression(expression_ptr left) -> expression_ptr; - auto parse_boolean() -> expression_ptr; - auto parse_grouped_expression() -> expression_ptr; - auto parse_if_expression() -> expression_ptr; - auto parse_function_expression() -> expression_ptr; + auto parse_expression(int precedence) -> expression*; + auto parse_identifier() const -> identifier*; + auto parse_integer_literal() -> expression*; + auto parse_unary_expression() -> expression*; + auto parse_binary_expression(expression* left) -> expression*; + auto parse_boolean() -> expression*; + auto parse_grouped_expression() -> expression*; + auto parse_if_expression() -> expression*; + auto parse_function_expression() -> expression*; auto parse_function_parameters() -> std::vector; - auto parse_block_statement() -> block_statement_ptr; - auto parse_call_expression(expression_ptr function) -> expression_ptr; - auto parse_string_literal() const -> expression_ptr; - auto parse_array_expression() -> expression_ptr; - auto parse_index_expression(expression_ptr left) -> expression_ptr; - auto parse_hash_literal() -> expression_ptr; + auto parse_block_statement() -> block_statement*; + auto parse_call_expression(expression* function) -> expression*; + auto parse_string_literal() const -> expression*; + auto parse_array_expression() -> expression*; + auto parse_index_expression(expression* left) -> expression*; + auto parse_hash_literal() -> expression*; - auto parse_expression_list(token_type end) -> std::vector; + auto parse_expression_list(token_type end) -> std::vector; auto get(token_type type) -> bool; auto current_token_is(token_type type) const -> bool; auto peek_token_is(token_type type) const -> bool; diff --git a/source/vm/vm.cpp b/source/vm/vm.cpp index d35537b..e7acd06 100644 --- a/source/vm/vm.cpp +++ b/source/vm/vm.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -15,10 +14,10 @@ #include #include -vm::vm(frames&& frames, constants_ptr&& consts, constants_ptr globals) - : m_constans {std::move(consts)} - , m_globals {std::move(globals)} - , m_frames {std::move(frames)} +vm::vm(frames frames, const constants* consts, constants* globals) + : m_constans {consts} + , m_globals {globals} + , m_frames {frames} { } @@ -26,7 +25,7 @@ auto vm::run() -> void { for (; current_frame().ip < static_cast(current_frame().cl->fn->instrs.size()); current_frame().ip++) { const auto instr_ptr = static_cast(current_frame().ip); - auto& code = current_frame().cl->fn->instrs; + const auto& code = current_frame().cl->fn->instrs; const auto op_code = static_cast(code.at(instr_ptr)); switch (op_code) { case opcodes::constant: { @@ -49,10 +48,10 @@ auto vm::run() -> void pop(); break; case opcodes::tru: - push(true_object); + push(native_true()); break; case opcodes::fals: - push(false_object); + push(native_false()); break; case opcodes::bang: exec_bang(); @@ -74,7 +73,7 @@ auto vm::run() -> void } break; case opcodes::null: - push(null); + push(native_null()); break; case opcodes::set_global: { auto global_index = read_uint16_big_endian(code, instr_ptr + 1UL); @@ -97,13 +96,13 @@ auto vm::run() -> void auto num_elements = read_uint16_big_endian(code, instr_ptr + 1UL); current_frame().ip += 2; - auto hsh = build_hash(m_sp - num_elements, m_sp); + auto* hsh = build_hash(m_sp - num_elements, m_sp); m_sp -= num_elements; push(hsh); } break; case opcodes::index: { - auto index = pop(); - auto left = pop(); + const auto* index = pop(); + const auto* left = pop(); exec_index(left, index); } break; case opcodes::call: { @@ -112,7 +111,7 @@ auto vm::run() -> void exec_call(num_args); } break; case opcodes::return_value: { - auto return_value = pop(); + const auto* return_value = pop(); auto frame = pop_frame(); m_sp = static_cast(frame.base_ptr) - 1; push(return_value); @@ -120,7 +119,7 @@ auto vm::run() -> void case opcodes::ret: { auto& frame = pop_frame(); m_sp = static_cast(frame.base_ptr) - 1; - push(null); + push(native_null()); } break; case opcodes::set_local: { auto local_index = code.at(instr_ptr + 1UL); @@ -137,8 +136,8 @@ auto vm::run() -> void case opcodes::get_builtin: { auto builtin_index = code.at(instr_ptr + 1UL); current_frame().ip += 1; - const auto& builtin = builtin_function_expression::builtins[builtin_index]; - push(std::make_shared(&builtin)); + const auto* const builtin = builtin_function_expression::builtins[builtin_index]; + push(make(builtin)); } break; case opcodes::closure: { auto const_idx = read_uint16_big_endian(code, instr_ptr + 1UL); @@ -149,11 +148,11 @@ auto vm::run() -> void case opcodes::get_free: { auto free_index = code.at(instr_ptr + 1UL); current_frame().ip += 1; - auto current_closure = current_frame().cl; + const auto* current_closure = current_frame().cl; push(current_closure->free[free_index]); } break; case opcodes::current_closure: { - push({current_frame().cl}); + push(current_frame().cl); } break; default: throw std::runtime_error(fmt::format("opcode {} not implemented yet", op_code)); @@ -161,7 +160,7 @@ auto vm::run() -> void } } -auto vm::push(const object_ptr& obj) -> void +auto vm::push(const object* obj) -> void { if (m_sp >= stack_size) { throw std::runtime_error("stack overflow"); @@ -170,41 +169,41 @@ auto vm::push(const object_ptr& obj) -> void m_sp++; } -auto vm::pop() -> object_ptr +auto vm::pop() -> const object* { if (m_sp == 0) { throw std::runtime_error("stack empty"); } - auto result = m_stack[m_sp - 1]; + const auto* const result = m_stack[m_sp - 1]; m_sp--; return result; } -auto vm::last_popped() const -> object_ptr +auto vm::last_popped() const -> const object* { return m_stack[m_sp]; } auto vm::exec_binary_op(opcodes opcode) -> void { - auto right = pop(); - auto left = pop(); + const auto* right = pop(); + const auto* left = pop(); using enum object::object_type; if (left->is(integer) && right->is(integer)) { auto left_value = left->as()->value; auto right_value = right->as()->value; switch (opcode) { case opcodes::sub: - push(std::make_shared(left_value - right_value)); + push(make(left_value - right_value)); break; case opcodes::mul: - push(std::make_shared(left_value * right_value)); + push(make(left_value * right_value)); break; case opcodes::div: - push(std::make_shared(left_value / right_value)); + push(make(left_value / right_value)); break; case opcodes::add: - push(std::make_shared(left_value + right_value)); + push(make(left_value + right_value)); break; default: throw std::runtime_error(fmt::format("unknown integer operator")); @@ -216,7 +215,7 @@ auto vm::exec_binary_op(opcodes opcode) -> void const auto& right_value = right->as()->value; switch (opcode) { case opcodes::add: - push(std::make_shared(left_value + right_value)); + push(make(left_value + right_value)); break; default: throw std::runtime_error(fmt::format("unknown string {} string operator", opcode)); @@ -227,8 +226,10 @@ auto vm::exec_binary_op(opcodes opcode) -> void fmt::format("unsupported types for binary operation: {} {} {}", left->type(), opcode, right->type())); } +namespace +{ template -auto exec_value_cmp(opcodes opcode, const ValueType& lhs, const ValueType& rhs) -> object_ptr +auto exec_value_cmp(opcodes opcode, const ValueType& lhs, const ValueType& rhs) -> const object* { using enum opcodes; switch (opcode) { @@ -243,10 +244,12 @@ auto exec_value_cmp(opcodes opcode, const ValueType& lhs, const ValueType& rhs) } } +} // namespace + auto vm::exec_cmp(opcodes opcode) -> void { - auto right = pop(); - auto left = pop(); + const auto* right = pop(); + const auto* left = pop(); using enum object::object_type; if (left->is(integer) && right->is(integer)) { push(exec_value_cmp(opcode, left->as()->value, right->as()->value)); @@ -259,10 +262,14 @@ auto vm::exec_cmp(opcodes opcode) -> void } switch (opcode) { - case opcodes::equal: - return push(native_bool_to_object(left->equals_to(right))); - case opcodes::not_equal: - return push(native_bool_to_object(!left->equals_to(right))); + case opcodes::equal: { + push(native_bool_to_object(left->equals_to(right))); + return; + } + case opcodes::not_equal: { + push(native_bool_to_object(!left->equals_to(right))); + return; + } default: throw std::runtime_error(fmt::format("unknown operator {} ({} {})", opcode, left->type(), right->type())); } @@ -271,92 +278,101 @@ auto vm::exec_cmp(opcodes opcode) -> void auto vm::exec_bang() -> void { using enum object::object_type; - auto operand = pop(); + const auto* operand = pop(); if (operand->is(boolean)) { - return push(native_bool_to_object(!operand->as()->value)); + push(native_bool_to_object(!operand->as()->value)); + return; } if (operand->is(null)) { - return push(true_object); + push(native_true()); + return; } - push(false_object); + push(native_false()); } auto vm::exec_minus() -> void { - auto operand = pop(); + const auto* operand = pop(); if (!operand->is(object::object_type::integer)) { throw std::runtime_error(fmt::format("unsupported type for negation {}", operand->type())); } - push(std::make_shared(-operand->as()->value)); + push(make(-operand->as()->value)); } -auto vm::build_array(size_t start, size_t end) const -> object_ptr +auto vm::build_array(size_t start, size_t end) const -> object* { array_object::array arr; for (auto idx = start; idx < end; idx++) { arr.push_back(m_stack.at(idx)); } - return std::make_shared(std::move(arr)); + return make(std::move(arr)); } -auto vm::build_hash(size_t start, size_t end) const -> object_ptr +auto vm::build_hash(size_t start, size_t end) const -> object* { hash_object::hash hsh; for (auto idx = start; idx < end; idx += 2) { - auto key = m_stack[idx]; - auto val = m_stack[idx + 1]; + const auto* key = m_stack[idx]; + const auto* val = m_stack[idx + 1]; hsh[key->as()->hash_key()] = val; } - return std::make_shared(std::move(hsh)); + return make(std::move(hsh)); } -auto exec_hash(const hash_object::hash& hsh, const hashable_object::hash_key_type& key) -> object_ptr +namespace +{ +auto exec_hash(const hash_object::hash& hsh, const hashable_object::hash_key_type& key) -> const object* { if (!hsh.contains(key)) { - return null; + return native_null(); } return hsh.at(key); } +} // namespace -auto vm::exec_index(const object_ptr& left, const object_ptr& index) -> void +auto vm::exec_index(const object* left, const object* index) -> void { using enum object::object_type; if (left->is(array) && index->is(integer)) { auto idx = index->as()->value; auto max = static_cast(left->as()->elements.size()) - 1; if (idx < 0 || idx > max) { - return push(::null); + push(native_null()); + return; } - return push(left->as()->elements.at(static_cast(idx))); + push(left->as()->elements.at(static_cast(idx))); + return; } if (left->is(string) && index->is(integer)) { auto idx = index->as()->value; auto max = static_cast(left->as()->value.size()) - 1; if (idx < 0 || idx > max) { - return push(::null); + push(native_null()); + return; } - return push( - std::make_shared(left->as()->value.substr(static_cast(idx), 1))); + push(make(left->as()->value.substr(static_cast(idx), 1))); + return; } if (left->is(hash) && index->is_hashable()) { - return push(exec_hash(left->as()->pairs, index->as()->hash_key())); + push(exec_hash(left->as()->pairs, index->as()->hash_key())); + return; } push(make_error("invalid index operation: {}[{}]", left->type(), index->type())); } auto vm::exec_call(size_t num_args) -> void { - auto callee = m_stack[m_sp - 1 - num_args]; + const auto* callee = m_stack[m_sp - 1 - num_args]; using enum object::object_type; if (callee->is(closure)) { - const auto clsr = callee->as(); + const auto* const clsr = callee->as(); if (num_args != clsr->fn->num_arguments) { throw std::runtime_error( fmt::format("wrong number of arguments: want={}, got={}", clsr->fn->num_arguments, num_args)); } - frame frm {clsr, -1, static_cast(m_sp - num_args)}; + frame frm {.cl = clsr, .ip = -1, .base_ptr = static_cast(m_sp - num_args)}; m_sp = static_cast(frm.base_ptr) + clsr->fn->num_locals; - push_frame(std::move(frm)); + push_frame(frm); return; } if (callee->is(builtin)) { @@ -365,7 +381,7 @@ auto vm::exec_call(size_t num_args) -> void for (auto idx = m_sp - num_args; idx < m_sp; idx++) { args.push_back(m_stack[idx]); } - auto result = builtin->body(std::move(args)); + const auto* result = builtin->body(std::move(args)); push(result); return; } @@ -374,12 +390,12 @@ auto vm::exec_call(size_t num_args) -> void auto vm::current_frame() -> frame& { - return m_frames[m_frame_index - 1]; + return m_frames.at(m_frame_index - 1); } -auto vm::push_frame(frame&& frm) -> void +auto vm::push_frame(frame frm) -> void { - m_frames[m_frame_index] = std::move(frm); + m_frames.at(m_frame_index) = frm; m_frame_index++; } @@ -391,7 +407,7 @@ auto vm::pop_frame() -> frame& auto vm::push_closure(uint16_t const_idx, uint8_t num_free) -> void { - auto constant = m_constans->at(const_idx); + const auto* constant = m_constans->at(const_idx); if (!constant->is(object::object_type::compiled_function)) { throw std::runtime_error("not a function"); } @@ -400,7 +416,7 @@ auto vm::push_closure(uint16_t const_idx, uint8_t num_free) -> void free.push_back(m_stack.at(m_sp - num_free + i)); } m_sp -= num_free; - push(std::make_shared(constant->as(), free)); + push(make(constant->as(), free)); } namespace @@ -418,15 +434,15 @@ auto check_no_parse_errors(const parser& prsr) -> bool return prsr.errors().empty(); } -using parsed_program = std::pair; +using parsed_program = std::pair; auto check_program(std::string_view input) -> parsed_program { auto prsr = parser {lexer {input}}; - auto prgrm = prsr.parse_program(); + auto* prgrm = prsr.parse_program(); INFO("while parsing: `", input, "`"); CHECK(check_no_parse_errors(prsr)); - return {std::move(prgrm), std::move(prsr)}; + return {prgrm, std::move(prsr)}; } struct error @@ -438,7 +454,7 @@ using hash = std::unordered_map; using null_type = std::monostate; const null_type null_value {}; -auto require_is(const bool expected, const object_ptr& actual_obj, std::string_view input) -> void +auto require_is(const bool expected, const object*& actual_obj, std::string_view input) -> void { INFO(input, " expected: boolean with: ", @@ -453,7 +469,7 @@ auto require_is(const bool expected, const object_ptr& actual_obj, std::string_v REQUIRE(actual == expected); } -auto require_is(const int64_t expected, const object_ptr& actual_obj, std::string_view input) -> void +auto require_is(const int64_t expected, const object*& actual_obj, std::string_view input) -> void { INFO(input, " expected: integer with: ", @@ -468,7 +484,7 @@ auto require_is(const int64_t expected, const object_ptr& actual_obj, std::strin REQUIRE(actual == expected); } -auto require_is(const std::string& expected, const object_ptr& actual_obj, std::string_view input) -> void +auto require_is(const std::string& expected, const object*& actual_obj, std::string_view input) -> void { INFO(input, " expected: string with: ", @@ -483,7 +499,7 @@ auto require_is(const std::string& expected, const object_ptr& actual_obj, std:: REQUIRE(actual == expected); } -auto require_is(const error& expected, const object_ptr& actual_obj, std::string_view input) -> void +auto require_is(const error& expected, const object*& actual_obj, std::string_view input) -> void { INFO(input, " expected: error with: ", @@ -498,7 +514,7 @@ auto require_is(const error& expected, const object_ptr& actual_obj, std::string REQUIRE(actual == expected.message); } -auto require_array_object(const std::vector& expected, const object_ptr& actual, std::string_view input) -> void +auto require_array_object(const std::vector& expected, const object*& actual, std::string_view input) -> void { INFO(input, " expected: array with: ", @@ -517,7 +533,7 @@ auto require_array_object(const std::vector& expected, const object_ptr& ac } } -auto require_hash_object(const hash& expected, const object_ptr& actual, std::string_view input) -> void +auto require_hash_object(const hash& expected, const object*& actual, std::string_view input) -> void { INFO(input, " expected: hash with: ", @@ -540,7 +556,7 @@ struct vt }; template -auto require_eq(const std::variant& expected, const object_ptr& actual, std::string_view input) -> void +auto require_eq(const std::variant& expected, const object*& actual, std::string_view input) -> void { using enum object::object_type; std::visit( diff --git a/source/vm/vm.hpp b/source/vm/vm.hpp index 6e9117e..a98d8af 100644 --- a/source/vm/vm.hpp +++ b/source/vm/vm.hpp @@ -2,8 +2,8 @@ #include #include -#include +#include #include #include #include @@ -13,11 +13,11 @@ constexpr auto stack_size = 2048UL; constexpr auto globals_size = 65536UL; constexpr auto max_frames = 1024UL; -using ssize_type = std::make_signed::type; +using ssize_type = std::make_signed_t; struct frame { - closure_object::ptr cl {}; + const closure_object* cl {}; ssize_type ip {}; ssize_type base_ptr {}; }; @@ -26,44 +26,45 @@ using frames = std::array; struct vm { - inline static auto create(bytecode&& code) -> vm + static auto create(bytecode code) -> vm { - return create_with_state(std::move(code), std::make_shared(globals_size)); + return create_with_state(std::move(code), make(globals_size)); } - inline static auto create_with_state(bytecode&& code, constants_ptr globals) -> vm + static auto create_with_state(bytecode code, constants* globals) -> vm { - auto main_fn = std::make_shared(std::move(code.instrs), 0, 0); - auto main_clousre = std::make_shared(std::move(main_fn)); - const frame main_frame {.cl = std::move(main_clousre)}; + auto* main_fn = make(std::move(code.instrs), 0, 0); + auto* main_clousre = make(main_fn); + const frame main_frame {.cl = main_clousre}; frames frms; frms[0] = main_frame; - return vm {std::move(frms), std::move(code.consts), std::move(globals)}; + return vm {frms, code.consts, globals}; } auto run() -> void; - auto push(const object_ptr& obj) -> void; - [[nodiscard]] auto last_popped() const -> object_ptr; + auto push(const object* obj) -> void; + [[nodiscard]] auto last_popped() const -> const object*; private: - vm(frames&& frames, constants_ptr&& consts, constants_ptr globals); - auto pop() -> object_ptr; + vm(frames frames, const constants* consts, constants* globals); + auto pop() -> const object*; auto exec_binary_op(opcodes opcode) -> void; + void extracted(); auto exec_cmp(opcodes opcode) -> void; auto exec_bang() -> void; auto exec_minus() -> void; - auto exec_index(const object_ptr& left, const object_ptr& index) -> void; + auto exec_index(const object* left, const object* index) -> void; auto exec_call(size_t num_args) -> void; - [[nodiscard]] auto build_array(size_t start, size_t end) const -> object_ptr; - [[nodiscard]] auto build_hash(size_t start, size_t end) const -> object_ptr; + [[nodiscard]] auto build_array(size_t start, size_t end) const -> object*; + [[nodiscard]] auto build_hash(size_t start, size_t end) const -> object*; auto current_frame() -> frame&; - auto push_frame(frame&& frm) -> void; + auto push_frame(frame frm) -> void; auto pop_frame() -> frame&; auto push_closure(uint16_t const_idx, uint8_t num_free) -> void; - constants_ptr m_constans; - constants_ptr m_globals; + const constants* m_constans {}; + constants* m_globals {}; constants m_stack {stack_size}; size_t m_sp {0}; frames m_frames; diff --git a/test/benchmark/source/main.cpp b/test/benchmark/source/main.cpp index 766a5c5..564fa0b 100644 --- a/test/benchmark/source/main.cpp +++ b/test/benchmark/source/main.cpp @@ -39,8 +39,8 @@ fibonacci(35); auto lxr = lexer {input}; auto prsr = parser {lxr}; - auto prgrm = prsr.parse_program(); - object_ptr result; + auto* prgrm = prsr.parse_program(); + const object* result = nullptr; std::chrono::duration duration {}; if (engine_vm) { auto cmplr = compiler::create(); @@ -52,12 +52,11 @@ fibonacci(35); auto end = std::chrono::steady_clock::now(); duration = end - start; } else { - auto env = std::make_shared(); + environment env; auto start = std::chrono::steady_clock::now(); - result = prgrm->eval(env); + result = prgrm->eval(&env); auto end = std::chrono::steady_clock::now(); duration = end - start; - env->break_cycle(); } fmt::print("engine={}, result={}, duration={}\n", (engine_vm ? "vm" : "eval"), result->inspect(), duration.count()); return 0;