diff --git a/boot/ast.cc b/boot/ast.cc index 5609939..a0c21ed 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -19,6 +19,191 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { + void empty_visitor::not_implemented() + { + __builtin_unreachable(); + } + + void empty_visitor::visit(named_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(array_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(pointer_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(program *) + { + not_implemented(); + } + + void empty_visitor::visit(type_declaration *) + { + not_implemented(); + } + + void empty_visitor::visit(record_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(union_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(procedure_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(enumeration_type_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(variable_declaration *) + { + not_implemented(); + } + + void empty_visitor::visit(constant_declaration *) + { + not_implemented(); + } + + void empty_visitor::visit(procedure_declaration *) + { + not_implemented(); + } + + void empty_visitor::visit(assign_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(if_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(import_declaration *) + { + not_implemented(); + } + + void empty_visitor::visit(while_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(return_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(defer_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(case_statement *) + { + not_implemented(); + } + + void empty_visitor::visit(procedure_call *) + { + not_implemented(); + } + + void empty_visitor::visit(unit *) + { + not_implemented(); + } + + void empty_visitor::visit(cast_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(traits_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(binary_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(unary_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(variable_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(array_access_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(field_access_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(dereference_expression *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + + void empty_visitor::visit(literal *) + { + not_implemented(); + } + node::node(const struct position position) : source_position(position) { diff --git a/boot/dependency.cc b/boot/dependency.cc index a598068..07f3525 100644 --- a/boot/dependency.cc +++ b/boot/dependency.cc @@ -32,7 +32,7 @@ namespace elna::boot { } - dependency read_sources(std::istream& entry_point, const char *entry_path) + dependency read_source(std::istream& entry_point, const char *entry_path) { driver parse_driver{ entry_path }; lexer tokenizer(entry_point); @@ -60,6 +60,25 @@ namespace elna::boot return outcome; } + error_list analyze_semantics(const char *path, std::unique_ptr& tree, symbol_bag bag) + { + name_analysis_visitor name_analyser(path, bag); + tree->accept(&name_analyser); + + if (name_analyser.has_errors()) + { + return std::move(name_analyser.errors()); + } + type_analysis_visitor type_analyzer(path); + tree->accept(&type_analyzer); + + if (type_analyzer.has_errors()) + { + return std::move(type_analyzer.errors()); + } + return error_list{}; + } + std::filesystem::path build_path(const std::vector& segments) { std::filesystem::path result; diff --git a/boot/semantic.cc b/boot/semantic.cc index 5489959..79f2047 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -32,7 +32,6 @@ namespace elna::boot return "Type '" + identifier + "' not declared"; } - already_declared_error::already_declared_error(const std::string& identifier, const char *path, const struct position position) : error(path, position), identifier(identifier) @@ -74,6 +73,83 @@ namespace elna::boot return message; } + return_error::return_error(const std::string& identifier, const char *path, const struct position position) + : error(path, position), identifier(identifier) + { + } + + std::string return_error::what() const + { + return "Procedure '" + identifier + "' is expected to return, but does not have a return statement"; + } + + type_analysis_visitor::type_analysis_visitor(const char *path) + : error_container(path) + { + } + + void type_analysis_visitor::visit(program *program) + { + visit(static_cast(program)); + } + + void type_analysis_visitor::visit(procedure_declaration *definition) + { + if (definition->body.has_value() && definition->heading().return_type.proper_type != nullptr) + { + for (statement *const statement : definition->body.value().body()) + { + statement->accept(this); + } + if (!this->returns) + { + add_error(definition->identifier.identifier, this->input_file, definition->position()); + } + } + } + + void type_analysis_visitor::visit(assign_statement *) + { + } + + void type_analysis_visitor::visit(if_statement *) + { + } + + void type_analysis_visitor::visit(import_declaration *) + { + } + + void type_analysis_visitor::visit(while_statement *) + { + } + + void type_analysis_visitor::visit(return_statement *) + { + this->returns = true; + } + + void type_analysis_visitor::visit(defer_statement *) + { + } + + void type_analysis_visitor::visit(case_statement *) + { + } + + void type_analysis_visitor::visit(procedure_call *) + { + } + + void type_analysis_visitor::visit(unit *unit) + { + for (procedure_declaration *const procedure : unit->procedures) + { + this->returns = false; + procedure->accept(this); + } + } + name_analysis_visitor::name_analysis_visitor(const char *path, symbol_bag bag) : error_container(path), bag(bag) { @@ -484,87 +560,15 @@ namespace elna::boot { } - void declaration_visitor::visit(named_type_expression *) - { - } - - void declaration_visitor::visit(array_type_expression *) - { - } - - void declaration_visitor::visit(pointer_type_expression *) - { - } - void declaration_visitor::visit(program *program) { visit(static_cast(program)); } - void declaration_visitor::visit(type_declaration *) - { - } - - void declaration_visitor::visit(record_type_expression *) - { - } - - void declaration_visitor::visit(union_type_expression *) - { - } - - void declaration_visitor::visit(procedure_type_expression *) - { - } - - void declaration_visitor::visit(enumeration_type_expression *) - { - } - - void declaration_visitor::visit(variable_declaration *) - { - } - - void declaration_visitor::visit(constant_declaration *) - { - } - - void declaration_visitor::visit(procedure_declaration *) - { - } - - void declaration_visitor::visit(assign_statement *) - { - } - - void declaration_visitor::visit(if_statement *) - { - } - void declaration_visitor::visit(import_declaration *) { } - void declaration_visitor::visit(while_statement *) - { - } - - void declaration_visitor::visit(return_statement *) - { - } - - void declaration_visitor::visit(defer_statement *) - { - } - - void declaration_visitor::visit(case_statement *) - { - } - - void declaration_visitor::visit(procedure_call *) - { - } - void declaration_visitor::visit(unit *unit) { for (import_declaration *const _import : unit->imports) @@ -581,64 +585,4 @@ namespace elna::boot } } } - - void declaration_visitor::visit(cast_expression *) - { - } - - void declaration_visitor::visit(traits_expression *) - { - } - - void declaration_visitor::visit(binary_expression *) - { - } - - void declaration_visitor::visit(unary_expression *) - { - } - - void declaration_visitor::visit(variable_expression *) - { - } - - void declaration_visitor::visit(array_access_expression *) - { - } - - void declaration_visitor::visit(field_access_expression *) - { - } - - void declaration_visitor::visit(dereference_expression *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } - - void declaration_visitor::visit(literal *) - { - } } diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 620c32c..266470b 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -77,7 +77,7 @@ namespace elna::gcc if (!VOID_TYPE_P(TREE_VALUE(current_parameter))) { - error_at(call_location, "too few arguments, expected %i, got %lu", + error_at(call_location, "Too few arguments, expected %i, got %lu", list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); this->current_expression = error_mark_node; } @@ -1179,46 +1179,6 @@ namespace elna::gcc this->current_expression = NULL_TREE; } - void generic_visitor::visit(boot::named_type_expression *type) - { - this->current_expression = TREE_TYPE(this->symbols->lookup(type->name)); - } - - void generic_visitor::visit(boot::array_type_expression *) - { - gcc_unreachable(); - } - - void generic_visitor::visit(boot::pointer_type_expression *type) - { - type->base().accept(this); - - if (this->current_expression != NULL_TREE && this->current_expression != error_mark_node) - { - this->current_expression = build_global_pointer_type(this->current_expression); - } - } - - void generic_visitor::visit(boot::record_type_expression *) - { - gcc_unreachable(); - } - - void generic_visitor::visit(boot::union_type_expression *) - { - gcc_unreachable(); - } - - void generic_visitor::visit(boot::procedure_type_expression *) - { - gcc_unreachable(); - } - - void generic_visitor::visit(boot::enumeration_type_expression *) - { - gcc_unreachable(); - } - void generic_visitor::visit(boot::defer_statement *statement) { enter_scope(); diff --git a/gcc/elna1.cc b/gcc/elna1.cc index c267c32..f912241 100644 --- a/gcc/elna1.cc +++ b/gcc/elna1.cc @@ -74,7 +74,7 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename); } linemap_add(line_table, LC_ENTER, 0, filename, 1); - elna::boot::dependency outcome = elna::boot::read_sources(entry_point, filename); + elna::boot::dependency outcome = elna::boot::read_source(entry_point, filename); if (outcome.has_errors()) { @@ -92,12 +92,11 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha } outcome_bag.add_import(state.cache.find(sub_path)->second); } - elna::boot::name_analysis_visitor name_analysis_visitor(filename, outcome_bag); - outcome.tree->accept(&name_analysis_visitor); + elna::boot::error_list semantic_errors = analyze_semantics(filename, outcome.tree, outcome_bag); - if (name_analysis_visitor.has_errors()) + if (!semantic_errors.empty()) { - elna::gcc::report_errors(name_analysis_visitor.errors()); + elna::gcc::report_errors(semantic_errors); } state.cache.insert({ filename, outcome_bag }); elna::gcc::rewrite_symbol_table(module_table, state.custom); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 28241c5..2f015df 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -131,6 +131,53 @@ namespace elna::boot virtual void visit(literal *) = 0; }; + /** + * Abstract visitor that doesn't visit any nodes by default. + */ + class empty_visitor : public parser_visitor + { + [[noreturn]] void not_implemented(); + + public: + [[noreturn]] virtual void visit(named_type_expression *) override; + [[noreturn]] virtual void visit(array_type_expression *) override; + [[noreturn]] virtual void visit(pointer_type_expression *) override; + [[noreturn]] virtual void visit(program *) override; + [[noreturn]] virtual void visit(type_declaration *) override; + [[noreturn]] virtual void visit(record_type_expression *) override; + [[noreturn]] virtual void visit(union_type_expression *) override; + [[noreturn]] virtual void visit(procedure_type_expression *) override; + [[noreturn]] virtual void visit(enumeration_type_expression *) override; + + [[noreturn]] virtual void visit(variable_declaration *) override; + [[noreturn]] virtual void visit(constant_declaration *) override; + [[noreturn]] virtual void visit(procedure_declaration *) override; + [[noreturn]] virtual void visit(assign_statement *) override; + [[noreturn]] virtual void visit(if_statement *) override; + [[noreturn]] virtual void visit(import_declaration *) override; + [[noreturn]] virtual void visit(while_statement *) override; + [[noreturn]] virtual void visit(return_statement *) override; + [[noreturn]] virtual void visit(defer_statement *) override; + [[noreturn]] virtual void visit(case_statement *) override; + [[noreturn]] virtual void visit(procedure_call *) override; + [[noreturn]] virtual void visit(unit *) override; + [[noreturn]] virtual void visit(cast_expression *) override; + [[noreturn]] virtual void visit(traits_expression *) override; + [[noreturn]] virtual void visit(binary_expression *) override; + [[noreturn]] virtual void visit(unary_expression *) override; + [[noreturn]] virtual void visit(variable_expression *) override; + [[noreturn]] virtual void visit(array_access_expression *) override; + [[noreturn]] virtual void visit(field_access_expression *) override; + [[noreturn]] virtual void visit(dereference_expression *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + [[noreturn]] virtual void visit(literal *) override; + }; + /** * AST node. */ diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index b2deb86..b419ff2 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -35,15 +35,16 @@ namespace elna::boot explicit dependency(const char *path); }; - dependency read_sources(std::istream& entry_point, const char *entry_path); + dependency read_source(std::istream& entry_point, const char *entry_path); std::filesystem::path build_path(const std::vector& segments); + error_list analyze_semantics(const char *path, std::unique_ptr& tree, symbol_bag bag); template struct dependency_state { const std::shared_ptr globals; T custom; - std::unordered_map cache; + std::unordered_map cache; explicit dependency_state(T custom) : globals(elna::boot::builtin_symbol_table()), custom(custom) diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index a510c3c..ecedb27 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -69,6 +69,40 @@ namespace elna::boot std::string what() const override; }; + class return_error : public error + { + const std::string identifier; + + public: + return_error(const std::string& identifier, const char *path, const struct position position); + + std::string what() const override; + }; + + /** + * Checks types. + */ + class type_analysis_visitor final : public empty_visitor, public error_container + { + bool returns; + + public: + explicit type_analysis_visitor(const char *path); + + void visit(program *program) override; + + void visit(procedure_declaration *definition) override; + void visit(assign_statement *) override; + void visit(if_statement *) override; + void visit(import_declaration *) override; + void visit(while_statement *) override; + void visit(return_statement *) override; + void visit(defer_statement *) override; + void visit(case_statement *) override; + void visit(procedure_call *) override; + void visit(unit *unit) override; + }; + /** * Performs name analysis. */ @@ -128,51 +162,17 @@ namespace elna::boot }; /** - * Collects global declarations. + * Collects global declarations without resolving any symbols. */ - class declaration_visitor final : public parser_visitor, public error_container + class declaration_visitor final : public empty_visitor, public error_container { public: std::unordered_map> unresolved; explicit declaration_visitor(const char *path); - void visit(named_type_expression *) override; - void visit(array_type_expression *) override; - void visit(pointer_type_expression *) override; void visit(program *program) override; - void visit(type_declaration *) override; - void visit(record_type_expression *) override; - void visit(union_type_expression *) override; - void visit(procedure_type_expression *) override; - void visit(enumeration_type_expression *) override; - - void visit(variable_declaration *) override; - void visit(constant_declaration *) override; - void visit(procedure_declaration *) override; - void visit(assign_statement *) override; - void visit(if_statement *) override; void visit(import_declaration *) override; - void visit(while_statement *) override; - void visit(return_statement *) override; - void visit(defer_statement *) override; - void visit(case_statement *) override; - void visit(procedure_call *) override; void visit(unit *unit) override; - void visit(cast_expression *) override; - void visit(traits_expression *) override; - void visit(binary_expression *) override; - void visit(unary_expression *) override; - void visit(variable_expression *) override; - void visit(array_access_expression *) override; - void visit(field_access_expression *) override; - void visit(dereference_expression *) override; - void visit(literal *) override; - void visit(literal *) override; - void visit(literal *) override; - void visit(literal *) override; - void visit(literal *) override; - void visit(literal *) override; - void visit(literal *) override; }; } diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 54cad5d..ad74bc2 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -33,7 +33,7 @@ along with GCC; see the file COPYING3. If not see namespace elna::gcc { - class generic_visitor final : public boot::parser_visitor + class generic_visitor final : public boot::empty_visitor { tree current_expression{ NULL_TREE }; elna::boot::symbol_bag bag; @@ -91,13 +91,6 @@ namespace elna::gcc void visit(boot::if_statement *statement) override; void visit(boot::import_declaration *) override; void visit(boot::while_statement *statement) override; - void visit(boot::named_type_expression *type) override; - void visit(boot::array_type_expression *) override; - void visit(boot::pointer_type_expression *type) override; - void visit(boot::record_type_expression *) override; - void visit(boot::union_type_expression *) override; - void visit(boot::procedure_type_expression *) override; - void visit(boot::enumeration_type_expression *) override; void visit(boot::return_statement *statement) override; void visit(boot::defer_statement *statement) override; void visit(boot::case_statement *statement) override; diff --git a/source/main.elna b/source/main.elna index 2da63e7..0f3d001 100644 --- a/source/main.elna +++ b/source/main.elna @@ -1074,12 +1074,6 @@ begin return return_code end; -proc f(); -var x: Char; -begin - x := '\x4' -end; - begin exit(process(count, parameters)) end.