diff --git a/boot/ast.cc b/boot/ast.cc index c36ffde..3dece9e 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -275,7 +275,7 @@ namespace elna::boot } constant_definition::constant_definition(const struct position position, const std::string& identifier, - const bool exported, literal_expression *body) + const bool exported, expression *body) : definition(position, identifier, exported), m_body(body) { } @@ -285,7 +285,7 @@ namespace elna::boot visitor->visit(this); } - literal_expression& constant_definition::body() + expression& constant_definition::body() { return *m_body; } diff --git a/boot/parser.yy b/boot/parser.yy index 90657d9..9554372 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -127,7 +127,7 @@ along with GCC; see the file COPYING3. If not see %left "*" "/" "%" %type literal; -%type > case_labels; +%type > case_labels; %type switch_case; %type > switch_cases; %type constant_definition; @@ -137,7 +137,6 @@ along with GCC; see the file COPYING3. If not see %type > type_expressions; %type traits_expression; %type expression operand simple_expression; -%type qualident; %type unary_expression; %type binary_expression; %type > expressions actual_parameter_list; @@ -296,34 +295,13 @@ return_statement: $$ = new boot::return_statement(boot::make_position(@1)); } literal: - INTEGER - { - $$ = new boot::literal(boot::make_position(@1), $1); - } - | WORD - { - $$ = new boot::literal(boot::make_position(@1), $1); - } - | FLOAT - { - $$ = new boot::literal(boot::make_position(@1), $1); - } - | BOOLEAN - { - $$ = new boot::literal(boot::make_position(@1), $1); - } - | CHARACTER - { - $$ = new boot::literal(boot::make_position(@1), $1.at(0)); - } - | "nil" - { - $$ = new boot::literal(boot::make_position(@1), nullptr); - } - | STRING - { - $$ = new boot::literal(boot::make_position(@1), $1); - } + INTEGER { $$ = new boot::literal(boot::make_position(@1), $1); } + | WORD { $$ = new boot::literal(boot::make_position(@1), $1); } + | FLOAT { $$ = new boot::literal(boot::make_position(@1), $1); } + | BOOLEAN { $$ = new boot::literal(boot::make_position(@1), $1); } + | CHARACTER { $$ = new boot::literal(boot::make_position(@1), $1.at(0)); } + | "nil" { $$ = new boot::literal(boot::make_position(@1), nullptr); } + | STRING { $$ = new boot::literal(boot::make_position(@1), $1); } traits_expression: TRAIT "(" type_expressions ")" { @@ -439,33 +417,22 @@ type_expressions: | type_expression { $$.push_back($1); } designator_expression: simple_expression "[" expression "]" - { - $$ = new boot::array_access_expression(boot::make_position(@2), $1, $3); - } - | qualident { $$ = $1; } + { $$ = new boot::array_access_expression(boot::make_position(@2), $1, $3); } + | simple_expression "." IDENTIFIER + { $$ = new boot::field_access_expression(boot::make_position(@2), $1, $3); } | simple_expression "^" - { - $$ = new boot::dereference_expression(boot::make_position(@1), $1); - } + { $$ = new boot::dereference_expression(boot::make_position(@1), $1); } | IDENTIFIER - { - $$ = new boot::variable_expression(boot::make_position(@1), $1); - } -qualident: simple_expression "." IDENTIFIER - { - $$ = new boot::field_access_expression(boot::make_position(@2), $1, $3); - } + { $$ = new boot::variable_expression(boot::make_position(@1), $1); } statement: designator_expression ":=" expression - { - $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); - } + { $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); } | while_statement { $$ = $1; } | if_statement { $$ = $1; } | return_statement { $$ = $1; } | call_expression { $$ = $1; } - | DEFER statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); } - | CASE expression "of" switch_cases "end" + | "defer" statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); } + | "case" expression "of" switch_cases "end" { $$ = new boot::case_statement(boot::make_position(@1), $2, std::move($4)); } switch_case: case_labels ":" statements { $$ = { .labels = std::move($1), .statements = std::move($3) }; } switch_cases: @@ -476,12 +443,12 @@ switch_cases: } | switch_case { $$.push_back($1); } case_labels: - literal "," case_labels + expression "," case_labels { std::swap($$, $3); $$.emplace($$.cbegin(), $1); } - | literal { $$.push_back($1); } + | expression { $$.push_back($1); } statements: statement ";" statements { @@ -562,7 +529,7 @@ variable_declarations: variable_part: /* no variable declarations */ {} | "var" variable_declarations { std::swap($$, $2); } -constant_definition: identifier_definition "=" literal +constant_definition: identifier_definition "=" expression { $$ = new boot::constant_definition(boot::make_position(@1), $1.first, $1.second, $3); } diff --git a/boot/semantic.cc b/boot/semantic.cc index ad2e9d6..782d6ea 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -278,7 +278,7 @@ namespace elna::boot statement->condition().accept(this); for (const switch_case& case_block : statement->cases) { - for (literal_expression *const case_label : case_block.labels) + for (expression *const case_label : case_block.labels) { case_label->accept(this); } 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..92ba0f2 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; @@ -632,7 +632,8 @@ namespace elna::gcc && (expression->operation() == boot::binary_operator::sum || expression->operation() == boot::binary_operator::subtraction)) { - this->current_expression = do_pointer_arithmetic(expression->operation(), left, right); + this->current_expression = do_pointer_arithmetic(expression->operation(), + left, right, expression_location); if (this->current_expression == error_mark_node) { error_at(expression_location, @@ -767,6 +768,15 @@ namespace elna::gcc location_t definition_location = get_location(&definition->position()); definition->body().accept(this); + if (extract_constant(definition_location)) + { + this->current_expression = fold_init(this->current_expression); + } + else + { + 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); @@ -923,17 +933,16 @@ namespace elna::gcc expression->index().accept(this); if (!is_integral_type(TREE_TYPE(this->current_expression))) { - error_at(location, "type '%s' cannot be used as index", + error_at(location, "Type '%s' cannot be used as index", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; return; } if (this->current_expression != elna_word_type_node) { - this->current_expression = fold_convert(elna_word_type_node, this->current_expression); + this->current_expression = convert(elna_word_type_node, this->current_expression); } - tree offset = build2(MINUS_EXPR, elna_word_type_node, - this->current_expression, size_one_node); + tree offset = build2(MINUS_EXPR, elna_word_type_node, this->current_expression, size_one_node); if (is_array_type(TREE_TYPE(designator))) { @@ -947,14 +956,14 @@ namespace elna::gcc tree string_ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), designator, elna_string_ptr_field_node, NULL_TREE); - tree target_pointer = do_pointer_arithmetic(boot::binary_operator::sum, string_ptr, offset); + tree target_pointer = do_pointer_arithmetic(boot::binary_operator::sum, string_ptr, offset, location); this->current_expression = build1_loc(location, INDIRECT_REF, elna_char_type_node, target_pointer); } else { - error_at(location, "indexing is not allowed on type '%s'", + error_at(location, "Indexing is not allowed on type '%s'", print_type(TREE_TYPE(designator)).c_str()); this->current_expression = error_mark_node; } @@ -1071,7 +1080,7 @@ namespace elna::gcc if (is_array_type(aggregate_type) && expression->field() == "length") { - this->current_expression = fold_convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST), + this->current_expression = convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST), TYPE_MAX_VALUE(TYPE_DOMAIN(aggregate_type))); } else if (is_array_type(aggregate_type) && expression->field() == "ptr") @@ -1419,17 +1428,36 @@ 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) { - for (boot::literal_expression *const case_label : case_block.labels) + for (boot::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 +1480,21 @@ namespace elna::gcc this->current_expression = NULL_TREE; } + + bool generic_visitor::extract_constant(location_t expression_location) + { + int code = TREE_CODE(this->current_expression); + + if (code == CONST_DECL) + { + this->current_expression = DECL_INITIAL(this->current_expression); + } + else if (TREE_CODE_CLASS(code) != tcc_constant) + { + 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..a1d7f0b 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) @@ -139,7 +129,8 @@ namespace elna::gcc return field_declaration; } - tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right) + tree do_pointer_arithmetic(boot::binary_operator binary_operator, + tree left, tree right, location_t operation_location) { tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); @@ -167,7 +158,7 @@ namespace elna::gcc offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp); offset = fold_convert(sizetype, offset); - return fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset); + return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset); } else if (binary_operator == boot::binary_operator::subtraction) { @@ -181,11 +172,11 @@ namespace elna::gcc convert_expression = fold_convert(sizetype, convert_expression); convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression); - return fold_build2(POINTER_PLUS_EXPR, pointer_type, left, convert_expression); + return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, pointer_type, left, convert_expression); } else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type) { - return fold_build2(POINTER_DIFF_EXPR, ssizetype, left, right); + return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right); } } gcc_unreachable(); @@ -200,7 +191,7 @@ namespace elna::gcc if (condition) { - return build2_loc(expression_location, operator_code, target_type, left, right); + return fold_build2_loc(expression_location, operator_code, target_type, left, right); } else { diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index c5d2c76..4dacd85 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -321,14 +321,14 @@ namespace elna::boot */ class constant_definition : public definition { - literal_expression *m_body; + expression *m_body; public: constant_definition(const struct position position, const std::string& identifier, - const bool exported, literal_expression *body); + const bool exported, expression *body); void accept(parser_visitor *visitor) override; - literal_expression& body(); + expression& body(); virtual ~constant_definition() override; }; @@ -451,7 +451,7 @@ namespace elna::boot struct switch_case { - std::vector labels; + std::vector labels; std::vector statements; }; @@ -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..335dc0c 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); @@ -73,7 +72,8 @@ namespace elna::gcc void defer(tree statement_tree); tree chain_defer(); - tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right); + tree do_pointer_arithmetic(boot::binary_operator binary_operator, + tree left, tree right, location_t expression_location); tree build_binary_operation(bool condition, boot::binary_expression *expression, tree_code operator_code, tree left, tree right, tree target_type); tree build_arithmetic_operation(boot::binary_expression *expression,