Support procedure calls without arguments
This commit is contained in:
		| @@ -76,8 +76,9 @@ namespace elna::riscv | |||||||
|         return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation); |         return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     visitor::visitor(std::shared_ptr<source::writer> writer) |     visitor::visitor(std::shared_ptr<source::writer> writer, | ||||||
|         : writer(writer) |             std::shared_ptr<source::symbol_table> table) | ||||||
|  |         : writer(writer), table(table) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -87,15 +88,7 @@ namespace elna::riscv | |||||||
|         { |         { | ||||||
|             auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4); |             auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4); | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |             prologue(); | ||||||
|                     .i(x_register::sp, funct3_t::addi, x_register::sp, -8)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::store) |  | ||||||
|                     .s(4, funct3_t::sw, x_register::sp, x_register::s0)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::store) |  | ||||||
|                     .s(0, funct3_t::sw, x_register::sp, x_register::ra)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |  | ||||||
|                     .i(x_register::s0, funct3_t::addi, x_register::sp, -8)); |  | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |             this->instructions.push_back(instruction(base_opcode::opImm) | ||||||
|                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); |                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); | ||||||
|             this->instructions.push_back(instruction(base_opcode::branch) |             this->instructions.push_back(instruction(base_opcode::branch) | ||||||
| @@ -114,32 +107,16 @@ namespace elna::riscv | |||||||
|             this->instructions.push_back(instruction(base_opcode::jalr) |             this->instructions.push_back(instruction(base_opcode::jalr) | ||||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); |                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::load) |             epilogue(8); | ||||||
|                     .i(x_register::s0, funct3_t::lw, x_register::sp, 4)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::load) |  | ||||||
|                     .i(x_register::ra, funct3_t::lw, x_register::sp, 0)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |  | ||||||
|                     .i(x_register::sp, funct3_t::addi, x_register::sp, 8)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::jalr) |  | ||||||
|                     .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); |  | ||||||
|  |  | ||||||
|             this->writer->sink("writeb", reinterpret_cast<const std::byte *>(this->instructions.data()), |             this->writer->sink("writeb", reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||||
|                     this->instructions.size() * sizeof(instruction)); |                     this->instructions.size() * sizeof(instruction)); | ||||||
|       |  | ||||||
|             this->instructions.clear(); |             this->instructions.clear(); | ||||||
|         } |         } | ||||||
|         { |         { | ||||||
|             auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4); |             auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4); | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |             prologue(); | ||||||
|                     .i(x_register::sp, funct3_t::addi, x_register::sp, -8)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::store) |  | ||||||
|                     .s(4, funct3_t::sw, x_register::sp, x_register::s0)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::store) |  | ||||||
|                     .s(0, funct3_t::sw, x_register::sp, x_register::ra)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |  | ||||||
|                     .i(x_register::s0, funct3_t::addi, x_register::sp, -8)); |  | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |             this->instructions.push_back(instruction(base_opcode::opImm) | ||||||
|                     .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); |                     .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); | ||||||
|  |  | ||||||
| @@ -154,18 +131,10 @@ namespace elna::riscv | |||||||
|             this->instructions.push_back(instruction(base_opcode::jalr) |             this->instructions.push_back(instruction(base_opcode::jalr) | ||||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); |                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||||
|  |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::load) |             epilogue(8); | ||||||
|                     .i(x_register::s0, funct3_t::lw, x_register::sp, 4)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::load) |  | ||||||
|                     .i(x_register::ra, funct3_t::lw, x_register::sp, 0)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::opImm) |  | ||||||
|                     .i(x_register::sp, funct3_t::addi, x_register::sp, 8)); |  | ||||||
|             this->instructions.push_back(instruction(base_opcode::jalr) |  | ||||||
|                     .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); |  | ||||||
|  |  | ||||||
|             this->writer->sink("writei", reinterpret_cast<const std::byte *>(this->instructions.data()), |             this->writer->sink("writei", reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||||
|                     this->instructions.size() * sizeof(instruction)); |                     this->instructions.size() * sizeof(instruction)); | ||||||
|       |  | ||||||
|             this->instructions.clear(); |             this->instructions.clear(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -186,29 +155,16 @@ namespace elna::riscv | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void visitor::visit(source::procedure_definition *definition) |     void visitor::prologue() | ||||||
|     { |     { | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::opImm)); | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::store)); | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::store)); | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::opImm)); | ||||||
|    } |    } | ||||||
|  |  | ||||||
|     void visitor::visit(source::block *block) |     void visitor::epilogue(const std::size_t stack_size) | ||||||
|     { |     { | ||||||
|         this->instructions.push_back(instruction(base_opcode::opImm)); |  | ||||||
|         this->instructions.push_back(instruction(base_opcode::store)); |  | ||||||
|         this->instructions.push_back(instruction(base_opcode::store)); |  | ||||||
|         this->instructions.push_back(instruction(base_opcode::opImm)); |  | ||||||
|  |  | ||||||
|         table = block->table(); |  | ||||||
|         block->body().accept(this); |  | ||||||
|  |  | ||||||
|         // Set the return value (0). |  | ||||||
|         this->instructions.push_back(instruction(base_opcode::op) |  | ||||||
|             .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); |  | ||||||
|  |  | ||||||
|         // Prologue. |  | ||||||
|         auto main_symbol = |  | ||||||
|             std::dynamic_pointer_cast<source::procedure_info>(table->lookup("main")); |  | ||||||
|         const uint stack_size = static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->stack_size()); |  | ||||||
|  |  | ||||||
|         this->instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size); |         this->instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size); | ||||||
|         this->instructions[1].s(stack_size - 4, funct3_t::sw, x_register::sp, x_register::s0); |         this->instructions[1].s(stack_size - 4, funct3_t::sw, x_register::sp, x_register::s0); | ||||||
|         this->instructions[2].s(stack_size - 8, funct3_t::sw, x_register::sp, x_register::ra); |         this->instructions[2].s(stack_size - 8, funct3_t::sw, x_register::sp, x_register::ra); | ||||||
| @@ -225,19 +181,60 @@ namespace elna::riscv | |||||||
|             .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); |             .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void visitor::visit(source::procedure_definition *definition) | ||||||
|  |     { | ||||||
|  |         prologue(); | ||||||
|  |  | ||||||
|  |         auto main_symbol = | ||||||
|  |             std::dynamic_pointer_cast<source::procedure_info>(this->table->lookup(definition->identifier())); | ||||||
|  |         this->table = main_symbol->scope(); | ||||||
|  |         definition->body().accept(this); | ||||||
|  |         this->table = main_symbol->scope()->scope(); | ||||||
|  |  | ||||||
|  |         // Set the return value (0). | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::op) | ||||||
|  |             .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); | ||||||
|  |  | ||||||
|  |         epilogue(static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->stack_size())); | ||||||
|  |         this->writer->sink(definition->identifier(), | ||||||
|  |                 reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||||
|  |                 this->instructions.size() * sizeof(instruction)); | ||||||
|  |         this->instructions.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void visitor::visit(source::block *block) | ||||||
|  |     { | ||||||
|  |         block->body().accept(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void visitor::visit(source::program *program) |     void visitor::visit(source::program *program) | ||||||
|     { |     { | ||||||
|         generate_intrinsics(); |         generate_intrinsics(); | ||||||
|  |         for (auto& definition : program->definitions()) | ||||||
|  |         { | ||||||
|  |             definition->accept(this); | ||||||
|  |         } | ||||||
|  |         prologue(); | ||||||
|  |  | ||||||
|         visit(dynamic_cast<source::block *>(program)); |         auto main_symbol = | ||||||
|  |             std::dynamic_pointer_cast<source::procedure_info>(this->table->lookup("main")); | ||||||
|  |         program->body().accept(this); | ||||||
|  |  | ||||||
|  |         // Set the return value (0). | ||||||
|  |         this->instructions.push_back(instruction(base_opcode::op) | ||||||
|  |             .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); | ||||||
|  |  | ||||||
|  |         epilogue(static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->stack_size())); | ||||||
|         this->writer->sink("main", reinterpret_cast<const std::byte *>(this->instructions.data()), |         this->writer->sink("main", reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||||
|                 this->instructions.size() * sizeof(instruction)); |                 this->instructions.size() * sizeof(instruction)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void visitor::visit(source::call_statement *statement) |     void visitor::visit(source::call_statement *statement) | ||||||
|     { |     { | ||||||
|         statement->arguments().accept(this); |         for (auto& argument : statement->arguments()) | ||||||
|  |         { | ||||||
|  |             argument->accept(this); | ||||||
|  |         } | ||||||
|         relocate(statement->name(), address_t::text); |         relocate(statement->name(), address_t::text); | ||||||
|         this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); |         this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); | ||||||
|         this->instructions.push_back(instruction(base_opcode::jalr) |         this->instructions.push_back(instruction(base_opcode::jalr) | ||||||
|   | |||||||
| @@ -146,7 +146,8 @@ namespace elna::riscv | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void riscv32_elf(source::program *ast, const std::filesystem::path& out_file) |     void riscv32_elf(source::program *ast, std::shared_ptr<source::symbol_table> table, | ||||||
|  |             const std::filesystem::path& out_file) | ||||||
|     { |     { | ||||||
|         ELFIO::elfio writer; |         ELFIO::elfio writer; | ||||||
|  |  | ||||||
| @@ -197,7 +198,7 @@ namespace elna::riscv | |||||||
|         ELFIO::relocation_section_accessor rela(writer, rel_sec); |         ELFIO::relocation_section_accessor rela(writer, rel_sec); | ||||||
|         auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra); |         auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra); | ||||||
|  |  | ||||||
|         visitor _visitor{ _writer }; |         visitor _visitor{ _writer, table }; | ||||||
|         _visitor.visit(ast); |         _visitor.visit(ast); | ||||||
|  |  | ||||||
|         syma.arrange_local_symbols(); |         syma.arrange_local_symbols(); | ||||||
|   | |||||||
| @@ -54,10 +54,11 @@ namespace elna::cli | |||||||
|             } |             } | ||||||
|             return 2; |             return 2; | ||||||
|         } |         } | ||||||
|         source::name_analysis_visitor().visit(ast.get()); |         auto global_scope = std::make_shared<source::symbol_table>(); | ||||||
|  |         source::name_analysis_visitor(global_scope).visit(ast.get()); | ||||||
|         source::type_analysis_visitor().visit(ast.get()); |         source::type_analysis_visitor().visit(ast.get()); | ||||||
|         source::allocator_visitor().visit(ast.get()); |         source::allocator_visitor(global_scope).visit(ast.get()); | ||||||
|         riscv::riscv32_elf(ast.get(), out_file); |         riscv::riscv32_elf(ast.get(), global_scope, out_file); | ||||||
|  |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -165,13 +165,16 @@ namespace elna::riscv | |||||||
|  |  | ||||||
|         void generate_intrinsics(); |         void generate_intrinsics(); | ||||||
|         void relocate(std::string_view name, address_t target); |         void relocate(std::string_view name, address_t target); | ||||||
|  |         void prologue(); | ||||||
|  |         void epilogue(const std::size_t stack_size); | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         std::uint32_t variable_counter = 1; |         std::uint32_t variable_counter = 1; | ||||||
|         std::vector<reference> references; |         std::vector<reference> references; | ||||||
|         std::shared_ptr<source::symbol_table> table; |         std::shared_ptr<source::symbol_table> table; | ||||||
|  |  | ||||||
|         visitor(std::shared_ptr<source::writer> writer); |         visitor(std::shared_ptr<source::writer> writer, | ||||||
|  |                 std::shared_ptr<source::symbol_table> table); | ||||||
|  |  | ||||||
|         virtual void visit(source::declaration *declaration) override; |         virtual void visit(source::declaration *declaration) override; | ||||||
|         virtual void visit(source::constant_definition *definition) override; |         virtual void visit(source::constant_definition *definition) override; | ||||||
|   | |||||||
| @@ -99,5 +99,6 @@ namespace elna::riscv | |||||||
|      */ |      */ | ||||||
|     std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label); |     std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label); | ||||||
|  |  | ||||||
|     void riscv32_elf(source::program *ast, const std::filesystem::path& out_file); |     void riscv32_elf(source::program *ast, std::shared_ptr<source::symbol_table> table, | ||||||
|  |             const std::filesystem::path& out_file); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -88,12 +88,14 @@ namespace elna::source | |||||||
|     class declaration : public node |     class declaration : public node | ||||||
|     { |     { | ||||||
|         std::string m_identifier; |         std::string m_identifier; | ||||||
|  |         std::string m_type; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         declaration(const std::string& identifier); |         declaration(const std::string& identifier, const std::string& type); | ||||||
|         virtual void accept(parser_visitor *visitor) override; |         virtual void accept(parser_visitor *visitor) override; | ||||||
|  |  | ||||||
|         std::string& identifier() noexcept; |         std::string& identifier() noexcept; | ||||||
|  |         std::string& type() noexcept; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -124,25 +126,27 @@ namespace elna::source | |||||||
|     class procedure_definition : public definition |     class procedure_definition : public definition | ||||||
|     { |     { | ||||||
|         std::unique_ptr<block> m_body; |         std::unique_ptr<block> m_body; | ||||||
|  |         std::vector<std::unique_ptr<declaration>> m_parameters; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         procedure_definition(const std::string& identifier, std::unique_ptr<block>&& body); |         procedure_definition(const std::string& identifier, std::unique_ptr<block>&& body); | ||||||
|         virtual void accept(parser_visitor *visitor) override; |         virtual void accept(parser_visitor *visitor) override; | ||||||
|  |  | ||||||
|         block& body(); |         block& body(); | ||||||
|  |         std::vector<std::unique_ptr<declaration>>& parameters() noexcept; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class call_statement : public statement |     class call_statement : public statement | ||||||
|     { |     { | ||||||
|         std::string m_name; |         std::string m_name; | ||||||
|         std::unique_ptr<expression> m_body; |         std::vector<std::unique_ptr<expression>> m_arguments; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         call_statement(const std::string& name, std::unique_ptr<expression>&& body); |         call_statement(const std::string& name); | ||||||
|         virtual void accept(parser_visitor *visitor) override; |         virtual void accept(parser_visitor *visitor) override; | ||||||
|  |  | ||||||
|         std::string& name() noexcept; |         std::string& name() noexcept; | ||||||
|         expression& arguments(); |         std::vector<std::unique_ptr<expression>>& arguments() noexcept; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class compound_statement : public statement |     class compound_statement : public statement | ||||||
| @@ -201,7 +205,6 @@ namespace elna::source | |||||||
|         std::unique_ptr<statement> m_body; |         std::unique_ptr<statement> m_body; | ||||||
|         std::vector<std::unique_ptr<definition>> m_definitions; |         std::vector<std::unique_ptr<definition>> m_definitions; | ||||||
|         std::vector<std::unique_ptr<declaration>> m_declarations; |         std::vector<std::unique_ptr<declaration>> m_declarations; | ||||||
|         std::shared_ptr<symbol_table> m_table; |  | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         block(std::vector<std::unique_ptr<definition>>&& definitions, |         block(std::vector<std::unique_ptr<definition>>&& definitions, | ||||||
| @@ -212,7 +215,6 @@ namespace elna::source | |||||||
|         statement& body(); |         statement& body(); | ||||||
|         std::vector<std::unique_ptr<definition>>& definitions() noexcept; |         std::vector<std::unique_ptr<definition>>& definitions() noexcept; | ||||||
|         std::vector<std::unique_ptr<declaration>>& declarations() noexcept; |         std::vector<std::unique_ptr<declaration>>& declarations() noexcept; | ||||||
|         std::shared_ptr<symbol_table> table(); |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class program : public block |     class program : public block | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
| namespace elna::source | namespace elna::source | ||||||
| { | { | ||||||
| @@ -110,6 +111,32 @@ namespace elna::source | |||||||
|         std::string what() const override; |         std::string what() const override; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     class symbol_table; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Type representation. | ||||||
|  |      */ | ||||||
|  |     struct type | ||||||
|  |     { | ||||||
|  |         const std::size_t byte_size; | ||||||
|  |  | ||||||
|  |     protected: | ||||||
|  |         explicit type(const std::size_t byte_size); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Built-in type representation. | ||||||
|  |      */ | ||||||
|  |     struct primitive_type : public type | ||||||
|  |     { | ||||||
|  |         const std::string type_name; | ||||||
|  |  | ||||||
|  |         primitive_type(const std::string& type_name, const std::size_t byte_size); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     inline const primitive_type boolean_type{ "Boolean", 1 }; | ||||||
|  |     inline const primitive_type int_type{ "Int", 4 }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generic language entity information. |      * Generic language entity information. | ||||||
|      */ |      */ | ||||||
| @@ -122,6 +149,19 @@ namespace elna::source | |||||||
|         info(); |         info(); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Type information. | ||||||
|  |      */ | ||||||
|  |     class type_info final : public info | ||||||
|  |     { | ||||||
|  |         class type m_type; | ||||||
|  |  | ||||||
|  |     public: | ||||||
|  |         explicit type_info(const class type& type); | ||||||
|  |         ~type_info() override; | ||||||
|  |         const class type& type() const noexcept; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Constant information. |      * Constant information. | ||||||
|      */ |      */ | ||||||
| @@ -145,18 +185,36 @@ namespace elna::source | |||||||
|         ~variable_info() override; |         ~variable_info() override; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Procedure parameter information. | ||||||
|  |      */ | ||||||
|  |     class parameter_info final : public info | ||||||
|  |     { | ||||||
|  |         class type m_type; | ||||||
|  |  | ||||||
|  |     public: | ||||||
|  |         explicit parameter_info(const class type& type); | ||||||
|  |         ~parameter_info() override; | ||||||
|  |         const class type& type() const noexcept; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Procedure information. |      * Procedure information. | ||||||
|      */ |      */ | ||||||
|     class procedure_info final : public info |     class procedure_info final : public info | ||||||
|     { |     { | ||||||
|         std::size_t local_stack_size{ 0 }; |         std::size_t local_stack_size{ 0 }; | ||||||
|  |         std::shared_ptr<symbol_table> local_table; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |         std::vector<parameter_info> parameter_infos; | ||||||
|  |  | ||||||
|  |         explicit procedure_info(std::shared_ptr<symbol_table> outer_scope); | ||||||
|         ~procedure_info() override; |         ~procedure_info() override; | ||||||
|  |  | ||||||
|         void stack_size(const std::size_t size) noexcept; |         void stack_size(const std::size_t size) noexcept; | ||||||
|         std::size_t stack_size() const noexcept; |         std::size_t stack_size() const noexcept; | ||||||
|  |         std::shared_ptr<symbol_table> scope(); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -174,12 +232,39 @@ namespace elna::source | |||||||
|     class symbol_table |     class symbol_table | ||||||
|     { |     { | ||||||
|         std::unordered_map<std::string, std::shared_ptr<info>> entries; |         std::unordered_map<std::string, std::shared_ptr<info>> entries; | ||||||
|  |         std::shared_ptr<symbol_table> outer_scope; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         symbol_table(); |         /** | ||||||
|  |          * Constructs a new symbol with an optional outer scope. | ||||||
|  |          * | ||||||
|  |          * \param scope Outer scope. | ||||||
|  |          */ | ||||||
|  |         explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr); | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Looks for symbol in the table by name. Returns nullptr if the symbol | ||||||
|  |          * can not be found. | ||||||
|  |          * | ||||||
|  |          * \param name Symbol name. | ||||||
|  |          * \return Symbol from the table if found. | ||||||
|  |          */ | ||||||
|         std::shared_ptr<info> lookup(const std::string& name); |         std::shared_ptr<info> lookup(const std::string& name); | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Registers new symbol. | ||||||
|  |          * | ||||||
|  |          * \param name Symbol name. | ||||||
|  |          * \param entry Symbol information. | ||||||
|  |          */ | ||||||
|         void enter(const std::string& name, std::shared_ptr<info> entry); |         void enter(const std::string& name, std::shared_ptr<info> entry); | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Returns the outer scope or nullptr if the this is the global scope. | ||||||
|  |          * | ||||||
|  |          * \return Outer scope. | ||||||
|  |          */ | ||||||
|  |         std::shared_ptr<symbol_table> scope(); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     struct writer |     struct writer | ||||||
|   | |||||||
| @@ -6,21 +6,28 @@ namespace elna::source | |||||||
| { | { | ||||||
|     class name_analysis_visitor final : public empty_visitor |     class name_analysis_visitor final : public empty_visitor | ||||||
|     { |     { | ||||||
|         std::shared_ptr<symbol_table> table; |         std::shared_ptr<symbol_table> table = std::make_shared<symbol_table>(); | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |         name_analysis_visitor(std::shared_ptr<symbol_table> table); | ||||||
|  |  | ||||||
|         void visit(constant_definition *definition) override; |         void visit(constant_definition *definition) override; | ||||||
|         void visit(declaration *declaration) override; |         void visit(declaration *declaration) override; | ||||||
|         void visit(block *block) override; |         void visit(program *program) override; | ||||||
|  |         void visit(procedure_definition *procedure) override; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class allocator_visitor final : public empty_visitor |     class allocator_visitor final : public empty_visitor | ||||||
|     { |     { | ||||||
|         std::ptrdiff_t offset; |         std::ptrdiff_t offset; | ||||||
|  |         std::shared_ptr<symbol_table> table; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |         allocator_visitor(std::shared_ptr<symbol_table> table); | ||||||
|  |  | ||||||
|         void visit(declaration *declaration) override; |         void visit(declaration *declaration) override; | ||||||
|         void visit(block *block) override; |         void visit(program *program) override; | ||||||
|  |         void visit(procedure_definition *procedure) override; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class type_analysis_visitor final : public empty_visitor |     class type_analysis_visitor final : public empty_visitor | ||||||
|   | |||||||
| @@ -19,7 +19,10 @@ namespace elna::source | |||||||
|  |  | ||||||
|     void empty_visitor::visit(call_statement *statement) |     void empty_visitor::visit(call_statement *statement) | ||||||
|     { |     { | ||||||
|         statement->arguments().accept(this); |         for (auto& argument : statement->arguments()) | ||||||
|  |         { | ||||||
|  |             argument->accept(this); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void empty_visitor::visit(compound_statement *statement) |     void empty_visitor::visit(compound_statement *statement) | ||||||
| @@ -87,8 +90,8 @@ namespace elna::source | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     declaration::declaration(const std::string& identifier) |     declaration::declaration(const std::string& identifier, const std::string& type) | ||||||
|         : m_identifier(identifier) |         : m_identifier(identifier), m_type(type) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -102,6 +105,11 @@ namespace elna::source | |||||||
|         return m_identifier; |         return m_identifier; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::string& declaration::type() noexcept | ||||||
|  |     { | ||||||
|  |         return m_type; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     definition::definition(const std::string& identifier) |     definition::definition(const std::string& identifier) | ||||||
|         : m_identifier(identifier) |         : m_identifier(identifier) | ||||||
|     { |     { | ||||||
| @@ -142,12 +150,16 @@ namespace elna::source | |||||||
|         return *m_body; |         return *m_body; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::vector<std::unique_ptr<declaration>>& procedure_definition::parameters() noexcept | ||||||
|  |     { | ||||||
|  |         return m_parameters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     block::block(std::vector<std::unique_ptr<definition>>&& definitions, |     block::block(std::vector<std::unique_ptr<definition>>&& definitions, | ||||||
|             std::vector<std::unique_ptr<declaration>>&& declarations, |             std::vector<std::unique_ptr<declaration>>&& declarations, | ||||||
|             std::unique_ptr<statement>&& body) |             std::unique_ptr<statement>&& body) | ||||||
|         : m_definitions(std::move(definitions)), |         : m_definitions(std::move(definitions)), | ||||||
|         m_declarations(std::move(declarations)), m_body(std::move(body)), |         m_declarations(std::move(declarations)), m_body(std::move(body)) | ||||||
|         m_table(std::make_shared<symbol_table>()) |  | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -171,11 +183,6 @@ namespace elna::source | |||||||
|         return m_declarations; |         return m_declarations; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::shared_ptr<symbol_table> block::table() |  | ||||||
|     { |  | ||||||
|         return m_table; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     program::program(std::vector<std::unique_ptr<definition>>&& definitions, |     program::program(std::vector<std::unique_ptr<definition>>&& definitions, | ||||||
|             std::vector<std::unique_ptr<declaration>>&& declarations, |             std::vector<std::unique_ptr<declaration>>&& declarations, | ||||||
|             std::unique_ptr<statement>&& body) |             std::unique_ptr<statement>&& body) | ||||||
| @@ -276,8 +283,8 @@ namespace elna::source | |||||||
|         return m_operator; |         return m_operator; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     call_statement::call_statement(const std::string& name, std::unique_ptr<expression>&& body) |     call_statement::call_statement(const std::string& name) | ||||||
|         : m_name(name), m_body(std::move(body)) |         : m_name(name) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -291,9 +298,9 @@ namespace elna::source | |||||||
|         return m_name; |         return m_name; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     expression& call_statement::arguments() |     std::vector<std::unique_ptr<expression>>& call_statement::arguments() noexcept | ||||||
|     { |     { | ||||||
|         return *m_body; |         return m_arguments; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     compound_statement::compound_statement(std::vector<std::unique_ptr<statement>>&& statements) |     compound_statement::compound_statement(std::vector<std::unique_ptr<statement>>&& statements) | ||||||
| @@ -537,7 +544,8 @@ namespace elna::source | |||||||
|         { |         { | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         } |         } | ||||||
|         return std::make_unique<declaration>(declaration_identifier.value().get().identifier()); |         return std::make_unique<declaration>(declaration_identifier.value().get().identifier(), | ||||||
|  |                 type_identifier.value().get().identifier()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::unique_ptr<statement> parser::parse_statement() |     std::unique_ptr<statement> parser::parse_statement() | ||||||
| @@ -573,11 +581,27 @@ namespace elna::source | |||||||
|         { |         { | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         } |         } | ||||||
|         auto bang_body = parse_expression(); |         auto call = std::make_unique<call_statement>(function_name->get().identifier()); | ||||||
|  |         std::unique_ptr<expression> argument_expression; | ||||||
|  |  | ||||||
|         if (bang_body != nullptr && iterator.skip(token::type::right_paren)) |         if (iterator.current(token::type::right_paren)) | ||||||
|         { |         { | ||||||
|             return std::make_unique<call_statement>(function_name->get().identifier(), std::move(bang_body)); |             ++iterator; | ||||||
|  |             return call; | ||||||
|  |         } | ||||||
|  |         while ((argument_expression = parse_expression()) != nullptr) | ||||||
|  |         { | ||||||
|  |             call->arguments().push_back(std::move(argument_expression)); | ||||||
|  |  | ||||||
|  |             if (iterator.current(token::type::right_paren)) | ||||||
|  |             { | ||||||
|  |                 ++iterator; | ||||||
|  |                 return call; | ||||||
|  |             } | ||||||
|  |             if (!iterator.skip(token::type::comma)) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -27,23 +27,39 @@ namespace elna::source | |||||||
|         return "Name '" + name + "' was already defined"; |         return "Name '" + name + "' was already defined"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     symbol_table::symbol_table() |     type::type(const std::size_t byte_size) | ||||||
|  |         : byte_size(byte_size) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size) | ||||||
|  |         : type(byte_size), type_name(type_name) | ||||||
|  |    { | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |     symbol_table::symbol_table(std::shared_ptr<symbol_table> scope) | ||||||
|  |         : outer_scope(scope) | ||||||
|  |     { | ||||||
|  |         if (scope == nullptr) | ||||||
|         { |         { | ||||||
|             enter("writei", std::make_shared<intrinsic_info>()); |             enter("writei", std::make_shared<intrinsic_info>()); | ||||||
|             enter("writeb", std::make_shared<intrinsic_info>()); |             enter("writeb", std::make_shared<intrinsic_info>()); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     std::shared_ptr<info> symbol_table::lookup(const std::string& name) |     std::shared_ptr<info> symbol_table::lookup(const std::string& name) | ||||||
|     { |     { | ||||||
|         auto entry = entries.find(name); |         auto entry = entries.find(name); | ||||||
|         if (entry == entries.cend()) |  | ||||||
|         { |         if (entry != entries.cend()) | ||||||
|             return nullptr; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |         { | ||||||
|             return entry->second; |             return entry->second; | ||||||
|         } |         } | ||||||
|  |         if (this->outer_scope != nullptr) | ||||||
|  |         { | ||||||
|  |             return this->outer_scope->lookup(name); | ||||||
|  |         } | ||||||
|  |         return nullptr; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry) |     void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry) | ||||||
| @@ -51,6 +67,11 @@ namespace elna::source | |||||||
|         entries.insert_or_assign(name, entry); |         entries.insert_or_assign(name, entry); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::shared_ptr<symbol_table> symbol_table::scope() | ||||||
|  |     { | ||||||
|  |         return this->outer_scope; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     info::~info() |     info::~info() | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -59,6 +80,20 @@ namespace elna::source | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     type_info::type_info(const class type& type) | ||||||
|  |         : info(), m_type(type) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     type_info::~type_info() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const class type& type_info::type() const noexcept | ||||||
|  |     { | ||||||
|  |         return m_type; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     constant_info::constant_info(const std::int32_t value) |     constant_info::constant_info(const std::int32_t value) | ||||||
|         : m_value(value) |         : m_value(value) | ||||||
|     { |     { | ||||||
| @@ -77,6 +112,25 @@ namespace elna::source | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     parameter_info::parameter_info(const class type& type) | ||||||
|  |         : m_type(type) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     parameter_info::~parameter_info() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const class type& parameter_info::type() const noexcept | ||||||
|  |     { | ||||||
|  |         return m_type; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope) | ||||||
|  |         : local_table(std::make_shared<symbol_table>(outer_scope)) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     procedure_info::~procedure_info() |     procedure_info::~procedure_info() | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -91,6 +145,11 @@ namespace elna::source | |||||||
|         this->local_stack_size = size; |         this->local_stack_size = size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::shared_ptr<symbol_table> procedure_info::scope() | ||||||
|  |     { | ||||||
|  |         return local_table; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     intrinsic_info::~intrinsic_info() |     intrinsic_info::~intrinsic_info() | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,6 +3,11 @@ | |||||||
|  |  | ||||||
| namespace elna::source | namespace elna::source | ||||||
| { | { | ||||||
|  |     name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table) | ||||||
|  |         : table(table) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void name_analysis_visitor::visit(constant_definition *definition) |     void name_analysis_visitor::visit(constant_definition *definition) | ||||||
|     { |     { | ||||||
|         this->table->enter(definition->identifier(), |         this->table->enter(definition->identifier(), | ||||||
| @@ -15,12 +20,24 @@ namespace elna::source | |||||||
|                 std::make_shared<variable_info>(variable_info())); |                 std::make_shared<variable_info>(variable_info())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void name_analysis_visitor::visit(block *block) |     void name_analysis_visitor::visit(program *program) | ||||||
|  |     { | ||||||
|  |         this->table->enter("main", std::make_shared<procedure_info>(this->table)); | ||||||
|  |         empty_visitor::visit(program); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void name_analysis_visitor::visit(procedure_definition *procedure) | ||||||
|  |     { | ||||||
|  |         auto info = std::make_shared<procedure_info>(this->table); | ||||||
|  |         this->table->enter(procedure->identifier(), info); | ||||||
|  |         this->table = info->scope(); | ||||||
|  |         empty_visitor::visit(procedure); | ||||||
|  |         this->table = info->scope()->scope(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table) | ||||||
|  |         : table(table) | ||||||
|     { |     { | ||||||
|         this->table = block->table(); |  | ||||||
|         empty_visitor::visit(block); |  | ||||||
|         this->table->enter("main", |  | ||||||
|                 std::make_shared<procedure_info>()); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void allocator_visitor::visit(declaration *declaration) |     void allocator_visitor::visit(declaration *declaration) | ||||||
| @@ -28,11 +45,19 @@ namespace elna::source | |||||||
|         this->offset -= sizeof(std::int32_t); |         this->offset -= sizeof(std::int32_t); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void allocator_visitor::visit(block *block) |     void allocator_visitor::visit(program *program) | ||||||
|     { |     { | ||||||
|         this->offset = 0; |         this->offset = 0; | ||||||
|         empty_visitor::visit(block); |         empty_visitor::visit(program); | ||||||
|         std::dynamic_pointer_cast<procedure_info>(block->table()->lookup("main")) |         std::dynamic_pointer_cast<procedure_info>(table->lookup("main")) | ||||||
|  |             ->stack_size(std::abs(this->offset)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void allocator_visitor::visit(procedure_definition *procedure) | ||||||
|  |     { | ||||||
|  |         this->offset = 0; | ||||||
|  |         empty_visitor::visit(procedure); | ||||||
|  |         std::dynamic_pointer_cast<procedure_info>(table->lookup(procedure->identifier())) | ||||||
|             ->stack_size(std::abs(this->offset)); |             ->stack_size(std::abs(this->offset)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| 2 | 5 | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| proc f; | proc f; writei(5); | ||||||
|   writei(5); |  | ||||||
|  |  | ||||||
| begin | begin | ||||||
|   writei(2) |   f() | ||||||
| end. | end. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user