diff --git a/source/eval/ast_eval.cpp b/source/eval/ast_eval.cpp index d06fa08b..ae24e7d7 100644 --- a/source/eval/ast_eval.cpp +++ b/source/eval/ast_eval.cpp @@ -322,10 +322,7 @@ auto unary_expression::eval(environment* env) const -> const object* } return make(-evaluated_value->as()->value); case exclamation: - if (!evaluated_value->is(object::object_type::boolean)) { - return native_bool_to_object(/*val=*/false); - } - return native_bool_to_object(!evaluated_value->as()->value); + return native_bool_to_object(!evaluated_value->is_truthy()); default: return make_error("unknown operator: {}{}", op, evaluated_value->type()); } @@ -386,6 +383,7 @@ const builtin_function_expression builtin_puts {"puts", fmt::print("\n"); return native_null(); }}; + const builtin_function_expression builtin_first { "first", {"arr"}, @@ -412,6 +410,7 @@ const builtin_function_expression builtin_first { } return make_error("argument of type {} to first() is not supported", maybe_string_or_array->type()); }}; + const builtin_function_expression builtin_last { "last", {"arr"}, @@ -438,6 +437,7 @@ const builtin_function_expression builtin_last { } return make_error("argument of type {} to last() is not supported", maybe_string_or_array->type()); }}; + const builtin_function_expression builtin_rest { "rest", {"arr"}, @@ -466,6 +466,7 @@ const builtin_function_expression builtin_rest { } 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"}, @@ -694,12 +695,16 @@ TEST_CASE("bangOperator") et {"!true", false}, et {"!false", true}, et {"!false", true}, + et {"!0", true}, et {"!5", false}, et {R"(!"a")", false}, + et {R"(!"")", true}, et {"!!true", true}, et {"!!false", false}, et {"!!5", true}, et {R"(!!"a")", true}, + et {R"(![])", true}, + et {R"(!{})", true}, }; for (const auto& [input, expected] : tests) { const auto evaluated = run(input); diff --git a/source/eval/environment.cpp b/source/eval/environment.cpp index 03e56ab8..d71d17a9 100644 --- a/source/eval/environment.cpp +++ b/source/eval/environment.cpp @@ -4,18 +4,15 @@ #include "object.hpp" -environment::environment(environment* parent_env) - : m_parent(parent_env) +environment::environment(environment* outer_env) + : outer(outer_env) { - if (m_parent == this) { - abort(); - } } auto environment::get(const std::string& name) const -> const object* { - 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()) { + for (const auto* ptr = this; ptr != nullptr; ptr = ptr->outer) { + if (const auto itr = ptr->store.find(name); itr != ptr->store.end()) { return itr->second; } } @@ -24,12 +21,12 @@ auto environment::get(const std::string& name) const -> const object* auto environment::set(const std::string& name, const object* val) -> void { - m_store[name] = val; + store[name] = val; } auto environment::debug() const -> void { - for (const auto& [k, v] : m_store) { + for (const auto& [k, v] : store) { fmt::print("[{}] = {}\n", k, v->inspect()); } } diff --git a/source/eval/environment.hpp b/source/eval/environment.hpp index df85d773..2e8d2d11 100644 --- a/source/eval/environment.hpp +++ b/source/eval/environment.hpp @@ -6,13 +6,12 @@ struct object; struct environment { - explicit environment(environment* parent_env = nullptr); + explicit environment(environment* outer_env = nullptr); auto debug() const -> 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* m_parent {}; + std::unordered_map store; + environment* outer {}; }; diff --git a/source/eval/object.cpp b/source/eval/object.cpp index 1ce96d78..39c5d64c 100644 --- a/source/eval/object.cpp +++ b/source/eval/object.cpp @@ -117,6 +117,14 @@ const boolean_object true_obj {/*val=*/true}; const null_object null_obj; } // namespace +auto native_bool_to_object(bool val) -> const object* +{ + if (val) { + return &true_obj; + } + return &false_obj; +} + auto native_true() -> const object* { return &true_obj; diff --git a/source/eval/object.hpp b/source/eval/object.hpp index a09348d8..d0825349 100644 --- a/source/eval/object.hpp +++ b/source/eval/object.hpp @@ -85,6 +85,8 @@ struct integer_object : hashable_object { } + [[nodiscard]] auto is_truthy() const -> bool override { return value != 0; } + [[nodiscard]] auto type() const -> object_type override { return object_type::integer; } [[nodiscard]] auto inspect() const -> std::string override { return std::to_string(value); } @@ -124,14 +126,7 @@ struct boolean_object : hashable_object auto native_true() -> const object*; auto native_false() -> const object*; - -inline auto native_bool_to_object(bool val) -> const object* -{ - if (val) { - return native_true(); - } - return native_false(); -} +auto native_bool_to_object(bool val) -> const object*; struct string_object : hashable_object { @@ -140,6 +135,8 @@ struct string_object : hashable_object { } + [[nodiscard]] auto is_truthy() const -> bool override { return !value.empty(); } + [[nodiscard]] auto type() const -> object_type override { return object_type::string; } [[nodiscard]] auto inspect() const -> std::string override { return fmt::format(R"("{}")", value); } @@ -201,6 +198,8 @@ struct array_object : object { } + [[nodiscard]] auto is_truthy() const -> bool override { return !elements.empty(); } + [[nodiscard]] auto type() const -> object_type override { return object_type::array; } [[nodiscard]] auto inspect() const -> std::string override; @@ -230,6 +229,8 @@ struct hash_object : object { } + [[nodiscard]] auto is_truthy() const -> bool override { return !pairs.empty(); } + [[nodiscard]] auto type() const -> object_type override { return object_type::hash; } [[nodiscard]] auto inspect() const -> std::string override; diff --git a/source/vm/vm.cpp b/source/vm/vm.cpp index 8881a587..1a39c88c 100644 --- a/source/vm/vm.cpp +++ b/source/vm/vm.cpp @@ -281,15 +281,7 @@ auto vm::exec_bang() -> void { using enum object::object_type; const auto* operand = pop(); - if (operand->is(boolean)) { - push(native_bool_to_object(!operand->as()->value)); - return; - } - if (operand->is(null)) { - push(native_true()); - return; - } - push(native_false()); + push(native_bool_to_object(!operand->is_truthy())); } auto vm::exec_minus() -> void @@ -647,8 +639,12 @@ TEST_CASE("booleanExpressions") vt {R"(("a" > "b") == false)", true}, vt {R"(!true)", false}, vt {R"(!false)", true}, + vt {R"(!!"")", false}, vt {R"(!"a")", false}, vt {R"(!!"a")", true}, + vt {R"(!0)", true}, + vt {R"(![])", true}, + vt {R"(!{})", true}, vt {R"(!5)", false}, vt {R"(!!true)", true}, vt {R"(!!false)", false},