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_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})
|
||||||
|
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
|
- 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.
|
|
||||||
|
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
|
#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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,92 @@
|
|||||||
#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.
|
|
||||||
*/
|
|
||||||
struct Position
|
|
||||||
{
|
{
|
||||||
/// 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.
|
* Position in the source text.
|
||||||
* @param position Error position in the source text.
|
|
||||||
*/
|
*/
|
||||||
CompileError(char const *message, const Position position) noexcept;
|
struct position
|
||||||
|
{
|
||||||
|
/// Line.
|
||||||
|
std::size_t line = 1;
|
||||||
|
|
||||||
/// Error text.
|
/// Column.
|
||||||
const char *what() const noexcept;
|
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.
|
public:
|
||||||
std::size_t column() const noexcept;
|
/**
|
||||||
};
|
* \param message Error text.
|
||||||
|
* \param position Error position in the source text.
|
||||||
|
*/
|
||||||
|
error(char const *message, const source::position position) noexcept;
|
||||||
|
|
||||||
template<typename T>
|
/// Error text.
|
||||||
using result = boost::outcome_v2::result<T, CompileError>;
|
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
|
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];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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 };
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 "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
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->lhs = lhs;
|
||||||
this->rhs = rhs;
|
this->rhs = rhs;
|
||||||
|
|
||||||
if (_operator == '+')
|
switch (_operator)
|
||||||
{
|
{
|
||||||
this->_operator = BinaryOperator::sum;
|
case '+':
|
||||||
}
|
this->_operator = BinaryOperator::sum;
|
||||||
else if (_operator == '-')
|
break;
|
||||||
{
|
case '-':
|
||||||
this->_operator = BinaryOperator::subtraction;
|
this->_operator = BinaryOperator::subtraction;
|
||||||
}
|
break;
|
||||||
else
|
case '*':
|
||||||
{
|
this->_operator = BinaryOperator::multiplication;
|
||||||
throw std::logic_error("Invalid binary operator");
|
break;
|
||||||
|
case '/':
|
||||||
|
this->_operator = BinaryOperator::division;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
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_);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
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)
|
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" };
|
||||||
|
Loading…
Reference in New Issue
Block a user