Implement multiplication

This commit is contained in:
Eugen Wissner 2024-03-03 13:11:39 +01:00
parent 223f54d38d
commit 632dc53b53
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
25 changed files with 343 additions and 1989 deletions

View File

@ -5,6 +5,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 17) 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) add_executable(tester tests/runner.cpp include/elna/tester.hpp)
target_include_directories(tester PRIVATE include) target_include_directories(tester PRIVATE include)
@ -15,7 +18,7 @@ add_executable(elnsh shell/main.cpp
) )
target_include_directories(elnsh PRIVATE include) target_include_directories(elnsh PRIVATE include)
add_library(elna add_executable(elna source/main.cpp
source/lexer.cpp include/elna/lexer.hpp source/lexer.cpp include/elna/lexer.hpp
source/result.cpp include/elna/result.hpp source/result.cpp include/elna/result.hpp
source/riscv.cpp include/elna/riscv.hpp source/riscv.cpp include/elna/riscv.hpp
@ -24,3 +27,4 @@ add_library(elna
source/cl.cpp include/elna/cl.hpp source/cl.cpp include/elna/cl.hpp
) )
target_include_directories(elna PRIVATE include) target_include_directories(elna PRIVATE include)
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})

View File

@ -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

30
TODO
View File

@ -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 - Configure fzf to show only the current directory files
- Support multiple selections for insertion in fzf. - Support multiple selections for insertion in fzf.
@ -7,17 +26,12 @@
- Home directory expansion. - Home directory expansion.
- Show files in the PATH if starting at the beginning of the prompt - 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. - Add a bar with additional information under the prompt (edit_bar), like the hostname.
- Show current time in the prompt. - Show current time in the prompt.
# Input ## Input
- DEL handling. - DEL handling.
- Starting long running process and killing it with Ctrl-C kills the shell. - 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.

View File

@ -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"]
}

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <filesystem>
namespace elna namespace elna
{ {
char *readSource(const char *source); 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);
} }

View File

@ -1,10 +1,13 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include "elna/result.hpp" #include "elna/result.hpp"
namespace elna namespace elna
{
namespace lex
{ {
/** /**
* Range over the source text that keeps track of the current position. * Range over the source text that keeps track of the current position.
@ -14,10 +17,10 @@ namespace elna
class const_iterator class const_iterator
{ {
std::string::const_iterator m_buffer; std::string::const_iterator m_buffer;
Position m_position; elna::source::position m_position;
const_iterator(std::string::const_iterator buffer, const_iterator(std::string::const_iterator buffer,
const Position position = Position()); const elna::source::position start_position = elna::source::position());
public: public:
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
@ -26,7 +29,7 @@ namespace elna
using pointer = const value_type *; using pointer = const value_type *;
using reference = const value_type&; using reference = const value_type&;
const Position& position() const noexcept; const elna::source::position& position() const noexcept;
reference operator*() const noexcept; reference operator*() const noexcept;
pointer operator->() const noexcept; pointer operator->() const noexcept;
@ -54,20 +57,21 @@ namespace elna
/** /**
* Token type. * Token type.
*/ */
enum Type : std::uint16_t enum class type : std::uint16_t
{ {
TOKEN_NUMBER = 0, number = 0,
TOKEN_OPERATOR = 1, term_operator = 1,
TOKEN_LET = 2, let = 2,
TOKEN_IDENTIFIER = 3, identifier = 3,
TOKEN_EQUALS = 4, equals = 4,
TOKEN_VAR = 5, var = 5,
TOKEN_SEMICOLON = 6, semicolon = 6,
TOKEN_LEFT_PAREN = 7, left_paren = 7,
TOKEN_RIGHT_PAREN = 8, right_paren = 8,
TOKEN_BANG = 9, bang = 9,
TOKEN_DOT = 10, dot = 10,
TOKEN_COMMA = 11, comma = 11,
factor_operator = 12,
}; };
/** /**
@ -79,9 +83,9 @@ namespace elna
const char *identifier; const char *identifier;
}; };
Token(Type of, Position position); Token(type of, elna::source::position position);
Token(Type of, std::int32_t value, Position position); Token(type of, std::int32_t value, elna::source::position position);
Token(Type of, const char *value, Position position); Token(type of, const char *value, elna::source::position position);
Token(const Token& that); Token(const Token& that);
Token(Token&& that); Token(Token&& that);
~Token(); ~Token();
@ -89,21 +93,23 @@ namespace elna
Token& operator=(const Token& that); Token& operator=(const Token& that);
Token& operator=(Token&& that); Token& operator=(Token&& that);
Type of() const noexcept; type of() const noexcept;
const char *identifier() const noexcept; const char *identifier() const noexcept;
std::int32_t number() const noexcept; std::int32_t number() const noexcept;
const Position& position() const noexcept; const elna::source::position& position() const noexcept;
private: private:
Type m_type; type m_type;
Value m_value; Value m_value;
Position m_position; elna::source::position m_position;
}; };
/** /**
* Split the source into tokens. * Split the source into tokens.
* *
* \param buffer Source text.
* \return Tokens or error. * \return Tokens or error.
*/ */
Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length); elna::source::result<std::vector<Token>> lex(const char *buffer);
}
} }

