From ebd68d78780cef233495ae95a482f7f32daff097 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 19 May 2024 00:43:59 +0200 Subject: [PATCH] Introduce a procedure type --- cli/cl.cpp | 19 ++++++--- include/elna/source/result.hpp | 1 + include/elna/source/semantic.hpp | 11 +++-- include/elna/source/symbol_table.hpp | 15 ++++--- include/elna/source/types.hpp | 18 ++++++++ source/semantic.cpp | 64 ++++++++++++++++++++++------ source/symbol_table.cpp | 27 +++++++++--- source/types.cpp | 5 +++ 8 files changed, 128 insertions(+), 32 deletions(-) diff --git a/cli/cl.cpp b/cli/cl.cpp index 0c95133..4638c1a 100644 --- a/cli/cl.cpp +++ b/cli/cl.cpp @@ -14,22 +14,29 @@ namespace elna::cli << ": " << compile_error->what() << std::endl; } + constexpr std::size_t pointer_size = 4; + static std::shared_ptr add_builtin_symbols() { source::symbol_table result; + std::vector> intrinsic_arguments; auto boolean_info = std::make_shared(source::boolean_type); auto int_info = std::make_shared(source::int_type); result.enter("Boolean", boolean_info); result.enter("Int", int_info); - auto writei = std::make_shared(); - writei->parameter_infos.emplace_back(int_info->type()); + intrinsic_arguments.push_back(int_info->type()); + auto writei = std::make_shared( + source::procedure_type{ intrinsic_arguments, pointer_size }); result.enter("writei", writei); + intrinsic_arguments.clear(); - auto writeb = std::make_shared(); - writeb->parameter_infos.emplace_back(boolean_info->type()); + intrinsic_arguments.push_back(boolean_info->type()); + auto writeb = std::make_shared( + source::procedure_type{ intrinsic_arguments, pointer_size }); result.enter("writeb", writeb); + intrinsic_arguments.clear(); return std::make_shared(std::move(result)); } @@ -53,8 +60,8 @@ namespace elna::cli return 2; } auto global_scope = add_builtin_symbols(); - source::name_analysis_visitor(global_scope, in_file).visit(ast.get()); - source::type_analysis_visitor(global_scope, in_file, 4).visit(ast.get()); + source::name_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get()); + source::type_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get()); source::allocator_visitor(global_scope).visit(ast.get()); source::intermediate_code_generator intermediate_code_generator{ global_scope }; diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index d735945..f3761fa 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -132,6 +132,7 @@ namespace elna::source enum class operation { dereference, + argument }; /** diff --git a/include/elna/source/semantic.hpp b/include/elna/source/semantic.hpp index 66c37dd..94ea5dc 100644 --- a/include/elna/source/semantic.hpp +++ b/include/elna/source/semantic.hpp @@ -10,6 +10,7 @@ namespace elna::source std::shared_ptr table; const std::filesystem::path filename; std::list> m_errors; + const std::size_t pointer_size; std::shared_ptr convert_declaration_type(const type_expression& ast_type) const; @@ -17,8 +18,10 @@ namespace elna::source /** * \param table Symbol table. * \param path Source filename. + * \param target_pointer_size Pointer size on the target platform. */ - name_analysis_visitor(std::shared_ptr table, const std::filesystem::path& filename); + name_analysis_visitor(std::shared_ptr table, const std::filesystem::path& filename, + const std::size_t target_pointer_size); /** * \return Collected errors. @@ -67,7 +70,7 @@ namespace elna::source * \param target_pointer_size Pointer size on the target platform. */ type_analysis_visitor(std::shared_ptr table, const std::filesystem::path& filename, - std::size_t target_pointer_size); + const std::size_t target_pointer_size); /** * \return Collected errors. @@ -75,9 +78,11 @@ namespace elna::source const std::list>& errors() const noexcept; void visit(program *program) override; - void visit(procedure_definition *definition) override; + void visit(procedure_definition *procedure) override; void visit(integer_literal *literal) override; void visit(boolean_literal *literal) override; void visit(unary_expression *expression) override; + void visit(call_statement *statement) override; + void visit(constant_definition *definition) override; }; } diff --git a/include/elna/source/symbol_table.hpp b/include/elna/source/symbol_table.hpp index 73b4110..820c77c 100644 --- a/include/elna/source/symbol_table.hpp +++ b/include/elna/source/symbol_table.hpp @@ -4,7 +4,6 @@ #include #include #include -#include namespace elna::source { @@ -40,11 +39,14 @@ namespace elna::source */ class constant_info final : public info { + std::shared_ptr m_type; std::int32_t m_value; public: - constant_info(const std::int32_t value); + constant_info(std::shared_ptr type, const std::int32_t value); ~constant_info() override; + + std::shared_ptr type() const noexcept; std::int32_t value() const noexcept; }; @@ -83,10 +85,13 @@ namespace elna::source */ class intrinsic_info : public info { - public: - std::vector parameter_infos; + std::shared_ptr m_type; + public: + explicit intrinsic_info(const class procedure_type& type); ~intrinsic_info() override; + + std::shared_ptr type() const noexcept; std::size_t parameter_stack_size() const noexcept; }; @@ -101,7 +106,7 @@ namespace elna::source std::size_t local_stack_size{ 0 }; std::size_t argument_stack_size{ 0 }; - explicit procedure_info(std::shared_ptr outer_scope); + procedure_info(const class procedure_type& type, std::shared_ptr outer_scope); ~procedure_info() override; std::shared_ptr scope(); diff --git a/include/elna/source/types.hpp b/include/elna/source/types.hpp index 132b92c..64bc3a3 100644 --- a/include/elna/source/types.hpp +++ b/include/elna/source/types.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace elna::source { @@ -61,6 +62,23 @@ namespace elna::source pointer_type(std::shared_ptr base_type, const std::size_t byte_size); }; + /** + * Type of a procedure. + */ + struct procedure_type : public type + { + /// Argument types. + std::vector> arguments; + + /** + * Constructor. + * + * \param arguments Argument types. + * \param byte_size Function pointer size. + */ + procedure_type(std::vector> arguments, const std::size_t byte_size); + }; + inline const primitive_type boolean_type{ "Boolean", 1 }; inline const primitive_type int_type{ "Int", 4 }; } diff --git a/source/semantic.cpp b/source/semantic.cpp index 525e636..108303a 100644 --- a/source/semantic.cpp +++ b/source/semantic.cpp @@ -5,15 +5,16 @@ namespace elna::source { name_analysis_visitor::name_analysis_visitor(std::shared_ptr table, - const std::filesystem::path& filename) - : table(table), filename(filename) + const std::filesystem::path& filename, const std::size_t target_pointer_size) + : table(table), filename(filename), pointer_size(target_pointer_size) { } void name_analysis_visitor::visit(constant_definition *definition) { + auto constant_type = std::make_shared(int_type); this->table->enter(definition->identifier(), - std::make_shared(constant_info(definition->body().number()))); + std::make_shared(constant_type, definition->body().number())); } std::shared_ptr name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const @@ -42,22 +43,30 @@ namespace elna::source void name_analysis_visitor::visit(program *program) { - this->table->enter("main", std::make_shared(this->table)); + class procedure_type main_type{ std::vector>(), this->pointer_size }; + this->table->enter("main", std::make_shared(main_type, this->table)); empty_visitor::visit(program); } void name_analysis_visitor::visit(procedure_definition *procedure) { - auto info = std::make_shared(this->table); - this->table->enter(procedure->identifier(), info); - this->table = info->scope(); + std::vector> arguments; for (auto& parameter : procedure->parameters()) { auto declaration_type = convert_declaration_type(parameter->type()); + arguments.push_back(declaration_type); + } + procedure_type definition_type{ std::move(arguments), this->pointer_size }; + auto info = std::make_shared(definition_type, this->table); - this->table->enter(parameter->identifier(), - std::make_shared(declaration_type)); + this->table->enter(procedure->identifier(), info); + this->table = info->scope(); + + for (std::size_t i = 0; i < procedure->parameters().size(); ++i) + { + this->table->enter(procedure->parameters().at(i)->identifier(), + std::make_shared(definition_type.arguments.at(i))); } procedure->body().accept(this); @@ -123,7 +132,7 @@ namespace elna::source } type_analysis_visitor::type_analysis_visitor(std::shared_ptr table, - const std::filesystem::path& filename, std::size_t target_pointer_size) + const std::filesystem::path& filename, const std::size_t target_pointer_size) : table(table), filename(filename), pointer_size(target_pointer_size) { } @@ -140,9 +149,14 @@ namespace elna::source program->body().accept(this); } - void type_analysis_visitor::visit(procedure_definition *definition) + void type_analysis_visitor::visit(procedure_definition *procedure) { - definition->body().accept(this); + auto info = std::dynamic_pointer_cast(this->table->lookup(procedure->identifier())); + this->table = info->scope(); + + procedure->body().accept(this); + + this->table = info->scope()->scope(); } void type_analysis_visitor::visit(integer_literal *literal) @@ -182,6 +196,32 @@ namespace elna::source } } + void type_analysis_visitor::visit(call_statement *statement) + { + auto call_info = std::dynamic_pointer_cast(this->table->lookup(statement->name())); + + std::size_t i{ 0 }; + for (const auto& argument : statement->arguments()) + { + argument->accept(this); + + if (argument->data_type != nullptr && i < call_info->type()->arguments.size() + && call_info->type()->arguments[i] != argument->data_type) + { + auto new_error = std::make_unique(argument->data_type, + type_mismatch::operation::argument, this->filename, argument->position()); + m_errors.push_back(std::move(new_error)); + } + + ++i; + } + } + + void type_analysis_visitor::visit(constant_definition *definition) + { + definition->body().accept(this); + } + const std::list>& type_analysis_visitor::errors() const noexcept { return m_errors; diff --git a/source/symbol_table.cpp b/source/symbol_table.cpp index d5c2621..86ac1f2 100644 --- a/source/symbol_table.cpp +++ b/source/symbol_table.cpp @@ -55,8 +55,8 @@ namespace elna::source return m_type; } - constant_info::constant_info(const std::int32_t value) - : m_value(value) + constant_info::constant_info(const std::shared_ptr type, const std::int32_t value) + : m_type(type), m_value(value) { } @@ -64,6 +64,11 @@ namespace elna::source { } + std::shared_ptr constant_info::type() const noexcept + { + return m_type; + } + std::int32_t constant_info::value() const noexcept { return m_value; @@ -97,17 +102,27 @@ namespace elna::source return m_type; } + intrinsic_info::intrinsic_info(const class procedure_type& type) + : m_type(std::make_shared(type)) + { + } + intrinsic_info::~intrinsic_info() { } - std::size_t intrinsic_info::parameter_stack_size() const noexcept + std::shared_ptr intrinsic_info::type() const noexcept { - return this->parameter_infos.size() * sizeof(std::int32_t); + return m_type; } - procedure_info::procedure_info(std::shared_ptr outer_scope) - : local_table(std::make_shared(outer_scope)) + std::size_t intrinsic_info::parameter_stack_size() const noexcept + { + return type()->arguments.size() * sizeof(std::int32_t); + } + + procedure_info::procedure_info(const class procedure_type& type, std::shared_ptr outer_scope) + : intrinsic_info(type), local_table(std::make_shared(outer_scope)) { } diff --git a/source/types.cpp b/source/types.cpp index af53626..6e86771 100644 --- a/source/types.cpp +++ b/source/types.cpp @@ -21,4 +21,9 @@ namespace elna::source : type(byte_size), base_type(base_type) { } + + procedure_type::procedure_type(std::vector> arguments, const std::size_t byte_size) + : arguments(std::move(arguments)), type(byte_size) + { + } }