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); | ||||
|     } | ||||
|  | ||||
|     visitor::visitor(std::shared_ptr<source::writer> writer) | ||||
|         : writer(writer) | ||||
|     visitor::visitor(std::shared_ptr<source::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); | ||||
|  | ||||
|             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::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)); | ||||
|  | ||||
|             prologue(); | ||||
|             this->instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); | ||||
|             this->instructions.push_back(instruction(base_opcode::branch) | ||||
| @@ -114,32 +107,16 @@ namespace elna::riscv | ||||
|             this->instructions.push_back(instruction(base_opcode::jalr) | ||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||
|  | ||||
|             this->instructions.push_back(instruction(base_opcode::load) | ||||
|                     .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)); | ||||
|             epilogue(8); | ||||
|  | ||||
|             this->writer->sink("writeb", reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||
|                     this->instructions.size() * sizeof(instruction)); | ||||
|       | ||||
|             this->instructions.clear(); | ||||
|         } | ||||
|         { | ||||
|             auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4); | ||||
|  | ||||
|             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::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)); | ||||
|  | ||||
|             prologue(); | ||||
|             this->instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .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) | ||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||
|  | ||||
|             this->instructions.push_back(instruction(base_opcode::load) | ||||
|                     .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)); | ||||
|             epilogue(8); | ||||
|  | ||||
|             this->writer->sink("writei", reinterpret_cast<const std::byte *>(this->instructions.data()), | ||||
|                     this->instructions.size() * sizeof(instruction)); | ||||
|       | ||||
|             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[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); | ||||
| @@ -225,19 +181,60 @@ namespace elna::riscv | ||||
|             .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) | ||||
|     { | ||||
|         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->instructions.size() * sizeof(instruction)); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); | ||||
|         this->instructions.push_back(instruction(base_opcode::jalr) | ||||
|   | ||||
| @@ -146,7 +146,8 @@ namespace elna::riscv | ||||
|         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; | ||||
|  | ||||
| @@ -197,7 +198,7 @@ namespace elna::riscv | ||||
|         ELFIO::relocation_section_accessor rela(writer, rel_sec); | ||||
|         auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra); | ||||
|  | ||||
|         visitor _visitor{ _writer }; | ||||
|         visitor _visitor{ _writer, table }; | ||||
|         _visitor.visit(ast); | ||||
|  | ||||
|         syma.arrange_local_symbols(); | ||||
|   | ||||
| @@ -54,10 +54,11 @@ namespace elna::cli | ||||
|             } | ||||
|             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::allocator_visitor().visit(ast.get()); | ||||
|         riscv::riscv32_elf(ast.get(), out_file); | ||||
|         source::allocator_visitor(global_scope).visit(ast.get()); | ||||
|         riscv::riscv32_elf(ast.get(), global_scope, out_file); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|   | ||||
| @@ -165,13 +165,16 @@ namespace elna::riscv | ||||
|  | ||||
|         void generate_intrinsics(); | ||||
|         void relocate(std::string_view name, address_t target); | ||||
|         void prologue(); | ||||
|         void epilogue(const std::size_t stack_size); | ||||
|  | ||||
|     public: | ||||
|         std::uint32_t variable_counter = 1; | ||||
|         std::vector<reference> references; | ||||
|         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::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); | ||||
|  | ||||
|     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 | ||||
|     { | ||||
|         std::string m_identifier; | ||||
|         std::string m_type; | ||||
|  | ||||
|     public: | ||||
|         declaration(const std::string& identifier); | ||||
|         declaration(const std::string& identifier, const std::string& type); | ||||
|         virtual void accept(parser_visitor *visitor) override; | ||||
|  | ||||
|         std::string& identifier() noexcept; | ||||
|         std::string& type() noexcept; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -124,25 +126,27 @@ namespace elna::source | ||||
|     class procedure_definition : public definition | ||||
|     { | ||||
|         std::unique_ptr<block> m_body; | ||||
|         std::vector<std::unique_ptr<declaration>> m_parameters; | ||||
|  | ||||
|     public: | ||||
|         procedure_definition(const std::string& identifier, std::unique_ptr<block>&& body); | ||||
|         virtual void accept(parser_visitor *visitor) override; | ||||
|  | ||||
|         block& body(); | ||||
|         std::vector<std::unique_ptr<declaration>>& parameters() noexcept; | ||||
|     }; | ||||
|  | ||||
|     class call_statement : public statement | ||||
|     { | ||||
|         std::string m_name; | ||||
|         std::unique_ptr<expression> m_body; | ||||
|         std::vector<std::unique_ptr<expression>> m_arguments; | ||||
|  | ||||
|     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; | ||||
|  | ||||
|         std::string& name() noexcept; | ||||
|         expression& arguments(); | ||||
|         std::vector<std::unique_ptr<expression>>& arguments() noexcept; | ||||
|     }; | ||||
|  | ||||
|     class compound_statement : public statement | ||||
| @@ -201,7 +205,6 @@ namespace elna::source | ||||
|         std::unique_ptr<statement> m_body; | ||||
|         std::vector<std::unique_ptr<definition>> m_definitions; | ||||
|         std::vector<std::unique_ptr<declaration>> m_declarations; | ||||
|         std::shared_ptr<symbol_table> m_table; | ||||
|  | ||||
|     public: | ||||
|         block(std::vector<std::unique_ptr<definition>>&& definitions, | ||||
| @@ -212,7 +215,6 @@ namespace elna::source | ||||
|         statement& body(); | ||||
|         std::vector<std::unique_ptr<definition>>& definitions() noexcept; | ||||
|         std::vector<std::unique_ptr<declaration>>& declarations() noexcept; | ||||
|         std::shared_ptr<symbol_table> table(); | ||||
|     }; | ||||
|  | ||||
|     class program : public block | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <string> | ||||
| #include <type_traits> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
|  | ||||
| namespace elna::source | ||||
| { | ||||
| @@ -110,6 +111,32 @@ namespace elna::source | ||||
|         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. | ||||
|      */ | ||||
| @@ -122,6 +149,19 @@ namespace elna::source | ||||
|         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. | ||||
|      */ | ||||
| @@ -145,18 +185,36 @@ namespace elna::source | ||||
|         ~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. | ||||
|      */ | ||||
|     class procedure_info final : public info | ||||
|     { | ||||
|         std::size_t local_stack_size{ 0 }; | ||||
|         std::shared_ptr<symbol_table> local_table; | ||||
|  | ||||
|     public: | ||||
|         std::vector<parameter_info> parameter_infos; | ||||
|  | ||||
|         explicit procedure_info(std::shared_ptr<symbol_table> outer_scope); | ||||
|         ~procedure_info() override; | ||||
|  | ||||
|         void stack_size(const std::size_t size) noexcept; | ||||
|         std::size_t stack_size() const noexcept; | ||||
|         std::shared_ptr<symbol_table> scope(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -174,12 +232,39 @@ namespace elna::source | ||||
|     class symbol_table | ||||
|     { | ||||
|         std::unordered_map<std::string, std::shared_ptr<info>> entries; | ||||
|         std::shared_ptr<symbol_table> outer_scope; | ||||
|  | ||||
|     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); | ||||
|  | ||||
|         /** | ||||
|          * Registers new symbol. | ||||
|          * | ||||
|          * \param name Symbol name. | ||||
|          * \param entry Symbol information. | ||||
|          */ | ||||
|         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 | ||||
|   | ||||
| @@ -6,21 +6,28 @@ namespace elna::source | ||||
| { | ||||
|     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: | ||||
|         name_analysis_visitor(std::shared_ptr<symbol_table> table); | ||||
|  | ||||
|         void visit(constant_definition *definition) 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 | ||||
|     { | ||||
|         std::ptrdiff_t offset; | ||||
|         std::shared_ptr<symbol_table> table; | ||||
|  | ||||
|     public: | ||||
|         allocator_visitor(std::shared_ptr<symbol_table> table); | ||||
|  | ||||
|         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 | ||||
|   | ||||
| @@ -19,7 +19,10 @@ namespace elna::source | ||||
|  | ||||
|     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) | ||||
| @@ -87,8 +90,8 @@ namespace elna::source | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     declaration::declaration(const std::string& identifier) | ||||
|         : m_identifier(identifier) | ||||
|     declaration::declaration(const std::string& identifier, const std::string& type) | ||||
|         : m_identifier(identifier), m_type(type) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| @@ -102,6 +105,11 @@ namespace elna::source | ||||
|         return m_identifier; | ||||
|     } | ||||
|  | ||||
|     std::string& declaration::type() noexcept | ||||
|     { | ||||
|         return m_type; | ||||
|     } | ||||
|  | ||||
|     definition::definition(const std::string& identifier) | ||||
|         : m_identifier(identifier) | ||||
|     { | ||||
| @@ -142,12 +150,16 @@ namespace elna::source | ||||
|         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, | ||||
|             std::vector<std::unique_ptr<declaration>>&& declarations, | ||||
|             std::unique_ptr<statement>&& body) | ||||
|         : m_definitions(std::move(definitions)), | ||||
|         m_declarations(std::move(declarations)), m_body(std::move(body)), | ||||
|         m_table(std::make_shared<symbol_table>()) | ||||
|         m_declarations(std::move(declarations)), m_body(std::move(body)) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| @@ -171,11 +183,6 @@ namespace elna::source | ||||
|         return m_declarations; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<symbol_table> block::table() | ||||
|     { | ||||
|         return m_table; | ||||
|     } | ||||
|  | ||||
|     program::program(std::vector<std::unique_ptr<definition>>&& definitions, | ||||
|             std::vector<std::unique_ptr<declaration>>&& declarations, | ||||
|             std::unique_ptr<statement>&& body) | ||||
| @@ -276,8 +283,8 @@ namespace elna::source | ||||
|         return m_operator; | ||||
|     } | ||||
|  | ||||
|     call_statement::call_statement(const std::string& name, std::unique_ptr<expression>&& body) | ||||
|         : m_name(name), m_body(std::move(body)) | ||||
|     call_statement::call_statement(const std::string& name) | ||||
|         : m_name(name) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| @@ -291,9 +298,9 @@ namespace elna::source | ||||
|         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) | ||||
| @@ -537,7 +544,8 @@ namespace elna::source | ||||
|         { | ||||
|             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() | ||||
| @@ -573,11 +581,27 @@ namespace elna::source | ||||
|         { | ||||
|             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; | ||||
|     } | ||||
|   | ||||
| @@ -27,23 +27,39 @@ namespace elna::source | ||||
|         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("writeb", std::make_shared<intrinsic_info>()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<info> symbol_table::lookup(const std::string& name) | ||||
|     { | ||||
|         auto entry = entries.find(name); | ||||
|         if (entry == entries.cend()) | ||||
|         { | ||||
|             return nullptr; | ||||
|         } | ||||
|         else | ||||
|  | ||||
|         if (entry != entries.cend()) | ||||
|         { | ||||
|             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) | ||||
| @@ -51,6 +67,11 @@ namespace elna::source | ||||
|         entries.insert_or_assign(name, entry); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<symbol_table> symbol_table::scope() | ||||
|     { | ||||
|         return this->outer_scope; | ||||
|     } | ||||
|  | ||||
|     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) | ||||
|         : 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() | ||||
|     { | ||||
|     } | ||||
| @@ -91,6 +145,11 @@ namespace elna::source | ||||
|         this->local_stack_size = size; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<symbol_table> procedure_info::scope() | ||||
|     { | ||||
|         return local_table; | ||||
|     } | ||||
|  | ||||
|     intrinsic_info::~intrinsic_info() | ||||
|     { | ||||
|     } | ||||
|   | ||||
| @@ -3,6 +3,11 @@ | ||||
|  | ||||
| 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) | ||||
|     { | ||||
|         this->table->enter(definition->identifier(), | ||||
| @@ -15,12 +20,24 @@ namespace elna::source | ||||
|                 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) | ||||
| @@ -28,11 +45,19 @@ namespace elna::source | ||||
|         this->offset -= sizeof(std::int32_t); | ||||
|     } | ||||
|  | ||||
|     void allocator_visitor::visit(block *block) | ||||
|     void allocator_visitor::visit(program *program) | ||||
|     { | ||||
|         this->offset = 0; | ||||
|         empty_visitor::visit(block); | ||||
|         std::dynamic_pointer_cast<procedure_info>(block->table()->lookup("main")) | ||||
|         empty_visitor::visit(program); | ||||
|         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)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 2 | ||||
| 5 | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| proc f; | ||||
|   writei(5); | ||||
| proc f; writei(5); | ||||
|  | ||||
| begin | ||||
|   writei(2) | ||||
|   f() | ||||
| end. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user