From 1af995eafd304330ef1614ed228b342bd6346533 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 9 Mar 2024 08:36:07 +0100 Subject: [PATCH] Implement division --- CMakeLists.txt | 2 +- TODO | 17 +-- backend/riscv.cpp | 173 +++++++++++++++------------- backend/target.cpp | 22 ++-- cli/cl.cpp | 6 +- include/elna/backend/riscv.hpp | 58 +++++----- include/elna/backend/target.hpp | 2 +- include/elna/source/ir.hpp | 16 --- include/elna/source/lexer.hpp | 10 ++ include/elna/source/parser.hpp | 16 +-- include/elna/source/result.hpp | 32 ++--- include/elna/source/symboltable.hpp | 54 +++++++++ source/ir.cpp | 36 ------ source/lexer.cpp | 19 ++- source/parser.cpp | 43 ++++--- source/result.cpp | 14 +-- source/symboltable.cpp | 72 ++++++++++++ tests/7_member_sum.eln | 1 + tests/divide.eln | 1 + tests/expectations/divide.txt | 1 + tests/expectations/multiply_3.txt | 1 + tests/expectations/plus_minus.txt | 1 + tests/multiply.eln | 1 + tests/multiply_3.eln | 2 + tests/plus_minus.eln | 2 + 25 files changed, 357 insertions(+), 245 deletions(-) delete mode 100644 include/elna/source/ir.hpp create mode 100644 include/elna/source/symboltable.hpp delete mode 100644 source/ir.cpp create mode 100644 source/symboltable.cpp create mode 100644 tests/divide.eln create mode 100644 tests/expectations/divide.txt create mode 100644 tests/expectations/multiply_3.txt create mode 100644 tests/expectations/plus_minus.txt create mode 100644 tests/multiply_3.eln create mode 100644 tests/plus_minus.eln diff --git a/CMakeLists.txt b/CMakeLists.txt index fb8a8ed..5a494b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ add_executable(elna cli/main.cpp source/lexer.cpp include/elna/source/lexer.hpp source/parser.cpp include/elna/source/parser.hpp source/result.cpp include/elna/source/result.hpp - source/ir.cpp include/elna/source/ir.hpp + source/symboltable.cpp include/elna/source/symboltable.hpp backend/riscv.cpp include/elna/backend/riscv.hpp backend/target.cpp include/elna/backend/target.hpp cli/cl.cpp include/elna/cli/cl.hpp diff --git a/TODO b/TODO index e331d1e..4d0e299 100644 --- a/TODO +++ b/TODO @@ -1,18 +1,13 @@ # Compiler -- Separate headers and sources for the compiler, shell and tests. -- Make writing ELF independent from the code generation. -- Argument parsing. - Whitespaces are checked twice, in the source class and by lexing. - Catch exceptions thrown by the argument parser and print them normally. -- Division. -- Expressions contain one or more terms. Currently it parses another expression on the right side. -- Multiple factors. -- Put riscv code into the backend namespace. -- Rename riscv backend structs to be snake case. -- Make reference and Target be nested types of the visitor. -- Rename visitors to snake case. -- The backend visitor seems to generate code before the prologue. +- Parser should be able to collect errors. +- Provide position information on parse tree nodes. +- Move constants to the symbol table, so we can check at parse time for duplicates. +- Allow defining variables. +- Don't pass raw pointers to the visitor methods. +- Make error abstract and derive unexpected_token in the lex module from it. # Shell - Persist the history. diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 84c5353..005bb3f 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -1,68 +1,68 @@ #include "elna/backend/riscv.hpp" #include -namespace elna::backend +namespace elna::riscv { - Instruction::Instruction(BaseOpcode opcode) + instruction::instruction(base_opcode opcode) { - this->instruction = static_cast::type>(opcode); + this->representation = static_cast::type>(opcode); } - Instruction& Instruction::i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate) + instruction& instruction::i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate) { - this->instruction |= (static_cast::type>(rd) << 7) - | (static_cast::type>(funct3) << 12) - | (static_cast::type>(rs1) << 15) + this->representation |= (static_cast::type>(rd) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) | (immediate << 20); return *this; } - Instruction& Instruction::s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2) + instruction& instruction::s(std::uint32_t imm1, funct3_t funct3, x_register rs1, x_register rs2) { - this->instruction |= ((imm1 & 0b11111) << 7) - | (static_cast::type>(funct3) << 12) - | (static_cast::type>(rs1) << 15) - | (static_cast::type>(rs2) << 20) + this->representation |= ((imm1 & 0b11111) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::type>(rs2) << 20) | ((imm1 & 0b111111100000) << 20); return *this; } - Instruction& Instruction::r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7) + instruction& instruction::r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7) { - this->instruction |= (static_cast::type>(rd) << 7) - | (static_cast::type>(funct3) << 12) - | (static_cast::type>(rs1) << 15) - | (static_cast::type>(rs2) << 20) - | (static_cast::type>(funct7) << 25); + this->representation |= (static_cast::type>(rd) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::type>(rs2) << 20) + | (static_cast::type>(funct7) << 25); return *this; } - Instruction& Instruction::u(XRegister rd, std::uint32_t imm) + instruction& instruction::u(x_register rd, std::uint32_t imm) { - this->instruction |= (static_cast::type>(rd) << 7) | (imm << 12); + this->representation |= (static_cast::type>(rd) << 7) | (imm << 12); return *this; } - const std::byte *Instruction::cbegin() const + const std::byte *instruction::cbegin() const { - return reinterpret_cast(&this->instruction); + return reinterpret_cast(&this->representation); } - const std::byte *Instruction::cend() const + const std::byte *instruction::cend() const { - return reinterpret_cast(&this->instruction) + sizeof(this->instruction); + return reinterpret_cast(&this->representation) + sizeof(this->representation); } - void RiscVVisitor::visit(source::definition *definition) + void visitor::visit(source::definition *definition) { constants[definition->identifier()] = definition->body().number(); } - void RiscVVisitor::visit(source::block *block) + void visitor::visit(source::block *block) { for (const auto& block_definition : block->definitions()) { @@ -71,97 +71,100 @@ namespace elna::backend block->body().accept(this); // Prologue. - const uint stackSize = static_cast(variableCounter * 4 + 12); + const uint stackSize = static_cast(variable_counter * 4 + 12); - this->instructions.push_back(Instruction(BaseOpcode::opImm) - .i(XRegister::sp, Funct3::addi, XRegister::sp, -stackSize)); - this->instructions.push_back(Instruction(BaseOpcode::store) - .s(stackSize - 4, Funct3::sw, XRegister::sp, XRegister::s0)); - this->instructions.push_back(Instruction(BaseOpcode::store) - .s(stackSize - 8, Funct3::sw, XRegister::sp, XRegister::ra)); - this->instructions.push_back(Instruction(BaseOpcode::opImm) - .i(XRegister::s0, Funct3::addi, XRegister::sp, stackSize)); + std::vector prologue{ + instruction(base_opcode::opImm) + .i(x_register::sp, funct3_t::addi, x_register::sp, -stackSize), + instruction(base_opcode::store) + .s(stackSize - 4, funct3_t::sw, x_register::sp, x_register::s0), + instruction(base_opcode::store) + .s(stackSize - 8, funct3_t::sw, x_register::sp, x_register::ra), + instruction(base_opcode::opImm) + .i(x_register::s0, funct3_t::addi, x_register::sp, stackSize) + }; + this->instructions.insert(this->instructions.cbegin(), prologue.begin(), prologue.end()); // Print the result. - this->instructions.push_back(Instruction(BaseOpcode::opImm) - .i(XRegister::a1, Funct3::addi, XRegister::a0, 0)); - this->references[0] = Reference(); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); + this->references[0] = reference(); this->references[0].name = ".CL0"; this->references[0].offset = instructions.size() * 4; - this->references[0].target = Target::high20; - this->instructions.push_back(Instruction(BaseOpcode::lui).u(XRegister::a5, 0)); - this->references[1] = Reference(); + this->references[0].target = address_t::high20; + this->instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); + this->references[1] = reference(); this->references[1].name = ".CL0"; this->references[1].offset = instructions.size() * 4; - this->references[1].target = Target::lower12i; + this->references[1].target = address_t::lower12i; - this->instructions.push_back(Instruction(BaseOpcode::opImm) - .i(XRegister::a0, Funct3::addi, XRegister::a5, 0)); - this->references[2] = Reference(); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + this->references[2] = reference(); this->references[2].name = "printf"; this->references[2].offset = instructions.size() * 4; - this->references[2].target = Target::text; - this->instructions.push_back(Instruction(BaseOpcode::auipc).u(XRegister::ra, 0)); - this->instructions.push_back(Instruction(BaseOpcode::jalr) - .i(XRegister::ra, Funct3::jalr, XRegister::ra, 0)); + this->references[2].target = address_t::text; + this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); + this->instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); // Set the return value (0). - this->instructions.push_back(Instruction(BaseOpcode::op) - .r(XRegister::a0, Funct3::_and, XRegister::zero, XRegister::zero)); + this->instructions.push_back(instruction(base_opcode::op) + .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); // Epilogue. - this->instructions.push_back(Instruction(BaseOpcode::load) - .i(XRegister::s0, Funct3::lw, XRegister::sp, stackSize - 4)); - this->instructions.push_back(Instruction(BaseOpcode::load) - .i(XRegister::ra, Funct3::lw, XRegister::sp, stackSize - 8)); - this->instructions.push_back(Instruction(BaseOpcode::opImm) - .i(XRegister::sp, Funct3::addi, XRegister::sp, stackSize)); - this->instructions.push_back(Instruction(BaseOpcode::jalr) - .i(XRegister::zero, Funct3::jalr, XRegister::ra, 0)); + this->instructions.push_back(instruction(base_opcode::load) + .i(x_register::s0, funct3_t::lw, x_register::sp, stackSize - 4)); + this->instructions.push_back(instruction(base_opcode::load) + .i(x_register::ra, funct3_t::lw, x_register::sp, stackSize - 8)); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::sp, funct3_t::addi, x_register::sp, stackSize)); + this->instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); } - void RiscVVisitor::visit(source::bang_statement *statement) + void visitor::visit(source::bang_statement *statement) { statement->body().accept(this); } - void RiscVVisitor::visit(source::variable_expression *variable) + void visitor::visit(source::variable_expression *variable) { - const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0; + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; this->instructions.push_back( - Instruction(BaseOpcode::opImm) // movl $x, %eax; where $x is a number. - .i(freeRegister, Funct3::addi, XRegister::zero, constants[variable->name()]) + instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. + .i(free_register, funct3_t::addi, x_register::zero, constants[variable->name()]) ); } - void RiscVVisitor::visit(source::integer_literal *number) + void visitor::visit(source::integer_literal *number) { - const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0; + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; this->instructions.push_back( - Instruction(BaseOpcode::opImm) // movl $x, %eax; where $x is a number. - .i(freeRegister, Funct3::addi, XRegister::zero, number->number()) + instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. + .i(free_register, funct3_t::addi, x_register::zero, number->number()) ); } - void RiscVVisitor::visit(source::binary_expression *expression) + void visitor::visit(source::binary_expression *expression) { - const auto lhs_register = this->registerInUse ? XRegister::a0 : XRegister::t0; + const auto lhs_register = this->register_in_use ? x_register::a0 : x_register::t0; - this->registerInUse = true; + this->register_in_use = true; expression->lhs().accept(this); this->instructions.push_back( // movl %eax, -x(%rbp); where x is a number. - Instruction(BaseOpcode::store) - .s(static_cast(this->variableCounter * 4), Funct3::sw, XRegister::sp, XRegister::a0) + instruction(base_opcode::store) + .s(static_cast(this->variable_counter * 4), funct3_t::sw, x_register::sp, x_register::a0) ); - auto lhs_stack_position = ++this->variableCounter; + auto lhs_stack_position = ++this->variable_counter; - this->registerInUse = false; + this->register_in_use = false; expression->rhs().accept(this); - this->instructions.push_back(Instruction(BaseOpcode::load) - .i(XRegister::a0, Funct3::lw, XRegister::sp, + this->instructions.push_back(instruction(base_opcode::load) + .i(x_register::a0, funct3_t::lw, x_register::sp, static_cast((lhs_stack_position - 1) * 4)) ); @@ -169,16 +172,20 @@ namespace elna::backend switch (expression->operation()) { case source::binary_operator::sum: - this->instructions.push_back(Instruction(BaseOpcode::op) - .r(lhs_register, Funct3::add, XRegister::a0, XRegister::t0)); + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::add, x_register::a0, x_register::t0)); break; case source::binary_operator::subtraction: - this->instructions.push_back(Instruction(BaseOpcode::op) - .r(lhs_register, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub)); + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); break; case source::binary_operator::multiplication: - this->instructions.push_back(Instruction(BaseOpcode::op) - .r(lhs_register, Funct3::mul, XRegister::a0, XRegister::t0, Funct7::muldiv)); + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::mul, x_register::a0, x_register::t0, funct7_t::muldiv)); + break; + case source::binary_operator::division: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv)); break; } } diff --git a/backend/target.cpp b/backend/target.cpp index 7506757..666a806 100644 --- a/backend/target.cpp +++ b/backend/target.cpp @@ -2,15 +2,15 @@ #include "elna/backend/riscv.hpp" #include -namespace elna::backend +namespace elna::riscv { void riscv32_elf(source::block *ast, const std::filesystem::path& out_file) { - auto visitor = std::make_unique(); - visitor->visit(ast); + auto _visitor = std::make_unique(); + _visitor->visit(ast); ELFIO::elfio writer; - const ELFIO::Elf_Word instructions_size = visitor->instructions.size() * sizeof(Instruction); + const ELFIO::Elf_Word instructions_size = _visitor->instructions.size() * sizeof(instruction); writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB); @@ -23,7 +23,7 @@ namespace elna::backend text_sec->set_type(ELFIO::SHT_PROGBITS); text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR); text_sec->set_addr_align(0x1); - text_sec->set_data(reinterpret_cast(visitor->instructions.data()), + text_sec->set_data(reinterpret_cast(_visitor->instructions.data()), instructions_size); // Create string table section @@ -71,12 +71,12 @@ namespace elna::backend // Create relocation table writer ELFIO::relocation_section_accessor rela(writer, rel_sec); // Add relocation entry (adjust address at offset 11) - rela.add_entry(visitor->references[0].offset, label_sym, 26 /* ELFIO::R_RISCV_HI20 */); - rela.add_entry(visitor->references[0].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */); - rela.add_entry(visitor->references[1].offset, label_sym, 27 /* ELFIO::R_RISCV_LO12_I */); - rela.add_entry(visitor->references[1].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */); - rela.add_entry(visitor->references[2].offset, printf_sym, 18 /* ELFIO::R_RISCV_CALL */); - rela.add_entry(visitor->references[2].offset, printf_sym, 51 /* ELFIO::R_RISCV_RELAX */); + rela.add_entry(_visitor->references[0].offset, label_sym, 26 /* ELFIO::R_RISCV_HI20 */); + rela.add_entry(_visitor->references[0].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */); + rela.add_entry(_visitor->references[1].offset, label_sym, 27 /* ELFIO::R_RISCV_LO12_I */); + rela.add_entry(_visitor->references[1].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */); + rela.add_entry(_visitor->references[2].offset, printf_sym, 18 /* ELFIO::R_RISCV_CALL */); + rela.add_entry(_visitor->references[2].offset, printf_sym, 51 /* ELFIO::R_RISCV_RELAX */); // Create ELF object file writer.save(out_file); diff --git a/cli/cl.cpp b/cli/cl.cpp index 36cb10e..fe1a124 100644 --- a/cli/cl.cpp +++ b/cli/cl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace elna::cli { @@ -36,7 +37,8 @@ namespace elna::cli { for (const auto& compile_error : lex_result.errors()) { - printf("%lu:%lu: %s\n", compile_error.line(), compile_error.column(), compile_error.what()); + std::cout << compile_error->line() << ':' << compile_error->column() + << ": " << compile_error->what() << std::endl; } return 1; } @@ -45,7 +47,7 @@ namespace elna::cli { return 2; } - backend::riscv32_elf(ast.get(), out_file); + riscv::riscv32_elf(ast.get(), out_file); return 0; } diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp index 7ca80a4..a61eb5e 100644 --- a/include/elna/backend/riscv.hpp +++ b/include/elna/backend/riscv.hpp @@ -4,23 +4,23 @@ #include #include "elna/source/parser.hpp" -namespace elna::backend +namespace elna::riscv { - enum class Target + enum class address_t { text, high20, lower12i }; - struct Reference + struct reference { const char* name; std::size_t offset; - Target target; + address_t target; }; - enum class XRegister : std::uint8_t + enum class x_register : std::uint8_t { zero = 0, ra = 1, @@ -56,7 +56,7 @@ namespace elna::backend t6 = 31, }; - enum class Funct3 : std::uint8_t + enum class funct3_t : std::uint8_t { addi = 0b000, slti = 0b001, @@ -101,30 +101,30 @@ namespace elna::backend lbu = 0b100, lhu = 0b101, jalr = 0b000, - mul = 000, - mulh = 001, - mulhsu = 010, - mulhu = 011, - div = 100, - divu = 101, - rem = 110, - remu = 111 + mul = 0b000, + mulh = 0b001, + mulhsu = 0b010, + mulhu = 0b011, + div = 0b100, + divu = 0b101, + rem = 0b110, + remu = 0b111 }; - enum class Funct12 : std::uint8_t + enum class funct12_t : std::uint8_t { ecall = 0b000000000000, ebreak = 0b000000000001, }; - enum class Funct7 : std::uint8_t + enum class funct7_t : std::uint8_t { none = 0, sub = 0b0100000, muldiv = 0b0000001 }; - enum class BaseOpcode : std::uint8_t + enum class base_opcode : std::uint8_t { opImm = 0b0010011, lui = 0b0110111, @@ -139,29 +139,29 @@ namespace elna::backend system = 0b1110011, }; - struct Instruction + struct instruction { - Instruction(BaseOpcode opcode); + instruction(base_opcode opcode); - Instruction& i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate); - Instruction& s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2); - Instruction& r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7::none); - Instruction& u(XRegister rd, std::uint32_t imm); + instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate); + instruction& s(std::uint32_t imm1, funct3_t funct3, x_register rs1, x_register rs2); + instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7 = funct7_t::none); + instruction& u(x_register rd, std::uint32_t imm); const std::byte *cbegin() const; const std::byte *cend() const; private: - std::uint32_t instruction{ 0 }; + std::uint32_t representation{ 0 }; }; - class RiscVVisitor : public source::ParserVisitor + class visitor : public source::parser_visitor { public: - std::vector instructions; - bool registerInUse{ true }; - std::uint32_t variableCounter = 1; - Reference references[3]; + std::vector instructions; + bool register_in_use{ true }; + std::uint32_t variable_counter = 1; + reference references[3]; std::unordered_map constants; virtual void visit(source::definition *definition) override; diff --git a/include/elna/backend/target.hpp b/include/elna/backend/target.hpp index 45ebb26..e2be438 100644 --- a/include/elna/backend/target.hpp +++ b/include/elna/backend/target.hpp @@ -1,7 +1,7 @@ #include "elna/source/parser.hpp" #include -namespace elna::backend +namespace elna::riscv { void riscv32_elf(source::block *ast, const std::filesystem::path& out_file); } diff --git a/include/elna/source/ir.hpp b/include/elna/source/ir.hpp deleted file mode 100644 index 9acad11..0000000 --- a/include/elna/source/ir.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "elna/source/parser.hpp" - -namespace elna::source -{ - class TransformVisitor final : public source::ParserVisitor - { - void visit(source::definition *definition) override; - void visit(source::bang_statement *statement) override; - void visit(source::block *block) override; - void visit(source::integer_literal *number) override; - void visit(source::variable_expression *variable) override; - void visit(source::binary_expression *binaryExpression) override; - }; -} diff --git a/include/elna/source/lexer.hpp b/include/elna/source/lexer.hpp index 5ad4f03..4395bfe 100644 --- a/include/elna/source/lexer.hpp +++ b/include/elna/source/lexer.hpp @@ -103,6 +103,16 @@ namespace elna::source bool has_identifier() const noexcept; }; + class unexpected_character final : public error + { + std::string character; + + public: + unexpected_character(const std::string& character, const source::position position); + + std::string what() const override; + }; + /** * Split the source into tokens. * diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index 587df1d..e39b015 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -21,7 +21,7 @@ namespace elna::source class variable_expression; class integer_literal; - struct ParserVisitor + struct parser_visitor { virtual void visit(definition *) = 0; virtual void visit(bang_statement *) = 0; @@ -37,7 +37,7 @@ namespace elna::source class node { public: - virtual void accept(ParserVisitor *) = 0; + virtual void accept(parser_visitor *) = 0; }; class statement : public node @@ -58,7 +58,7 @@ namespace elna::source public: definition(std::string&& identifier, std::unique_ptr&& body); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; std::string& identifier() noexcept; integer_literal& body(); @@ -70,7 +70,7 @@ namespace elna::source public: bang_statement(std::unique_ptr&& body); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; expression& body(); }; @@ -85,7 +85,7 @@ namespace elna::source public: block(std::vector>&& definitions, std::unique_ptr&& body); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; statement& body(); std::vector>& definitions() noexcept; @@ -97,7 +97,7 @@ namespace elna::source public: integer_literal(const std::int32_t value); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; std::int32_t number() const noexcept; }; @@ -108,7 +108,7 @@ namespace elna::source public: variable_expression(const std::string& name); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; const std::string& name() const noexcept; }; @@ -123,7 +123,7 @@ namespace elna::source binary_expression(std::unique_ptr&& lhs, std::unique_ptr&& rhs, const unsigned char operation); - virtual void accept(ParserVisitor *visitor) override; + virtual void accept(parser_visitor *visitor) override; expression& lhs(); expression& rhs(); binary_operator operation() const noexcept; diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index b383ed4..a0ea7b9 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -2,7 +2,9 @@ #include #include -#include +#include +#include +#include namespace elna::source { @@ -21,21 +23,19 @@ namespace elna::source /** * A compilation error consists of an error message and position. */ - struct error + class error { - private: - char const *message; - source::position position; + position m_position; - public: + protected: /** - * \param message Error text. * \param position Error position in the source text. */ - error(char const *message, const source::position position) noexcept; + error(const position position); + public: /// Error text. - const char *what() const noexcept; + virtual std::string what() const = 0; /// Error line in the source text. std::size_t line() const noexcept; @@ -47,19 +47,25 @@ namespace elna::source template struct result { - using E = std::forward_list; + using E = std::list>; - template + template>> explicit result(Args&&... arguments) : payload(std::forward(arguments)...) { } - explicit result(const char *message, const source::position position) - : payload(E{ source::error(message, position) }) + explicit result(T&& result) + : payload(std::move(result)) { } + template>> + explicit result(U&& first) + { + errors().emplace_back(std::make_unique(first)); + } + bool has_errors() const noexcept { return std::holds_alternative(payload); diff --git a/include/elna/source/symboltable.hpp b/include/elna/source/symboltable.hpp new file mode 100644 index 0000000..9a54d4b --- /dev/null +++ b/include/elna/source/symboltable.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "elna/source/parser.hpp" +#include +#include + +namespace elna::source +{ + class info + { + public: + virtual ~info() = 0; + + protected: + info(); + }; + + class constant_info final : public info + { + std::int32_t m_value; + + public: + constant_info(const std::int32_t value); + ~constant_info() override; + std::int32_t value() const noexcept; + }; + + class variable_info final : public info + { + public: + ~variable_info() override; + }; + + class symbol_table + { + std::unordered_map> entries; + + public: + symbol_table() = default; + + std::shared_ptr lookup(const std::string& name); + void enter(const std::string& name, std::shared_ptr entry); + }; + + class name_analysis_visitor final : public source::parser_visitor + { + void visit(definition *definition) override; + void visit(bang_statement *statement) override; + void visit(block *block) override; + void visit(integer_literal *number) override; + void visit(variable_expression *variable) override; + void visit(binary_expression *expression) override; + }; +} diff --git a/source/ir.cpp b/source/ir.cpp deleted file mode 100644 index 118eb09..0000000 --- a/source/ir.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "elna/source/ir.hpp" - -#include - -namespace elna::source -{ - void TransformVisitor::visit(source::definition *definition) - { - assert(false); - } - - void TransformVisitor::visit(source::bang_statement *statement) - { - assert(false); - } - - void TransformVisitor::visit(source::block *block) - { - assert(false); - } - - void TransformVisitor::visit(source::integer_literal *number) - { - assert(false); - } - - void TransformVisitor::visit(source::variable_expression *variable) - { - assert(false); - } - - void TransformVisitor::visit(source::binary_expression *binaryExpression) - { - assert(false); - } -} diff --git a/source/lexer.cpp b/source/lexer.cpp index ed5117b..3e26c1f 100644 --- a/source/lexer.cpp +++ b/source/lexer.cpp @@ -1,5 +1,6 @@ #include "elna/source/lexer.hpp" #include +#include namespace elna::source { @@ -194,6 +195,20 @@ namespace elna::source || of() == type::factor_operator; } + unexpected_character::unexpected_character(const std::string& character, const source::position position) + : error(position), character(character) + { + } + + std::string unexpected_character::what() const + { + std::stringstream ss{ "Unexpected character '" }; + + ss << character << "'"; + + return ss.str(); + } + source_result lex(const std::string& buffer) { std::vector tokens; @@ -278,10 +293,10 @@ namespace elna::source } else { - return source_result("Unexpected next character", iterator.position()); + return source_result(unexpected_character{ std::string{ *iterator }, iterator.position() }); } ++iterator; } - return source_result(tokens); + return source_result(std::move(tokens)); } } diff --git a/source/parser.cpp b/source/parser.cpp index 7866b94..eb27736 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -6,7 +6,7 @@ namespace elna::source /** * AST node. */ - void node::accept(ParserVisitor *) + void node::accept(parser_visitor *) { } @@ -15,7 +15,7 @@ namespace elna::source { } - void definition::accept(ParserVisitor *visitor) + void definition::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -35,7 +35,7 @@ namespace elna::source { } - void block::accept(ParserVisitor *visitor) + void block::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -55,7 +55,7 @@ namespace elna::source { } - void integer_literal::accept(ParserVisitor *visitor) + void integer_literal::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -70,7 +70,7 @@ namespace elna::source { } - void variable_expression::accept(ParserVisitor *visitor) + void variable_expression::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -103,7 +103,7 @@ namespace elna::source } } - void binary_expression::accept(ParserVisitor *visitor) + void binary_expression::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -128,7 +128,7 @@ namespace elna::source { } - void bang_statement::accept(ParserVisitor *visitor) + void bang_statement::accept(parser_visitor *visitor) { visitor->visit(this); } @@ -182,16 +182,16 @@ namespace elna::source { return lhs; } - auto _operator = tokens->identifier()[0]; - ++tokens; - - auto rhs = parse_factor(); - if (rhs != nullptr) + while (tokens->of() == source::token::type::factor_operator) { - return std::make_unique(std::move(lhs), + auto _operator = tokens->identifier()[0]; + ++tokens; + + auto rhs = parse_factor(); + lhs = std::make_unique(std::move(lhs), std::move(rhs), _operator); } - return nullptr; + return lhs; } std::unique_ptr parser::parse_expression() @@ -201,17 +201,16 @@ namespace elna::source { return term; } - auto _operator = tokens->identifier()[0]; - ++tokens; - - auto rhs = parse_expression(); - - if (rhs != nullptr) + while (tokens->of() == source::token::type::term_operator) { - return std::make_unique(std::move(term), + auto _operator = tokens->identifier()[0]; + ++tokens; + + auto rhs = parse_term(); + term = std::make_unique(std::move(term), std::move(rhs), _operator); } - return nullptr; + return term; } std::unique_ptr parser::parse_definition() diff --git a/source/result.cpp b/source/result.cpp index 9fec0b6..42f53fe 100644 --- a/source/result.cpp +++ b/source/result.cpp @@ -2,24 +2,18 @@ namespace elna::source { - error::error(const char *message, const source::position position) noexcept + error::error(const position position) + : m_position(position) { - this->message = message; - this->position = position; - } - - char const *error::what() const noexcept - { - return this->message; } std::size_t error::line() const noexcept { - return this->position.line; + return this->m_position.line; } std::size_t error::column() const noexcept { - return this->position.column; + return this->m_position.column; } } diff --git a/source/symboltable.cpp b/source/symboltable.cpp new file mode 100644 index 0000000..81e27ef --- /dev/null +++ b/source/symboltable.cpp @@ -0,0 +1,72 @@ +#include "elna/source/symboltable.hpp" + +namespace elna::source +{ + std::shared_ptr symbol_table::lookup(const std::string& name) + { + auto entry = entries.find(name); + if (entry == entries.cend()) + { + return nullptr; + } + else + { + return entry->second; + } + } + + void symbol_table::enter(const std::string& name, std::shared_ptr entry) + { + entries.insert_or_assign(name, entry); + } + + info::~info() + { + } + + info::info() + { + } + + constant_info::constant_info(const std::int32_t value) + : m_value(value) + { + } + + constant_info::~constant_info() + { + } + + std::int32_t constant_info::value() const noexcept + { + return m_value; + } + + variable_info::~variable_info() + { + } + + void name_analysis_visitor::visit(source::definition *definition) + { + } + + void name_analysis_visitor::visit(source::bang_statement *statement) + { + } + + void name_analysis_visitor::visit(source::block *block) + { + } + + void name_analysis_visitor::visit(source::integer_literal *number) + { + } + + void name_analysis_visitor::visit(source::variable_expression *variable) + { + } + + void name_analysis_visitor::visit(source::binary_expression *expression) + { + } +} diff --git a/tests/7_member_sum.eln b/tests/7_member_sum.eln index a54a60a..287c06f 100644 --- a/tests/7_member_sum.eln +++ b/tests/7_member_sum.eln @@ -1 +1,2 @@ ! 3 + 4 + 5 + 1 + 2 + 4 + 3 +. diff --git a/tests/divide.eln b/tests/divide.eln new file mode 100644 index 0000000..cc7173a --- /dev/null +++ b/tests/divide.eln @@ -0,0 +1 @@ +! 6 / 3 diff --git a/tests/expectations/divide.txt b/tests/expectations/divide.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/tests/expectations/divide.txt @@ -0,0 +1 @@ +2 diff --git a/tests/expectations/multiply_3.txt b/tests/expectations/multiply_3.txt new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/tests/expectations/multiply_3.txt @@ -0,0 +1 @@ +24 diff --git a/tests/expectations/plus_minus.txt b/tests/expectations/plus_minus.txt new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/tests/expectations/plus_minus.txt @@ -0,0 +1 @@ +4 diff --git a/tests/multiply.eln b/tests/multiply.eln index b6aef9d..1161c66 100644 --- a/tests/multiply.eln +++ b/tests/multiply.eln @@ -1 +1,2 @@ ! 5 * 2 +. diff --git a/tests/multiply_3.eln b/tests/multiply_3.eln new file mode 100644 index 0000000..e488b8f --- /dev/null +++ b/tests/multiply_3.eln @@ -0,0 +1,2 @@ +! 3 * 4 * 2 +. diff --git a/tests/plus_minus.eln b/tests/plus_minus.eln new file mode 100644 index 0000000..06bd367 --- /dev/null +++ b/tests/plus_minus.eln @@ -0,0 +1,2 @@ +! 2 + 3 - 1 +.