From 632dc53b536857032885fe3afdab34ac3fbdef2f Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 3 Mar 2024 13:11:39 +0100 Subject: [PATCH] Implement multiplication --- CMakeLists.txt | 6 +- Rakefile | 19 - TODO | 30 +- dub.json | 11 - include/elna/cl.hpp | 4 +- include/elna/lexer.hpp | 54 +- include/elna/parser.hpp | 20 +- include/elna/result.hpp | 113 ++-- include/elna/riscv.hpp | 15 +- source/cl.cpp | 35 +- source/elna/backend.d | 93 --- source/elna/elf.d | 1060 ------------------------------- source/elna/extended.d | 335 ---------- source/elna/lexer.d | 55 -- source/elna/parser.d | 110 ---- source/elna/result.d | 101 --- source/lexer.cpp | 81 +-- source/main.cpp | 47 ++ source/main.d | 15 - source/parser.cpp | 84 ++- source/result.cpp | 11 +- source/riscv.cpp | 22 +- tests/expectations/multiply.txt | 1 + tests/multiply.eln | 1 + tests/runner.cpp | 9 +- 25 files changed, 343 insertions(+), 1989 deletions(-) delete mode 100644 Rakefile delete mode 100644 dub.json delete mode 100644 source/elna/backend.d delete mode 100644 source/elna/elf.d delete mode 100644 source/elna/extended.d delete mode 100644 source/elna/lexer.d delete mode 100644 source/elna/parser.d delete mode 100644 source/elna/result.d create mode 100644 source/main.cpp delete mode 100644 source/main.d create mode 100644 tests/expectations/multiply.txt create mode 100644 tests/multiply.eln diff --git a/CMakeLists.txt b/CMakeLists.txt index d732f0d..961adc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_CXX_STANDARD 17) +find_package(Boost COMPONENTS program_options REQUIRED) +include_directories(${Boost_INCLUDE_DIR}) + add_executable(tester tests/runner.cpp include/elna/tester.hpp) target_include_directories(tester PRIVATE include) @@ -15,7 +18,7 @@ add_executable(elnsh shell/main.cpp ) target_include_directories(elnsh PRIVATE include) -add_library(elna +add_executable(elna source/main.cpp source/lexer.cpp include/elna/lexer.hpp source/result.cpp include/elna/result.hpp source/riscv.cpp include/elna/riscv.hpp @@ -24,3 +27,4 @@ add_library(elna source/cl.cpp include/elna/cl.hpp ) target_include_directories(elna PRIVATE include) +target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES}) diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 773d560..0000000 --- a/Rakefile +++ /dev/null @@ -1,19 +0,0 @@ -require 'pathname' -require 'rake/clean' -require 'open3' - -DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12'] -BINARY = 'build/bin/elna' -SOURCES = FileList['source/**/*.d'] - -directory 'build/riscv' - -CLEAN.include 'build' -CLEAN.include '.dub' - -file BINARY => SOURCES do |t| - sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc') -end - -task default: 'build/riscv' -task default: BINARY diff --git a/TODO b/TODO index 6a08d34..fb7f24e 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,23 @@ -# Completion +# 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. +- Replace pointer and length with vectors and strings. +- Choose one name for runner.cpp and tester.hpp. +- Move argument handling into the cl module. +- Catch exceptions thrown by the argument parser and print them normally. +- cmake clean target. +- Division. +- Expressions contain one or more terms. Currently it parses another expression on the right side. +- Multiple factors. + +# Shell +- Persist the history. +- Replace hard coded ANSI codes with constants or functions. + +## Completion - Configure fzf to show only the current directory files - Support multiple selections for insertion in fzf. @@ -7,17 +26,12 @@ - Home directory expansion. - Show files in the PATH if starting at the beginning of the prompt -# Appearance +## Appearance - Add a bar with additional information under the prompt (edit_bar), like the hostname. - Show current time in the prompt. -# Input +## Input - DEL handling. - Starting long running process and killing it with Ctrl-C kills the shell. - -# Other - -- Persist the history. -- Replace hard coded ANSI codes with constants or functions. diff --git a/dub.json b/dub.json deleted file mode 100644 index 2e76980..0000000 --- a/dub.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "dependencies": { - "tanya": "~>0.19.0" - }, - "name": "elna", - "targetType": "executable", - "targetPath": "build/bin", - "mainSourceFile": "source/main.d", - "libs": ["elna", "stdc++"], - "lflags": ["-Lbuild"] -} diff --git a/include/elna/cl.hpp b/include/elna/cl.hpp index 36160b4..d431d94 100644 --- a/include/elna/cl.hpp +++ b/include/elna/cl.hpp @@ -1,7 +1,9 @@ #pragma once +#include + namespace elna { char *readSource(const char *source); - int compile(const char *inFile, const char *outputFilename); + int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file); } diff --git a/include/elna/lexer.hpp b/include/elna/lexer.hpp index 09ab8a6..a6383c9 100644 --- a/include/elna/lexer.hpp +++ b/include/elna/lexer.hpp @@ -1,10 +1,13 @@ #pragma once +#include #include #include "elna/result.hpp" namespace elna +{ +namespace lex { /** * Range over the source text that keeps track of the current position. @@ -14,10 +17,10 @@ namespace elna class const_iterator { std::string::const_iterator m_buffer; - Position m_position; + elna::source::position m_position; const_iterator(std::string::const_iterator buffer, - const Position position = Position()); + const elna::source::position start_position = elna::source::position()); public: using iterator_category = std::forward_iterator_tag; @@ -26,7 +29,7 @@ namespace elna using pointer = const value_type *; using reference = const value_type&; - const Position& position() const noexcept; + const elna::source::position& position() const noexcept; reference operator*() const noexcept; pointer operator->() const noexcept; @@ -54,20 +57,21 @@ namespace elna /** * Token type. */ - enum Type : std::uint16_t + enum class type : std::uint16_t { - TOKEN_NUMBER = 0, - TOKEN_OPERATOR = 1, - TOKEN_LET = 2, - TOKEN_IDENTIFIER = 3, - TOKEN_EQUALS = 4, - TOKEN_VAR = 5, - TOKEN_SEMICOLON = 6, - TOKEN_LEFT_PAREN = 7, - TOKEN_RIGHT_PAREN = 8, - TOKEN_BANG = 9, - TOKEN_DOT = 10, - TOKEN_COMMA = 11, + number = 0, + term_operator = 1, + let = 2, + identifier = 3, + equals = 4, + var = 5, + semicolon = 6, + left_paren = 7, + right_paren = 8, + bang = 9, + dot = 10, + comma = 11, + factor_operator = 12, }; /** @@ -79,9 +83,9 @@ namespace elna const char *identifier; }; - Token(Type of, Position position); - Token(Type of, std::int32_t value, Position position); - Token(Type of, const char *value, Position position); + Token(type of, elna::source::position position); + Token(type of, std::int32_t value, elna::source::position position); + Token(type of, const char *value, elna::source::position position); Token(const Token& that); Token(Token&& that); ~Token(); @@ -89,21 +93,23 @@ namespace elna Token& operator=(const Token& that); Token& operator=(Token&& that); - Type of() const noexcept; + type of() const noexcept; const char *identifier() const noexcept; std::int32_t number() const noexcept; - const Position& position() const noexcept; + const elna::source::position& position() const noexcept; private: - Type m_type; + type m_type; Value m_value; - Position m_position; + elna::source::position m_position; }; /** * Split the source into tokens. * + * \param buffer Source text. * \return Tokens or error. */ - Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length); + elna::source::result> lex(const char *buffer); +} } diff --git a/include/elna/parser.hpp b/include/elna/parser.hpp index 152aec8..0ee369e 100644 --- a/include/elna/parser.hpp +++ b/include/elna/parser.hpp @@ -9,7 +9,9 @@ namespace elna enum class BinaryOperator { sum, - subtraction + subtraction, + multiplication, + division }; class Node; @@ -112,12 +114,12 @@ namespace elna virtual void accept(ParserVisitor *visitor) override; }; - Expression *parseFactor(Token **tokens, std::size_t *length); - Expression *parseTerm(Token **tokens, std::size_t *length); - Expression *parseExpression(Token **tokens, std::size_t *length); - Definition *parseDefinition(Token **tokens, std::size_t *length); - Statement *parseStatement(Token **tokens, std::size_t *length); - Definition **parseDefinitions(Token **tokens, std::size_t *length, std::size_t *resultLength); - Block *parseBlock(Token **tokens, std::size_t *length); - Block *parse(Token *tokenStream, std::size_t length); + Expression *parseFactor(lex::Token **tokens, std::size_t *length); + Expression *parseTerm(lex::Token **tokens, std::size_t *length); + Expression *parseExpression(lex::Token **tokens, std::size_t *length); + Definition *parseDefinition(lex::Token **tokens, std::size_t *length); + Statement *parseStatement(lex::Token **tokens, std::size_t *length); + Definition **parseDefinitions(lex::Token **tokens, std::size_t *length, std::size_t *resultLength); + Block *parseBlock(lex::Token **tokens, std::size_t *length); + Block *parse(lex::Token *tokenStream, std::size_t length); } diff --git a/include/elna/result.hpp b/include/elna/result.hpp index 5740b62..7e81129 100644 --- a/include/elna/result.hpp +++ b/include/elna/result.hpp @@ -1,50 +1,92 @@ #pragma once #include -#include +#include +#include +#include namespace elna { - /** - * Position in the source text. - */ - struct Position + namespace source { - /// Line. - std::size_t line = 1; - - /// Column. - std::size_t column = 1; - }; - - /** - * A compilation error consists of an error message and position. - */ - struct CompileError - { - private: - char const *message; - Position position; - - public: /** - * @param message Error text. - * @param position Error position in the source text. + * Position in the source text. */ - CompileError(char const *message, const Position position) noexcept; + struct position + { + /// Line. + std::size_t line = 1; - /// Error text. - const char *what() const noexcept; + /// Column. + std::size_t column = 1; + }; - /// Error line in the source text. - std::size_t line() const noexcept; + /** + * A compilation error consists of an error message and position. + */ + struct error + { + private: + char const *message; + source::position position; - /// Error column in the source text. - std::size_t column() const noexcept; - }; + public: + /** + * \param message Error text. + * \param position Error position in the source text. + */ + error(char const *message, const source::position position) noexcept; - template - using result = boost::outcome_v2::result; + /// Error text. + const char *what() const noexcept; + + /// Error line in the source text. + std::size_t line() const noexcept; + + /// Error column in the source text. + std::size_t column() const noexcept; + }; + + template + struct result + { + using E = std::forward_list; + + template + explicit result(Args&&... arguments) + : payload(std::forward(arguments)...) + { + } + + explicit result(const char *message, const source::position position) + : payload(E{ source::error(message, position) }) + { + } + + bool has_errors() const noexcept + { + return std::holds_alternative(payload); + } + + bool is_success() const noexcept + { + return std::holds_alternative(payload); + } + + T& success() + { + return std::get(payload); + } + + E& errors() + { + return std::get(payload); + } + + private: + std::variant payload; + }; + } enum class Target { @@ -64,8 +106,7 @@ namespace elna { Symbol(const char *name); const char *name; - unsigned char *text; - std::size_t length; + std::vector text; Reference symbols[3]; }; } diff --git a/include/elna/riscv.hpp b/include/elna/riscv.hpp index 3dba193..a55723d 100644 --- a/include/elna/riscv.hpp +++ b/include/elna/riscv.hpp @@ -87,6 +87,14 @@ namespace elna lbu = 0b100, lhu = 0b101, jalr = 0b000, + mul = 000, + mulh = 001, + mulhsu = 010, + mulhu = 011, + div = 100, + divu = 101, + rem = 110, + remu = 111 }; enum class Funct12 : std::uint8_t @@ -98,7 +106,8 @@ namespace elna enum class Funct7 : std::uint8_t { none = 0, - sub = 0b0100000 + sub = 0b0100000, + muldiv = 0b0000001 }; enum class BaseOpcode : std::uint8_t @@ -124,7 +133,9 @@ namespace elna 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); - std::uint8_t *encode(); + + const std::byte *cbegin(); + const std::byte *cend(); private: std::uint32_t instruction{ 0 }; diff --git a/source/cl.cpp b/source/cl.cpp index fa27377..b3090a8 100644 --- a/source/cl.cpp +++ b/source/cl.cpp @@ -24,7 +24,7 @@ namespace elna return result; } - int compile(const char *inFile, const char *outputFilename) + int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file) { ELFIO::elfio writer; @@ -35,21 +35,23 @@ namespace elna writer.set_type(ELFIO::ET_REL); writer.set_machine(ELFIO::EM_RISCV); - auto sourceText = readSource(inFile); + auto sourceText = readSource(in_file.c_str()); if (sourceText == nullptr) { return 3; } - CompileError *compileError = nullptr; size_t tokensCount{ 0 }; - auto tokens = lex(sourceText, compileError, &tokensCount); + auto lex_result = lex::lex(sourceText); free(sourceText); - if (tokens == nullptr) + if (lex_result.has_errors()) { - printf("%lu:%lu: %s\n", compileError->line(), compileError->column(), compileError->what()); + for (const auto& compile_error : lex_result.errors()) + { + printf("%lu:%lu: %s\n", compile_error.line(), compile_error.column(), compile_error.what()); + } return 1; } - auto ast = parse(tokens, tokensCount); + auto ast = parse(lex_result.success().data(), tokensCount); if (ast == nullptr) { return 2; @@ -61,7 +63,7 @@ namespace elna 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(program.text), program.length); + text_sec->set_data(reinterpret_cast(program.text.data()), program.text.size()); // Create string table section ELFIO::section* str_sec = writer.sections.add(".strtab"); @@ -91,7 +93,7 @@ namespace elna ELFIO::symbol_section_accessor syma(writer, sym_sec); auto label_sym = syma.add_symbol(stra, ".CL0", 0x00000000, strlen("%d\n") + 1, ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ro_sec->get_index()); - syma.add_symbol(stra, program.name, 0x00000000, program.length, + syma.add_symbol(stra, program.name, 0x00000000, program.text.size(), ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text_sec->get_index()); auto printf_sym = syma.add_symbol(stra, "printf", 0x00000000, 0, ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF); @@ -115,21 +117,8 @@ namespace elna rela.add_entry(program.symbols[2].offset, printf_sym, 18 /* ELFIO::R_RISCV_CALL */); rela.add_entry(program.symbols[2].offset, printf_sym, 51 /* ELFIO::R_RISCV_RELAX */); - // Another method to add the same relocation entry at one step is: - // rela.add_entry( stra, "msg", - // syma, 29, 0, - // ELF_ST_INFO( STB_GLOBAL, STT_OBJECT ), 0, - // text_sec->get_index(), - // place_to_adjust, (unsigned char)R_386_RELATIVE ); - - /* We don't use local symbols here. There is no need to rearrange them. - // But, for the completeness, we do this just prior 'save' - syma.arrange_local_symbols([&](ELFIO::Elf_Xword first, ELFIO::Elf_Xword second) { - rela.swap_symbols( first, second ); - }); */ - // Create ELF object file - writer.save(outputFilename); + writer.save(out_file); return 0; } diff --git a/source/elna/backend.d b/source/elna/backend.d deleted file mode 100644 index 50a7cf1..0000000 --- a/source/elna/backend.d +++ /dev/null @@ -1,93 +0,0 @@ -module elna.backend; - -import core.stdc.stdio; -import core.stdc.stdlib; -import core.stdc.string; -import elna.elf; -import elna.extended; -import elna.lexer; -import elna.parser; -import elna.result; -import std.algorithm; -import std.sumtype; -import std.typecons; -import tanya.os.error; -import tanya.container.array; -import tanya.container.string; -import tanya.memory.allocator; - -extern(C++, "elna") -Symbol writeNext(Block ast) @nogc; - -extern(C++, "elna") -char* readSource(const(char)* source) @nogc; - -extern(C++, "elna") -int compile(const(char)* inFile, const(char)* outputFilename) @nogc; - -int generate(const(char)* inFile, const(char)* outputFilename) @nogc -{ - auto sourceText = readSource(inFile); - if (sourceText is null) - { - return 3; - } - CompileError compileError = void; - size_t tokensCount; - auto tokens = lex(sourceText, &compileError, &tokensCount); - free(sourceText); - if (tokens is null) - { - printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what); - return 1; - } - auto ast = parse(tokens, tokensCount); - if (ast is null) - { - return 2; - } - auto handle = File.open(outputFilename, BitFlags!(File.Mode)(File.Mode.truncate)); - - if (!handle.valid) - { - return 1; - } - auto program = writeNext(ast); - auto elf = Elf!ELFCLASS32(move(handle)); - auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d\n".ptr[0 .. 4]); // With \0. - auto text = Array!ubyte(program.text[0 .. program.length]); - - elf.addReadOnlyData(String(".CL0"), readOnlyData); - elf.addCode(String(program.name[0 .. strlen(program.name)]), text); - - elf.addExternSymbol(String("printf")); - foreach (ref reference; program.symbols) - { - elf.Rela relocationEntry = { - r_offset: cast(elf.Addr) reference.offset - }; - elf.Rela relocationSub = { - r_offset: cast(elf.Addr) reference.offset, - r_info: R_RISCV_RELAX - }; - - final switch (reference.target) - { - case Target.text: - relocationEntry.r_info = R_RISCV_CALL; - break; - case Target.high20: - relocationEntry.r_info = R_RISCV_HI20; - break; - case Target.lower12i: - relocationEntry.r_info = R_RISCV_LO12_I; - break; - } - - elf.relocate(String(reference.name[0 .. strlen(reference.name)]), relocationEntry, relocationSub); - } - - elf.finish(); - - return 0; -} diff --git a/source/elna/elf.d b/source/elna/elf.d deleted file mode 100644 index 81dd7da..0000000 --- a/source/elna/elf.d +++ /dev/null @@ -1,1060 +0,0 @@ -module elna.elf; - -import elna.extended; -import elna.result; -import std.algorithm; -import tanya.container.array; -import tanya.container.hashtable; -import tanya.container.string; - -/// Unsigned program address. -alias Elf64_Addr = ulong; -/// Unsigned file offset. -alias Elf64_Off = ulong; -/// Unsigned medium integer. -alias Elf64_Half = ushort; -/// Unsigned integer. -alias Elf64_Word = uint; -/// Signed integer. -alias Elf64_Sword = int; -/// Unsigned long integer. -alias Elf64_Xword = ulong; -/// Signed long integer. -alias Elf64_Sxword = long; - -/// Unsigned program address. -alias Elf32_Addr = uint; -/// Unsigned file offset. -alias Elf32_Off = uint; -/// Unsigned medium integer. -alias Elf32_Half = ushort; -/// Unsigned integer. -alias Elf32_Word = uint; -/// Signed integer. -alias Elf32_Sword = int; - -enum : size_t -{ - /// File identification. - EI_MAG0 = 0, - /// File identification. - EI_MAG1 = 1, - /// File identification. - EI_MAG2 = 2, - /// File identification. - EI_MAG3 = 3, - /// File class. - EI_CLASS = 4, - /// Data encoding. - EI_DATA = 5, - /// File version. - EI_VERSION = 6, - /// Start of padding bytes. - EI_PAD = 7, - /// Size of e_ident[] - EI_NIDENT = 16 -} - -enum : ubyte -{ - /// e_ident[EI_MAG0]. - ELFMAG0 = 0x7f, - /// e_ident[EI_MAG1]. - ELFMAG1 = 'E', - /// e_ident[EI_MAG2]. - ELFMAG2 = 'L', - /// e_ident[EI_MAG3]. - ELFMAG3 = 'F' -} - -/** - * File header. - */ -struct Elf64_Ehdr -{ - /// ELF identification. - ubyte[EI_NIDENT] e_ident; - /// Object file type. - Elf64_Half e_type; - /// Machine type. - Elf64_Half e_machine; - /// Object file version - Elf64_Word e_version; - /// Entry point address. - Elf64_Addr e_entry; - /// Program header offset. - Elf64_Off e_phoff; - /// Section header offset. - Elf64_Off e_shoff; - /// Processor-specific flags. - Elf64_Word e_flags; - /// ELF header size. - Elf64_Half e_ehsize; - /// Size of program header entry. - Elf64_Half e_phentsize; - /// Number of program header entries. - Elf64_Half e_phnum; - /// Size of section header entry. - Elf64_Half e_shentsize; - /// Number of section header entries. - Elf64_Half e_shnum; - /// Section name string table index. - Elf64_Half e_shstrndx; -} - -/** - * File header. - */ -struct Elf32_Ehdr { - /// ELF identification. - ubyte[EI_NIDENT] e_ident; - /// Object file type. - Elf32_Half e_type; - /// Machine type. - Elf32_Half e_machine; - /// Object file version - Elf32_Word e_version; - /// Entry point address. - Elf32_Addr e_entry; - /// Program header offset. - Elf32_Off e_phoff; - /// Section header offset. - Elf32_Off e_shoff; - /// Processor-specific flags. - Elf32_Word e_flags; - /// ELF header size. - Elf32_Half e_ehsize; - /// Size of program header entry. - Elf32_Half e_phentsize; - /// Number of program header entries. - Elf32_Half e_phnum; - /// Size of section header entry. - Elf32_Half e_shentsize; - /// Number of section header entries. - Elf32_Half e_shnum; - /// Section name string table index. - Elf32_Half e_shstrndx; -} - -/** - * Section header. - */ -struct Elf64_Shdr -{ - /// Section name. - Elf64_Word sh_name; - /// Section type. - Elf64_Word sh_type; - /// Section attributes. - Elf64_Xword sh_flags; - /// Virtual address in memory. - Elf64_Addr sh_addr; - /// Offset in file. - Elf64_Off sh_offset; - /// Size of section. - Elf64_Xword sh_size; - /// Link to other section. - Elf64_Word sh_link; - /// Miscellaneous information. - Elf64_Word sh_info; - /// Address alignment boundary. - Elf64_Xword sh_addralign; - /// Size of entries, if section has table. - Elf64_Xword sh_entsize; -} - -/** - * Section header. - */ -struct Elf32_Shdr -{ - /// Section name. - Elf32_Word sh_name; - /// Section type. - Elf32_Word sh_type; - /// Section attributes. - Elf32_Word sh_flags; - /// Virtual address in memory. - Elf32_Addr sh_addr; - /// Offset in file. - Elf32_Off sh_offset; - /// Size of section. - Elf32_Word sh_size; - /// Link to other section. - Elf32_Word sh_link; - /// Miscellaneous information. - Elf32_Word sh_info; - /// Address alignment boundary. - Elf32_Word sh_addralign; - /// Size of entries, if section has table. - Elf32_Word sh_entsize; -} - -/** - * Symbol table entry. - */ -struct Elf64_Sym -{ - /// Symbol name. - Elf64_Word st_name; - /// Type and Binding attributes. - ubyte st_info; - /// Reserved. - ubyte st_other; - /// Section table index. - Elf64_Half st_shndx; - /// Symbol value. - Elf64_Addr st_value; - /// Size of object (e.g., common). - Elf64_Xword st_size; -} - -/** - * Relocation entry. - */ -struct Elf64_Rel -{ - /// Address of reference. - Elf64_Addr r_offset; - /// Symbol index and type of relocation. - Elf64_Xword r_info; -} - -/** - * Relocation entry with explicit addend. - */ -struct Elf64_Rela -{ - /// Address of reference. - Elf64_Addr r_offset; - /// Symbol index and type of relocation. - Elf64_Xword r_info; - /// Constant part of expression. - Elf64_Sxword r_addend; -} - -/** - * Symbol table entry. - */ -struct Elf32_Sym -{ - /// Symbol name. - Elf32_Word st_name; - /// Symbol value. - Elf32_Addr st_value; - /// Size of object (e.g., common). - Elf32_Word st_size; - /// Type and Binding attributes. - ubyte st_info; - /// Reserved. - ubyte st_other; - /// Section table index. - Elf32_Half st_shndx; -} - -/** - * Relocation entry. - */ -struct Elf32_Rel -{ - /// Address of reference. - Elf32_Addr r_offset; - /// Symbol index and type of relocation. - Elf32_Word r_info; -} - -/** - * Relocation entry with explicit addend. - */ -struct Elf32_Rela -{ - /// Address of reference. - Elf32_Addr r_offset; - /// Symbol index and type of relocation. - Elf32_Word r_info; - /// Constant part of expression. - Elf32_Sword r_addend; -} - -/// Section Types, sh_type. -enum : Elf64_Word -{ - /// Marks an unused section header. - SHT_NULL = 0, - /// Contains information defined by the program. - SHT_PROGBITS = 1, - /// Contains a linker symbol table. - SHT_SYMTAB = 2, - /// Contains a string table. - SHT_STRTAB = 3, - /// Contains “Rela” type relocation entries. - SHT_RELA = 4, - /// Contains a symbol hash table - SHT_HASH = 5, - /// Contains dynamic linking tables - SHT_DYNAMIC = 6, - /// Contains note information - SHT_NOTE = 7, - /// Contains uninitialized space; does not occupy any space in the file. - SHT_NOBITS = 8, - /// Contains "Rel" type relocation entries. - SHT_REL = 9, - /// Reserved. - SHT_SHLIB = 10, - /// Contains a dynamic loader symbol table. - SHT_DYNSYM = 11, - /// Environment-specific use. - SHT_LOOS = 0x60000000, - SHT_HIOS = 0x6FFFFFFF, - /// Processor-specific use. - SHT_LOPROC = 0x70000000, - SHT_HIPROC = 0x7FFFFFFF, -} - -/** - * Section Attributes, sh_flags. - */ -enum : Elf64_Xword -{ - /// Section contains writable data. - SHF_WRITE = 0x1, - /// Section is allocated in memory image of program. - SHF_ALLOC = 0x2, - /// Section contains executable instructions. - SHF_EXECINSTR = 0x4, - /// Environment-specific use. - SHF_MASKOS = 0x0F000000, - /// Processor-specific use. - SHF_MASKPROC = 0xF0000000, -} - -ubyte ELF64_R_SYM(Elf64_Xword i) @nogc nothrow pure @safe -{ - return cast(ubyte) (i >> 32); -} - -Elf64_Xword ELF64_R_TYPE(Elf64_Xword i) @nogc nothrow pure @safe -{ - return i & 0xffffffffL; -} - -Elf64_Xword ELF64_R_INFO(Elf64_Xword s, Elf64_Xword t) @nogc nothrow pure @safe -{ - return (s << 32) + (t & 0xffffffffL); -} - -ubyte ELF32_ST_BIND(ubyte i) @nogc nothrow pure @safe -{ - return i >> 4; -} - -ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe -{ - return i & 0xf; -} - -ubyte ELF32_ST_INFO(Elf32_Word b, ubyte t) @nogc nothrow pure @safe -{ - return cast(ubyte) ((b << 4) + (t & 0xf)); -} - -Elf32_Word ELF32_R_SYM(Elf32_Word i) @nogc nothrow pure @safe -{ - return i >> 8; -} - -ubyte ELF32_R_TYPE(Elf32_Word i) @nogc nothrow pure @safe -{ - return cast(ubyte) i; -} - -Elf32_Word ELF32_R_INFO(Elf32_Word s, Elf32_Word t) @nogc nothrow pure @safe -{ - return (s << 8) + t; -} - -enum : uint -{ - /// Not visible outside the object file. - STB_LOCAL = 0, - /// Global symbol, visible to all object files. - STB_GLOBAL = 1, - /// Global scope, but with lower precedence than global symbols. - STB_WEAK = 2, - /// Environment-specific use. - STB_LOOS = 10, - STB_HIOS = 12, - /// Processor-specific use. - STB_LOPROC = 13, - STB_HIPROC = 15, -} - -enum : uint -{ - /// No type specified (e.g., an absolute symbol). - STT_NOTYPE = 0, - /// Data object. - STT_OBJECT = 1, - /// Function entry point. - STT_FUNC = 2, - /// Symbol is associated with a section. - STT_SECTION = 3, - /// Source file associated with the object file. - STT_FILE = 4, - /// Environment-specific use. - STT_LOOS = 10, - STT_HIOS = 12, - /// Processor-specific use. - STT_LOPROC = 13, - STT_HIPROC = 15, -} - -/// Special Section Indices. -enum : ushort -{ - /// Used to mark an undefined or meaningless section reference. - SHN_UNDEF = 0, - /// This value specifies the lower bound of the range of reserved indexes. - SHN_LORESERVE = 0xff00, - /// Processor-specific use. - SHN_LOPROC = 0xFF00, - SHN_HIPROC = 0xFF1F, - /// Environment-specific use. - SHN_LOOS = 0xFF20, - SHN_HIOS = 0xFF3F, - /// Indicates that the corresponding reference is an absolute value. - SHN_ABS = 0xFFF1, - /** - * Indicates a symbol that has been declared as a common block (Fortran - * COMMON or C tentative declaration). - */ - SHN_COMMON = 0xFFF2, -} - -/** - * Object File Classes, e_ident[EI_CLASS]. - */ -enum : ubyte -{ - /// Invalid class. - ELFCLASSNONE = 0, - /// 32-bit objects. - ELFCLASS32 = 1, - /// 64-bit objects. - ELFCLASS64 = 2 -} - -enum : ubyte { - /// Invalid version. - EV_NONE = 0, - /// Current version. - EV_CURRENT = 1 -} - -/** - * Data Encodings, e_ident[EI_DATA]. - */ -enum : ubyte -{ - /// Object file data structures are little-endian. - ELFDATA2LSB = 1, - /// Object file data structures are big-endian. - ELFDATA2MSB = 2, -} - -/** - * Operating System and ABI Identifiers, e_ident[EI_OSABI]. - */ -enum EI_OSABI : ubyte -{ - /// System V ABI. - ELFOSABI_SYSV = 0, - /// HP-UX operating system. - ELFOSABI_HPUX = 1, - /// Standalone (embedded) application. - ELFOSABI_STANDALONE = 255, -} - -enum : Elf64_Half -{ - ET_NONE = 0, /// No file type. - ET_REL = 1, /// Relocatable object file. - ET_EXEC = 2, /// Executable file. - ET_DYN = 3, /// Shared object file. - ET_CORE = 4, /// Core file. - ET_LOOS = 0xFE00, /// Environment-specific use. - ET_HIOS = 0xFEFF, - ET_LOPROC = 0xFF00, /// Processor-specific use. - ET_HIPROC = 0xFFFF, -} - -enum : ubyte -{ - R_RISCV_NONE = 0, - /// 32-bit relocation. - R_RISCV_32 = 1, - /// 64-bit relocation. - R_RISCV_64 = 2, - /// Relocation against a local symbol in a shared object. - R_RISCV_RELATIVE = 3, - /// Must be in executable; not allowed in shared library. - R_RISCV_COPY = 4, - /// Indicates the symbol associated with a PLT entry. - R_RISCV_JUMP_SLOT = 5, - R_RISCV_TLS_DTPMOD32 = 6, - R_RISCV_TLS_DTPMOD64 = 7, - R_RISCV_TLS_DTPREL32 = 8, - R_RISCV_TLS_DTPREL64 = 9, - R_RISCV_TLS_TPREL32 = 10, - R_RISCV_TLS_TPREL64 = 11, - /// 12-bit PC-relative branch offset. - R_RISCV_BRANCH = 16, - /// 20-bit PC-relative jump offset. - R_RISCV_JAL = 17, - /// 32-bit PC-relative function call, macros `call`, `tail`. - R_RISCV_CALL = 18, - /// 32-bit PC-relative function call, macros `call`, `tail` (PIC). - R_RISCV_CALL_PLT = 19, - /// High 20 bits of 32-bit PC-relative GOT access, `%got_pcrel_hi(symbol)`. - R_RISCV_GOT_HI20 = 20, - /// High 20 bits of 32-bit PC-relative TLS IE GOT access, macro `la.tls.ie`. - R_RISCV_TLS_GOT_HI20 = 21, - /// High 20 bits of 32-bit PC-relative TLS GD GOT reference, macro `la.tls.gd`. - R_RISCV_TLS_GD_HI20 = 22, - /// High 20 bits of 32-bit PC-relative reference, `%pcrel_hi(symbol)`. - R_RISCV_PCREL_HI20 = 23, - /// Low 12 bits of a 32-bit PC-relative, `%pcrel_lo(address of %pcrel_hi)`, the addend must be 0. - R_RISCV_PCREL_LO12_I = 24, - /// Low 12 bits of a 32-bit PC-relative, `%pcrel_lo(address of %pcrel_hi)`, the addend must be 0. - R_RISCV_PCREL_LO12_S = 25, - /// High 20 bits of 32-bit absolute address, `%hi(symbol)`. - R_RISCV_HI20 = 26, - /// Low 12 bits of 32-bit absolute address, `%lo(symbol)`. - R_RISCV_LO12_I = 27, - /// Low 12 bits of 32-bit absolute address, `%lo(symbol)`. - R_RISCV_LO12_S = 28, - /// High 20 bits of TLS LE thread pointer offset, `%tprel_hi(symbol)`. - R_RISCV_TPREL_HI20 = 29, - /// Low 12 bits of TLS LE thread pointer offset, `%tprel_lo(symbol)`. - R_RISCV_TPREL_LO12_I = 30, - /// Low 12 bits of TLS LE thread pointer offset, `%tprel_lo(symbol)`. - R_RISCV_TPREL_LO12_S = 31, - /// TLS LE thread pointer usage, `%tprel_add(symbol)`. - R_RISCV_TPREL_ADD = 32, - /// 8-bit label addition. - R_RISCV_ADD8 = 33, - /// 16-bit label addition. - R_RISCV_ADD16 = 34, - /// 32-bit label addition. - R_RISCV_ADD32 = 35, - /// 64-bit label addition. - R_RISCV_ADD64 = 36, - /// 8-bit label subtraction. - R_RISCV_SUB8 = 37, - /// 16-bit label subtraction. - R_RISCV_SUB16 = 38, - /// 32-bit label subtraction. - R_RISCV_SUB32 = 39, - /// 64-bit label subtraction. - R_RISCV_SUB64 = 40, - /// GNU {Cpp} vtable hierarchy. - R_RISCV_GNU_VTINHERIT = 41, - /// GNU {Cpp} vtable member usage. - R_RISCV_GNU_VTENTRY = 42, - /// Alignment statement. - R_RISCV_ALIGN = 43, - /// 8-bit PC-relative branch offset. - R_RISCV_RVC_BRANCH = 44, - /// 11-bit PC-relative jump offset. - R_RISCV_RVC_JUMP = 45, - /// High 6 bits of 18-bit absolute address. - R_RISCV_RVC_LUI = 46, - /// Instruction can be relaxed, paired with a normal relocation at the same address. - R_RISCV_RELAX = 51, - /// Local label subtraction. - R_RISCV_SUB6 = 52, - /// Local label assignment. - R_RISCV_SET6 = 53, - /// Local label assignment. - R_RISCV_SET8 = 54, - /// Local label assignment. - R_RISCV_SET16 = 55, - /// Local label assignment. - R_RISCV_SET32 = 56, - /// 32-bit PC relative. - R_RISCV_32_PCREL = 57, - /// Relocation against a local ifunc symbol in a shared object. - R_RISCV_IRELATIVE = 58 -} - -auto pad(ubyte elfClass)(size_t value) @nogc -{ - static if (elfClass == ELFCLASS32) - { - return cast(Elf32_Word) (value / 4 + 1) * 4; - } - else static if (elfClass == ELFCLASS64) - { - return cast(Elf64_Xword) (value / 8 + 1) * 8; - } - else - { - static assert(false, "Invalid ELF class"); - } -} - -private struct Relocation(Sym, Rel) -{ - Sym symbol; - Array!Rel relocations; -} - -struct Elf(ubyte elfClass) -{ - static if (elfClass == ELFCLASS32) - { - alias Addr = Elf32_Addr; - alias Off = Elf32_Off; - alias Half = Elf32_Half; - alias Word = Elf32_Word; - alias Sword = Elf32_Sword; - alias Xword = Elf32_Word; - alias Sxword = Elf32_Sword; - - alias Ehdr = Elf32_Ehdr; - alias Shdr = Elf32_Shdr; - alias Rel = Elf32_Rel; - alias Rela = Elf32_Rela; - alias Sym = Elf32_Sym; - - alias R_SYM = ELF32_R_SYM; - alias R_TYPE = ELF32_R_TYPE; - alias R_INFO = ELF32_R_INFO; - alias ST_BIND = ELF32_ST_BIND; - alias ST_TYPE = ELF32_ST_TYPE; - alias ST_INFO = ELF32_ST_INFO; - } - else static if (elfClass == ELFCLASS64) - { - alias Addr = Elf64_Addr; - alias Off = Elf64_Off; - alias Half = Elf64_Half; - alias Word = Elf64_Word; - alias Sword = Elf64_Sword; - alias Xword = Elf64_Xword; - alias Sxword = Elf64_Sxword; - - alias Ehdr = Elf64_Ehdr; - alias Shdr = Elf64_Shdr; - alias Rel = Elf64_Rel; - alias Rela = Elf64_Rela; - alias Sym = Elf64_Sym; - - alias R_SYM = ELF64_R_SYM; - alias R_TYPE = ELF64_R_TYPE; - alias R_INFO = ELF64_R_INFO; - alias ST_BIND = ELF64_ST_BIND; - alias ST_TYPE = ELF64_ST_TYPE; - alias ST_INFO = ELF64_ST_INFO; - } - else - { - static assert(false, "Invalid ELF class"); - } - - private alias Relocation = .Relocation!(Sym, Rela); - - private Array!Shdr sectionHeaders; - private Off currentOffset = Elf32_Ehdr.sizeof; - static immutable char[52] sections = - "\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0"; - private String strings; - private File output; - private Array!ubyte readOnly; - - private HashTable!(String, Relocation) symbolTable; - - private enum HeaderName - { - text = 0x1b, - roData = 0x21, - string_ = 0x09, - headerString = 0x11, - symbol = 0x01, - rela = 0x29 - } - - static Elf opCall(File output) @nogc - { - Elf elf = Elf.init; - - elf.initializeSectionHeaders(); - elf.output = move(output); - - elf.output.seek(Ehdr.sizeof, File.Whence.set); - - elf.makeTextHeader(); - elf.makeRoDataHeader(); - elf.makeSymbolHeader(); - elf.makeRelaHeader(); - elf.makeStringHeader!(HeaderName.string_)(); - elf.makeStringHeader!(HeaderName.headerString)(); - - return elf; - } - - @disable this(this); - - void finish() @nogc - { - writeRoDataTable(); - writeSymbolTable(); - writeStringTables(); - - // End writing data, start writing headers. - - output.write((cast(ubyte*) this.sectionHeaders.get)[0 .. Shdr.sizeof * this.sectionHeaders.length]); - - writeFileHeader(); - } - - private Sym initializeSymbols() @nogc - { - // Zero symbol - Sym symbol; - symbol.st_name = 0; // Word - symbol.st_value = 0; // Addr - symbol.st_size = 0; // Word - symbol.st_info = 0; // char - symbol.st_other = 0; // char - symbol.st_shndx = 0; // Half word - - return symbol; - } - - private void makeStringHeader(HeaderName position)() @nogc - { - Shdr table; - - table.sh_name = position; - table.sh_type = SHT_STRTAB; - table.sh_flags = 0; - table.sh_addr = 0; - table.sh_offset = 0; - table.sh_size = 0; - table.sh_link = SHN_UNDEF; - table.sh_info = 0; - table.sh_addralign = 1; - table.sh_entsize = 0; - - this.sectionHeaders.insertBack(table); - } - - private void writeStringTables() @nogc - { - auto stringIndex = findHeader!(HeaderName.string_); - assert(stringIndex != -1); - - this.sectionHeaders[stringIndex].sh_offset = this.currentOffset; - this.sectionHeaders[stringIndex].sh_size = cast(Word) strings.length; - - output.write(cast(ubyte[]) this.strings.toStringz[0 .. this.strings.length + 1]); - this.currentOffset += this.strings.length + 1; - - auto headerStringIndex = findHeader!(HeaderName.headerString); - assert(stringIndex != -1); - - this.sectionHeaders[headerStringIndex].sh_offset = this.currentOffset; - this.sectionHeaders[headerStringIndex].sh_size = cast(Word) sections.length; - - output.write(cast(const(ubyte)[]) this.sections); - this.currentOffset += this.sections.length; - auto alignment = pad!ELFCLASS32(this.strings.length + 1 + this.sections.length); - const(ubyte)[4] padding = 0; - output.write(padding[0 .. alignment - this.strings.length - 1 - this.sections.length]); - this.currentOffset += alignment - this.strings.length - 1 - this.sections.length; - } - - private void makeSymbolHeader() @nogc - { - Shdr symbolTableHeader; - - symbolTableHeader.sh_name = HeaderName.symbol; - symbolTableHeader.sh_type = SHT_SYMTAB; - symbolTableHeader.sh_flags = 0; - symbolTableHeader.sh_addr = 0; - symbolTableHeader.sh_offset = 0; - symbolTableHeader.sh_size = 0; - // String table used by entries in this section. - symbolTableHeader.sh_link = 0; - symbolTableHeader.sh_info = 0; - symbolTableHeader.sh_addralign = 4; - symbolTableHeader.sh_entsize = Sym.sizeof; - - this.sectionHeaders.insertBack(symbolTableHeader); - } - - private void writeSymbolTable() @nogc - { - const index = findHeader!(HeaderName.symbol)(); - const stringIndex = findHeader!(HeaderName.string_)(); - const relaIndex = findHeader!(HeaderName.rela); - const textIndex = findHeader!(HeaderName.text)(); - - assert(index != -1); - assert(stringIndex != -1); - assert(relaIndex != -1); - assert(textIndex != -1); - - this.sectionHeaders[index].sh_offset = this.currentOffset; - this.sectionHeaders[index].sh_link = cast(Word) stringIndex; - this.sectionHeaders[index].sh_size = cast(Word) ((1 + symbolTable.length) * Sym.sizeof); - - this.sectionHeaders[relaIndex].sh_link = cast(Word) index; - this.sectionHeaders[relaIndex].sh_info = cast(Word) textIndex; - this.sectionHeaders[relaIndex].sh_offset = this.sectionHeaders[index].sh_offset - + this.sectionHeaders[index].sh_size; - - auto initialSymbol = initializeSymbols(); - output.write((cast(ubyte*) &initialSymbol)[0 .. Sym.sizeof]); - this.currentOffset += Sym.sizeof; - - int i = 1; - Array!Relocation symbols = Array!Relocation(this.symbolTable.byValue()); - auto rightRange = symbols[].partition!(symbol => ST_BIND(symbol.symbol.st_info) != STB_GLOBAL); - - // Greater than last local symbol. - this.sectionHeaders[index].sh_info = cast(Word) (symbols.length - rightRange.length + 1); - - foreach (ref symbol; symbols[]) - { - this.output.seek(this.sectionHeaders[relaIndex].sh_offset + this.sectionHeaders[relaIndex].sh_size, - File.Whence.set); - - if (!symbol.relocations.empty) - { - foreach (ref relocation; symbol.relocations[]) - { - relocation.r_info = R_INFO(i, R_TYPE(relocation.r_info)); - } - this.sectionHeaders[relaIndex].sh_flags = SHF_ALLOC; - const size = cast(Word) (Rela.sizeof * symbol.relocations.length); - - this.output.write((cast(ubyte*) symbol.relocations.get)[0 .. size]); - this.sectionHeaders[relaIndex].sh_size += size; - this.currentOffset += size; - } - - this.output.seek(this.sectionHeaders[index].sh_offset + i * Sym.sizeof, File.Whence.set); - output.write((cast(ubyte*) &symbol)[0 .. Sym.sizeof]); - this.currentOffset += Sym.sizeof; - ++i; - } - this.output.seek(0, File.Whence.end); - } - - void addCode(String name, ref Array!ubyte text) - @nogc - { - this.output.write(text.get); - - auto textHeaderIndex = findHeader!(HeaderName.text)(); - assert(textHeaderIndex != -1); - - this.strings.insertBack("\0"); - - Sym symbol; - // Main function - symbol.st_name = cast(Word) this.strings.length; - symbol.st_value = 0; - symbol.st_size = cast(Word) text.length; - symbol.st_info = ST_INFO(STB_GLOBAL, STT_FUNC); - symbol.st_other = 0; // char - // .text header index, half word - symbol.st_shndx = cast(Half) textHeaderIndex; - this.symbolTable[name] = Relocation(symbol); - - this.strings.insertBack(name[]); - - this.sectionHeaders[textHeaderIndex].sh_size += text.length; - this.currentOffset += text.length; - } - - void addReadOnlyData(String name, ref Array!ubyte data) @nogc - { - auto roDataIndex = findHeader!(HeaderName.roData)(); - assert(roDataIndex != -1); - - this.strings.insertBack("\0"); - - Sym symbol; - // Main function - symbol.st_name = cast(Word) this.strings.length; - symbol.st_value = 0; - symbol.st_size = cast(Word) data.length; - symbol.st_info = ST_INFO(STB_LOCAL, STT_NOTYPE); - symbol.st_other = 0; // char - // .text header index, half word - symbol.st_shndx = cast(Half) roDataIndex; - this.symbolTable[name] = Relocation(symbol); - - this.strings.insertBack(name[]); - this.readOnly.insertBack(data[]); - } - - void addExternSymbol(String name) @nogc - { - Sym usedSymbolEntry; - - this.strings.insertBack("\0"); - usedSymbolEntry.st_name = cast(Word) this.strings.length; - usedSymbolEntry.st_value = 0; - usedSymbolEntry.st_size = 0; - usedSymbolEntry.st_info = ST_INFO(STB_GLOBAL, STT_NOTYPE); - usedSymbolEntry.st_other = 0; - usedSymbolEntry.st_shndx = SHN_UNDEF; - - this.strings.insertBack(name[]); - this.strings.insertBack("\0"); - this.symbolTable[name] = Relocation(usedSymbolEntry); - } - - void relocate(String name, Rela[] usedSymbols...) @nogc - { - foreach (usedSymbol; usedSymbols) - { - Rela relocationEntry = usedSymbol; - - relocationEntry.r_info = usedSymbol.r_info; - this.symbolTable[name].relocations.insertBack(relocationEntry); - } - } - - private ptrdiff_t findHeader(HeaderName position)() - { - return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]); - } - - private void makeTextHeader() @nogc - { - Shdr textHeader; - - textHeader.sh_name = HeaderName.text; - textHeader.sh_type = SHT_PROGBITS; - textHeader.sh_flags = SHF_EXECINSTR | SHF_ALLOC; - textHeader.sh_addr = 0; - textHeader.sh_offset = this.currentOffset; - textHeader.sh_size = 0; - textHeader.sh_link = SHN_UNDEF; - textHeader.sh_info = 0; - textHeader.sh_addralign = 1; - textHeader.sh_entsize = 0; - - this.sectionHeaders.insertBack(textHeader); - } - - private void initializeSectionHeaders() @nogc - { - Shdr table; - - table.sh_name = 0; - table.sh_type = SHT_NULL; - table.sh_flags = 0; - table.sh_addr = 0; - table.sh_offset = 0; - table.sh_size = 0; - table.sh_link = SHN_UNDEF; - table.sh_info = 0; - table.sh_addralign = 0; - table.sh_entsize = 0; - - this.sectionHeaders.insertBack(table); - } - - private void writeFileHeader() @nogc - { - Ehdr fileHeader; - auto headerStringIndex = findHeader!(HeaderName.headerString)(); - - assert(headerStringIndex != -1); - - // Magic number. - fileHeader.e_ident[0] = '\x7f'; - fileHeader.e_ident[1] = 'E'; - fileHeader.e_ident[2] = 'L'; - fileHeader.e_ident[3] = 'F'; - - fileHeader.e_ident[4] = ELFCLASS32; - fileHeader.e_ident[5] = ELFDATA2LSB; - fileHeader.e_ident[6] = EV_CURRENT; - fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV; - fileHeader.e_ident[8] = 0; - - fileHeader.e_type = ET_REL; - fileHeader.e_machine = 0xf3; // EM_RISCV - fileHeader.e_version = EV_CURRENT; - fileHeader.e_entry = 0; - fileHeader.e_phoff = 0; - fileHeader.e_shoff = this.currentOffset; - fileHeader.e_flags = 0; - fileHeader.e_ehsize = Elf32_Ehdr.sizeof; - fileHeader.e_phentsize = 0; - fileHeader.e_phnum = 0; - fileHeader.e_shentsize = Elf32_Shdr.sizeof; - fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length; - - // String table is the last one - fileHeader.e_shstrndx = cast(Half) headerStringIndex; - - output.seek(0, File.Whence.set); - output.write((cast(ubyte*) &fileHeader)[0 .. fileHeader.sizeof]); - } - - private void makeRoDataHeader() @nogc - { - Shdr table; - - table.sh_name = HeaderName.roData; - table.sh_type = SHT_PROGBITS; - table.sh_flags = SHF_ALLOC; - table.sh_addr = 0; - table.sh_offset = 0; - table.sh_size = 0; - table.sh_link = SHN_UNDEF; - table.sh_info = 0; - table.sh_addralign = 4; - table.sh_entsize = 0; - - this.sectionHeaders.insertBack(table); - } - - private void writeRoDataTable() @nogc - { - auto index = findHeader!(HeaderName.roData)(); - assert(index != -1); - - this.sectionHeaders[index].sh_offset = this.currentOffset; - this.sectionHeaders[index].sh_size = cast(Xword) this.readOnly.length; - - output.write(this.readOnly.get); - this.currentOffset += this.readOnly.length; - } - - private void makeRelaHeader() @nogc - { - Shdr table; - - table.sh_name = HeaderName.rela; - table.sh_type = SHT_RELA; - table.sh_flags = 0; - table.sh_addr = 0; - table.sh_offset = 0; - table.sh_size = 0; - table.sh_link = SHN_UNDEF; - table.sh_info = 0; - table.sh_addralign = 4; - table.sh_entsize = Rela.sizeof; - - this.sectionHeaders.insertBack(table); - } -} diff --git a/source/elna/extended.d b/source/elna/extended.d deleted file mode 100644 index 6baea71..0000000 --- a/source/elna/extended.d +++ /dev/null @@ -1,335 +0,0 @@ -/** - * File I/O that can be moved into more generic library when and if finished. - */ -module elna.extended; - -import core.stdc.errno; -import core.stdc.stdio; -import std.sumtype; -import std.typecons; -import tanya.os.error; -import tanya.container.array; -import tanya.container.string; - -/** - * File handle abstraction. - */ -struct File -{ - /// Plattform dependent file type. - alias Handle = FILE*; - - /// Uninitialized file handle value. - enum Handle invalid = null; - - /** - * Relative position. - */ - enum Whence - { - /// Relative to the start of the file. - set = SEEK_SET, - /// Relative to the current cursor position. - currentt = SEEK_CUR, - /// Relative from the end of the file. - end = SEEK_END, - } - - /** - * File open modes. - */ - enum Mode - { - /// Open the file for reading. - read = 1 << 0, - /// Open the file for writing. The stream is positioned at the beginning - /// of the file. - write = 1 << 1, - /// Open the file for writing and remove its contents. - truncate = 1 << 2, - /// Open the file for writing. The stream is positioned at the end of - /// the file. - append = 1 << 3, - } - - private enum Status - { - invalid, - owned, - borrowed, - } - - private union Storage - { - Handle handle; - ErrorCode errorCode; - } - private Storage storage; - private Status status = Status.invalid; - - @disable this(scope return ref File f); - @disable this(); - - /** - * Closes the file. - */ - ~this() @nogc nothrow - { - if (this.status == Status.owned) - { - fclose(this.storage.handle); - } - this.storage.handle = invalid; - this.status = Status.invalid; - } - - /** - * Construct the object with the given system handle. The won't be claused - * in the descructor if this constructor is used. - * - * Params: - * handle = File handle to be wrapped by this structure. - */ - this(Handle handle) @nogc nothrow pure @safe - { - this.storage.handle = handle; - this.status = Status.borrowed; - } - - /** - * Returns: Plattform dependent file handle. - */ - @property Handle handle() @nogc nothrow pure @trusted - { - return valid ? this.storage.handle : invalid; - } - - /** - * Returns: An error code if an error has occurred. - */ - @property ErrorCode errorCode() @nogc nothrow pure @safe - { - return valid ? ErrorCode() : this.storage.errorCode; - } - - /** - * Returns: Whether a valid, opened file is represented. - */ - @property bool valid() @nogc nothrow pure @safe - { - return this.status != Status.invalid; - } - - /** - * Transfers the file into invalid state. - * - * Returns: The old file handle. - */ - Handle reset() @nogc nothrow pure @safe - { - if (!valid) - { - return invalid; - } - auto oldHandle = handle; - - this.status = Status.invalid; - this.storage.errorCode = ErrorCode(); - - return oldHandle; - } - - /** - * Sets stream position in the file. - * - * Params: - * offset = File offset. - * whence = File position to add the offset to. - * - * Returns: Error code if any. - */ - ErrorCode seek(size_t offset, Whence whence) @nogc nothrow - { - if (!valid) - { - return ErrorCode(ErrorCode.ErrorNo.badDescriptor); - } - if (fseek(this.storage.handle, offset, whence)) - { - return ErrorCode(cast(ErrorCode.ErrorNo) errno); - } - return ErrorCode(); - } - - /** - * Returns: Current offset or an error. - */ - SumType!(ErrorCode, size_t) tell() @nogc nothrow - { - if (!valid) - { - return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor)); - } - auto result = ftell(this.storage.handle); - - if (result < 0) - { - return typeof(return)(ErrorCode(cast(ErrorCode.ErrorNo) errno)); - } - return typeof(return)(cast(size_t) result); - } - - /** - * Params: - * buffer = Destination buffer. - * - * Returns: Bytes read. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that - * while reading the file an unknown error has occurred. - */ - SumType!(ErrorCode, size_t) read(ubyte[] buffer) @nogc nothrow - { - if (!valid) - { - return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor)); - } - const bytesRead = fread(buffer.ptr, 1, buffer.length, this.storage.handle); - if (bytesRead == buffer.length || eof()) - { - return typeof(return)(bytesRead); - } - return typeof(return)(ErrorCode()); - } - - /** - * Params: - * buffer = Source buffer. - * - * Returns: Bytes written. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that - * while reading the file an unknown error has occurred. - */ - SumType!(ErrorCode, size_t) write(const(ubyte)[] buffer) @nogc nothrow - { - if (!valid) - { - return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor)); - } - const bytesWritten = fwrite(buffer.ptr, 1, buffer.length, this.storage.handle); - if (bytesWritten == buffer.length) - { - return typeof(return)(buffer.length); - } - return typeof(return)(ErrorCode()); - } - - /** - * Returns: EOF status of the file. - */ - bool eof() @nogc nothrow - { - return valid && feof(this.storage.handle) != 0; - } - - /** - * Constructs a file object that will be closed in the destructor. - * - * Params: - * filename = The file to open. - * - * Returns: Opened file or an error. - */ - static File open(const(char)* filename, BitFlags!Mode mode) @nogc nothrow - { - char[3] modeBuffer = "\0\0\0"; - - if (mode.truncate) - { - modeBuffer[0] = 'w'; - if (mode.read) - { - modeBuffer[1] = '+'; - } - } - else if (mode.append) - { - modeBuffer[0] = 'a'; - if (mode.read) - { - modeBuffer[1] = '+'; - } - } - else if (mode.read) - { - modeBuffer[0] = 'r'; - if (mode.write) - { - modeBuffer[1] = '+'; - } - } - - auto newHandle = fopen(filename, modeBuffer.ptr); - auto newFile = File(newHandle); - - if (newHandle is null) - { - newFile.status = Status.invalid; - newFile.storage.errorCode = ErrorCode(cast(ErrorCode.ErrorNo) errno); - } - else - { - if (mode == BitFlags!Mode(Mode.write)) - { - rewind(newHandle); - } - newFile.status = Status.owned; - } - - return newFile; - } -} - -/** - * Reads the whole file and returns its contents. - * - * Params: - * sourceFilename = Source filename. - * - * Returns: File contents or an error. - * - * See_Also: $(D_PSYMBOL File.read) - */ -SumType!(ErrorCode, Array!ubyte) readFile(const(char)* sourceFilename) @nogc -{ - enum size_t bufferSize = 255; - auto sourceFile = File.open(sourceFilename, BitFlags!(File.Mode)(File.Mode.read)); - - if (!sourceFile.valid) - { - return typeof(return)(sourceFile.errorCode); - } - Array!ubyte sourceText; - size_t totalRead; - size_t bytesRead; - do - { - sourceText.length = sourceText.length + bufferSize; - const readStatus = sourceFile - .read(sourceText[totalRead .. $].get) - .match!( - (ErrorCode errorCode) => nullable(errorCode), - (size_t bytesRead_) { - bytesRead = bytesRead_; - return Nullable!ErrorCode(); - } - ); - if (!readStatus.isNull) - { - return typeof(return)(readStatus.get); - } - totalRead += bytesRead; - } - while (bytesRead == bufferSize); - - sourceText.length = totalRead; - - return typeof(return)(sourceText); -} diff --git a/source/elna/lexer.d b/source/elna/lexer.d deleted file mode 100644 index eb0d6dd..0000000 --- a/source/elna/lexer.d +++ /dev/null @@ -1,55 +0,0 @@ -module elna.lexer; - -import core.stdc.string; -import elna.result; - -extern(C++, "elna") -struct Token -{ - enum Type : ushort - { - number = 0, - operator = 1, - let = 2, - identifier = 3, - equals = 4, - var = 5, - semicolon = 6, - leftParen = 7, - rightParen = 8, - bang = 9, - dot = 10, - comma = 11, - } - - union Value - { - int number; - const(char)* identifier; - } - - private Type type; - private Value value_; - private Position position_; - - @disable this(); - - this(Type type, Position position) @nogc nothrow pure @safe; - this(Type type, int value, Position position) @nogc nothrow pure @trusted; - this(Type type, const(char)* value, Position position) @nogc nothrow; - - /** - * Returns: Expected token type. - */ - Type of() const @nogc nothrow pure @safe; - const(char)* identifier() const @nogc nothrow pure; - int number() const @nogc nothrow pure; - - /** - * Returns: The token position in the source text. - */ - @property const(Position) position() const @nogc nothrow pure @safe; -} - -extern(C++, "elna") -Token* lex(const(char)* buffer, CompileError* compileError, size_t* length) @nogc; diff --git a/source/elna/parser.d b/source/elna/parser.d deleted file mode 100644 index ca8064e..0000000 --- a/source/elna/parser.d +++ /dev/null @@ -1,110 +0,0 @@ -module elna.parser; - -import core.stdc.string; -import core.stdc.stdlib; -import elna.lexer; -import elna.result; -import tanya.memory.allocator; -import std.array; - -/** - * Parser visitor. - */ -extern(C++, "elna") -abstract class ParserVisitor -{ - abstract void visit(Node) @nogc; - abstract void visit(Definition) @nogc; - abstract void visit(BangStatement) @nogc; - abstract void visit(Block) @nogc; - abstract void visit(Expression) @nogc; - abstract void visit(BinaryExpression) @nogc; - abstract void visit(Variable) @nogc; - abstract void visit(Number) @nogc; -} - -/** - * AST node. - */ -extern(C++, "elna") -abstract class Node -{ - abstract void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -abstract class Statement : Node -{ -} - -/** - * Constant definition. - */ -extern(C++, "elna") -class Definition : Node -{ - Number number; - const(char)* identifier; - - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -class BangStatement : Statement -{ - Expression expression; - - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -class Block : Node -{ - Definition* definitions; - size_t definitionsLength; - Statement statement; - - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -abstract class Expression : Node -{ - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -class Number : Expression -{ - int value; - - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -class Variable : Expression -{ - const(char)* identifier; - - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -enum BinaryOperator -{ - sum, - subtraction -} - -extern(C++, "elna") -class BinaryExpression : Expression -{ - Expression lhs, rhs; - BinaryOperator operator; - - this(Expression lhs, Expression rhs, ubyte operator) @nogc; - override void accept(ParserVisitor visitor) @nogc; -} - -extern(C++, "elna") -Block parse(Token* tokenStream, size_t length) @nogc; diff --git a/source/elna/result.d b/source/elna/result.d deleted file mode 100644 index 2b0a521..0000000 --- a/source/elna/result.d +++ /dev/null @@ -1,101 +0,0 @@ -module elna.result; - -import std.typecons; -import tanya.container.array; -import tanya.container.string; - -/** - * Position in the source text. - */ -extern(C++, "elna") -struct Position -{ - /// Line. - size_t line = 1; - - /// Column. - size_t column = 1; -} - -extern(C++, "elna") -struct CompileError -{ - private const(char)* message_; - - private Position position_; - - @disable this(); - - /** - * Params: - * message = Error text. - * position = Error position in the source text. - */ - this(const(char)* message, const Position position) @nogc nothrow pure @safe; - - /// Error text. - @property const(char)* what() const @nogc nothrow pure @safe; - - /// Error line in the source text. - @property size_t line() const @nogc nothrow pure @safe; - - /// Error column in the source text. - @property size_t column() const @nogc nothrow pure @safe; -} - -struct Result(T) -{ - Nullable!CompileError error; - T result; - - this(T result) - { - this.result = result; - this.error = typeof(this.error).init; - } - - this(const(char)* message, Position position) - { - this.result = T.init; - this.error = CompileError(message, position); - } - - this(CompileError compileError) - { - this.result = null; - this.error = compileError; - } - - @disable this(); - - @property bool valid() const - { - return error.isNull; - } -} - -extern(C++, "elna") -enum Target -{ - text, - high20, - lower12i -} - -extern(C++, "elna") -struct Reference -{ - const(char)* name; - size_t offset; - Target target; -} - -extern(C++, "elna") -struct Symbol -{ - this(const(char)* name) @nogc; - const(char)* name; - ubyte* text; - size_t length; - Reference[3] symbols; -} diff --git a/source/lexer.cpp b/source/lexer.cpp index 00de021..732d63a 100644 --- a/source/lexer.cpp +++ b/source/lexer.cpp @@ -1,7 +1,15 @@ #include "elna/lexer.hpp" +#include + namespace elna { +namespace lex +{ + using source_position = elna::source::position; + using source_error = elna::source::error; + using source_result = elna::source::result>; + source::source(const std::string& buffer) : m_buffer(buffer) { @@ -14,18 +22,18 @@ namespace elna source::const_iterator source::end() const { - Position end_position{ 0, 0 }; + source_position end_position{ 0, 0 }; return source::const_iterator(std::cend(m_buffer), end_position); } source::const_iterator::const_iterator(std::string::const_iterator buffer, - const Position start_position) + const source_position start_position) : m_buffer(buffer), m_position(start_position) { } - const Position& source::const_iterator::position() const noexcept + const source_position& source::const_iterator::position() const noexcept { return this->m_position; } @@ -73,7 +81,7 @@ namespace elna return !(*this == that); } - Token::Token(const Type of, const char *value, Position position) + Token::Token(const type of, const char *value, source_position position) : m_type(of), m_position(position) { std::size_t value_length = strlen(value); @@ -85,13 +93,13 @@ namespace elna m_value.identifier = buffer; } - Token::Token(const Type of, std::int32_t number, Position position) + Token::Token(const type of, std::int32_t number, source_position position) : m_type(of), m_position(position) { m_value.number = number; } - Token::Token(const Type of, Position position) + Token::Token(const type of, source_position position) : m_type(of), m_position(position) { } @@ -110,7 +118,7 @@ namespace elna Token::~Token() { - if (m_type == TOKEN_IDENTIFIER || m_type == TOKEN_OPERATOR) + if (m_type == type::identifier || m_type == type::term_operator || m_type == type::factor_operator) { std::free(const_cast(m_value.identifier)); } @@ -120,7 +128,7 @@ namespace elna { m_type = that.of(); m_position = that.position(); - if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR) + if (that.of() == type::identifier || that.of() == type::term_operator || m_type == type::factor_operator) { std::size_t value_length = strlen(that.identifier()); char *buffer = reinterpret_cast(malloc(value_length + 1)); @@ -130,7 +138,7 @@ namespace elna m_value.identifier = buffer; } - else if (that.of() == TOKEN_NUMBER) + else if (that.of() == type::number) { m_value.number = that.number(); } @@ -141,19 +149,19 @@ namespace elna { m_type = that.of(); m_position = that.position(); - if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR) + if (that.of() == type::identifier || that.of() == type::term_operator || that.of() == type::factor_operator) { m_value.identifier = that.identifier(); that.m_value.identifier = nullptr; } - else if (that.of() == TOKEN_NUMBER) + else if (that.of() == type::number) { m_value.number = that.number(); } return *this; } - Token::Type Token::of() const noexcept + Token::type Token::of() const noexcept { return m_type; } @@ -168,12 +176,12 @@ namespace elna return m_value.number; } - const Position& Token::position() const noexcept + const source_position& Token::position() const noexcept { return m_position; } - Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length) + source_result lex(const char *buffer) { std::vector tokens; source input{ buffer }; @@ -186,38 +194,38 @@ namespace elna else if (std::isdigit(*iterator)) { tokens.emplace_back( - Token::TOKEN_NUMBER, + Token::type::number, static_cast(*iterator - '0'), iterator.position() ); } else if (*iterator == '=') { - tokens.emplace_back(Token::TOKEN_EQUALS, iterator.position()); + tokens.emplace_back(Token::type::equals, iterator.position()); } else if (*iterator == '(') { - tokens.emplace_back(Token::TOKEN_LEFT_PAREN, iterator.position()); + tokens.emplace_back(Token::type::left_paren, iterator.position()); } else if (*iterator == ')') { - tokens.emplace_back(Token::TOKEN_RIGHT_PAREN, iterator.position()); + tokens.emplace_back(Token::type::right_paren, iterator.position()); } else if (*iterator == ';') { - tokens.emplace_back(Token::TOKEN_SEMICOLON, iterator.position()); + tokens.emplace_back(Token::type::semicolon, iterator.position()); } else if (*iterator == ',') { - tokens.emplace_back(Token::TOKEN_COMMA, iterator.position()); + tokens.emplace_back(Token::type::comma, iterator.position()); } else if (*iterator == '!') { - tokens.emplace_back(Token::TOKEN_BANG, iterator.position()); + tokens.emplace_back(Token::type::bang, iterator.position()); } else if (*iterator == '.') { - tokens.emplace_back(Token::TOKEN_DOT, iterator.position()); + tokens.emplace_back(Token::type::dot, iterator.position()); } else if (std::isalpha(*iterator)) { @@ -230,15 +238,15 @@ namespace elna } if (word == "const") { - tokens.emplace_back(Token::TOKEN_LET, iterator.position()); + tokens.emplace_back(Token::type::let, iterator.position()); } else if (word == "var") { - tokens.emplace_back(Token::TOKEN_VAR, iterator.position()); + tokens.emplace_back(Token::type::var, iterator.position()); } else { - tokens.emplace_back(Token::TOKEN_IDENTIFIER, word.c_str(), iterator.position()); + tokens.emplace_back(Token::type::identifier, word.c_str(), iterator.position()); } iterator = i; continue; @@ -247,24 +255,21 @@ namespace elna { std::string _operator{ *iterator }; - tokens.emplace_back(Token::TOKEN_OPERATOR, _operator.c_str(), iterator.position()); + tokens.emplace_back(Token::type::term_operator, _operator.c_str(), iterator.position()); + } + else if (*iterator == '*' || *iterator == '/') + { + std::string _operator{ *iterator }; + + tokens.emplace_back(Token::type::factor_operator, _operator.c_str(), iterator.position()); } else { - *compile_error = CompileError("Unexpected next character", iterator.position()); - return nullptr; + return source_result("Unexpected next character", iterator.position()); } ++iterator; } - Token *target = reinterpret_cast(malloc(tokens.size() * sizeof(Token) + sizeof(Token))); - int i = 0; - for (auto& token : tokens) - { - target[i] = std::move(token); - ++i; - } - *length = i; - - return target; + return source_result(tokens); } } +} diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..6cd8ba2 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,47 @@ +#include "elna/cl.hpp" + +#include +#include +#include + +int main(int argc, char **argv) +{ + boost::program_options::options_description visible{ "Allowed options" }; + boost::program_options::options_description description; + boost::program_options::positional_options_description positional; + boost::program_options::variables_map variables_map; + + visible.add_options() + ("help", "Print this help message") + ("output,o", boost::program_options::value(), "Output object file") + ; + description.add_options() + ("input", boost::program_options::value()->required()) + ; + description.add(visible); + positional.add("input", 1); + + auto parsed = boost::program_options::command_line_parser(argc, argv) + .options(description).positional(positional) + .run(); + boost::program_options::store(parsed, variables_map); + boost::program_options::notify(variables_map); + + if (variables_map.count("help")) + { + std::cout << description << std::endl; + return 0; + } + const auto in_file = variables_map["input"].as(); + std::filesystem::path out_file; + + if (variables_map.count("output")) + { + out_file = variables_map["output"].as(); + } + else + { + out_file = in_file.filename().replace_extension(".o"); + } + return elna::compile(in_file, out_file); +} diff --git a/source/main.d b/source/main.d deleted file mode 100644 index 3bd823b..0000000 --- a/source/main.d +++ /dev/null @@ -1,15 +0,0 @@ -import elna.backend; -import std.path; -import std.sumtype; -import tanya.container.string; -import tanya.memory.allocator; -import tanya.memory.mmappool; - -extern(C) -int main(int argc, char** args) -{ - defaultAllocator = MmapPool.instance; - - // return generate(args[3], args[2]); - return compile(args[3], args[2]); -} diff --git a/source/parser.cpp b/source/parser.cpp index 699a9ee..094a405 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -40,17 +40,22 @@ namespace elna this->lhs = lhs; this->rhs = rhs; - if (_operator == '+') + switch (_operator) { - this->_operator = BinaryOperator::sum; - } - else if (_operator == '-') - { - this->_operator = BinaryOperator::subtraction; - } - else - { - throw std::logic_error("Invalid binary operator"); + case '+': + this->_operator = BinaryOperator::sum; + break; + case '-': + this->_operator = BinaryOperator::subtraction; + break; + case '*': + this->_operator = BinaryOperator::multiplication; + break; + case '/': + this->_operator = BinaryOperator::division; + break; + default: + throw std::logic_error("Invalid binary operator"); } } @@ -64,14 +69,14 @@ namespace elna visitor->visit(this); } - Block *parse(Token *tokenStream, std::size_t length) + Block *parse(lex::Token *tokenStream, std::size_t length) { return parseBlock(&tokenStream, &length); } - Expression *parseFactor(Token **tokens, size_t *length) + Expression *parseFactor(lex::Token **tokens, size_t *length) { - if ((*tokens)[0].of() == Token::TOKEN_IDENTIFIER) + if ((*tokens)[0].of() == lex::Token::type::identifier) { auto variable = new Variable(); variable->identifier = (*tokens)[0].identifier(); @@ -79,7 +84,7 @@ namespace elna --(*length); return variable; } - else if ((*tokens)[0].of() == Token::TOKEN_NUMBER) + else if ((*tokens)[0].of() == lex::Token::Token::type::number) { auto number = new Number(); number->value = (*tokens)[0].number(); @@ -87,7 +92,7 @@ namespace elna --(*length); return number; } - else if ((*tokens)[0].of() == Token::TOKEN_LEFT_PAREN) + else if ((*tokens)[0].of() == lex::Token::type::left_paren) { ++(*tokens); --(*length); @@ -102,15 +107,29 @@ namespace elna return nullptr; } - Expression *parseTerm(Token **tokens, size_t *length) + Expression *parseTerm(lex::Token **tokens, size_t *length) { - return parseFactor(tokens, length); + auto lhs = parseFactor(tokens, length); + if (lhs == nullptr || *length == 0 || (*tokens)[0].of() != lex::Token::type::factor_operator) + { + return lhs; + } + auto _operator = (*tokens)[0].identifier()[0]; + ++(*tokens); + --(*length); + + auto rhs = parseFactor(tokens, length); + if (rhs != nullptr) + { + return new BinaryExpression(lhs, rhs, _operator); + } + return nullptr; } - Expression *parseExpression(Token **tokens, size_t *length) + Expression *parseExpression(lex::Token **tokens, size_t *length) { auto term = parseTerm(tokens, length); - if (term == nullptr || *length == 0 || (*tokens)[0].of() != Token::TOKEN_OPERATOR) + if (term == nullptr || *length == 0 || (*tokens)[0].of() != lex::Token::type::term_operator) { return term; } @@ -122,17 +141,12 @@ namespace elna if (expression != nullptr) { - auto binaryExpression = new BinaryExpression(term, expression, _operator); - - return binaryExpression; - } - else - { - return nullptr; + return new BinaryExpression(term, expression, _operator); } + return nullptr; } - Definition *parseDefinition(Token **tokens, size_t *length) + Definition *parseDefinition(lex::Token **tokens, size_t *length) { auto definition = new Definition(); definition->identifier = (*tokens)[0].identifier(); // Copy. @@ -141,7 +155,7 @@ namespace elna ++(*tokens); // Skip the equals sign. *length -= 2; - if ((*tokens)[0].of() == Token::TOKEN_NUMBER) + if ((*tokens)[0].of() == lex::Token::type::number) { auto number = new Number(); number->value = (*tokens)[0].number(); @@ -153,9 +167,9 @@ namespace elna return nullptr; } - Statement *parseStatement(Token **tokens, std::size_t *length) + Statement *parseStatement(lex::Token **tokens, std::size_t *length) { - if ((*tokens)[0].of() == Token::TOKEN_BANG) + if ((*tokens)[0].of() == lex::Token::type::bang) { ++(*tokens); --(*length); @@ -174,7 +188,7 @@ namespace elna return nullptr; } - Definition **parseDefinitions(Token **tokens, size_t *length, size_t *resultLength) + Definition **parseDefinitions(lex::Token **tokens, size_t *length, size_t *resultLength) { ++(*tokens); // Skip const. --(*length); @@ -193,11 +207,11 @@ namespace elna realloc(definitions, (*resultLength + 1) * sizeof(Definition*))); definitions[(*resultLength)++] = definition; - if ((*tokens)[0].of() == Token::TOKEN_SEMICOLON) + if ((*tokens)[0].of() == lex::Token::type::semicolon) { break; } - if ((*tokens)[0].of() == Token::TOKEN_COMMA) + if ((*tokens)[0].of() == lex::Token::type::comma) { ++(*tokens); --(*length); @@ -207,10 +221,10 @@ namespace elna return definitions; } - Block *parseBlock(Token **tokens, std::size_t *length) + Block *parseBlock(lex::Token **tokens, std::size_t *length) { auto block = new Block(); - if ((*tokens)[0].of() == Token::TOKEN_LET) + if ((*tokens)[0].of() == lex::Token::type::let) { size_t length_ = 0; auto constDefinitions = parseDefinitions(tokens, length, &length_); diff --git a/source/result.cpp b/source/result.cpp index a65e55f..15fc56a 100644 --- a/source/result.cpp +++ b/source/result.cpp @@ -2,26 +2,29 @@ namespace elna { - CompileError::CompileError(const char *message, const Position position) noexcept +namespace source +{ + error::error(const char *message, const source::position position) noexcept { this->message = message; this->position = position; } - char const *CompileError::what() const noexcept + char const *error::what() const noexcept { return this->message; } - std::size_t CompileError::line() const noexcept + std::size_t error::line() const noexcept { return this->position.line; } - std::size_t CompileError::column() const noexcept + std::size_t error::column() const noexcept { return this->position.column; } +} Symbol::Symbol(const char *name) { diff --git a/source/riscv.cpp b/source/riscv.cpp index ac1787a..c2bf3cc 100644 --- a/source/riscv.cpp +++ b/source/riscv.cpp @@ -1,7 +1,7 @@ #include "elna/parser.hpp" #include "elna/riscv.hpp" #include -#include +#include namespace elna { @@ -49,9 +49,14 @@ namespace elna return *this; } - std::uint8_t *Instruction::encode() + const std::byte *Instruction::cbegin() { - return reinterpret_cast(&this->instruction); + return reinterpret_cast(&this->instruction); + } + + const std::byte *Instruction::cend() + { + return reinterpret_cast(&this->instruction) + sizeof(this->instruction); } void RiscVVisitor::visit(Node *) @@ -154,7 +159,7 @@ namespace elna std::size_t i = 0; for (; i < constCount; ++i) { - if (strcmp(variable->identifier, constNames[i]) == 0) + if (std::strcmp(variable->identifier, constNames[i]) == 0) { break; } @@ -218,6 +223,10 @@ namespace elna this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op) .r(lhs_register, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub); break; + case BinaryOperator::multiplication: + this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op) + .r(lhs_register, Funct3::mul, XRegister::a0, XRegister::t0, Funct7::muldiv); + break; } } @@ -232,11 +241,10 @@ namespace elna { program.symbols[i] = visitor->references[i]; } - program.text = reinterpret_cast(malloc(sizeof(std::uint32_t) * visitor->instructionsLength)); for (std::size_t i = 0; i < visitor->instructionsLength; ++i) { - memcpy(program.text + program.length, visitor->instructions[i].encode(), sizeof(std::uint32_t)); - program.length += sizeof(std::uint32_t); + program.text.insert(program.text.cend(), + visitor->instructions[i].cbegin(), visitor->instructions[i].cend()); } return program; } diff --git a/tests/expectations/multiply.txt b/tests/expectations/multiply.txt new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/tests/expectations/multiply.txt @@ -0,0 +1 @@ +10 diff --git a/tests/multiply.eln b/tests/multiply.eln new file mode 100644 index 0000000..b6aef9d --- /dev/null +++ b/tests/multiply.eln @@ -0,0 +1 @@ +! 5 * 2 diff --git a/tests/runner.cpp b/tests/runner.cpp index 27c6052..bd9eadf 100755 --- a/tests/runner.cpp +++ b/tests/runner.cpp @@ -35,9 +35,14 @@ namespace elna } } + static std::filesystem::path in_build_directory() + { + return "build/riscv"; + } + static std::string in_build_directory(const std::filesystem::path& path) { - return "build/riscv" / path; + return in_build_directory() / path; } static int build_test(const std::filesystem::directory_entry& test_entry) @@ -136,7 +141,7 @@ namespace elna int main() { - boost::process::system("rake"); + std::filesystem::create_directory(elna::in_build_directory()); std::cout << "Run all tests and check the results" << std::endl; std::filesystem::path test_directory{ "tests" };