View File

@ -9,7 +9,9 @@ namespace elna
enum class BinaryOperator enum class BinaryOperator
{ {
sum, sum,
subtraction subtraction,
multiplication,
division
}; };
class Node; class Node;
@ -112,12 +114,12 @@ namespace elna
virtual void accept(ParserVisitor *visitor) override; virtual void accept(ParserVisitor *visitor) override;
}; };
Expression *parseFactor(Token **tokens, std::size_t *length); Expression *parseFactor(lex::Token **tokens, std::size_t *length);
Expression *parseTerm(Token **tokens, std::size_t *length); Expression *parseTerm(lex::Token **tokens, std::size_t *length);
Expression *parseExpression(Token **tokens, std::size_t *length); Expression *parseExpression(lex::Token **tokens, std::size_t *length);
Definition *parseDefinition(Token **tokens, std::size_t *length); Definition *parseDefinition(lex::Token **tokens, std::size_t *length);
Statement *parseStatement(Token **tokens, std::size_t *length); Statement *parseStatement(lex::Token **tokens, std::size_t *length);
Definition **parseDefinitions(Token **tokens, std::size_t *length, std::size_t *resultLength); Definition **parseDefinitions(lex::Token **tokens, std::size_t *length, std::size_t *resultLength);
Block *parseBlock(Token **tokens, std::size_t *length); Block *parseBlock(lex::Token **tokens, std::size_t *length);
Block *parse(Token *tokenStream, std::size_t length); Block *parse(lex::Token *tokenStream, std::size_t length);
} }

View File

