diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ast.cc | 142 | ||||
| -rw-r--r-- | boot/driver.cc | 4 | ||||
| -rw-r--r-- | boot/parser.yy | 53 | ||||
| -rw-r--r-- | boot/result.cc | 107 | ||||
| -rw-r--r-- | boot/semantic.cc | 331 | ||||
| -rw-r--r-- | boot/symbol.cc | 48 |
6 files changed, 502 insertions, 183 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 2a22d69..37e89ac 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -413,7 +413,7 @@ namespace elna::boot { for (const field_initializer& initializer : expression->field_initializers) { - initializer.value->accept(this); + initializer.value().accept(this); } } @@ -493,8 +493,8 @@ namespace elna::boot { } - node::node(const struct position position) - : source_position(position) + node::node(const source_position position) + : m_position(position) { } @@ -502,9 +502,9 @@ namespace elna::boot { } - const struct position& node::position() const + const source_position& node::position() const { - return this->source_position; + return this->m_position; } cast_expression *expression::is_cast() @@ -572,7 +572,7 @@ namespace elna::boot return nullptr; } - array_type_expression::array_type_expression(const struct position position, + array_type_expression::array_type_expression(const source_position position, type_expression *base, const std::uint32_t size) : node(position), m_base(base), size(size) { @@ -598,7 +598,7 @@ namespace elna::boot return *m_base; } - pointer_type_expression::pointer_type_expression(const struct position position, + pointer_type_expression::pointer_type_expression(const source_position position, type_expression *base) : node(position), m_base(base) { @@ -624,15 +624,15 @@ namespace elna::boot return *m_base; } - record_type_expression::record_type_expression(const struct position position, + record_type_expression::record_type_expression(const source_position position, std::vector<field_declaration>&& fields) : node(position), fields(std::move(fields)) { } - record_type_expression::record_type_expression(const struct position position, - std::vector<field_declaration>&& fields, std::string&& base) - : node(position), fields(std::move(fields)), base(std::make_optional<std::string>(std::move(base))) + record_type_expression::record_type_expression(const source_position position, + std::vector<field_declaration>&& fields, identifier&& base) + : node(position), fields(std::move(fields)), base(std::make_optional<identifier>(std::move(base))) { } @@ -646,8 +646,48 @@ namespace elna::boot return this; } - record_constructor_expression::record_constructor_expression(const struct position position, - std::string&& type_name, + field_initializer::field_initializer(identifier name, expression *value) + : m_name(std::move(name)), m_value(value) + { + } + + field_initializer::~field_initializer() + { + delete this->m_value; + } + + field_initializer::field_initializer(field_initializer&& other) noexcept + : m_name(std::move(other.m_name)), m_value(other.m_value) + { + other.m_value = nullptr; + } + + field_initializer& field_initializer::operator=(field_initializer&& other) noexcept + { + delete this->m_value; + this->m_name = std::move(other.m_name); + this->m_value = other.m_value; + other.m_value = nullptr; + return *this; + } + + const std::string& field_initializer::name() const + { + return this->m_name.name(); + } + + const identifier& field_initializer::id() const + { + return this->m_name; + } + + expression& field_initializer::value() const + { + return *this->m_value; + } + + record_constructor_expression::record_constructor_expression(const source_position position, + identifier&& type_name, std::vector<field_initializer>&& field_initializers) : node(position), type_name(std::move(type_name)), field_initializers(std::move(field_initializers)) { @@ -658,15 +698,7 @@ namespace elna::boot visitor->visit(this); } - record_constructor_expression::~record_constructor_expression() - { - for (const field_initializer& initializer : field_initializers) - { - delete initializer.value; - } - } - - array_constructor_expression::array_constructor_expression(const struct position position, + array_constructor_expression::array_constructor_expression(const source_position position, std::uint32_t size, type_expression *element_type, std::vector<expression *>&& elements) : node(position), size(size), m_element_type(element_type), elements(std::move(elements)) @@ -687,14 +719,14 @@ namespace elna::boot } } - variable_declaration::variable_declaration(const struct position position, + variable_declaration::variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, expression *initializer) : node(position), m_variable_type(variable_type), identifiers(std::move(identifier)), initializer(initializer) { } - variable_declaration::variable_declaration(const struct position position, + variable_declaration::variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, std::monostate) : node(position), m_variable_type(variable_type), identifiers(std::move(identifier)), is_extern(true) @@ -716,12 +748,12 @@ namespace elna::boot return *m_variable_type; } - declaration::declaration(const struct position position, identifier_definition identifier) + declaration::declaration(const source_position position, identifier_definition identifier) : node(position), identifier(identifier) { } - constant_declaration::constant_declaration(const struct position position, identifier_definition identifier, + constant_declaration::constant_declaration(const source_position position, identifier_definition identifier, expression *initializer) : declaration(position, identifier), m_initializer(initializer) { @@ -742,7 +774,7 @@ namespace elna::boot delete m_initializer; } - procedure_type_expression::procedure_type_expression(const struct position position, + procedure_type_expression::procedure_type_expression(const source_position position, std::vector<field_declaration>&& parameters, return_t return_type) : node(position), return_type(return_type), parameters(std::move(parameters)) { @@ -766,9 +798,9 @@ namespace elna::boot return this; } - enumeration_type_expression::enumeration_type_expression(const struct position position, - std::vector<std::string>&& members) - : node(position), members(members) + enumeration_type_expression::enumeration_type_expression(const source_position position, + std::vector<identifier>&& members) + : node(position), members(std::move(members)) { } @@ -782,14 +814,14 @@ namespace elna::boot return this; } - procedure_declaration::procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration::procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading, procedure_body&& body) : declaration(position, identifier), m_heading(heading), body(std::make_optional<procedure_body>(std::move(body))) { } - procedure_declaration::procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration::procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading) : declaration(position, identifier), m_heading(heading) { @@ -810,7 +842,7 @@ namespace elna::boot delete m_heading; } - type_declaration::type_declaration(const struct position position, identifier_definition identifier, + type_declaration::type_declaration(const source_position position, identifier_definition identifier, type_expression *underlying_type) : declaration(position, identifier), m_underlying_type(underlying_type) { @@ -863,13 +895,13 @@ namespace elna::boot } } - unit::unit(const struct position position) + unit::unit(const source_position position) : node(position), procedure_body(std::vector<constant_declaration *>{}, std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr) { } - unit::unit(const struct position position, + unit::unit(const source_position position, std::vector<import_declaration *>&& imports, std::vector<constant_declaration *>&& constants, std::vector<type_declaration *>&& types, @@ -913,7 +945,7 @@ namespace elna::boot return this; } - defer_statement::defer_statement(const struct position position, std::vector<statement *>&& statements) + defer_statement::defer_statement(const source_position position, std::vector<statement *>&& statements) : node(position), statements(std::move(statements)) { } @@ -936,7 +968,7 @@ namespace elna::boot visitor->visit(this); } - empty_statement::empty_statement(const struct position position) + empty_statement::empty_statement(const source_position position) : node(position) { } @@ -971,7 +1003,7 @@ namespace elna::boot __builtin_unreachable(); } - named_expression::named_expression(const struct position position, const std::string& name) + named_expression::named_expression(const source_position position, const std::string& name) : node(position), name(name) { } @@ -986,7 +1018,7 @@ namespace elna::boot return this; } - array_access_expression::array_access_expression(const struct position position, + array_access_expression::array_access_expression(const source_position position, expression *base, expression *index) : node(position), m_base(base), m_index(index) { @@ -1018,9 +1050,9 @@ namespace elna::boot delete m_base; } - field_access_expression::field_access_expression(const struct position position, - expression *base, const std::string& field) - : node(position), m_base(base), m_field(field) + field_access_expression::field_access_expression(const source_position position, + expression *base, identifier&& field) + : node(position), m_base(base), m_field(std::move(field)) { } @@ -1034,7 +1066,7 @@ namespace elna::boot return *m_base; } - std::string& field_access_expression::field() + const identifier& field_access_expression::field() { return m_field; } @@ -1049,7 +1081,7 @@ namespace elna::boot delete m_base; } - dereference_expression::dereference_expression(const struct position position, + dereference_expression::dereference_expression(const source_position position, expression *base) : node(position), m_base(base) { @@ -1075,7 +1107,7 @@ namespace elna::boot delete m_base; } - binary_expression::binary_expression(const struct position position, expression *lhs, + binary_expression::binary_expression(const source_position position, expression *lhs, expression *rhs, const binary_operator operation) : node(position), m_lhs(lhs), m_rhs(rhs), m_operator(operation) { @@ -1112,7 +1144,7 @@ namespace elna::boot delete m_rhs; } - unary_expression::unary_expression(const struct position position, expression *operand, + unary_expression::unary_expression(const source_position position, expression *operand, const unary_operator operation) : node(position), m_operand(std::move(operand)), m_operator(operation) { @@ -1143,7 +1175,7 @@ namespace elna::boot delete m_operand; } - procedure_call::procedure_call(const struct position position, designator_expression *callable, + procedure_call::procedure_call(const source_position position, designator_expression *callable, std::vector<expression *>&& arguments) : node(position), m_callable(callable), arguments(std::move(arguments)) { @@ -1173,7 +1205,7 @@ namespace elna::boot delete m_callable; } - cast_expression::cast_expression(const struct position position, type_expression *target, expression *value) + cast_expression::cast_expression(const source_position position, type_expression *target, expression *value) : node(position), m_target(target), m_value(value) { } @@ -1204,9 +1236,9 @@ namespace elna::boot delete m_value; } - traits_expression::traits_expression(const struct position position, const std::string& name, + traits_expression::traits_expression(const source_position position, identifier&& name, std::vector<type_expression *>&& arguments) - : node(position), arguments(std::move(arguments)), name(name) + : node(position), arguments(std::move(arguments)), name(std::move(name)) { } @@ -1247,7 +1279,7 @@ namespace elna::boot } } - case_statement::case_statement(const struct position position, + case_statement::case_statement(const source_position position, expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative) : node(position), m_condition(condition), cases(std::move(cases)), alternative(alternative) { @@ -1263,7 +1295,7 @@ namespace elna::boot return *m_condition; } - assign_statement::assign_statement(const struct position position, designator_expression *lvalue, + assign_statement::assign_statement(const source_position position, designator_expression *lvalue, expression *rvalue) : node(position), m_lvalue(lvalue), m_rvalue(rvalue) { @@ -1309,7 +1341,7 @@ namespace elna::boot delete m_rvalue; } - if_statement::if_statement(const struct position position, conditional_statements *branch, + if_statement::if_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches, std::vector<statement *> *alternative) : node(position), m_branch(branch), branches(std::move(branches)), alternative(alternative) @@ -1336,7 +1368,7 @@ namespace elna::boot delete this->alternative; } - import_declaration::import_declaration(const struct position position, std::vector<std::string>&& segments) + import_declaration::import_declaration(const source_position position, std::vector<std::string>&& segments) : node(position), segments(std::move(segments)) { } @@ -1346,7 +1378,7 @@ namespace elna::boot visitor->visit(this); } - while_statement::while_statement(const struct position position, conditional_statements *branch, + while_statement::while_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches) : node(position), m_branch(branch), branches(std::move(branches)) { diff --git a/boot/driver.cc b/boot/driver.cc index b51da32..8dc95f8 100644 --- a/boot/driver.cc +++ b/boot/driver.cc @@ -19,14 +19,14 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - position make_position(const yy::location& location) + source_position make_position(const yy::location& location) { auto start_location = boot::location(static_cast<std::size_t>(location.begin.line), static_cast<std::size_t>(location.begin.column)); auto end_location = boot::location(static_cast<std::size_t>(location.end.line), static_cast<std::size_t>(location.end.column)); - return position(start_location, end_location); + return source_position(start_location, end_location); } syntax_error::syntax_error(const std::string& message, const yy::location& location) diff --git a/boot/parser.yy b/boot/parser.yy index 9f610ee..c34c905 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -153,14 +153,16 @@ along with GCC; see the file COPYING3. If not see %type <std::unique_ptr<elna::boot::procedure_body>> procedure_body; %type <elna::boot::field_declaration> field_declaration; %type <std::vector<elna::boot::field_declaration>> optional_fields required_fields; -%type <elna::boot::field_initializer> field_initializer; +%type <std::unique_ptr<elna::boot::field_initializer>> field_initializer; %type <std::vector<elna::boot::field_initializer>> field_initializers; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; %type <std::vector<elna::boot::statement *> *> else_statements; %type <elna::boot::cast_expression *> cast_expression; -%type <elna::boot::identifier_definition> identifier_definition; +%type <std::unique_ptr<elna::boot::identifier>> identifier; +%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition; %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; -%type <std::vector<std::string>> identifiers import_declaration; +%type <std::vector<std::string>> import_declaration; +%type <std::vector<elna::boot::identifier>> identifiers; %type <std::vector<elna::boot::import_declaration *>> import_declarations import_part; %% program: @@ -176,16 +178,18 @@ procedure_body: statement_part: /* no statements */ {} | "begin" statements { $$ = $2; } +identifier: + IDENTIFIER { $$ = std::make_unique<boot::identifier>($1, boot::make_position(@1)); } identifier_definition: - IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; } - | IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; } + IDENTIFIER "*" { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), true); } + | IDENTIFIER { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), false); } identifier_definitions: identifier_definition "," identifier_definitions { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | identifier_definition { $$.emplace_back($1); } + | identifier_definition { $$.emplace_back(std::move(*$1)); } return_declaration: /* proper procedure */ {} | ":" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } @@ -195,11 +199,11 @@ procedure_heading: "(" optional_fields ")" return_declaration procedure_declaration: "proc" identifier_definition procedure_heading procedure_body { - $$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3, std::move(*$4)); + $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $3, std::move(*$4)); } | "proc" identifier_definition procedure_heading "extern" { - $$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3); + $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $3); } procedure_part: /* no procedure declarations */ {} @@ -247,7 +251,8 @@ literal: traits_expression: TRAIT "(" type_expressions ")" { - $$ = new boot::traits_expression(boot::make_position(@$), $1, $3); + $$ = new boot::traits_expression(boot::make_position(@$), + boot::identifier($1, boot::make_position(@1)), $3); } simple_expression: literal { $$ = $1; } @@ -256,9 +261,9 @@ simple_expression: | cast_expression { $$ = $1; } | call_expression { $$ = $1; } | "(" expression ")" { $$ = $2; } - | IDENTIFIER "{" field_initializers "}" + | identifier "{" field_initializers "}" { - $$ = new boot::record_constructor_expression(boot::make_position(@$), $1, $3); + $$ = new boot::record_constructor_expression(boot::make_position(@$), std::move(*$1), $3); } | "[" INTEGER "]" type_expression "{" expressions "}" { @@ -365,8 +370,8 @@ type_expressions: designator_expression: simple_expression "[" expression "]" { $$ = new boot::array_access_expression(boot::make_position(@$), $1, $3); } - | simple_expression "." IDENTIFIER - { $$ = new boot::field_access_expression(boot::make_position(@$), $1, $3); } + | simple_expression "." identifier + { $$ = new boot::field_access_expression(boot::make_position(@$), $1, std::move(*$3)); } | simple_expression "^" { $$ = new boot::dereference_expression(boot::make_position(@$), $1); } | IDENTIFIER @@ -426,14 +431,14 @@ optional_fields: required_fields { $$ = $1; } | /* no fields */ {} field_initializer: - IDENTIFIER ":" expression { $$.name = $1; $$.value = $3; } + identifier ":" expression { $$ = std::make_unique<boot::field_initializer>(std::move(*$1), $3); } field_initializers: field_initializer "," field_initializers { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | field_initializer { $$.push_back($1); } + | field_initializer { $$.push_back(std::move(*$1)); } type_expression: "[" INTEGER "]" type_expression { @@ -447,9 +452,9 @@ type_expression: { $$ = new boot::record_type_expression(boot::make_position(@$), $2); } - | "record" "(" IDENTIFIER ")" optional_fields "end" + | "record" "(" identifier ")" optional_fields "end" { - $$ = new boot::record_type_expression(boot::make_position(@$), $5, $3); + $$ = new boot::record_type_expression(boot::make_position(@$), $5, std::move(*$3)); } | "proc" procedure_heading { @@ -464,12 +469,12 @@ type_expression: $$ = new boot::named_expression(boot::make_position(@$), $1); } identifiers: - IDENTIFIER "," identifiers + identifier "," identifiers { $$ = $3; - $$.emplace($$.cbegin(), $1); + $$.emplace($$.cbegin(), std::move(*$1)); } - | IDENTIFIER { $$.emplace_back($1); } + | identifier { $$.emplace_back(std::move(*$1)); } variable_declaration: identifier_definitions ":" type_expression { @@ -498,7 +503,7 @@ variable_part: | "var" variable_declarations { $$ = $2; } constant_declaration: identifier_definition ":=" expression { - $$ = new boot::constant_declaration(boot::make_position(@$), $1, $3); + $$ = new boot::constant_declaration(boot::make_position(@$), std::move(*$1), $3); } constant_declarations: constant_declaration constant_declarations @@ -532,7 +537,7 @@ import_part: | "import" import_declarations { $$ = $2; } type_declaration: identifier_definition "=" type_expression { - $$ = new boot::type_declaration(boot::make_position(@$), $1, $3); + $$ = new boot::type_declaration(boot::make_position(@$), std::move(*$1), $3); } type_declarations: type_declaration type_declarations diff --git a/boot/result.cc b/boot/result.cc index 90d0ad6..5f3c516 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -19,22 +19,57 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - location::location(std::size_t line, std::size_t column) - : line(line), column(column) + location::location(const std::size_t line, const std::size_t column) + : m_line(line), m_column(column) { } + std::size_t location::line() const + { + return this->m_line; + } + + std::size_t location::column() const + { + return this->m_column; + } + bool location::available() const { - return this->line != 0 || this->column != 0; + return this->m_line != 0 || this->m_column != 0; + } + + bool location::operator==(const location& that) const + { + return this->m_line == that.m_line && this->m_column == that.m_column; + } + + bool location::operator!=(const location& that) const + { + return !(*this == that); } - position::position(location start, location end) - : start(start), end(end) + source_position::source_position(location start, location end) + : m_start(start), m_end(end) { } - error::error(const struct position position) + const location& source_position::start() const + { + return this->m_start; + } + + const location& source_position::end() const + { + return this->m_end; + } + + bool source_position::is_span() const + { + return this->m_start != this->m_end; + } + + error::error(const source_position position) : position(position) { } @@ -49,19 +84,65 @@ namespace elna::boot return !m_errors.empty(); } - bool identifier_definition::operator==(const identifier_definition& that) const + identifier::identifier(const std::string& name, const source_position& position) + : m_name(name), m_position(position) + { + } + + const std::string& identifier::name() const + { + return this->m_name; + } + + const source_position& identifier::position() const + { + return this->m_position; + } + + bool identifier::operator==(const identifier& that) const + { + return this->m_name == that.m_name; + } + + bool identifier::operator!=(const identifier& that) const + { + return !(*this == that); + } + + bool identifier::operator==(const std::string& that) const + { + return this->m_name == that; + } + + bool identifier::operator!=(const std::string& that) const + { + return !(*this == that); + } + + identifier_definition::identifier_definition(const std::string& name, + const source_position& position, const bool exported) + : m_identifier(name, position), m_exported(exported) + { + } + + const std::string& identifier_definition::name() const + { + return this->m_identifier.name(); + } + + const identifier& identifier_definition::id() const { - return *this == that.name; + return this->m_identifier; } - bool identifier_definition::operator==(const std::string& that) const + bool identifier_definition::exported() const { - return this->name == that; + return this->m_exported; } } -std::size_t std::hash<elna::boot::identifier_definition>::operator()( - const elna::boot::identifier_definition& key) const +std::size_t std::hash<elna::boot::identifier>::operator()( + const elna::boot::identifier& key) const { - return std::hash<std::string>{}(key.name); + return std::hash<std::string>{}(key.name()); } diff --git a/boot/semantic.cc b/boot/semantic.cc index dd9022b..031adfc 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -22,8 +22,8 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { declaration_error::declaration_error(const kind error_kind, - const std::string& identifier, const struct position position) - : error(position), identifier(identifier), error_kind(error_kind) + const boot::identifier& identifier) + : error(identifier.position()), identifier(identifier.name()), error_kind(error_kind) { } @@ -33,8 +33,6 @@ namespace elna::boot { case kind::undeclared: return "Type '" + identifier + "' not declared"; - case kind::already: - return "Symbol '" + identifier + "' has been already declared"; case kind::local_export: return "Local symbol '" + this->identifier + "' cannot be exported"; default: @@ -42,38 +40,107 @@ namespace elna::boot } } - type_expectation_error::type_expectation_error(const kind error_kind, const struct position position) - : error(position), error_kind(error_kind) + redefinition_error::redefinition_error(const boot::identifier& identifier, + std::optional<source_position> original) + : error(identifier.position()), identifier(identifier.name()), original(original) { } - std::string type_expectation_error::what() const + std::string redefinition_error::what() const { - switch (this->error_kind) + return "Symbol '" + identifier + "' has been already defined"; + } + + std::optional<std::pair<std::string, source_position>> + redefinition_error::note() const + { + if (original.has_value() && original->start().available()) { - case kind::assignment: - return "Assinging an incompatible type"; - case kind::result: - return "Procedure return type and type of the return expression are not compatible"; - case kind::argument: - return "Argument type does not match parameter"; - default: - __builtin_unreachable(); + return std::make_pair("previously declared here", *original); + } + return std::nullopt; + } + + type_mismatch_error::type_mismatch_error(const source_position position, + type expected, type actual) + : error(position), expected(expected), actual(actual) + { + } + + std::string type_mismatch_error::what() const + { + return "Expected type '" + expected.to_string() + + "', but got '" + actual.to_string() + "'"; + } + + 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(composite_type) + { + } + + std::string field_not_found_error::what() const + { + type resolved = inner_aliased_type(composite_type); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + bool is_record = resolved.get<record_type>() != nullptr; + + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = composite_type.get<alias_type>()) + { + 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 + "'"; } - field_duplication_error::field_duplication_error(const std::string& field_name, const struct position position) - : error(position), field_name(field_name) + duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original, + std::optional<std::string> base_name) + : error(member_name.position()), member_name(member_name.name()), aggregate(aggregate), + original(original), base_name(base_name) { } - std::string field_duplication_error::what() const + std::string duplicate_member_error::what() const { - return "Repeated field name '" + field_name + "'"; + type resolved = inner_aliased_type(aggregate); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + std::string kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = aggregate.get<alias_type>()) + { + 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<std::pair<std::string, source_position>> 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<std::string>& cycle, - const struct position position) + const source_position position) : error(position), cycle(cycle) { } @@ -91,28 +158,36 @@ namespace elna::boot return message; } - return_error::return_error(const std::string& identifier, const struct position position) - : error(position), identifier(identifier) + return_error::return_error(const std::string& identifier, const source_position position, + type return_type) + : error(position), identifier(identifier), return_type(return_type) { } std::string return_error::what() const { - return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement"; + if (!return_type.empty()) + { + return "Procedure '" + this->identifier + + "' does not return a value, but return expression has type '" + + return_type.to_string() + "'"; + } + return "Procedure '" + this->identifier + + "' is expected to return, but does not have a return statement"; } - base_type_error::base_type_error(const std::string& base, const struct position position) - : error(position), base(base) + base_type_error::base_type_error(type actual, const source_position position) + : error(position), actual(actual) { } std::string base_type_error::what() const { - return "Record base type '" + this->base + "' is not a record."; + return "'" + actual.to_string() + "' is not a record type"; } argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, - const struct position position) + const source_position position) : error(position), expected(expected), actual(actual) { } @@ -138,7 +213,7 @@ namespace elna::boot void type_analysis_visitor::visit(procedure_declaration *declaration) { - this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure(); + this->current_procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); if (declaration->body.has_value()) { @@ -157,17 +232,19 @@ namespace elna::boot { if (!is_assignable_from(return_type, return_expr->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position()); + add_error<type_mismatch_error>( + return_expr->position(), return_type, return_expr->type_decoration); } } else { - add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position()); + add_error<return_error>(declaration->identifier.name(), + return_expr->position(), return_expr->type_decoration); } } else if (declaration->heading().return_type.proper_type != nullptr) { - add_error<return_error>(declaration->identifier.name, declaration->position()); + add_error<return_error>(declaration->identifier.name(), declaration->position()); } this->bag.leave(); } @@ -185,7 +262,8 @@ namespace elna::boot if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, statement->position()); + add_error<type_mismatch_error>(statement->position(), + statement->lvalue().type_decoration, statement->rvalue().type_decoration); } } @@ -199,11 +277,12 @@ namespace elna::boot } for (const identifier_definition& variable_identifier : declaration->identifiers) { - auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, - declaration->initializer->position()); + add_error<type_mismatch_error>( + declaration->initializer->position(), + variable_symbol->symbol, declaration->initializer->type_decoration); } } } @@ -219,8 +298,8 @@ namespace elna::boot { if (!is_assignable_from(condition_type, case_label->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::assignment, - case_label->position()); + add_error<type_mismatch_error>( + case_label->position(), condition_type, case_label->type_decoration); } } } @@ -245,7 +324,7 @@ namespace elna::boot void type_analysis_visitor::visit(type_declaration *declaration) { std::vector<std::string> alias_path; - auto unresolved_type = this->bag.lookup(declaration->identifier.name)->is_type()->symbol.get<alias_type>(); + auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>(); if (!check_unresolved_symbol(unresolved_type, alias_path)) { @@ -261,10 +340,10 @@ namespace elna::boot { if (expression->base.has_value()) { - type base_type = inner_aliased_type(this->bag.lookup(expression->base.value())->is_type()->symbol); + type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); if (base_type.get<record_type>() == nullptr) { - add_error<base_type_error>(expression->base.value(), expression->position()); + add_error<base_type_error>(base_type, expression->position()); } } walking_visitor::visit(expression); @@ -285,8 +364,9 @@ namespace elna::boot (*argument_iterator)->accept(this); if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - (*argument_iterator)->position()); + add_error<type_mismatch_error>( + (*argument_iterator)->position(), *type_iterator, + (*argument_iterator)->type_decoration); } ++argument_iterator; ++type_iterator; @@ -305,19 +385,22 @@ namespace elna::boot if (record == nullptr) { - add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position()); + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<record_type>()), + expression->type_decoration); return; } for (const field_initializer& initializer : expression->field_initializers) { for (const type_field& field : record->fields) { - if (field.first == initializer.name) + if (field.first == initializer.name()) { - if (!is_assignable_from(field.second, initializer.value->type_decoration)) + if (!is_assignable_from(field.second, initializer.value().type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - initializer.value->position()); + add_error<type_mismatch_error>( + initializer.value().position(), field.second, + initializer.value().type_decoration); } break; } @@ -331,7 +414,9 @@ namespace elna::boot if (array == nullptr) { - add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position()); + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<array_type>(type(), 0)), + expression->type_decoration); return; } if (expression->elements.size() > array->size) @@ -344,8 +429,8 @@ namespace elna::boot { if (!is_assignable_from(array->base, element->type_decoration)) { - add_error<type_expectation_error>(type_expectation_error::kind::argument, - element->position()); + add_error<type_mismatch_error>( + element->position(), array->base, element->type_decoration); } } } @@ -382,7 +467,7 @@ namespace elna::boot for (auto& parameter_name : parameter_names) { result_type.first.parameters.push_back(this->current_type); - result_type.second.push_back(parameter_name); + result_type.second.push_back(parameter_name.name()); } } return result_type; @@ -468,11 +553,12 @@ namespace elna::boot void name_analysis_visitor::visit(type_declaration *declaration) { walking_visitor::visit(declaration); - auto resolved = this->bag.resolve(declaration->identifier.name, this->current_type); + auto resolved = this->bag.resolve(declaration->identifier.name(), this->current_type); auto info = std::make_shared<type_info>(type(resolved)); - info->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, info); + info->exported = declaration->identifier.exported(); + info->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), info); } void name_analysis_visitor::visit(pointer_type_expression *expression) @@ -492,7 +578,8 @@ namespace elna::boot /** * Collects field names from a record type recursively, base first. */ - static void collect_field_names(const type& composite_type, std::set<std::string>& names) + static void collect_field_names(const type& composite_type, + std::map<std::string, field_origin>& names) { auto record = inner_aliased_type(composite_type).get<record_type>(); if (record == nullptr) @@ -505,12 +592,14 @@ namespace elna::boot } for (auto& field : record->fields) { - names.insert(field.first); + names.insert({ field.first, field_origin{ std::nullopt, composite_type } }); } } std::vector<type_field> name_analysis_visitor::build_composite_type( - const std::vector<field_declaration>& fields, std::set<std::string>& field_names) + const std::vector<field_declaration>& fields, + std::map<std::string, field_origin>& field_names, + type aggregate) { std::vector<type_field> result; @@ -519,13 +608,27 @@ namespace elna::boot field.second->accept(this); for (auto& field_name : field.first) { - if (!field_names.insert(field_name).second) + auto existing = field_names.find(field_name.name()); + if (existing != field_names.end()) { - add_error<field_duplication_error>(field_name, field.second->position()); + std::optional<std::string> base_name; + + if (!existing->second.declaration.has_value() + && !existing->second.base_type.empty()) + { + if (auto alias = existing->second.base_type.get<alias_type>()) + { + base_name = alias->name; + } + } + add_error<duplicate_member_error>(field_name, aggregate, + existing->second.declaration, base_name); } else { - result.push_back(std::make_pair(field_name, this->current_type)); + field_names.insert({ field_name.name(), + field_origin{ field.second->position(), type() } }); + result.push_back(std::make_pair(field_name.name(), this->current_type)); } } } @@ -537,11 +640,11 @@ namespace elna::boot std::shared_ptr<record_type> result_type; if (expression->base.has_value()) { - if (auto unresolved_alias = this->bag.declared(expression->base.value())) + if (auto unresolved_alias = this->bag.declared(expression->base.value().name())) { result_type = std::make_shared<record_type>(type(unresolved_alias)); } - else if (auto base_symbol = this->bag.lookup(expression->base.value())) + else if (auto base_symbol = this->bag.lookup(expression->base.value().name())) { if (auto base_type_info = base_symbol->is_type()) { @@ -549,7 +652,17 @@ namespace elna::boot } else { - add_error<base_type_error>(expression->base.value(), expression->position()); + 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<procedure_type>(proc->symbol)); + } + add_error<base_type_error>(actual, expression->position()); this->current_type = type(); return; } @@ -557,7 +670,7 @@ namespace elna::boot else { add_error<declaration_error>(declaration_error::kind::undeclared, - expression->base.value(), expression->position()); + expression->base.value()); this->current_type = type(); return; } @@ -567,30 +680,35 @@ namespace elna::boot result_type = std::make_shared<record_type>(); } - std::set<std::string> field_names; + std::map<std::string, field_origin> field_names; collect_field_names(result_type->base, field_names); - result_type->fields = build_composite_type(expression->fields, field_names); + result_type->fields = build_composite_type(expression->fields, field_names, type(result_type)); this->current_type = type(result_type); } void name_analysis_visitor::visit(record_constructor_expression *expression) { - if (auto type_symbol = this->bag.lookup(expression->type_name)) + if (auto type_symbol = this->bag.lookup(expression->type_name.name())) { if (auto type_info = type_symbol->is_type()) { expression->type_decoration = type_info->symbol; } } + else + { + add_error<declaration_error>(declaration_error::kind::undeclared, + expression->type_name); + } for (const field_initializer& initializer : expression->field_initializers) { - initializer.value->accept(this); + initializer.value().accept(this); if (!expression->type_decoration.empty() - && lookup_field(expression->type_decoration, initializer.name).empty()) + && lookup_field(expression->type_decoration, initializer.name()).empty()) { add_error<declaration_error>(declaration_error::kind::undeclared, - initializer.name, expression->position()); + initializer.id()); } } } @@ -616,19 +734,42 @@ namespace elna::boot void name_analysis_visitor::visit(enumeration_type_expression *expression) { - std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(expression->members); + std::vector<std::string> member_names; + for (auto& member : expression->members) + { + member_names.emplace_back(member.name()); + } + std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>( + member_names); + std::map<std::string, source_position> seen; + type aggregate(result_type); + for (auto& member : expression->members) + { + auto existing = seen.find(member.name()); + if (existing != seen.end()) + { + add_error<duplicate_member_error>(member, aggregate, existing->second); + } + else + { + seen.insert({ member.name(), member.position() }); + } + } this->current_type = type(result_type); } std::shared_ptr<variable_info> name_analysis_visitor::register_variable(const std::string& name, - const bool is_extern, const struct position position) + const bool is_extern, const source_position position) { auto variable_symbol = std::make_shared<variable_info>(this->current_type, is_extern); + variable_symbol->position.emplace(position); if (!this->bag.enter(name, variable_symbol)) { - add_error<declaration_error>(declaration_error::kind::already, name, position); + auto original = this->bag.lookup(name); + add_error<redefinition_error>(boot::identifier(name, position), + original->position); } return variable_symbol; } @@ -644,9 +785,9 @@ namespace elna::boot } for (const identifier_definition& variable_identifier : declaration->identifiers) { - auto variable_symbol = register_variable(variable_identifier.name, declaration->is_extern, + auto variable_symbol = register_variable(variable_identifier.name(), declaration->is_extern, declaration->position()); - variable_symbol->exported = variable_identifier.exported; + variable_symbol->exported = variable_identifier.exported(); } } @@ -655,8 +796,9 @@ namespace elna::boot walking_visitor::visit(declaration); auto constant_symbol = std::make_shared<constant_info>(this->current_literal); - constant_symbol->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, constant_symbol); + constant_symbol->exported = declaration->identifier.exported(); + constant_symbol->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), constant_symbol); } void name_analysis_visitor::visit(procedure_declaration *declaration) @@ -701,8 +843,9 @@ namespace elna::boot { info = std::make_shared<procedure_info>(heading, std::move(parameter_names)); } - info->exported = declaration->identifier.exported; - this->bag.enter(declaration->identifier.name, info); + info->exported = declaration->identifier.exported(); + info->position.emplace(declaration->position()); + this->bag.enter(declaration->identifier.name(), info); } void name_analysis_visitor::visit(procedure_call *call) @@ -775,6 +918,11 @@ namespace elna::boot { trait->type_decoration = trait->types.empty() ? type() : trait->types.front(); } + else + { + add_error<declaration_error>(declaration_error::kind::undeclared, + trait->name); + } } void name_analysis_visitor::visit(binary_expression *expression) @@ -841,13 +989,18 @@ namespace elna::boot void name_analysis_visitor::visit(field_access_expression *expression) { walking_visitor::visit(expression); - expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field()); + expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field().name()); auto is_designator = expression->base().is_designator(); if (expression->type_decoration.empty() && is_designator != nullptr && is_designator->is_named() != nullptr) { expression->type_decoration = this->current_type; } + if (expression->type_decoration.empty()) + { + add_error<field_not_found_error>(expression->field(), + expression->base().type_decoration); + } } void name_analysis_visitor::visit(dereference_expression *expression) @@ -896,7 +1049,7 @@ namespace elna::boot else { add_error<declaration_error>(declaration_error::kind::undeclared, - expression->name, expression->position()); + boot::identifier(expression->name, expression->position())); } } @@ -969,12 +1122,12 @@ namespace elna::boot void declaration_visitor::visit(type_declaration *declaration) { - const std::string& type_identifier = declaration->identifier.name; + const std::string& type_identifier = declaration->identifier.name(); if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second) { - add_error<declaration_error>(declaration_error::kind::already, - declaration->identifier.name, declaration->position()); + add_error<redefinition_error>(declaration->identifier.id(), + declaration->position()); } } @@ -998,20 +1151,20 @@ namespace elna::boot { for (const identifier_definition& variable_identifier : declaration->identifiers) { - if (variable_identifier.exported) + if (variable_identifier.exported()) { add_error<declaration_error>(declaration_error::kind::local_export, - variable_identifier.name, declaration->position()); + variable_identifier.id()); } } } void declaration_visitor::visit(constant_declaration *declaration) { - if (declaration->identifier.exported) + if (declaration->identifier.exported()) { add_error<declaration_error>(declaration_error::kind::local_export, - declaration->identifier.name, declaration->position()); + declaration->identifier.id()); } } diff --git a/boot/symbol.cc b/boot/symbol.cc index d7ceacb..a3a36c1 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -270,6 +270,54 @@ namespace elna::boot return tag == type_tag::empty; } + std::string type::to_string() const + { + switch (tag) + { + case type_tag::empty: + return "<empty>"; + case type_tag::alias: + return alias.lock()->name; + case type_tag::primitive: + return primitive->identifier; + case type_tag::pointer: + return "^" + pointer->base.to_string(); + case type_tag::array: + return "[" + std::to_string(array->size) + "]" + array->base.to_string(); + case type_tag::record: + if (record->base.empty()) + { + return "record ... end"; + } + else + { + return "record(" + record->base.to_string() + ") ... end"; + } + case type_tag::procedure: + { + std::string result = "proc("; + for (std::size_t i = 0; i < procedure->parameters.size(); ++i) + { + if (i > 0) result += ", "; + result += procedure->parameters[i].to_string(); + } + result += ")"; + if (procedure->return_type.no_return) + { + result += ": !"; + } + else if (!procedure->return_type.proper_type.empty()) + { + result += ": " + procedure->return_type.proper_type.to_string(); + } + return result; + } + case type_tag::enumeration: + return "(enumeration)"; + } + __builtin_unreachable(); + } + alias_type::alias_type(const std::string& name) : name(name), reference() { |
