aboutsummaryrefslogtreecommitdiff
path: root/boot/evaluator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/evaluator.cc')
-rw-r--r--boot/evaluator.cc101
1 files changed, 70 insertions, 31 deletions
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index 8f414fa..407370a 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -19,13 +19,56 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/ast.h"
-#include <algorithm>
#include <cstddef>
#include <limits>
#include <ranges>
namespace elna::boot
{
+ non_constant_expression_error::non_constant_expression_error(const source_position position, payload_type payload)
+ : error(position), payload(std::move(payload))
+ {
+ }
+
+ std::string non_constant_expression_error::what() const
+ {
+ return std::visit([](const auto& payload) -> std::string {
+ using T = std::decay_t<decltype(payload)>;
+
+ if constexpr (std::is_same_v<T, initializer>)
+ {
+ return "Variable initializers must be constant expressions";
+ }
+ else if constexpr (std::is_same_v<T, case_label>)
+ {
+ return "Case label must be a constant expression";
+ }
+ else if constexpr (std::is_same_v<T, array_dimensions>)
+ {
+ return "Array dimensions for type '" + payload.array_type.to_string()
+ + "' should be constant";
+ }
+ }, this->payload);
+ }
+
+ std::optional<std::pair<std::string, source_position>> non_constant_expression_error::note() const
+ {
+ return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> {
+ using T = std::decay_t<decltype(payload)>;
+
+ if constexpr (std::is_same_v<T, initializer>)
+ {
+ auto position_span = source_position(payload.identifiers.front().position().start(),
+ payload.identifiers.back().position().end());
+ return std::make_pair(join(payload.identifiers), position_span);
+ }
+ else
+ {
+ return std::nullopt;
+ }
+ }, this->payload);
+ }
+
std::optional<type_properties> get_type_properties(const type& subject, const target_info& target)
{
auto resolved = resolve_underlying_type(subject);
@@ -209,6 +252,8 @@ namespace elna::boot
std::optional<constant_value> evaluator::evaluate_literal(literal_expression& subject)
{
+ // The type decoration here is trustable since the name analysis derives
+ // the decoration from literal's own value.
type const decoration = subject.type_decoration;
if (is_primitive_type(decoration, "Int") || is_primitive_type(decoration, "Word"))
@@ -283,54 +328,49 @@ namespace elna::boot
auto resolved_base = resolve_underlying_type(type_to_check);
if (auto enumeration = resolved_base.get<enumeration_type>())
{
- auto enumeration_position = std::distance(enumeration->members.begin(),
+ auto enumeration_distance = std::distance(enumeration->members.begin(),
std::ranges::find(enumeration->members, subject.field().name()));
+ const std::size_t enumeration_position = static_cast<std::size_t>(enumeration_distance);
- return constant_value{
- integer_literal::from(static_cast<std::size_t>(enumeration_position + 1))
- };
+ if (enumeration_position >= enumeration->members.size())
+ {
+ return std::nullopt;
+ }
+ return constant_value{ integer_literal::from(enumeration_position + 1U) };
}
else if (auto base = evaluate(subject.base()))
{
if (auto *record = std::get_if<constant_aggregate<ordered_map>>(&base.value()))
{
- return (**record)[subject.field().name()];
+ auto field_iterator = (*record)->find(subject.field().name());
+
+ return field_iterator != std::cend(**record)
+ ? std::make_optional(field_iterator->second)
+ : std::nullopt;
+ }
+ else if (auto *vector = std::get_if<constant_aggregate<std::vector>>(&base.value());
+ subject.field() == "length")
+ {
+ return constant_value{ integer_literal::from((*vector)->size()) };
}
else if (auto *string_value = std::get_if<std::string>(&base.value()); subject.field() == "length")
{
return constant_value{ integer_literal::from(string_value->size()) };
}
}
- else if (auto array = resolved_base.get<array_type>(); subject.field() == "length")
- {
- return constant_value{ integer_literal::from(array->size) };
- }
return std::nullopt;
}
std::optional<std::size_t> evaluator::evaluate_index(expression& subject)
{
auto evaluated_index = evaluate(subject);
- if (!evaluated_index.has_value())
+ if (!evaluated_index.has_value() || !std::holds_alternative<integer_literal>(evaluated_index.value()))
{
return std::nullopt;
}
- auto index_literal = std::get<integer_literal>(evaluated_index.value());
-
- if (index_literal.is_signed())
- {
- auto signed_index = index_literal.to_signed();
+ auto& index_literal = std::get<integer_literal>(evaluated_index.value());
- if (!signed_index.has_value() || signed_index.value() <= 0)
- {
- return std::nullopt;
- }
- return static_cast<std::size_t>(signed_index.value());
- }
- else
- {
- return index_literal.to_unsigned();
- }
+ return index_literal.is_negative() ? std::nullopt : index_literal.to_unsigned();
}
std::optional<constant_value> evaluator::evaluate_slicing(slicing_expression& subject)
@@ -656,14 +696,13 @@ namespace elna::boot
std::optional<constant_value> evaluator::evaluate_cast(cast_expression& subject)
{
- if (auto value = evaluate(subject.value()))
- {
- return constant_value{ value.value() };
- }
- else
+ auto value = evaluate(subject.value());
+
+ if (!value.has_value() || subject.type_decoration.empty())
{
return std::nullopt;
}
+ return constant_value{ value.value() };
}
std::optional<std::size_t> evaluator::evaluate_traits_size(const type& subject)