From d01f36cc1a8747366822841c65d93259b3315caa Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 10 Apr 2025 23:55:56 +0200 Subject: [PATCH] Implement case statements --- boot/parser.yy | 14 +++++++--- boot/symbol.cc | 25 +++++++++++++++++ gcc/elna-generic.cc | 49 +++++++++++++++++++++++++++++---- gcc/elna-tree.cc | 12 +------- include/elna/boot/ast.h | 2 +- include/elna/boot/symbol.h | 16 +++++++++-- include/elna/gcc/elna-generic.h | 1 + include/elna/gcc/elna-tree.h | 1 - 8 files changed, 96 insertions(+), 24 deletions(-) diff --git a/boot/parser.yy b/boot/parser.yy index 90657d9..6c4e098 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -141,7 +141,7 @@ along with GCC; see the file COPYING3. If not see %type unary_expression; %type binary_expression; %type > expressions actual_parameter_list; -%type designator_expression; +%type designator_expression dereference_expression; %type call_expression; %type while_statement; %type if_statement; @@ -324,6 +324,10 @@ literal: { $$ = new boot::literal(boot::make_position(@1), $1); } + | IDENTIFIER + { + $$ = new boot::variable_expression(boot::make_position(@1), $1); + } traits_expression: TRAIT "(" type_expressions ")" { @@ -332,7 +336,7 @@ traits_expression: } simple_expression: literal { $$ = $1; } - | designator_expression { $$ = $1; } + | dereference_expression { $$ = $1; } | traits_expression { $$ = $1; } | cast_expression { $$ = $1; } | call_expression { $$ = $1; } @@ -437,16 +441,18 @@ type_expressions: $$.emplace($$.cbegin(), $1); } | type_expression { $$.push_back($1); } -designator_expression: +dereference_expression: simple_expression "[" expression "]" { $$ = new boot::array_access_expression(boot::make_position(@2), $1, $3); } - | qualident { $$ = $1; } + | qualident { $$ = $1; } | simple_expression "^" { $$ = new boot::dereference_expression(boot::make_position(@1), $1); } +designator_expression: + dereference_expression { $$ = $1; } | IDENTIFIER { $$ = new boot::variable_expression(boot::make_position(@1), $1); diff --git a/boot/symbol.cc b/boot/symbol.cc index 9712935..ffa56a7 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -292,6 +292,16 @@ namespace elna::boot return nullptr; } + std::shared_ptr info::is_constant() + { + return nullptr; + } + + std::shared_ptr info::is_variable() + { + return nullptr; + } + type_info::type_info(const type symbol) : symbol(symbol) { @@ -317,6 +327,21 @@ namespace elna::boot { } + std::shared_ptr constant_info::is_constant() + { + return std::static_pointer_cast(shared_from_this()); + } + + variable_info::variable_info(const std::string& name, const type symbol) + : name(name), symbol(symbol) + { + } + + std::shared_ptr variable_info::is_variable() + { + return std::static_pointer_cast(shared_from_this());; + } + std::shared_ptr builtin_symbol_table() { auto result = std::make_shared(); diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index ed68304..f3d1cd8 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -138,7 +138,7 @@ namespace elna::gcc location_t argument_location = get_location(&argument->position()); if (VOID_TYPE_P(TREE_VALUE(current_parameter))) { - error_at(argument_location, "too many arguments, expected %i, got %lu", + error_at(argument_location, "Too many arguments, expected %i, got %lu", list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); this->current_expression = error_mark_node; break; @@ -182,7 +182,7 @@ namespace elna::gcc if (is_void_type(record_fields)) { - error_at(argument_location, "too many arguments, expected %i, got %lu", + error_at(argument_location, "Too many arguments, expected %i, got %lu", list_length(TYPE_FIELDS(symbol)), arguments.size()); this->current_expression = error_mark_node; break; @@ -767,6 +767,11 @@ namespace elna::gcc location_t definition_location = get_location(&definition->position()); definition->body().accept(this); + if (!extract_constant(definition_location)) + { + this->current_expression = NULL_TREE; + return; + } tree definition_tree = build_decl(definition_location, CONST_DECL, get_identifier(definition->identifier.c_str()), TREE_TYPE(this->current_expression)); auto result = this->symbols->enter(definition->identifier, definition_tree); @@ -1419,9 +1424,18 @@ namespace elna::gcc void generic_visitor::visit(boot::case_statement *statement) { statement->condition().accept(this); - tree condition_expression = this->current_expression; - tree end_label_declaration = create_artificial_label(UNKNOWN_LOCATION); + tree unqualified_condition = get_qualified_type(TREE_TYPE(this->current_expression), TYPE_UNQUALIFIED); + + if (!INTEGRAL_TYPE_P(unqualified_condition)) + { + error_at(get_location(&statement->condition().position()), + "Case expressions can only be integral numbers, characters and enumerations, given '%s'", + print_type(unqualified_condition).c_str()); + this->current_expression = NULL_TREE; + return; + } + tree end_label_declaration = create_artificial_label(get_location(&statement->position())); tree switch_statements = alloc_stmt_list(); for (const boot::switch_case& case_block : statement->cases) @@ -1429,7 +1443,17 @@ namespace elna::gcc for (boot::literal_expression *const case_label : case_block.labels) { case_label->accept(this); - tree case_label_declaration = create_artificial_label(get_location(&case_label->position())); + location_t case_location = get_location(&case_label->position()); + + if (extract_constant(case_location) + && !is_assignable_from(unqualified_condition, this->current_expression)) + { + error_at(case_location, "Case type '%s' does not match the expression type '%s'", + print_type(TREE_TYPE(this->current_expression)).c_str(), + print_type(unqualified_condition).c_str()); + this->current_expression = error_mark_node; + } + tree case_label_declaration = create_artificial_label(case_location); tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration); append_to_statement_list(case_expression, &switch_statements); @@ -1452,4 +1476,19 @@ namespace elna::gcc this->current_expression = NULL_TREE; } + + bool generic_visitor::extract_constant(location_t expression_location) + { + if (TREE_CODE(this->current_expression) == CONST_DECL) + { + this->current_expression = DECL_INITIAL(this->current_expression); + } + if (TREE_CONSTANT(this->current_expression) == 0) + { + error_at(expression_location, "Expected a constant expression"); + this->current_expression = error_mark_node; + return false; + } + return true; + } } diff --git a/gcc/elna-tree.cc b/gcc/elna-tree.cc index a2d7b5a..47b6066 100644 --- a/gcc/elna-tree.cc +++ b/gcc/elna-tree.cc @@ -37,14 +37,6 @@ namespace elna::gcc return is_integral_type(type) || type == elna_float_type_node; } - bool is_primitive_type(tree type) - { - gcc_assert(TYPE_P(type)); - return TREE_CODE(type) == INTEGER_TYPE - || type == elna_float_type_node - || type == elna_bool_type_node; - } - bool is_array_type(tree type) { gcc_assert(TYPE_P(type)); @@ -59,9 +51,7 @@ namespace elna::gcc bool is_castable_type(tree type) { gcc_assert(TYPE_P(type)); - auto code = TREE_CODE(type); - - return is_primitive_type(type) || POINTER_TYPE_P(type) || code == ENUMERAL_TYPE; + return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE; } bool are_compatible_pointers(tree lhs_type, tree rhs) diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index c5d2c76..cf531d9 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -483,7 +483,7 @@ namespace elna::boot designator_expression(); }; - class variable_expression : public designator_expression + class variable_expression : public designator_expression, public literal_expression { public: const std::string name; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index ba48e17..698b585 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -155,6 +155,7 @@ namespace elna::boot class type_info; class procedure_info; class constant_info; + class variable_info; class info : public std::enable_shared_from_this { @@ -163,6 +164,8 @@ namespace elna::boot virtual std::shared_ptr is_type(); virtual std::shared_ptr is_procedure(); + virtual std::shared_ptr is_constant(); + virtual std::shared_ptr is_variable(); }; class type_info : public info @@ -171,7 +174,6 @@ namespace elna::boot const type symbol; explicit type_info(const type symbol); - std::shared_ptr is_type() override; }; @@ -182,7 +184,6 @@ namespace elna::boot const std::vector names; procedure_info(const procedure_type symbol, const std::vector names); - std::shared_ptr is_procedure() override; }; @@ -195,6 +196,17 @@ namespace elna::boot const variant symbol; explicit constant_info(const variant& symbol); + std::shared_ptr is_constant() override; + }; + + class variable_info : public info + { + public: + const std::string name; + const type symbol; + + variable_info(const std::string& name, const type symbol); + std::shared_ptr is_variable() override; }; /** diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index c2f13da..981b9b9 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -70,6 +70,7 @@ namespace elna::gcc bool expect_trait_type_only(boot::traits_expression *trait); bool expect_trait_for_integral_type(boot::traits_expression *trait); void visit_statements(const std::vector& statements); + bool extract_constant(location_t expression_location); public: generic_visitor(std::shared_ptr symbol_table, diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 223fe52..3bf7b32 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -35,7 +35,6 @@ namespace elna::gcc bool is_integral_type(tree type); bool is_numeric_type(tree type); - bool is_primitive_type(tree type); bool is_array_type(tree type); bool is_void_type(tree type);