From 8539c10542a4be1e1127daea60bf1b1e4c37e092 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 1 Aug 2026 09:30:39 +0200 Subject: Fix compile-time negation and array access --- boot/ast.cc | 11 +--- boot/evaluator.cc | 159 ++++++++++++++++++++------------------------------ boot/name_analysis.cc | 15 ++--- boot/parser.yy | 2 +- boot/result.cc | 55 +++++++++++++++-- 5 files changed, 121 insertions(+), 121 deletions(-) (limited to 'boot') diff --git a/boot/ast.cc b/boot/ast.cc index 2a90d0e..7157315 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -181,11 +181,6 @@ namespace elna::boot __builtin_unreachable(); } - void empty_visitor::visit(literal *) - { - __builtin_unreachable(); - } - void empty_visitor::visit(literal *) { __builtin_unreachable(); @@ -509,10 +504,6 @@ namespace elna::boot expression->base().accept(this); } - void walking_visitor::visit(literal *) - { - } - void walking_visitor::visit(literal *) { } @@ -979,7 +970,7 @@ namespace elna::boot { } - procedure_body::procedure_body(procedure_body&& that) + procedure_body::procedure_body(procedure_body&& that) noexcept : variables(std::move(const_cast&>(that.variables))), entry_point(std::move(const_cast&>(that.entry_point))), return_expression(that.return_expression) diff --git a/boot/evaluator.cc b/boot/evaluator.cc index bff727d..14b8ea3 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -214,7 +214,7 @@ namespace elna::boot if (is_primitive_type(decoration, "Int")) { - return constant_value{ static_cast&>(subject).value }; + return constant_value{ static_cast&>(subject).value }; } if (is_primitive_type(decoration, "Word")) { @@ -260,38 +260,18 @@ namespace elna::boot std::optional evaluator::evaluate_array_access(array_access_expression& subject) { auto base = evaluate(subject.base()); - auto index = evaluate(subject.index()); + auto index = evaluate_index(subject.index()); + if (!base.has_value() || !index.has_value()) { return std::nullopt; } auto *array = std::get_if>(&base.value()); - if (array == nullptr) - { - return std::nullopt; - } - std::size_t position; - if (auto *int_index = std::get_if(&index.value())) - { - if (*int_index < 0) - { - return std::nullopt; - } - position = static_cast(*int_index); - } - else if (auto *word_index = std::get_if(&index.value())) - { - position = word_index->to(); - } - else - { - return std::nullopt; - } - if (position >= (*array)->size()) + if (array == nullptr || index.value() > (*array)->size()) { return std::nullopt; } - return (*array)->at(position); + return (*array)->at(index.value() - 1); } std::optional evaluator::evaluate_field_access(field_access_expression& subject) @@ -307,8 +287,9 @@ namespace elna::boot auto member_iterator = std::ranges::find(enumeration->members, subject.field().name()); if (member_iterator != enumeration->members.end()) { + auto enumeration_position = std::distance(enumeration->members.begin(), member_iterator) + 1; return constant_value{ - static_cast(std::distance(enumeration->members.begin(), member_iterator) + 1) + integer_literal::from(static_cast(enumeration_position)) }; } return std::nullopt; @@ -331,31 +312,47 @@ namespace elna::boot return pos->second; } - std::optional evaluator::evaluate_slicing(slicing_expression& subject) + std::optional evaluator::evaluate_index(expression& subject) { - auto base = evaluate(subject.base()); - auto start = evaluate(subject.start()); - auto end = evaluate(subject.end()); - if (!base.has_value() || !start.has_value() || !end.has_value()) + auto evaluated_index = evaluate(subject); + if (!evaluated_index.has_value()) { return std::nullopt; } - auto *array = std::get_if>(&base.value()); - auto *start_idx = std::get_if(&start.value()); - auto *end_idx = std::get_if(&end.value()); - if (array == nullptr || start_idx == nullptr || end_idx == nullptr - || *start_idx < 0 || *end_idx < 0) + auto index_literal = std::get(evaluated_index.value()); + + if (index_literal.is_signed()) + { + auto signed_index = index_literal.to_signed(); + + if (!signed_index.has_value() || signed_index.value() <= 0) + { + return std::nullopt; + } + return static_cast(signed_index.value()); + } + else + { + return index_literal.to_unsigned(); + } + } + + std::optional evaluator::evaluate_slicing(slicing_expression& subject) + { + auto base = evaluate(subject.base()); + auto start_index = evaluate_index(subject.start()); + auto end_index = evaluate_index(subject.end()); + if (!base.has_value() || !start_index.has_value() || !end_index.has_value()) { return std::nullopt; } - auto start_pos = static_cast(*start_idx); - auto end_pos = static_cast(*end_idx); - if (start_pos > end_pos || end_pos > (*array)->size()) + auto *array = std::get_if>(&base.value()); + if (array == nullptr || start_index > end_index || end_index > (*array)->size()) { return std::nullopt; } - auto slice_begin = std::next((*array)->begin(), static_cast(start_pos)); - auto slice_end = std::next((*array)->begin(), static_cast(end_pos)); + auto slice_begin = std::next((*array)->begin(), static_cast(start_index.value() - 1)); + auto slice_end = std::next((*array)->begin(), static_cast(end_index.value())); return constant_value{ constant_aggregate{ std::vector(slice_begin, slice_end) } @@ -386,11 +383,13 @@ namespace elna::boot return std::visit([](auto&& value) -> std::optional { using T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { - return value == std::numeric_limits::min() - ? std::nullopt - : std::make_optional(constant_value{ -value }); + if (auto result = value.neg()) + { + return constant_value{ result.value() }; + } + return std::nullopt; } if constexpr (std::is_same_v) { @@ -416,7 +415,7 @@ namespace elna::boot return std::visit([](const auto& value) -> std::optional { using T = std::decay_t; - if constexpr (is_integral || std::is_same_v) + if constexpr (std::is_same_v) { return constant_value{ ~value }; } @@ -472,9 +471,7 @@ namespace elna::boot template static std::optional evaluate_operation(binary_operator operation, const T& lhs, const T& rhs) { - if constexpr (std::is_same_v - || std::is_same_v> - || std::is_same_v>) + if constexpr (std::is_same_v) { switch (operation) { @@ -487,19 +484,6 @@ namespace elna::boot return std::nullopt; } } - else if constexpr (std::is_same_v) - { - switch (operation) - { - using enum binary_operator; - case equals: - return constant_value{ lhs == rhs }; - case not_equals: - return constant_value{ lhs != rhs }; - default: - return std::nullopt; - } - } else { switch (operation) @@ -513,7 +497,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_arithmetic) + else if constexpr (std::is_floating_point_v) { if (auto result = add_overflow(lhs, rhs)) { @@ -529,7 +513,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_arithmetic) + else if constexpr (std::is_floating_point_v) { if (auto result = sub_overflow(lhs, rhs)) { @@ -545,7 +529,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_arithmetic) + else if constexpr (std::is_floating_point_v) { if (auto result = mul_overflow(lhs, rhs)) { @@ -561,7 +545,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_arithmetic) + else if constexpr (std::is_floating_point_v) { if (rhs != static_cast(0)) { @@ -577,31 +561,24 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_integral) - { - if (rhs != static_cast(0)) - { - return constant_value{ lhs % rhs }; - } - } return std::nullopt; case disjunction: case bitwise_disjunction: - if constexpr (is_integral || std::is_same_v) + if constexpr (std::is_same_v) { return constant_value{ lhs | rhs }; } return std::nullopt; case conjunction: case bitwise_conjunction: - if constexpr (is_integral || std::is_same_v) + if constexpr (std::is_same_v) { return constant_value{ lhs & rhs }; } return std::nullopt; case exclusive_disjunction: case bitwise_exclusive_disjunction: - if constexpr (is_integral || std::is_same_v) + if constexpr (std::is_same_v) { return constant_value{ lhs ^ rhs }; } @@ -632,14 +609,6 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (is_integral) - { - if (rhs < 0 || static_cast>(rhs) >= std::numeric_limits::digits) - { - return std::nullopt; - } - return constant_value{ lhs << rhs }; - } return std::nullopt; case shift_right: if constexpr (std::is_same_v) @@ -649,35 +618,31 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (std::is_integral_v) - { - return constant_value{ lhs >> rhs }; - } return std::nullopt; case equals: return constant_value{ lhs == rhs }; case not_equals: return constant_value{ lhs != rhs }; case less: - if constexpr (is_arithmetic || std::is_same_v) + if constexpr (std::is_floating_point_v || std::is_same_v) { return constant_value{ lhs < rhs }; } return std::nullopt; case greater: - if constexpr (is_arithmetic || std::is_same_v) + if constexpr (std::is_floating_point_v || std::is_same_v) { return constant_value{ lhs > rhs }; } return std::nullopt; case less_equal: - if constexpr (is_arithmetic || std::is_same_v) + if constexpr (std::is_floating_point_v || std::is_same_v) { return constant_value{ lhs <= rhs }; } return std::nullopt; case greater_equal: - if constexpr (is_arithmetic || std::is_same_v) + if constexpr (std::is_floating_point_v || std::is_same_v) { return constant_value{ lhs >= rhs }; } @@ -755,11 +720,11 @@ namespace elna::boot if (is_primitive_type(resolved, "Int")) { - return constant_value{ std::numeric_limits::min() }; + return constant_value{ integer_literal::from(std::numeric_limits::min()) }; } if (is_primitive_type(resolved, "Word")) { - return constant_value{ integer_literal::from(static_cast(0)) }; + return constant_value{ integer_literal::from(0) }; } if (is_primitive_type(resolved, "Char")) { @@ -775,7 +740,7 @@ namespace elna::boot } if (auto enumeration = resolved.get()) { - return constant_value{ 1 }; + return constant_value{ integer_literal::from(1) }; } } else if (subject.name.name() == "max") @@ -784,7 +749,7 @@ namespace elna::boot if (is_primitive_type(resolved, "Int")) { - return constant_value{ std::numeric_limits::max() }; + return constant_value{ integer_literal::from(std::numeric_limits::max()) }; } if (is_primitive_type(resolved, "Word")) { @@ -804,7 +769,7 @@ namespace elna::boot } if (auto enumeration = resolved.get()) { - return constant_value{ static_cast(enumeration->members.size()) }; + return constant_value{ integer_literal::from(static_cast(enumeration->members.size())) }; } } else if (subject.name.name() == "offset") diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index b8cd83e..7ced7ea 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -789,15 +789,16 @@ namespace elna::boot } } - void name_analysis_visitor::visit(literal *literal) - { - literal->type_decoration = lookup_primitive_type("Int"); - this->current_type = literal->type_decoration; - } - void name_analysis_visitor::visit(literal *literal) { - literal->type_decoration = lookup_primitive_type("Word"); + if (literal->value.is_signed()) + { + literal->type_decoration = lookup_primitive_type("Int"); + } + else + { + literal->type_decoration = lookup_primitive_type("Word"); + } this->current_type = literal->type_decoration; } diff --git a/boot/parser.yy b/boot/parser.yy index 85fc454..670a265 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -235,7 +235,7 @@ procedure_return: "return" expression { $$ = $2; } | "return" { $$ = nullptr; } literal: - INTEGER { $$ = new boot::literal(boot::make_position(@$), $1); } + INTEGER { $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from($1)); } | WORD { $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from($1)); } | FLOAT { $$ = new boot::literal(boot::make_position(@$), $1); } | BOOLEAN { $$ = new boot::literal(boot::make_position(@$), $1); } diff --git a/boot/result.cc b/boot/result.cc index f9f57db..4d4c20f 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -129,12 +129,14 @@ namespace elna::boot } integer_literal::integer_literal(integer_literal&& that) noexcept + : m_signed(that.is_signed()), m_size(that.size()) { mpz_init(this->raw); mpz_swap(this->raw, that.raw); } integer_literal::integer_literal(const integer_literal& that) + : m_signed(that.is_signed()), m_size(that.size()) { mpz_init(this->raw); mpz_set(this->raw, that.raw); @@ -293,10 +295,7 @@ namespace elna::boot integer_literal& integer_literal::operator=(integer_literal&& that) noexcept { - if (this != &that) - { - mpz_swap(this->raw, that.raw); - } + swap(*this, that); return *this; } @@ -304,11 +303,43 @@ namespace elna::boot { if (this != &that) { - mpz_set(this->raw, that.raw); + integer_literal temp(that); + + swap(*this, temp); } return *this; } + std::optional integer_literal::to_signed() const + { + if (!is_signed()) + { + return std::nullopt; + } + if (is_negative_minimum(sizeof(std::ptrdiff_t) * CHAR_BIT)) + { + return std::numeric_limits::min(); + } + auto [written, rop] = export_to_words(); + + if (written > 1 || rop < 0) + { + return std::nullopt; + } + return mpz_sgn(this->raw) < 0 ? -rop : rop; + } + + std::optional integer_literal::to_unsigned() const + { + if (is_signed()) + { + return std::nullopt; + } + auto [written, rop] = export_to_words(); + + return written > 1 ? std::nullopt : std::make_optional(rop); + } + bool integer_literal::is_signed() const { return this->m_signed; @@ -319,6 +350,13 @@ namespace elna::boot return this->m_size; } + void swap(integer_literal& lhs, integer_literal& rhs) noexcept + { + mpz_swap(lhs.raw, rhs.raw); + std::swap(lhs.m_signed, rhs.m_signed); + std::swap(lhs.m_size, rhs.m_size); + } + std::optional integer_literal::check() && { std::size_t required_bits = mpz_sizeinbase(this->raw, 2); @@ -337,9 +375,14 @@ namespace elna::boot } } + bool integer_literal::is_negative_minimum(const std::size_t bits) const + { + return mpz_sgn(this->raw) < 0 && mpz_scan1(this->raw, 0) == bits - 1; + } + bool integer_literal::is_negative_minimum() const { - return mpz_sgn(this->raw) < 0 && mpz_scan1(this->raw, 0) == bits() - 1; + return is_negative_minimum(bits()); } std::size_t integer_literal::bits() const -- cgit v1.2.3