diff options
Diffstat (limited to 'boot/evaluator.cc')
| -rw-r--r-- | boot/evaluator.cc | 340 |
1 files changed, 330 insertions, 10 deletions
diff --git a/boot/evaluator.cc b/boot/evaluator.cc index bb50bd3..6d3153e 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -19,11 +19,22 @@ 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_initializer_error::non_constant_initializer_error(const source_position position) + : error(position) + { + } + + std::string non_constant_initializer_error::what() const + { + return "Variable initializers must be constant expressions"; + } + std::optional<type_properties> get_type_properties(const type& subject, const target_info& target) { auto resolved = resolve_underlying_type(subject); @@ -127,9 +138,8 @@ namespace elna::boot }; } - evaluator::evaluator(symbol_bag& bag, const target_info& target, - const std::map<std::string, expression*>& evaluated_initializers) - : bag(bag), target(target), evaluated_initializers(evaluated_initializers) + evaluator::evaluator(symbol_bag& bag, const target_info& target) + : bag(bag), target(target) { } @@ -145,6 +155,18 @@ namespace elna::boot { return evaluate_named(*named); } + if (auto *array_access = designator->is_array_access()) + { + return evaluate_array_access(*array_access); + } + if (auto *field_access = designator->is_field_access()) + { + return evaluate_field_access(*field_access); + } + if (auto *slicing = designator->is_slicing()) + { + return evaluate_slicing(*slicing); + } } else if (auto *unary = subject.is_unary()) { @@ -240,20 +262,109 @@ namespace elna::boot { return std::nullopt; } - auto initializer = this->evaluated_initializers.find(subject.name); + return variable->value; + } + + std::optional<constant_value> evaluator::evaluate_array_access(array_access_expression& subject) + { + auto base = evaluate(subject.base()); + auto index = evaluate(subject.index()); + if (!base.has_value() || !index.has_value()) + { + return std::nullopt; + } + auto *array = std::get_if<constant_aggregate<std::vector>>(&base.value()); + if (array == nullptr) + { + return std::nullopt; + } + std::size_t position; + if (auto *int_index = std::get_if<std::int32_t>(&index.value())) + { + if (*int_index < 0) + { + return std::nullopt; + } + position = static_cast<std::size_t>(*int_index); + } + else if (auto *word_index = std::get_if<std::uint32_t>(&index.value())) + { + position = *word_index; + } + else + { + return std::nullopt; + } + if (position >= (*array)->size()) + { + return std::nullopt; + } + return (*array)->at(position); + } + + std::optional<constant_value> evaluator::evaluate_field_access(field_access_expression& subject) + { + auto base = evaluate(subject.base()); + if (!base.has_value()) + { + return std::nullopt; + } + auto *record = std::get_if<constant_aggregate<ordered_map>>(&base.value()); + if (record == nullptr) + { + return std::nullopt; + } + auto pos = (*record)->find(subject.field().name()); + if (pos == (*record)->end()) + { + return std::nullopt; + } + return pos->second; + } - return initializer == this->evaluated_initializers.end() - ? std::nullopt - : evaluate(*initializer->second); + std::optional<constant_value> evaluator::evaluate_slicing(slicing_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()) + { + return std::nullopt; + } + auto *array = std::get_if<constant_aggregate<std::vector>>(&base.value()); + auto *start_idx = std::get_if<std::int32_t>(&start.value()); + auto *end_idx = std::get_if<std::int32_t>(&end.value()); + if (array == nullptr || start_idx == nullptr || end_idx == nullptr + || *start_idx < 0 || *end_idx < 0) + { + return std::nullopt; + } + auto start_pos = static_cast<std::size_t>(*start_idx); + auto end_pos = static_cast<std::size_t>(*end_idx); + if (start_pos > end_pos || end_pos > (*array)->size()) + { + return std::nullopt; + } + auto slice_begin = std::next((*array)->begin(), static_cast<std::ptrdiff_t>(start_pos)); + auto slice_end = std::next((*array)->begin(), static_cast<std::ptrdiff_t>(end_pos)); + + return constant_value{ + constant_aggregate<std::vector>{ std::vector<constant_value>(slice_begin, slice_end) } + }; } std::optional<constant_value> evaluator::evaluate_unary(unary_expression& subject) { if (subject.operation() == unary_operator::reference) { - // The address of a module-level entity is a compile-time - // constant. The caller (type analysis) validates the context. - return constant_value{ std::nullptr_t{} }; + if (auto *designator = subject.operand().is_designator()) + { + if (auto *named = designator->is_named()) + { + return constant_value{ global_address{ .name = named->name } }; + } + } + return std::nullopt; } auto operand = evaluate(subject.operand()); @@ -367,6 +478,19 @@ namespace elna::boot return std::nullopt; } } + else if constexpr (std::is_same_v<T, global_address>) + { + 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) @@ -645,4 +769,200 @@ namespace elna::boot } return std::nullopt; } + + /** + * Converts a constant_value to an AST literal expression. + * + * \param value Evaluated constant to convert. + * \param position Source position for the new literal node. + * \param decoration Type decoration to apply to the literal. + * \return A new literal expression, or \c nullptr if \p value + * is an aggregate (handled separately by the caller). + */ + static expression *value_to_expression(const constant_value& value, + const source_position& position, const type& decoration) + { + return std::visit([&position, &decoration](auto&& value) -> expression* + { + using T = std::decay_t<decltype(value)>; + + if constexpr (std::is_same_v<T, std::int32_t>) + { + auto *lit = new literal<std::int32_t>(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v<T, std::uint32_t>) + { + auto *lit = new literal<std::uint32_t>(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v<T, double>) + { + auto *lit = new literal<double>(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v<T, bool>) + { + auto *lit = new literal<bool>(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v<T, unsigned char>) + { + auto *lit = new literal<unsigned char>(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v<T, std::nullptr_t> + || std::is_same_v<T, global_address>) + { + auto *lit = new literal<std::nullptr_t>(position, nullptr); + lit->type_decoration = decoration; + return lit; + } + else + { + return nullptr; + } + }, value); + } + + expression *evaluator::fold(expression& original) + { + auto value = evaluate(original); + if (!value.has_value()) + { + return &original; + } + if (expression *literal = value_to_expression(value.value(), original.position(), original.type_decoration)) + { + delete &original; + return literal; + } + if (auto *record = original.is_record_constructor()) + { + for (auto& field_init : record->field_initializers) + { + fold_aggregate_field(field_init); + } + } + else if (auto *array = original.is_array_constructor()) + { + for (auto& element : array->elements) + { + element = fold(*element); + } + } + return &original; + } + + void evaluator::fold_aggregate_field(field_initializer& field_init) + { + auto value = evaluate(field_init.value()); + if (!value.has_value()) + { + return; + } + expression *literal = value_to_expression(value.value(), + field_init.value().position(), field_init.value().type_decoration); + if (literal != nullptr) + { + field_init.value(*literal); + return; + } + expression *folded = fold(field_init.value()); + if (folded != &field_init.value()) + { + field_init.value(*folded); + } + } + + constant_folder::constant_folder(symbol_bag& bag, const target_info& target) + : bag(bag), target(target), constant_evaluator(this->bag, this->target) + { + } + + void constant_folder::visit(variable_declaration* declaration) + { + if (declaration->initializer == nullptr || has_errors()) + { + return; + } + auto computed = this->constant_evaluator.evaluate(*declaration->initializer); + if (!computed) + { + add_error<non_constant_initializer_error>(declaration->initializer->position()); + return; + } + declaration->initializer = this->constant_evaluator.fold(*declaration->initializer); + + for (const auto& identifier : declaration->identifiers) + { + auto symbol = this->bag.lookup(identifier.name()); + if (symbol == nullptr) + { + continue; + } + if (auto var = symbol->is_variable(); + resolve_aliases(var->symbol).get<constant_type>() != nullptr) + { + var->value = computed; + } + } + } + + void constant_folder::visit(cast_expression *expr) + { + expr->target().accept(this); + expr->value().accept(this); + expr->value(fold_trait(expr->value())); + } + + void constant_folder::visit(binary_expression *expr) + { + expr->lhs().accept(this); + expr->lhs(fold_trait(expr->lhs())); + expr->rhs().accept(this); + expr->rhs(fold_trait(expr->rhs())); + } + + void constant_folder::visit(unary_expression *expr) + { + expr->operand().accept(this); + expr->operand(fold_trait(expr->operand())); + } + + void constant_folder::visit(procedure_call *call) + { + call->callable().accept(this); + for (auto& argument : call->arguments) + { + argument->accept(this); + expression& folded = fold_trait(*argument); + if (&folded != argument) + { + delete argument; + argument = &folded; + } + } + } + + expression& constant_folder::fold_trait(expression& expr) + { + if (auto *trait = expr.is_traits()) + { + if (auto value = this->constant_evaluator.evaluate_traits(*trait)) + { + if (auto *literal = value_to_expression(value.value(), trait->position(), trait->type_decoration)) + { + return *literal; + } + } + } + return expr; + } + } |