@ -1,14 +1,18 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <boost/outcome.hpp> #include <variant>
#include <vector>
#include <forward_list>
namespace elna namespace elna
{
namespace source
{ {
/** /**
* Position in the source text. * Position in the source text.
*/ */
struct Position struct position
{ {
/// Line. /// Line.
std::size_t line = 1; std::size_t line = 1;
@ -20,18 +24,18 @@ namespace elna
/** /**
* A compilation error consists of an error message and position. * A compilation error consists of an error message and position.
*/ */
struct CompileError struct error
{ {
private: private:
char const *message; char const *message;
Position position; source::position position;
public: public:
/** /**
* @param message Error text. * \param message Error text.
* @param position Error position in the source text. * \param position Error position in the source text.
*/ */
CompileError(char const *message, const Position position) noexcept; error(char const *message, const source::position position) noexcept;
/// Error text. /// Error text.
const char *what() const noexcept; const char *what() const noexcept;
@ -44,7 +48,45 @@ namespace elna
}; };
template<typename T> template<typename T>
using result = boost::outcome_v2::result<T, CompileError>; struct result
{
using E = std::forward_list<source::error>;
template<typename... Args>
explicit result(Args&&... arguments)
: payload(std::forward<Args>(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<E>(payload);
}
bool is_success() const noexcept
{
return std::holds_alternative<T>(payload);
}
T& success()
{
return std::get<T>(payload);
}
E& errors()
{
return std::get<E>(payload);
}
private:
std::variant<T, E> payload;
};
}
enum class Target enum class Target
{ {
@ -64,8 +106,7 @@ namespace elna
{ {
Symbol(const char *name); Symbol(const char *name);
const char *name; const char *name;
unsigned char *text; std::vector<std::byte> text;
std::size_t length;
Reference symbols[3]; Reference symbols[3];
}; };
} }

View File

@ -87,6 +87,14 @@ namespace elna
lbu = 0b100, lbu = 0b100,
lhu = 0b101, lhu = 0b101,
jalr = 0b000, jalr = 0b000,
mul = 000,
mulh = 001,
mulhsu = 010,
mulhu = 011,
div = 100,
divu = 101,
rem = 110,
remu = 111
}; };
enum class Funct12 : std::uint8_t enum class Funct12 : std::uint8_t
@ -98,7 +106,8 @@ namespace elna
enum class Funct7 : std::uint8_t enum class Funct7 : std::uint8_t
{ {
none = 0, none = 0,
sub = 0b0100000 sub = 0b0100000,
muldiv = 0b0000001
}; };
enum class BaseOpcode : std::uint8_t 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& 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& r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7::none);
Instruction& u(XRegister rd, std::uint32_t imm); Instruction& u(XRegister rd, std::uint32_t imm);
std::uint8_t *encode();
const std::byte *cbegin();
const std::byte *cend();
private: private:
std::uint32_t instruction{ 0 }; std::uint32_t instruction{ 0 };

View File

@ -24,7 +24,7 @@ namespace elna
return result; 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; ELFIO::elfio writer;
@ -35,21 +35,23 @@ namespace elna
writer.set_type(ELFIO::ET_REL); writer.set_type(ELFIO::ET_REL);
writer.set_machine(ELFIO::EM_RISCV); writer.set_machine(ELFIO::EM_RISCV);
auto sourceText = readSource(inFile); auto sourceText = readSource(in_file.c_str());
if (sourceText == nullptr) if (sourceText == nullptr)
{ {
return 3; return 3;
} }
CompileError *compileError = nullptr;
size_t tokensCount{ 0 }; size_t tokensCount{ 0 };
auto tokens = lex(sourceText, compileError, &tokensCount); auto lex_result = lex::lex(sourceText);
free(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; return 1;
} }
auto ast = parse(tokens, tokensCount); auto ast = parse(lex_result.success().data(), tokensCount);
if (ast == nullptr) if (ast == nullptr)
{ {
return 2; return 2;
@ -61,7 +63,7 @@ namespace elna
text_sec->set_type(ELFIO::SHT_PROGBITS); text_sec->set_type(ELFIO::SHT_PROGBITS);
text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR); text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
text_sec->set_addr_align(0x1); text_sec->set_addr_align(0x1);
text_sec->set_data(reinterpret_cast<const char *>(program.text), program.length); text_sec->set_data(reinterpret_cast<const char *>(program.text.data()), program.text.size());
// Create string table section // Create string table section
ELFIO::section* str_sec = writer.sections.add(".strtab"); ELFIO::section* str_sec = writer.sections.add(".strtab");
@ -91,7 +93,7 @@ namespace elna
ELFIO::symbol_section_accessor syma(writer, sym_sec); ELFIO::symbol_section_accessor syma(writer, sym_sec);
auto label_sym = syma.add_symbol(stra, ".CL0", 0x00000000, strlen("%d\n") + 1, auto label_sym = syma.add_symbol(stra, ".CL0", 0x00000000, strlen("%d\n") + 1,
ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ro_sec->get_index()); 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()); ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text_sec->get_index());
auto printf_sym = syma.add_symbol(stra, "printf", 0x00000000, 0, auto printf_sym = syma.add_symbol(stra, "printf", 0x00000000, 0,
ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF); 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, 18 /* ELFIO::R_RISCV_CALL */);
rela.add_entry(program.symbols[2].offset, printf_sym, 51 /* ELFIO::R_RISCV_RELAX */); 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 // Create ELF object file
writer.save(outputFilename); writer.save(out_file);
return 0; return 0;
} }

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -1,7 +1,15 @@
#include "elna/lexer.hpp" #include "elna/lexer.hpp"
#include <cstring>
namespace elna namespace elna
{ {
namespace lex
{
using source_position = elna::source::position;
using source_error = elna::source::error;
using source_result = elna::source::result<std::vector<Token>>;
source::source(const std::string& buffer) source::source(const std::string& buffer)
: m_buffer(buffer) : m_buffer(buffer)
{ {
@ -14,18 +22,18 @@ namespace elna
source::const_iterator source::end() const 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); return source::const_iterator(std::cend(m_buffer), end_position);
} }
source::const_iterator::const_iterator(std::string::const_iterator buffer, 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) : 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; return this->m_position;
} }
@ -73,7 +81,7 @@ namespace elna
return !(*this == that); 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) : m_type(of), m_position(position)
{ {
std::size_t value_length = strlen(value); std::size_t value_length = strlen(value);
@ -85,13 +93,13 @@ namespace elna
m_value.identifier = buffer; 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_type(of), m_position(position)
{ {
m_value.number = number; 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) : m_type(of), m_position(position)
{ {
} }
@ -110,7 +118,7 @@ namespace elna
Token::~Token() 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<char*>(m_value.identifier)); std::free(const_cast<char*>(m_value.identifier));
} }
@ -120,7 +128,7 @@ namespace elna
{ {
m_type = that.of(); m_type = that.of();
m_position = that.position(); 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()); std::size_t value_length = strlen(that.identifier());
char *buffer = reinterpret_cast<char *>(malloc(value_length + 1)); char *buffer = reinterpret_cast<char *>(malloc(value_length + 1));
@ -130,7 +138,7 @@ namespace elna
m_value.identifier = buffer; m_value.identifier = buffer;
} }
else if (that.of() == TOKEN_NUMBER) else if (that.of() == type::number)
{ {
m_value.number = that.number(); m_value.number = that.number();
} }
@ -141,19 +149,19 @@ namespace elna
{ {
m_type = that.of(); m_type = that.of();
m_position = that.position(); 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(); m_value.identifier = that.identifier();
that.m_value.identifier = nullptr; that.m_value.identifier = nullptr;
} }
else if (that.of() == TOKEN_NUMBER) else if (that.of() == type::number)
{ {
m_value.number = that.number(); m_value.number = that.number();
} }
return *this; return *this;
} }
Token::Type Token::of() const noexcept Token::type Token::of() const noexcept
{ {
return m_type; return m_type;
} }
@ -168,12 +176,12 @@ namespace elna
return m_value.number; return m_value.number;
} }
const Position& Token::position() const noexcept const source_position& Token::position() const noexcept
{ {
return m_position; return m_position;
} }
Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length) source_result lex(const char *buffer)
{ {
std::vector<Token> tokens; std::vector<Token> tokens;
source input{ buffer }; source input{ buffer };
@ -186,38 +194,38 @@ namespace elna
else if (std::isdigit(*iterator)) else if (std::isdigit(*iterator))
{ {
tokens.emplace_back( tokens.emplace_back(
Token::TOKEN_NUMBER, Token::type::number,
static_cast<std::int32_t>(*iterator - '0'), static_cast<std::int32_t>(*iterator - '0'),
iterator.position() iterator.position()
); );
} }
else if (*iterator == '=') else if (*iterator == '=')
{ {
tokens.emplace_back(Token::TOKEN_EQUALS, iterator.position()); tokens.emplace_back(Token::type::equals, iterator.position());
} }
else if (*iterator == '(') else if (*iterator == '(')
{ {
tokens.emplace_back(Token::TOKEN_LEFT_PAREN, iterator.position()); tokens.emplace_back(Token::type::left_paren, iterator.position());
} }
else if (*iterator == ')') else if (*iterator == ')')
{ {
tokens.emplace_back(Token::TOKEN_RIGHT_PAREN, iterator.position()); tokens.emplace_back(Token::type::right_paren, iterator.position());
} }
else if (*iterator == ';') else if (*iterator == ';')
{ {
tokens.emplace_back(Token::TOKEN_SEMICOLON, iterator.position()); tokens.emplace_back(Token::type::semicolon, iterator.position());
} }
else if (*iterator == ',') else if (*iterator == ',')
{ {
tokens.emplace_back(Token::TOKEN_COMMA, iterator.position()); tokens.emplace_back(Token::type::comma, iterator.position());
} }
else if (*iterator == '!') else if (*iterator == '!')
{ {
tokens.emplace_back(Token::TOKEN_BANG, iterator.position()); tokens.emplace_back(Token::type::bang, iterator.position());
} }
else if (*iterator == '.') else if (*iterator == '.')
{ {
tokens.emplace_back(Token::TOKEN_DOT, iterator.position()); tokens.emplace_back(Token::type::dot, iterator.position());
} }
else if (std::isalpha(*iterator)) else if (std::isalpha(*iterator))
{ {
@ -230,15 +238,15 @@ namespace elna
} }
if (word == "const") if (word == "const")
{ {
tokens.emplace_back(Token::TOKEN_LET, iterator.position()); tokens.emplace_back(Token::type::let, iterator.position());
} }
else if (word == "var") else if (word == "var")
{ {
tokens.emplace_back(Token::TOKEN_VAR, iterator.position()); tokens.emplace_back(Token::type::var, iterator.position());
} }
else 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; iterator = i;
continue; continue;
@ -247,24 +255,21 @@ namespace elna
{ {
std::string _operator{ *iterator }; 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 else
{ {
*compile_error = CompileError("Unexpected next character", iterator.position()); return source_result("Unexpected next character", iterator.position());
return nullptr;
} }
++iterator; ++iterator;
} }
Token *target = reinterpret_cast<Token *>(malloc(tokens.size() * sizeof(Token) + sizeof(Token))); return source_result(tokens);
int i = 0; }
for (auto& token : tokens)
{
target[i] = std::move(token);
++i;
}
*length = i;
return target;
} }
} }

