From 5cc2947143f2a6ebd4ef1ae4d25ab956b87de808 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 21 Jul 2026 21:27:03 +0200 Subject: Redefine strings as slice of constant characters --- boot/ast.cc | 38 +++++--- boot/evaluator.cc | 42 +++++++-- boot/name_analysis.cc | 135 ++++++++++++++++++++------- boot/symbol.cc | 2 - boot/type_check.cc | 254 +++++++++++++++++++++++++++++++++----------------- 5 files changed, 333 insertions(+), 138 deletions(-) (limited to 'boot') diff --git a/boot/ast.cc b/boot/ast.cc index 3727aba..ec5b27d 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -438,8 +438,8 @@ namespace elna::boot void walking_visitor::visit(slicing_expression *expression) { expression->base().accept(this); - expression->offset().accept(this); - expression->length().accept(this); + expression->start().accept(this); + expression->end().accept(this); } void walking_visitor::visit(traits_expression *trait) @@ -1070,8 +1070,8 @@ namespace elna::boot } slicing_expression::slicing_expression(const source_position position, - expression *base, expression *offset, expression *length) - : node(position), m_base(base), m_offset(offset), m_length(length) + expression *base, expression *start, expression *end) + : node(position), m_base(base), m_start(start), m_end(end) { } @@ -1088,8 +1088,8 @@ namespace elna::boot slicing_expression::~slicing_expression() { delete m_base; - delete m_offset; - delete m_length; + delete m_start; + delete m_end; } expression& slicing_expression::base() @@ -1097,14 +1097,14 @@ namespace elna::boot return *this->m_base; } - expression& slicing_expression::offset() + expression& slicing_expression::start() { - return *this->m_offset; + return *this->m_start; } - expression& slicing_expression::length() + expression& slicing_expression::end() { - return *this->m_length; + return *this->m_end; } named_expression::named_expression(const source_position position, const std::string& name) @@ -1242,6 +1242,11 @@ namespace elna::boot return m_operator; } + void binary_expression::operation(binary_operator operation) + { + this->m_operator = operation; + } + binary_expression::~binary_expression() { delete m_lhs; @@ -1274,6 +1279,11 @@ namespace elna::boot return this->m_operator; } + void unary_expression::operation(unary_operator operation) + { + this->m_operator = operation; + } + unary_expression::~unary_expression() { delete m_operand; @@ -1540,10 +1550,16 @@ namespace elna::boot case greater_equal: return ">="; case conjunction: - return "and"; + case logical_conjunction: + case bitwise_conjunction: + return "&"; case disjunction: + case logical_disjunction: + case bitwise_disjunction: return "or"; case exclusive_disjunction: + case logical_exclusive_disjunction: + case bitwise_exclusive_disjunction: return "xor"; case shift_left: return "<<"; diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 990f358..77a56ad 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -169,7 +169,7 @@ namespace elna::boot return std::nullopt; }, operand.value()); } - if (subject.operation() == unary_operator::negation) + if (subject.operation() == unary_operator::logical_negation) { return std::visit([](auto value) -> std::optional { using T = std::decay_t; @@ -178,7 +178,15 @@ namespace elna::boot { return constant_value{ !value }; } - else if constexpr (std::is_integral_v) + return std::nullopt; + }, operand.value()); + } + if (subject.operation() == unary_operator::bitwise_negation) + { + return std::visit([](auto value) -> std::optional { + using T = std::decay_t; + + if constexpr (std::is_integral_v) { return constant_value{ ~value }; } @@ -299,23 +307,44 @@ namespace elna::boot } return std::nullopt; case disjunction: - if constexpr (std::is_integral_v) + case bitwise_disjunction: + if constexpr (std::is_integral_v && !std::is_same_v) { return constant_value{ lhs | rhs }; } return std::nullopt; case conjunction: - if constexpr (std::is_integral_v) + case bitwise_conjunction: + if constexpr (std::is_integral_v && !std::is_same_v) { return constant_value{ lhs & rhs }; } return std::nullopt; case exclusive_disjunction: - if constexpr (std::is_integral_v) + case bitwise_exclusive_disjunction: + if constexpr (std::is_integral_v && !std::is_same_v) { return constant_value{ lhs ^ rhs }; } return std::nullopt; + case logical_conjunction: + if constexpr (std::is_same_v) + { + return constant_value{ lhs && rhs }; + } + return std::nullopt; + case logical_disjunction: + if constexpr (std::is_same_v) + { + return constant_value{ lhs || rhs }; + } + return std::nullopt; + case logical_exclusive_disjunction: + if constexpr (std::is_same_v) + { + return constant_value{ lhs != rhs }; + } + return std::nullopt; case shift_left: if constexpr (std::is_integral_v && !std::is_same_v) { @@ -420,8 +449,7 @@ namespace elna::boot { return target.bool_size; } - else if (is_primitive_type(resolved, "String") - || resolved.get() != nullptr) + else if (resolved.get() != nullptr) { return target.pointer_size + target.word_size; } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index a2cacb2..907f318 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -17,7 +17,6 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/name_analysis.h" -#include #include namespace elna::boot @@ -81,6 +80,84 @@ namespace elna::boot return "Duplicate 'const' qualifier is not allowed"; } + field_not_found_error::field_not_found_error(const identifier& field_name, + type composite_type) + : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type)) + { + } + + std::string field_not_found_error::what() const + { + type const resolved = resolve_underlying_type(composite_type); + bool const is_enum = resolved.get() != nullptr; + bool const is_record = resolved.get() != nullptr; + + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = composite_type.get()) + { + message += " '" + alias->name + "'"; + } + message += " does not have a "; + message += is_enum ? "member" : "field"; + message += " named '" + field_name + "'"; + return message; + } + return "Type '" + composite_type.to_string() + + "' does not have a field named '" + field_name + "'"; + } + + duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional original, + std::optional base_name) + : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)), + original(original), base_name(std::move(base_name)) + { + } + + std::string duplicate_member_error::what() const + { + type const resolved = resolve_underlying_type(aggregate); + bool const is_enum = resolved.get() != nullptr; + std::string const kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = aggregate.get()) + { + message += " '" + alias->name + "'"; + } + message += " already has a " + kind + " named '" + member_name + "'"; + + if (base_name.has_value()) + { + message += " (defined in base type '" + *base_name + "')"; + } + return message; + } + + std::optional> duplicate_member_error::note() const + { + if (original.has_value() && original->start().available()) + { + return std::make_pair("previously declared here", *original); + } + return std::nullopt; + } + + unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, + type actual) + : error(trait.position()), actual(std::move(actual)), trait_name(trait.name()) + { + } + + std::string unsupported_trait_type_error::what() const + { + return "Type '" + actual.to_string() + + "' does not support trait '#" + trait_name + "'"; + } + // Members of a constant aggregate are constant themselves. static type qualify_member_type(const type& element, const type& aggregate) { @@ -133,6 +210,20 @@ namespace elna::boot return result_type; } + std::optional name_analysis_visitor::lookup_pointer_like_field( + const std::string& field_name, const type& element_type) + { + if (field_name == "length") + { + return lookup_primitive_type("Word"); + } + if (field_name == "ptr") + { + return type(std::make_shared(element_type)); + } + return std::nullopt; + } + type name_analysis_visitor::lookup_primitive_type(const std::string& name) { return this->bag.lookup(name)->is_type()->symbol; @@ -156,26 +247,18 @@ namespace elna::boot return lookup_field(record->base, field_name); } } - else if (auto slice = resolved_type.get()) + else if (auto array = resolved_type.get()) { - if (field_name == "length") - { - return lookup_primitive_type("Word"); - } - else if (field_name == "ptr") + if (auto field = lookup_pointer_like_field(field_name, array->base)) { - return type(std::make_shared(slice->base)); + return field.value(); } } - else if (auto primitive = resolved_type.get(); primitive != nullptr && primitive->identifier == "String") + else if (auto slice = resolved_type.get()) { - if (field_name == "length") + if (auto field = lookup_pointer_like_field(field_name, slice->base)) { - return lookup_primitive_type("Word"); - } - else if (field_name == "ptr") - { - return type(std::make_shared(lookup_primitive_type("Char"))); + return field.value(); } } return type(); @@ -303,17 +386,6 @@ namespace elna::boot } else { - type actual; - - if (auto var = base_symbol->is_variable()) - { - actual = var->symbol; - } - else if (auto proc = base_symbol->is_procedure()) - { - actual = type(std::make_shared(proc->symbol)); - } - add_error(actual, expression->position()); this->current_type = type(); return; } @@ -513,10 +585,6 @@ for (const auto& member : expression->members) { call->type_decoration = procedure->return_type.proper_type; } - else if (call->callable().is_named() != nullptr) - { - call->type_decoration = this->current_type; - } for (expression *const argument : call->arguments) { argument->accept(this); @@ -653,10 +721,6 @@ for (const auto& member : expression->members) { expression->type_decoration = slice->base; } - else if (resolved_base == lookup_primitive_type("String")) - { - expression->type_decoration = lookup_primitive_type("Char"); - } // Elements of a constant array are constant themselves since a static // array is a holistic type. if (!expression->type_decoration.empty()) @@ -765,7 +829,8 @@ for (const auto& member : expression->members) void name_analysis_visitor::visit(literal *literal) { - literal->type_decoration = lookup_primitive_type("String"); + literal->type_decoration = type(std::make_shared( + type(std::make_shared(lookup_primitive_type("Char"))))); } declaration_visitor::declaration_visitor() diff --git a/boot/symbol.cc b/boot/symbol.cc index 738e4e1..da948fd 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -329,8 +329,6 @@ namespace elna::boot result->enter("Char", std::make_shared(type(std::make_shared("Char")))); result->enter("Pointer", std::make_shared(type(std::make_shared("Pointer")))); result->enter("Float", std::make_shared(type(std::make_shared("Float")))); - result->enter("String", std::make_shared(type(std::make_shared("String")))); - type const boolean = type(std::make_shared("Bool")); result->enter("Bool", std::make_shared(boolean)); diff --git a/boot/type_check.cc b/boot/type_check.cc index 5d0c7a6..7989925 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -56,72 +56,6 @@ namespace elna::boot + "', because it is constant or contains constant members"; } - field_not_found_error::field_not_found_error(const identifier& field_name, - type composite_type) - : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type)) - { - } - - std::string field_not_found_error::what() const - { - type const resolved = resolve_underlying_type(composite_type); - bool const is_enum = resolved.get() != nullptr; - bool const is_record = resolved.get() != nullptr; - - if (is_enum || is_record) - { - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = composite_type.get()) - { - message += " '" + alias->name + "'"; - } - message += " does not have a "; - message += is_enum ? "member" : "field"; - message += " named '" + field_name + "'"; - return message; - } - return "Type '" + composite_type.to_string() - + "' does not have a field named '" + field_name + "'"; - } - - duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional original, - std::optional base_name) - : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)), - original(original), base_name(std::move(base_name)) - { - } - - std::string duplicate_member_error::what() const - { - type const resolved = resolve_underlying_type(aggregate); - bool const is_enum = resolved.get() != nullptr; - std::string const kind = is_enum ? "member" : "field"; - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = aggregate.get()) - { - message += " '" + alias->name + "'"; - } - message += " already has a " + kind + " named '" + member_name + "'"; - - if (base_name.has_value()) - { - message += " (defined in base type '" + *base_name + "')"; - } - return message; - } - - std::optional> duplicate_member_error::note() const - { - if (original.has_value() && original->start().available()) - { - return std::make_pair("previously declared here", *original); - } - return std::nullopt; - } - cyclic_declaration_error::cyclic_declaration_error(const std::vector& cycle, const source_position position) : error(position), cycle(cycle) @@ -189,18 +123,6 @@ namespace elna::boot } } - unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, - type actual) - : error(trait.position()), actual(std::move(actual)), trait_name(trait.name()) - { - } - - std::string unsupported_trait_type_error::what() const - { - return "Type '" + actual.to_string() - + "' does not support trait '#" + trait_name + "'"; - } - unary_operation_error::unary_operation_error(const source_position position, type actual, unary_operator operation) : error(position), actual(std::move(actual)), op(operation) @@ -215,6 +137,8 @@ namespace elna::boot case reference: return '@'; case negation: + case bitwise_negation: + case logical_negation: return '~'; case minus: return '-'; @@ -368,10 +292,36 @@ namespace elna::boot return verdict::pass; } + assign_check::verdict assign_check::check_slice_conversion() const + { + auto assignee_slice = ctx.resolved_assignee.get(); + auto assignment_slice = ctx.resolved_assignment.get(); + + if (assignee_slice == nullptr || assignment_slice == nullptr) + { + return verdict::pass; + } + auto assignee_element_const = resolve_aliases(assignee_slice->base).get(); + auto assignment_element_const = resolve_aliases(assignment_slice->base).get(); + + // Const can be added to the element type, but not removed. + if (assignee_element_const != nullptr + && assignee_element_const->unqualified == assignment_slice->base) + { + return verdict::accept; + } + if (assignment_element_const != nullptr && assignee_element_const == nullptr) + { + return verdict::reject; + } + return verdict::pass; + } + bool assign_check::run() { for (auto handler : {&assign_check::guard_const_laundering, &assign_check::check_exact_match, + &assign_check::check_slice_conversion, &assign_check::check_pointer_hatch, &assign_check::check_pointer_conversion}) { @@ -534,10 +484,18 @@ namespace elna::boot { if (expression->base.has_value()) { - type const base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); - if (base_type.get() == nullptr) + auto const base_symbol = this->bag.lookup(expression->base.value().name()); + if (base_symbol == nullptr || base_symbol->is_type() == nullptr) { - add_error(base_type, expression->position()); + add_error(type(), expression->position()); + } + else + { + type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol); + if (base_type.get() == nullptr) + { + add_error(base_type, expression->position()); + } } } walking_visitor::visit(expression); @@ -571,6 +529,12 @@ namespace elna::boot call->arguments.size(), call->position()); } } + else if (!call->callable().type_decoration.empty()) + { + add_error(call->position(), + type(std::make_shared()), + call->callable().type_decoration); + } } void type_analysis_visitor::visit(record_constructor_expression *expression) @@ -660,12 +624,136 @@ namespace elna::boot } else if (operation == unary_operator::negation) { - if (!is_primitive_type(resolved, "Bool") - && !is_integral_type(resolved)) + if (is_primitive_type(resolved, "Bool")) + { + expression->operation(unary_operator::logical_negation); + } + else if (is_integral_type(resolved)) + { + expression->operation(unary_operator::bitwise_negation); + } + else { add_error(expression->position(), expression->operand().type_decoration, operation); } } + else if (operation == unary_operator::reference) + { + auto *designator = expression->operand().is_designator(); + if (designator == nullptr || designator->is_slicing() != nullptr) + { + add_error(expression->position(), + expression->operand().type_decoration, operation); + } + } + } + + binary_operation_error::binary_operation_error(const source_position position, + type left, type right, binary_operator operation) + : error(position), left(std::move(left)), right(std::move(right)), op(operation) + { + } + + std::string binary_operation_error::what() const + { + return "Invalid operands of type '" + left.to_string() + + "' and '" + right.to_string() + + "' for operator " + print_binary_operator(op); + } + + void type_analysis_visitor::visit(binary_expression *expression) + { + walking_visitor::visit(expression); + + auto operation = expression->operation(); + type const lhs_resolved = resolve_underlying_type(expression->lhs().type_decoration); + type const rhs_resolved = resolve_underlying_type(expression->rhs().type_decoration); + bool valid = false; + + switch (operation) + { + using enum binary_operator; + case sum: + valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved)) + || (is_integral_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)) + || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved); + break; + case subtraction: + valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved)) + || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)) + || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved); + break; + case division: + case remainder: + case multiplication: + valid = is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved; + break; + case less: + case greater: + case less_equal: + case greater_equal: + valid = (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved) + || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)); + break; + case conjunction: + case disjunction: + case exclusive_disjunction: + if (is_primitive_type(lhs_resolved, "Bool") && lhs_resolved == rhs_resolved) + { + switch (operation) + { + case conjunction: + expression->operation(logical_conjunction); + break; + case disjunction: + expression->operation(logical_disjunction); + break; + case exclusive_disjunction: + expression->operation(logical_exclusive_disjunction); + break; + default: + break; + } + valid = true; + } + else if (is_integral_type(lhs_resolved) && lhs_resolved == rhs_resolved) + { + switch (operation) + { + case conjunction: + expression->operation(bitwise_conjunction); + break; + case disjunction: + expression->operation(bitwise_disjunction); + break; + case exclusive_disjunction: + expression->operation(bitwise_exclusive_disjunction); + break; + default: + break; + } + valid = true; + } + break; + case equals: + case not_equals: + valid = lhs_resolved == rhs_resolved + || (is_primitive_type(lhs_resolved, "Pointer") && is_any_pointer_type(rhs_resolved)) + || (is_any_pointer_type(lhs_resolved) && is_primitive_type(rhs_resolved, "Pointer")) + || (lhs_resolved.get() && rhs_resolved.get()) + || (lhs_resolved.get() && rhs_resolved.get()); + break; + case shift_left: + case shift_right: + valid = is_integral_type(lhs_resolved) + && is_primitive_type(rhs_resolved, "Word"); + break; + } + if (!valid) + { + add_error(expression->position(), + expression->lhs().type_decoration, expression->rhs().type_decoration, operation); + } } } -- cgit v1.2.3