Implement multiplication
This commit is contained in:
parent
223f54d38d
commit
632dc53b53
@ -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})
|
||||
|
19
Rakefile
19
Rakefile
@ -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
30
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.
|
||||
|
11
dub.json
11
dub.json
@ -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"]
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#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<std::vector<Token>> lex(const char *buffer);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,50 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <boost/outcome.hpp>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <forward_list>
|
||||
|
||||
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<typename T>
|
||||
using result = boost::outcome_v2::result<T, CompileError>;
|
||||
/// 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<typename T>
|
||||
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
|
||||
{
|
||||
@ -64,8 +106,7 @@ namespace elna
|
||||
{
|
||||
Symbol(const char *name);
|
||||
const char *name;
|
||||
unsigned char *text;
|
||||
std::size_t length;
|
||||
std::vector<std::byte> text;
|
||||
Reference symbols[3];
|
||||
};
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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<const char *>(program.text), program.length);
|
||||
text_sec->set_data(reinterpret_cast<const char *>(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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
1060
source/elna/elf.d
1060
source/elna/elf.d
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
}
|
@ -1,7 +1,15 @@
|
||||
#include "elna/lexer.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
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)
|
||||
: 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<char*>(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<char *>(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<Token> 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<std::int32_t>(*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<Token *>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
47
source/main.cpp
Normal file
47
source/main.cpp
Normal 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);
|
||||
}
|
@ -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]);
|
||||
}
|
@ -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_);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "elna/parser.hpp"
|
||||
#include "elna/riscv.hpp"
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
@ -49,9 +49,14 @@ namespace elna
|
||||
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 *)
|
||||
@ -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<unsigned char *>(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;
|
||||
}
|
||||
|
1
tests/expectations/multiply.txt
Normal file
1
tests/expectations/multiply.txt
Normal file
@ -0,0 +1 @@
|
||||
10
|
1
tests/multiply.eln
Normal file
1
tests/multiply.eln
Normal file
@ -0,0 +1 @@
|
||||
! 5 * 2
|
@ -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" };
|
||||
|
Loading…
Reference in New Issue
Block a user