47
source/main.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "elna/cl.hpp"
#include <filesystem>
#include <iostream>
#include <boost/program_options.hpp>
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<std::filesystem::path>(), "Output object file")
;
description.add_options()
("input", boost::program_options::value<std::filesystem::path>()->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>();
std::filesystem::path out_file;
if (variables_map.count("output"))
{
out_file = variables_map["output"].as<std::filesystem::path>();
}
else
{
out_file = in_file.filename().replace_extension(".o");
}
return elna::compile(in_file, out_file);
}

View File

@ -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]);
}

View File

@ -40,16 +40,21 @@ namespace elna
this->lhs = lhs; this->lhs = lhs;
this->rhs = rhs; this->rhs = rhs;
if (_operator == '+') switch (_operator)
{ {
case '+':
this->_operator = BinaryOperator::sum; this->_operator = BinaryOperator::sum;
} break;
else if (_operator == '-') case '-':
{
this->_operator = BinaryOperator::subtraction; this->_operator = BinaryOperator::subtraction;
} break;
else case '*':
{ this->_operator = BinaryOperator::multiplication;
break;
case '/':
this->_operator = BinaryOperator::division;
break;
default:
throw std::logic_error("Invalid binary operator"); throw std::logic_error("Invalid binary operator");
} }
} }
@ -64,14 +69,14 @@ namespace elna
visitor->visit(this); visitor->visit(this);
} }
Block *parse(Token *tokenStream, std::size_t length) Block *parse(lex::Token *tokenStream, std::size_t length)
{ {
return parseBlock(&tokenStream, &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(); auto variable = new Variable();
variable->identifier = (*tokens)[0].identifier(); variable->identifier = (*tokens)[0].identifier();
@ -79,7 +84,7 @@ namespace elna
--(*length); --(*length);
return variable; return variable;
} }
else if ((*tokens)[0].of() == Token::TOKEN_NUMBER) else if ((*tokens)[0].of() == lex::Token::Token::type::number)
{ {
auto number = new Number(); auto number = new Number();
number->value = (*tokens)[0].number(); number->value = (*tokens)[0].number();
@ -87,7 +92,7 @@ namespace elna
--(*length); --(*length);
return number; return number;
} }
else if ((*tokens)[0].of() == Token::TOKEN_LEFT_PAREN) else if ((*tokens)[0].of() == lex::Token::type::left_paren)
{ {
++(*tokens); ++(*tokens);
--(*length); --(*length);
@ -102,15 +107,29 @@ namespace elna
return nullptr; 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); 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; return term;
} }
@ -122,17 +141,12 @@ namespace elna
if (expression != nullptr) if (expression != nullptr)
{ {
auto binaryExpression = new BinaryExpression(term, expression, _operator); return new BinaryExpression(term, expression, _operator);
return binaryExpression;
} }
else
{
return nullptr; return nullptr;
} }
}
Definition *parseDefinition(Token **tokens, size_t *length) Definition *parseDefinition(lex::Token **tokens, size_t *length)
{ {
auto definition = new Definition(); auto definition = new Definition();
definition->identifier = (*tokens)[0].identifier(); // Copy. definition->identifier = (*tokens)[0].identifier(); // Copy.
@ -141,7 +155,7 @@ namespace elna
++(*tokens); // Skip the equals sign. ++(*tokens); // Skip the equals sign.
*length -= 2; *length -= 2;
if ((*tokens)[0].of() == Token::TOKEN_NUMBER) if ((*tokens)[0].of() == lex::Token::type::number)
{ {
auto number = new Number(); auto number = new Number();
number->value = (*tokens)[0].number(); number->value = (*tokens)[0].number();
@ -153,9 +167,9 @@ namespace elna
return nullptr; 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); ++(*tokens);
--(*length); --(*length);
@ -174,7 +188,7 @@ namespace elna
return nullptr; 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. ++(*tokens); // Skip const.
--(*length); --(*length);
@ -193,11 +207,11 @@ namespace elna
realloc(definitions, (*resultLength + 1) * sizeof(Definition*))); realloc(definitions, (*resultLength + 1) * sizeof(Definition*)));
definitions[(*resultLength)++] = definition; definitions[(*resultLength)++] = definition;
if ((*tokens)[0].of() == Token::TOKEN_SEMICOLON) if ((*tokens)[0].of() == lex::Token::type::semicolon)
{ {
break; break;
} }
if ((*tokens)[0].of() == Token::TOKEN_COMMA) if ((*tokens)[0].of() == lex::Token::type::comma)
{ {
++(*tokens); ++(*tokens);
--(*length); --(*length);
@ -207,10 +221,10 @@ namespace elna
return definitions; return definitions;
} }
Block *parseBlock(Token **tokens, std::size_t *length) Block *parseBlock(lex::Token **tokens, std::size_t *length)
{ {
auto block = new Block(); auto block = new Block();
if ((*tokens)[0].of() == Token::TOKEN_LET) if ((*tokens)[0].of() == lex::Token::type::let)
{ {
size_t length_ = 0; size_t length_ = 0;
auto constDefinitions = parseDefinitions(tokens, length, &length_); auto constDefinitions = parseDefinitions(tokens, length, &length_);

View File

@ -2,26 +2,29 @@
namespace elna 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->message = message;
this->position = position; this->position = position;
} }
char const *CompileError::what() const noexcept char const *error::what() const noexcept
{ {
return this->message; return this->message;
} }
std::size_t CompileError::line() const noexcept std::size_t error::line() const noexcept
{ {
return this->position.line; return this->position.line;
} }
std::size_t CompileError::column() const noexcept std::size_t error::column() const noexcept
{ {
return this->position.column; return this->position.column;
} }
}
Symbol::Symbol(const char *name) Symbol::Symbol(const char *name)
{ {

View File

@ -1,7 +1,7 @@
#include "elna/parser.hpp" #include "elna/parser.hpp"
#include "elna/riscv.hpp" #include "elna/riscv.hpp"
#include <memory> #include <memory>
#include <type_traits> #include <cstring>
namespace elna namespace elna
{ {
@ -49,9 +49,14 @@ namespace elna
return *this; return *this;
} }
std::uint8_t *Instruction::encode() const std::byte *Instruction::cbegin()
{ {
return reinterpret_cast<std::uint8_t *>(&this->instruction); return reinterpret_cast<std::byte *>(&this->instruction);
}
const std::byte *Instruction::cend()
{
return reinterpret_cast<std::byte *>(&this->instruction) + sizeof(this->instruction);
} }
void RiscVVisitor::visit(Node *) void RiscVVisitor::visit(Node *)
@ -154,7 +159,7 @@ namespace elna
std::size_t i = 0; std::size_t i = 0;
for (; i < constCount; ++i) for (; i < constCount; ++i)
{ {
if (strcmp(variable->identifier, constNames[i]) == 0) if (std::strcmp(variable->identifier, constNames[i]) == 0)
{ {
break; break;
} }
@ -218,6 +223,10 @@ namespace elna
this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op) this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op)
.r(lhs_register, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub); .r(lhs_register, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub);
break; 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.symbols[i] = visitor->references[i];
} }
program.text = reinterpret_cast<unsigned char *>(malloc(sizeof(std::uint32_t) * visitor->instructionsLength));
for (std::size_t i = 0; i < visitor->instructionsLength; ++i) for (std::size_t i = 0; i < visitor->instructionsLength; ++i)
{ {
memcpy(program.text + program.length, visitor->instructions[i].encode(), sizeof(std::uint32_t)); program.text.insert(program.text.cend(),
program.length += sizeof(std::uint32_t); visitor->instructions[i].cbegin(), visitor->instructions[i].cend());
} }
return program; return program;
} }

View File

@ -0,0 +1 @@
10

1
tests/multiply.eln Normal file
View File

@ -0,0 +1 @@
! 5 * 2

View File

@ -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) 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) static int build_test(const std::filesystem::directory_entry& test_entry)
@ -136,7 +141,7 @@ namespace elna
int main() 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::cout << "Run all tests and check the results" << std::endl;
std::filesystem::path test_directory{ "tests" }; std::filesystem::path test_directory{ "tests" };