diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-15 17:23:42 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-15 17:23:42 +0200 |
| commit | e7265af15f77abf60a899b3d53a59652b5ca71ea (patch) | |
| tree | 50b5594c4517da87b185268e709194e964c236af /include | |
| parent | 308a2addf655fe45f2098f8542f0ff1290612cd9 (diff) | |
| download | elna-e7265af15f77abf60a899b3d53a59652b5ca71ea.tar.gz | |
Track identifier positions for the diagnostics
Diffstat (limited to 'include')
| -rw-r--r-- | include/elna/boot/ast.h | 117 | ||||
| -rw-r--r-- | include/elna/boot/driver.h | 2 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 97 | ||||
| -rw-r--r-- | include/elna/boot/semantic.h | 112 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 7 | ||||
| -rw-r--r-- | include/elna/gcc/elna-diagnostic.h | 4 |
6 files changed, 229 insertions, 110 deletions
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 6a5f32d..b6aad70 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -62,7 +62,6 @@ namespace elna::boot class cast_expression; class record_constructor_expression; class array_constructor_expression; - struct field_initializer; class assign_statement; class if_statement; class import_declaration; @@ -224,13 +223,13 @@ namespace elna::boot */ class node { - const struct position source_position; + const source_position m_position; protected: /** * \param position Source code position. */ - explicit node(const position position); + explicit node(const source_position position); public: virtual void accept(parser_visitor *visitor) = 0; @@ -239,7 +238,7 @@ namespace elna::boot /** * \return Node position in the source code. */ - const struct position& position() const; + const source_position& position() const; }; class statement : public virtual node @@ -266,7 +265,7 @@ namespace elna::boot class declaration : public node { protected: - declaration(const struct position position, identifier_definition identifier); + declaration(const source_position position, identifier_definition identifier); public: const identifier_definition identifier; @@ -293,7 +292,7 @@ namespace elna::boot public: const std::uint32_t size; - array_type_expression(const struct position position, + array_type_expression(const source_position position, type_expression *base, const std::uint32_t size); ~array_type_expression() override; @@ -308,7 +307,7 @@ namespace elna::boot type_expression *m_base; public: - pointer_type_expression(const struct position position, type_expression *base); + pointer_type_expression(const source_position position, type_expression *base); ~pointer_type_expression() override; void accept(parser_visitor *visitor) override; @@ -317,18 +316,18 @@ namespace elna::boot type_expression& base(); }; - using field_declaration = std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>; + using field_declaration = std::pair<std::vector<identifier>, std::shared_ptr<type_expression>>; class record_type_expression : public type_expression { public: const std::vector<field_declaration> fields; - const std::optional<std::string> base; + const std::optional<identifier> base; - record_type_expression(const struct position position, + record_type_expression(const source_position position, std::vector<field_declaration>&& fields); - record_type_expression(const struct position position, - std::vector<field_declaration>&& fields, std::string&& base); + record_type_expression(const source_position position, + std::vector<field_declaration>&& fields, identifier&& base); void accept(parser_visitor *visitor) override; record_type_expression *is_record() override; @@ -336,22 +335,34 @@ namespace elna::boot struct field_initializer { - std::string name; - expression *value; + field_initializer(identifier name, expression *value); + field_initializer(const field_initializer&) = delete; + field_initializer(field_initializer&& other) noexcept; + + field_initializer& operator=(const field_initializer&) = delete; + field_initializer& operator=(field_initializer&& other) noexcept; + + ~field_initializer(); + + const std::string& name() const; + const identifier& id() const; + expression& value() const; + + private: + identifier m_name; + expression *m_value; }; class record_constructor_expression : public expression { public: - const std::string type_name; + const identifier type_name; const std::vector<field_initializer> field_initializers; - record_constructor_expression(const struct position position, - std::string&& type_name, + record_constructor_expression(const source_position position, + identifier&& type_name, std::vector<field_initializer>&& field_initializers); void accept(parser_visitor *visitor) override; - - virtual ~record_constructor_expression() override; }; class array_constructor_expression : public expression @@ -361,7 +372,7 @@ namespace elna::boot type_expression *const m_element_type; const std::vector<expression *> elements; - array_constructor_expression(const struct position position, + array_constructor_expression(const source_position position, std::uint32_t size, type_expression *element_type, std::vector<expression *>&& elements); void accept(parser_visitor *visitor) override; @@ -375,10 +386,10 @@ namespace elna::boot class enumeration_type_expression : public type_expression { public: - const std::vector<std::string> members; + const std::vector<identifier> members; - enumeration_type_expression(const struct position, - std::vector<std::string>&& members); + enumeration_type_expression(const source_position, + std::vector<identifier>&& members); void accept(parser_visitor *visitor) override; enumeration_type_expression *is_enumeration() override; @@ -392,10 +403,10 @@ namespace elna::boot std::shared_ptr<type_expression> m_variable_type; public: - variable_declaration(const struct position position, + variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, expression *initializer = nullptr); - variable_declaration(const struct position position, + variable_declaration(const source_position position, std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, std::monostate); @@ -426,7 +437,7 @@ namespace elna::boot expression *m_initializer; public: - constant_declaration(const struct position position, identifier_definition identifier, + constant_declaration(const source_position position, identifier_definition identifier, expression *initializer); void accept(parser_visitor *visitor) override; @@ -446,7 +457,7 @@ namespace elna::boot const return_t return_type; const std::vector<field_declaration> parameters; - procedure_type_expression(const struct position position, + procedure_type_expression(const source_position position, std::vector<field_declaration>&& parameters, return_t return_type = return_t()); ~procedure_type_expression(); @@ -483,9 +494,9 @@ namespace elna::boot public: std::optional<procedure_body> body; - procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading, procedure_body&& body); - procedure_declaration(const struct position position, identifier_definition identifier, + procedure_declaration(const source_position position, identifier_definition identifier, procedure_type_expression *heading); void accept(parser_visitor *visitor) override; @@ -502,7 +513,7 @@ namespace elna::boot type_expression *m_underlying_type; public: - type_declaration(const struct position position, identifier_definition identifier, + type_declaration(const source_position position, identifier_definition identifier, type_expression *expression); ~type_declaration() override; @@ -520,7 +531,7 @@ namespace elna::boot expression *m_value; public: - cast_expression(const struct position position, type_expression *target, expression *value); + cast_expression(const source_position position, type_expression *target, expression *value); void accept(parser_visitor *visitor) override; cast_expression *is_cast() override; @@ -534,10 +545,10 @@ namespace elna::boot { public: const std::vector<type_expression *> arguments; - const std::string name; + const identifier name; std::vector<type> types; - traits_expression(const struct position position, const std::string& name, + traits_expression(const source_position position, identifier&& name, std::vector<type_expression *>&& arguments); ~traits_expression(); @@ -576,7 +587,7 @@ namespace elna::boot const std::vector<switch_case> cases; const std::vector<statement *> *alternative; - case_statement(const struct position position, expression *condition, + case_statement(const source_position position, expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative = nullptr); void accept(parser_visitor *visitor) override; expression& condition(); @@ -603,7 +614,7 @@ namespace elna::boot public: const std::string name; - named_expression(const struct position position, const std::string& name); + named_expression(const source_position position, const std::string& name); void accept(parser_visitor *visitor) override; named_expression *is_named() override; @@ -615,7 +626,7 @@ namespace elna::boot expression *m_index; public: - array_access_expression(const struct position position, expression *base, expression *index); + array_access_expression(const source_position position, expression *base, expression *index); void accept(parser_visitor *visitor) override; expression& base(); @@ -629,15 +640,15 @@ namespace elna::boot class field_access_expression : public designator_expression { expression *m_base; - std::string m_field; + identifier m_field; public: - field_access_expression(const struct position position, expression *base, - const std::string& field); + field_access_expression(const source_position position, expression *base, + identifier&& field); void accept(parser_visitor *visitor) override; expression& base(); - std::string& field(); + const identifier& field(); field_access_expression *is_field_access() override; @@ -649,7 +660,7 @@ namespace elna::boot expression *m_base; public: - dereference_expression(const struct position position, expression *base); + dereference_expression(const source_position position, expression *base); void accept(parser_visitor *visitor) override; expression& base(); @@ -669,7 +680,7 @@ namespace elna::boot public: const std::vector<expression *> arguments; - procedure_call(const struct position position, designator_expression *callable, + procedure_call(const source_position position, designator_expression *callable, std::vector<expression *>&& arguments); void accept(parser_visitor *visitor) override; virtual procedure_call *is_call_expression() override; @@ -690,7 +701,7 @@ namespace elna::boot * \param lvalue Left-hand side. * \param rvalue Assigned expression. */ - assign_statement(const struct position position, designator_expression *lvalue, + assign_statement(const source_position position, designator_expression *lvalue, expression *rvalue); void accept(parser_visitor *visitor) override; @@ -711,7 +722,7 @@ namespace elna::boot const std::vector<conditional_statements *> branches; const std::vector<statement *> *alternative; - if_statement(const struct position position, conditional_statements *branch, + if_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches, std::vector<statement *> *alternative = nullptr); void accept(parser_visitor *visitor) override; @@ -729,7 +740,7 @@ namespace elna::boot public: const std::vector<std::string> segments; - import_declaration(const struct position position, std::vector<std::string>&& segments); + import_declaration(const source_position position, std::vector<std::string>&& segments); void accept(parser_visitor *visitor) override; }; @@ -743,7 +754,7 @@ namespace elna::boot public: const std::vector<conditional_statements *> branches; - while_statement(const struct position position, conditional_statements *branch, + while_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches); void accept(parser_visitor *visitor) override; @@ -759,8 +770,8 @@ namespace elna::boot const std::vector<type_declaration *> types; const std::vector<procedure_declaration *> procedures; - unit(const struct position position); - unit(const struct position position, + unit(const source_position position); + unit(const source_position position, std::vector<import_declaration *>&& imports, std::vector<constant_declaration *>&& constants, std::vector<type_declaration *>&& types, @@ -779,7 +790,7 @@ namespace elna::boot public: T value; - literal(const struct position position, const T& value) + literal(const source_position position, const T& value) : node(position), value(value) { } @@ -795,7 +806,7 @@ namespace elna::boot public: const std::vector<statement *> statements; - defer_statement(const struct position position, std::vector<statement *>&& statements); + defer_statement(const source_position position, std::vector<statement *>&& statements); void accept(parser_visitor *visitor) override; virtual ~defer_statement() override; @@ -804,7 +815,7 @@ namespace elna::boot class empty_statement : public statement { public: - empty_statement(const struct position); + empty_statement(const source_position); void accept(parser_visitor *visitor) override; }; @@ -815,7 +826,7 @@ namespace elna::boot binary_operator m_operator; public: - binary_expression(const struct position position, expression *lhs, + binary_expression(const source_position position, expression *lhs, expression *rhs, const binary_operator operation); void accept(parser_visitor *visitor) override; @@ -834,7 +845,7 @@ namespace elna::boot unary_operator m_operator; public: - unary_expression(const struct position position, expression *operand, + unary_expression(const source_position position, expression *operand, const unary_operator operation); void accept(parser_visitor *visitor) override; diff --git a/include/elna/boot/driver.h b/include/elna/boot/driver.h index cd29376..8f93dbd 100644 --- a/include/elna/boot/driver.h +++ b/include/elna/boot/driver.h @@ -23,7 +23,7 @@ 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); class syntax_error final : public error { diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 0556dbe..953f701 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include <string> #include <deque> #include <memory> +#include <optional> #include <variant> namespace elna::boot @@ -30,29 +31,44 @@ namespace elna::boot */ struct location { - /// Line. - const std::size_t line; + location() = default; + location(const std::size_t line, const std::size_t column); - /// Column. - const std::size_t column; + /// \return Line. + std::size_t line() const; - location(std::size_t line, std::size_t column); + /// \return Column. + std::size_t column() const; bool available() const; + + bool operator==(const location& that) const; + bool operator!=(const location& that) const; + + private: + std::size_t m_line{ 0 }; + std::size_t m_column{ 0 }; }; /** * Span in the source text. */ - struct position + struct source_position { - /// Start location. - const location start; + source_position(location start, location end); + + /// \return Start location. + const location& start() const; - /// End location. - const location end; + /// \return End location. + const location& end() const; - position(location start, location end); + /// \return Whether this position spans more than one location. + bool is_span() const; + + private: + location m_start; + location m_end; }; /** @@ -61,14 +77,25 @@ namespace elna::boot class error { protected: - error(const struct position position); + error(const source_position position); public: /// Error position. - const struct position position; + const source_position position; /// Error text. virtual std::string what() const = 0; + + /** + * Supplementary context shown alongside the primary error, + * e.g.\ the location of a previous declaration. + * + * \return Optional note message and position. + */ + virtual std::optional<std::pair<std::string, source_position>> note() const + { + return std::nullopt; + } }; using error_list = typename std::deque<std::unique_ptr<error>>; @@ -111,27 +138,55 @@ namespace elna::boot { } - bool operator==(const return_declaration& other) const + bool operator==(const return_declaration& that) const + { + return this->proper_type == that.proper_type && this->no_return == that.no_return; + } + + bool operator!=(const return_declaration& that) const { - return this->proper_type == other.proper_type && this->no_return == other.no_return; + return !(*this == that); } T proper_type{}; bool no_return{ false }; }; - struct identifier_definition + struct identifier { - std::string name; - bool exported; + identifier(const std::string& name, const source_position& position); + + const std::string& name() const; + const source_position& position() const; + + bool operator==(const identifier& that) const; + bool operator!=(const identifier& that) const; - bool operator==(const identifier_definition& that) const; bool operator==(const std::string& that) const; + bool operator!=(const std::string& that) const; + + private: + std::string m_name; + source_position m_position; + }; + + struct identifier_definition + { + identifier_definition(const std::string& name, const source_position& position, + const bool exported); + + const std::string& name() const; + const identifier& id() const; + bool exported() const; + + private: + identifier m_identifier; + bool m_exported{ false }; }; } template<> -struct std::hash<elna::boot::identifier_definition> +struct std::hash<elna::boot::identifier> { - std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept; + std::size_t operator()(const elna::boot::identifier& key) const noexcept; }; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index 4041f33..82ef5f1 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -19,7 +19,7 @@ along with GCC; see the file COPYING3. If not see #include <string> #include <memory> -#include <set> +#include <map> #include "elna/boot/ast.h" #include "elna/boot/result.h" @@ -32,57 +32,91 @@ namespace elna::boot */ class declaration_error : public error { - const std::string identifier; - public: enum class kind { undeclared, - already, local_export }; + private: + std::string identifier; + kind error_kind; + + public: + declaration_error(const kind error_kind, - const std::string& identifier, const struct position position); + const boot::identifier& identifier); std::string what() const override; - - private: - const kind error_kind; }; /** - * A symbol was already declared in this scope. + * Attempted to redefine a name that is already in use in the + * current scope. */ - class type_expectation_error : public error + class redefinition_error : public error { + std::string identifier; + std::optional<source_position> original; + public: - enum class kind - { - assignment, - result, - argument - }; + redefinition_error(const boot::identifier& identifier, + std::optional<source_position> original); - type_expectation_error(const kind error_kind, const struct position position); + std::string what() const override; + + std::optional<std::pair<std::string, source_position>> note() const override; + }; + + /** + * Expected type does not match the actual type of an expression. + */ + class type_mismatch_error : public error + { + type expected; + type actual; + + public: + type_mismatch_error(const source_position position, + type expected, type actual); std::string what() const override; + }; - private: - const kind error_kind; + /** + * Attempted to access a field that does not exist on the given type. + */ + class field_not_found_error : public error + { + std::string field_name; + type composite_type; + + public: + field_not_found_error(const identifier& field_name, + type composite_type); + + std::string what() const override; }; /** * Field with the same name is already declared in this type. */ - class field_duplication_error : public error + class duplicate_member_error : public error { - const std::string field_name; + std::string member_name; + type aggregate; + std::optional<source_position> original; + std::optional<std::string> base_name; public: - field_duplication_error(const std::string& field_name, const struct position position); + duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original = std::nullopt, + std::optional<std::string> base_name = std::nullopt); std::string what() const override; + + std::optional<std::pair<std::string, source_position>> note() const override; }; /** @@ -90,10 +124,10 @@ namespace elna::boot */ class cyclic_declaration_error : public error { - const std::vector<std::string> cycle; + std::vector<std::string> cycle; public: - cyclic_declaration_error(const std::vector<std::string>& cycle, const struct position position); + cyclic_declaration_error(const std::vector<std::string>& cycle, const source_position position); std::string what() const override; }; @@ -103,10 +137,12 @@ namespace elna::boot */ class return_error : public error { - const std::string identifier; + std::string identifier; + type return_type; public: - return_error(const std::string& identifier, const struct position position); + return_error(const std::string& identifier, const source_position position, + type return_type = type()); std::string what() const override; }; @@ -116,10 +152,10 @@ namespace elna::boot */ class base_type_error : public error { - const std::string base; + type actual; public: - base_type_error(const std::string& base, const struct position position); + base_type_error(type actual, const source_position position); std::string what() const override; }; @@ -130,12 +166,12 @@ namespace elna::boot */ class argument_count_error : public error { - const std::size_t expected; - const std::size_t actual; + std::size_t expected; + std::size_t actual; public: argument_count_error(std::size_t expected, std::size_t actual, - const struct position position); + const source_position position); std::string what() const override; }; @@ -166,6 +202,15 @@ namespace elna::boot }; /** + * Origin of a field in a composite type. + */ + struct field_origin + { + std::optional<source_position> declaration; + type base_type; + }; + + /** * Performs name analysis. */ class name_analysis_visitor final : public walking_visitor, public error_container @@ -178,9 +223,10 @@ namespace elna::boot std::pair<procedure_type, std::vector<std::string>> build_procedure( procedure_type_expression& expression); std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields, - std::set<std::string>& known_names); + std::map<std::string, field_origin>& known_names, + type aggregate); std::shared_ptr<variable_info> register_variable(const std::string& name, - const bool is_extern, const struct position position); + const bool is_extern, const source_position position); type lookup_primitive_type(const std::string& name); type type_of_constant(const constant_info::variant& value); diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index e13e842..ec5a051 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -89,6 +89,12 @@ namespace elna::boot bool operator==(const type& other) const; bool empty() const; + + /** + * \return Human-readable representation of this type suitable for + * diagnostics, e.g. \c "Int", \c "^Char", \c "record ... end". + */ + std::string to_string() const; }; struct alias_type @@ -157,6 +163,7 @@ namespace elna::boot { public: bool exported{ false }; + std::optional<source_position> position; virtual ~info() = 0; diff --git a/include/elna/gcc/elna-diagnostic.h b/include/elna/gcc/elna-diagnostic.h index 3957003..31e180e 100644 --- a/include/elna/gcc/elna-diagnostic.h +++ b/include/elna/gcc/elna-diagnostic.h @@ -41,8 +41,8 @@ namespace elna::gcc ~linemap_guard(); }; - location_t get_location(const boot::position *position); - void fill_range(const boot::position& position, location_t& start, location_t& end); + location_t get_location(const boot::source_position *position); + location_t make_range(const boot::source_position& position); std::string print_type(tree type); void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors); } |
