Skip to content

Commit

Permalink
refactor: sequence multiplication to own function (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
hrzlgnm authored Dec 16, 2024
1 parent 63803a9 commit 2ed09ca
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 75 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ target_sources(monkey_lib
source/eval/ast_eval.cpp
source/eval/environment.cpp
source/eval/object.cpp
source/eval/util.cpp
source/lexer/lexer.cpp
source/lexer/token.cpp
source/lexer/token_type.cpp
Expand Down
42 changes: 5 additions & 37 deletions source/eval/ast_eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include "environment.hpp"
#include "object.hpp"
#include "util.hpp"

auto array_expression::eval(environment* env) const -> const object*
{
Expand Down Expand Up @@ -95,16 +96,6 @@ auto apply_string_binary_operator(token_type oper, const std::string& left, cons
return {};
}
}

template<typename O>
auto multiply_sequence(const typename O::value_type& values, int64_t count) -> object*
{
typename O::value_type target;
for (int64_t i = 0; i < count; i++) {
std::copy(values.cbegin(), values.cend(), std::back_inserter(target));
}
return make<O>(std::move(target));
}
} // namespace

auto binary_expression::eval(environment* env) const -> const object*
Expand All @@ -118,35 +109,12 @@ auto binary_expression::eval(environment* env) const -> const object*
if (evaluated_right->is_error()) {
return evaluated_right;
}
using enum object::object_type;
if ((evaluated_left->is(integer) && evaluated_right->is(array))
|| (evaluated_left->is(array) && evaluated_right->is(integer)) && op == token_type::asterisk)
{
const array_object* arr {};
const integer_object* integer {};
if (evaluated_left->is(array)) {
arr = evaluated_left->as<array_object>();
integer = evaluated_right->as<integer_object>();
} else {
arr = evaluated_right->as<array_object>();
integer = evaluated_left->as<integer_object>();
}
return multiply_sequence<array_object>(arr->value, integer->value);
}
if ((evaluated_left->is(integer) && evaluated_right->is(string))
|| (evaluated_left->is(string) && evaluated_right->is(integer)) && op == token_type::asterisk)
{
const string_object* str {};
const integer_object* integer {};
if (evaluated_left->is(string)) {
str = evaluated_left->as<string_object>();
integer = evaluated_right->as<integer_object>();
} else {
str = evaluated_right->as<string_object>();
integer = evaluated_left->as<integer_object>();
if (op == token_type::asterisk) {
if (const auto* multiplied = evaluate_sequence_mul(evaluated_left, evaluated_right); multiplied != nullptr) {
return multiplied;
}
return multiply_sequence<string_object>(str->value, integer->value);
}
using enum object::object_type;
if (evaluated_left->type() != evaluated_right->type()) {
return make_error("type mismatch: {} {} {}", evaluated_left->type(), op, evaluated_right->type());
}
Expand Down
10 changes: 10 additions & 0 deletions source/eval/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct object

[[nodiscard]] virtual auto is_hashable() const -> bool { return false; }

[[nodiscard]] virtual auto is_sequence() const -> bool { return false; }

[[nodiscard]] virtual auto type() const -> object_type = 0;
[[nodiscard]] virtual auto inspect() const -> std::string = 0;

Expand Down Expand Up @@ -134,6 +136,8 @@ struct string_object : hashable_object
{
using value_type = std::string;

string_object() = default;

explicit string_object(std::string val)
: value {std::move(val)}
{
Expand All @@ -143,6 +147,8 @@ struct string_object : hashable_object

[[nodiscard]] auto type() const -> object_type override { return object_type::string; }

[[nodiscard]] auto is_sequence() const -> bool override { return true; }

[[nodiscard]] auto inspect() const -> std::string override { return fmt::format(R"("{}")", value); }

[[nodiscard]] auto hash_key() const -> hash_key_type override;
Expand Down Expand Up @@ -192,13 +198,17 @@ struct array_object : object
{
using value_type = std::vector<const object*>;

array_object() = default;

explicit array_object(value_type&& arr)
: value {std::move(arr)}
{
}

[[nodiscard]] auto is_truthy() const -> bool override { return !value.empty(); }

[[nodiscard]] auto is_sequence() const -> bool override { return true; }

[[nodiscard]] auto type() const -> object_type override { return object_type::array; }

[[nodiscard]] auto inspect() const -> std::string override;
Expand Down
63 changes: 63 additions & 0 deletions source/eval/util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <type_traits>

#include "util.hpp"

#include <gc.hpp>

#include "object.hpp"

namespace
{
template<class T, class U>
concept Derived = std::is_base_of_v<U, T>;

template<typename T>
concept IsSequenceObject = requires(T obj) {
{
obj.is_sequence()
} -> std::same_as<bool>;
} && requires(T obj) {
{
obj.is_sequence() == true
};
};

template<typename T>
concept SequenceObject = Derived<T, object> && IsSequenceObject<T>;

template<SequenceObject T>
auto multiply_sequence(const T* source, integer_object::value_type count) -> T*
{
typename T::value_type target;
for (integer_object::value_type i = 0; i < count; i++) {
std::copy(source->value.cbegin(), source->value.cend(), std::back_inserter(target));
}
return make<T>(std::move(target));
}

template<SequenceObject T>
auto try_mul(const object* lhs, const object* rhs) -> const object*
{
using enum object::object_type;
const auto seq_type = T {}.type();
if (lhs->is(integer) && rhs->is(seq_type)) {
return multiply_sequence(rhs->as<T>(), lhs->as<integer_object>()->value);
}
if (lhs->is(seq_type) && rhs->is(integer)) {
return multiply_sequence(lhs->as<T>(), rhs->as<integer_object>()->value);
}
return nullptr;
}

} // namespace

auto evaluate_sequence_mul(const object* lhs, const object* rhs) -> const object*
{
if (const auto* result = try_mul<array_object>(lhs, rhs)) {
return result;
}
if (const auto* result = try_mul<string_object>(lhs, rhs)) {
return result;
}
return nullptr;
}
3 changes: 3 additions & 0 deletions source/eval/util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "object.hpp"

auto evaluate_sequence_mul(const object* lhs, const object* rhs) -> const object*;
43 changes: 5 additions & 38 deletions source/vm/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <compiler/compiler.hpp>
#include <doctest/doctest.h>
#include <eval/object.hpp>
#include <eval/util.hpp>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <gc.hpp>
Expand Down Expand Up @@ -193,50 +194,16 @@ auto vm::last_popped() const -> const object*
return m_stack[m_sp];
}

namespace
{
template<typename O>
auto multiply_sequence(const typename O::value_type& values, int64_t count) -> object*
{
typename O::value_type target;
for (int64_t i = 0; i < count; i++) {
std::copy(values.cbegin(), values.cend(), std::back_inserter(target));
}
return make<O>(std::move(target));
}
} // namespace

auto vm::exec_binary_op(opcodes opcode) -> void
{
const auto* right = pop();
const auto* left = pop();
using enum object::object_type;
if ((left->is(integer) && right->is(array)) || (left->is(array) && right->is(integer)) && opcode == opcodes::mul) {
const array_object* arr {};
const integer_object* integer {};
if (left->is(array)) {
arr = left->as<array_object>();
integer = right->as<integer_object>();
} else {
arr = right->as<array_object>();
integer = left->as<integer_object>();
}
push(multiply_sequence<array_object>(arr->value, integer->value));
return;
}
if ((left->is(integer) && right->is(string)) || (left->is(string) && right->is(integer)) && opcode == opcodes::mul)
{
const string_object* str {};
const integer_object* integer {};
if (left->is(string)) {
str = left->as<string_object>();
integer = right->as<integer_object>();
} else {
str = right->as<string_object>();
integer = left->as<integer_object>();
if (opcode == opcodes::mul) {
if (const auto* multiplied = evaluate_sequence_mul(left, right); multiplied != nullptr) {
push(multiplied);
return;
}
push(multiply_sequence<string_object>(str->value, integer->value));
return;
}
if (left->is(integer) && right->is(integer)) {
auto left_value = left->as<integer_object>()->value;
Expand Down

0 comments on commit 2ed09ca

Please sign in to comment.