Print test summary
This commit is contained in:
parent
03a72fc583
commit
4d46fc6b4d
@ -12,11 +12,14 @@ add_executable(elnsh shell/main.cpp
|
|||||||
shell/interactive.cpp include/elna/interactive.hpp
|
shell/interactive.cpp include/elna/interactive.hpp
|
||||||
shell/history.cpp include/elna/history.hpp
|
shell/history.cpp include/elna/history.hpp
|
||||||
shell/state.cpp include/elna/state.hpp
|
shell/state.cpp include/elna/state.hpp
|
||||||
shell/result.cpp include/elna/result.hpp
|
|
||||||
)
|
)
|
||||||
target_include_directories(elnsh PRIVATE include)
|
target_include_directories(elnsh PRIVATE include)
|
||||||
|
|
||||||
add_library(elna
|
add_library(elna
|
||||||
source/lexer.cpp include/elna/lexer.hpp
|
source/lexer.cpp include/elna/lexer.hpp
|
||||||
|
source/result.cpp include/elna/result.hpp
|
||||||
|
source/riscv.cpp include/elna/riscv.hpp
|
||||||
|
source/ir.cpp include/elna/ir.hpp
|
||||||
|
include/elna/parser.hpp
|
||||||
)
|
)
|
||||||
target_include_directories(elna PRIVATE include)
|
target_include_directories(elna PRIVATE include)
|
||||||
|
75
Rakefile
75
Rakefile
@ -4,87 +4,16 @@ require 'open3'
|
|||||||
|
|
||||||
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
||||||
BINARY = 'build/bin/elna'
|
BINARY = 'build/bin/elna'
|
||||||
TESTS = FileList['tests/*.eln'].flat_map do |test|
|
|
||||||
build = Pathname.new 'build'
|
|
||||||
test_basename = Pathname.new(test).basename('')
|
|
||||||
|
|
||||||
[build + 'riscv' + test_basename].map { |path| path.sub_ext('').to_path }
|
|
||||||
end
|
|
||||||
|
|
||||||
SOURCES = FileList['source/**/*.d']
|
SOURCES = FileList['source/**/*.d']
|
||||||
|
|
||||||
directory 'build'
|
directory 'build/riscv'
|
||||||
|
|
||||||
CLEAN.include 'build'
|
CLEAN.include 'build'
|
||||||
CLEAN.include '.dub'
|
CLEAN.include '.dub'
|
||||||
|
|
||||||
rule(/build\/riscv\/[^\/\.]+$/ => ->(file) { test_for_out(file, '.o') }) do |t|
|
|
||||||
sh '/opt/riscv/bin/riscv32-unknown-elf-ld',
|
|
||||||
'-o', t.name,
|
|
||||||
'-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/',
|
|
||||||
'-L/opt/riscv/riscv32-unknown-elf/lib',
|
|
||||||
'/opt/riscv/riscv32-unknown-elf/lib/crt0.o',
|
|
||||||
'/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtbegin.o',
|
|
||||||
t.source,
|
|
||||||
'--start-group', '-lgcc', '-lc', '-lgloss', '--end-group',
|
|
||||||
'/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o'
|
|
||||||
end
|
|
||||||
|
|
||||||
rule(/build\/riscv\/.+\.o$/ => ->(file) { test_for_object(file, '.eln') }) do |t|
|
|
||||||
Pathname.new(t.name).dirname.mkpath
|
|
||||||
sh BINARY, '-o', t.name, t.source
|
|
||||||
end
|
|
||||||
|
|
||||||
file BINARY => SOURCES do |t|
|
file BINARY => SOURCES do |t|
|
||||||
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
|
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task default: 'build/riscv'
|
||||||
task default: BINARY
|
task default: BINARY
|
||||||
|
|
||||||
desc 'Run all tests and check the results'
|
|
||||||
task test: TESTS
|
|
||||||
task test: BINARY do
|
|
||||||
TESTS.each do |test|
|
|
||||||
expected = Pathname
|
|
||||||
.new(test)
|
|
||||||
.sub_ext('.txt')
|
|
||||||
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
|
|
||||||
.to_path
|
|
||||||
|
|
||||||
puts "Running #{test}"
|
|
||||||
if test.include? '/riscv/'
|
|
||||||
spike = [
|
|
||||||
'/opt/riscv/bin/spike',
|
|
||||||
'--isa=RV32IMAC',
|
|
||||||
'/opt/riscv/riscv32-unknown-elf/bin/pk',
|
|
||||||
test
|
|
||||||
]
|
|
||||||
diff = ['diff', '-Nur', '--color', expected, '-']
|
|
||||||
tail = ['tail', '-n', '1']
|
|
||||||
|
|
||||||
last_stdout, wait_threads = Open3.pipeline_r spike, tail, diff
|
|
||||||
else
|
|
||||||
raise 'Unsupported test platform'
|
|
||||||
end
|
|
||||||
print last_stdout.read
|
|
||||||
last_stdout.close
|
|
||||||
|
|
||||||
fail unless wait_threads.last.value.exitstatus.zero?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_object(out_file, extension)
|
|
||||||
test_source = Pathname
|
|
||||||
.new(out_file)
|
|
||||||
.sub_ext(extension)
|
|
||||||
.sub(/^build\/[[:alpha:]]+\//, 'tests/')
|
|
||||||
.to_path
|
|
||||||
[test_source, BINARY]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_out(out_file, extension)
|
|
||||||
Pathname
|
|
||||||
.new(out_file)
|
|
||||||
.sub_ext(extension)
|
|
||||||
.to_path
|
|
||||||
end
|
|
||||||
|
4
dub.json
4
dub.json
@ -5,5 +5,7 @@
|
|||||||
"name": "elna",
|
"name": "elna",
|
||||||
"targetType": "executable",
|
"targetType": "executable",
|
||||||
"targetPath": "build/bin",
|
"targetPath": "build/bin",
|
||||||
"mainSourceFile": "source/main.d"
|
"mainSourceFile": "source/main.d",
|
||||||
|
"libs": ["elna", "stdc++"],
|
||||||
|
"lflags": ["-Lbuild"]
|
||||||
}
|
}
|
||||||
|
95
include/elna/ir.hpp
Normal file
95
include/elna/ir.hpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "elna/parser.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace elna::ir
|
||||||
|
{
|
||||||
|
class Node;
|
||||||
|
class Definition;
|
||||||
|
class Operand;
|
||||||
|
class BinaryExpression;
|
||||||
|
class Variable;
|
||||||
|
class VariableDeclaration;
|
||||||
|
class Number;
|
||||||
|
|
||||||
|
struct IRVisitor
|
||||||
|
{
|
||||||
|
virtual void visit(Node *) = 0;
|
||||||
|
virtual void visit(Definition *) = 0;
|
||||||
|
virtual void visit(Operand *) = 0;
|
||||||
|
virtual void visit(BinaryExpression *) = 0;
|
||||||
|
virtual void visit(Variable *) = 0;
|
||||||
|
virtual void visit(Number *) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AST node.
|
||||||
|
*/
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void accept(IRVisitor *) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition.
|
||||||
|
*/
|
||||||
|
class Definition : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BinaryExpression **statements;
|
||||||
|
std::size_t statementsLength;
|
||||||
|
Operand *result;
|
||||||
|
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Statement : public Node
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class Operand : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Number : public Operand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::int32_t value;
|
||||||
|
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Variable : public Operand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::size_t counter;
|
||||||
|
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BinaryExpression : public Statement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Operand *lhs, *rhs;
|
||||||
|
BinaryOperator _operator;
|
||||||
|
|
||||||
|
BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator);
|
||||||
|
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BangExpression : public Statement
|
||||||
|
{
|
||||||
|
Operand *operand;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BangExpression(Operand *operand);
|
||||||
|
|
||||||
|
virtual void accept(IRVisitor *visitor) override;
|
||||||
|
};
|
||||||
|
}
|
@ -14,10 +14,10 @@ namespace elna
|
|||||||
class const_iterator
|
class const_iterator
|
||||||
{
|
{
|
||||||
std::string::const_iterator m_buffer;
|
std::string::const_iterator m_buffer;
|
||||||
source_position m_position;
|
Position m_position;
|
||||||
|
|
||||||
const_iterator(std::string::const_iterator buffer,
|
const_iterator(std::string::const_iterator buffer,
|
||||||
const source_position position = source_position());
|
const Position position = Position());
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iterator_category = std::forward_iterator_tag;
|
using iterator_category = std::forward_iterator_tag;
|
||||||
@ -26,7 +26,7 @@ namespace elna
|
|||||||
using pointer = const value_type *;
|
using pointer = const value_type *;
|
||||||
using reference = const value_type&;
|
using reference = const value_type&;
|
||||||
|
|
||||||
const source_position& position() const noexcept;
|
const Position& position() const noexcept;
|
||||||
|
|
||||||
reference operator*() const noexcept;
|
reference operator*() const noexcept;
|
||||||
pointer operator->() const noexcept;
|
pointer operator->() const noexcept;
|
||||||
@ -43,38 +43,61 @@ namespace elna
|
|||||||
const_iterator end() const;
|
const_iterator end() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string& m_buffer;
|
const std::string m_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Union type representing a single token.
|
* Union type representing a single token.
|
||||||
*/
|
*/
|
||||||
struct token
|
struct Token
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Token type.
|
* Token type.
|
||||||
*/
|
*/
|
||||||
enum class type
|
enum Type : std::uint16_t
|
||||||
{
|
{
|
||||||
word,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of the token value.
|
* Type of the token value.
|
||||||
*/
|
*/
|
||||||
using value = std::string;
|
union Value
|
||||||
|
{
|
||||||
|
std::int32_t number;
|
||||||
|
const char *identifier;
|
||||||
|
};
|
||||||
|
|
||||||
token(const type of, source::const_iterator begin, source::const_iterator end);
|
Token(Type of, Position position);
|
||||||
|
Token(Type of, std::int32_t value, Position position);
|
||||||
|
Token(Type of, const char *value, Position position);
|
||||||
|
Token(const Token& that);
|
||||||
|
Token(Token&& that);
|
||||||
|
~Token();
|
||||||
|
|
||||||
type of() const noexcept;
|
Token& operator=(const Token& that);
|
||||||
const value& identifier() const noexcept;
|
Token& operator=(Token&& that);
|
||||||
const source_position& position() const noexcept;
|
|
||||||
|
Type of() const noexcept;
|
||||||
|
const char *identifier() const noexcept;
|
||||||
|
std::int32_t number() const noexcept;
|
||||||
|
const Position& position() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_value;
|
Type m_type;
|
||||||
source_position m_position;
|
Value m_value;
|
||||||
type m_type;
|
Position m_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,5 +105,5 @@ namespace elna
|
|||||||
*
|
*
|
||||||
* \return Tokens or error.
|
* \return Tokens or error.
|
||||||
*/
|
*/
|
||||||
result<std::vector<token>> lex(const std::string& buffer);
|
Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length);
|
||||||
}
|
}
|
||||||
|
10
include/elna/parser.hpp
Normal file
10
include/elna/parser.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace elna
|
||||||
|
{
|
||||||
|
enum class BinaryOperator
|
||||||
|
{
|
||||||
|
sum,
|
||||||
|
subtraction
|
||||||
|
};
|
||||||
|
}
|
@ -8,7 +8,7 @@ namespace elna
|
|||||||
/**
|
/**
|
||||||
* Position in the source text.
|
* Position in the source text.
|
||||||
*/
|
*/
|
||||||
struct source_position
|
struct Position
|
||||||
{
|
{
|
||||||
/// Line.
|
/// Line.
|
||||||
std::size_t line = 1;
|
std::size_t line = 1;
|
||||||
@ -20,18 +20,18 @@ namespace elna
|
|||||||
/**
|
/**
|
||||||
* A compilation error consists of an error message and position.
|
* A compilation error consists of an error message and position.
|
||||||
*/
|
*/
|
||||||
struct compile_error
|
struct CompileError
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
char const *message;
|
char const *message;
|
||||||
source_position position;
|
Position position;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param message Error text.
|
* @param message Error text.
|
||||||
* @param position Error position in the source text.
|
* @param position Error position in the source text.
|
||||||
*/
|
*/
|
||||||
compile_error(char const *message, const source_position position) noexcept;
|
CompileError(char const *message, const Position position) noexcept;
|
||||||
|
|
||||||
/// Error text.
|
/// Error text.
|
||||||
const char *what() const noexcept;
|
const char *what() const noexcept;
|
||||||
@ -44,5 +44,19 @@ namespace elna
|
|||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using result = boost::outcome_v2::result<T, compile_error>;
|
using result = boost::outcome_v2::result<T, CompileError>;
|
||||||
|
|
||||||
|
enum class Target
|
||||||
|
{
|
||||||
|
text,
|
||||||
|
high20,
|
||||||
|
lower12i
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Reference
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
size_t offset;
|
||||||
|
Target target;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
148
include/elna/riscv.hpp
Normal file
148
include/elna/riscv.hpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "elna/ir.hpp"
|
||||||
|
#include "elna/result.hpp"
|
||||||
|
|
||||||
|
namespace elna
|
||||||
|
{
|
||||||
|
enum class XRegister : std::uint8_t
|
||||||
|
{
|
||||||
|
zero = 0,
|
||||||
|
ra = 1,
|
||||||
|
sp = 2,
|
||||||
|
gp = 3,
|
||||||
|
tp = 4,
|
||||||
|
t0 = 5,
|
||||||
|
t1 = 6,
|
||||||
|
t2 = 7,
|
||||||
|
s0 = 8,
|
||||||
|
s1 = 9,
|
||||||
|
a0 = 10,
|
||||||
|
a1 = 11,
|
||||||
|
a2 = 12,
|
||||||
|
a3 = 13,
|
||||||
|
a4 = 14,
|
||||||
|
a5 = 15,
|
||||||
|
a6 = 16,
|
||||||
|
a7 = 17,
|
||||||
|
s2 = 18,
|
||||||
|
s3 = 19,
|
||||||
|
s4 = 20,
|
||||||
|
s5 = 21,
|
||||||
|
s6 = 22,
|
||||||
|
s7 = 23,
|
||||||
|
s8 = 24,
|
||||||
|
s9 = 25,
|
||||||
|
s10 = 26,
|
||||||
|
s11 = 27,
|
||||||
|
t3 = 28,
|
||||||
|
t4 = 29,
|
||||||
|
t5 = 30,
|
||||||
|
t6 = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Funct3 : std::uint8_t
|
||||||
|
{
|
||||||
|
addi = 0b000,
|
||||||
|
slti = 0b001,
|
||||||
|
sltiu = 0b011,
|
||||||
|
andi = 0b111,
|
||||||
|
ori = 0b110,
|
||||||
|
xori = 0b100,
|
||||||
|
slli = 0b000,
|
||||||
|
srli = 0b101,
|
||||||
|
srai = 0b101,
|
||||||
|
add = 0b000,
|
||||||
|
slt = 0b010,
|
||||||
|
sltu = 0b011,
|
||||||
|
_and = 0b111,
|
||||||
|
_or = 0b110,
|
||||||
|
_xor = 0b100,
|
||||||
|
sll = 0b001,
|
||||||
|
srl = 0b101,
|
||||||
|
sub = 0b000,
|
||||||
|
sra = 0b101,
|
||||||
|
beq = 0b000,
|
||||||
|
bne = 0b001,
|
||||||
|
blt = 0b100,
|
||||||
|
bltu = 0b110,
|
||||||
|
bge = 0b101,
|
||||||
|
bgeu = 0b111,
|
||||||
|
fence = 0b000,
|
||||||
|
fenceI = 0b001,
|
||||||
|
csrrw = 0b001,
|
||||||
|
csrrs = 0b010,
|
||||||
|
csrrc = 0b011,
|
||||||
|
csrrwi = 0b101,
|
||||||
|
csrrsi = 0b110,
|
||||||
|
csrrci = 0b111,
|
||||||
|
priv = 0b000,
|
||||||
|
sb = 0b000,
|
||||||
|
sh = 0b001,
|
||||||
|
sw = 0b010,
|
||||||
|
lb = 0b000,
|
||||||
|
lh = 0b001,
|
||||||
|
lw = 0b010,
|
||||||
|
lbu = 0b100,
|
||||||
|
lhu = 0b101,
|
||||||
|
jalr = 0b000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Funct12 : std::uint8_t
|
||||||
|
{
|
||||||
|
ecall = 0b000000000000,
|
||||||
|
ebreak = 0b000000000001,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Funct7 : std::uint8_t
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
sub = 0b0100000
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BaseOpcode : std::uint8_t
|
||||||
|
{
|
||||||
|
opImm = 0b0010011,
|
||||||
|
lui = 0b0110111,
|
||||||
|
auipc = 0b0010111,
|
||||||
|
op = 0b0110011,
|
||||||
|
jal = 0b1101111,
|
||||||
|
jalr = 0b1100111,
|
||||||
|
branch = 0b1100011,
|
||||||
|
load = 0b0000011,
|
||||||
|
store = 0b0100011,
|
||||||
|
miscMem = 0b0001111,
|
||||||
|
system = 0b1110011,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instruction
|
||||||
|
{
|
||||||
|
Instruction(BaseOpcode opcode);
|
||||||
|
|
||||||
|
Instruction& i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate);
|
||||||
|
Instruction& s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2);
|
||||||
|
Instruction& r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7::none);
|
||||||
|
Instruction& u(XRegister rd, std::uint32_t imm);
|
||||||
|
std::uint8_t *encode();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uint32_t instruction{ 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class RiscVVisitor : public ir::IRVisitor
|
||||||
|
{
|
||||||
|
Instruction *instructions;
|
||||||
|
std::size_t instructionsLength;
|
||||||
|
bool registerInUse;
|
||||||
|
std::uint32_t variableCounter = 1;
|
||||||
|
Reference references[3];
|
||||||
|
|
||||||
|
virtual void visit(ir::Node *) override;
|
||||||
|
virtual void visit(ir::Definition *definition) override;
|
||||||
|
virtual void visit(ir::Operand *operand) override;
|
||||||
|
virtual void visit(ir::Variable *variable) override;
|
||||||
|
virtual void visit(ir::Number *number) override;
|
||||||
|
virtual void visit(ir::BinaryExpression *expression) override;
|
||||||
|
};
|
||||||
|
}
|
@ -2,6 +2,7 @@ module elna.backend;
|
|||||||
|
|
||||||
import core.stdc.stdio;
|
import core.stdc.stdio;
|
||||||
import core.stdc.stdlib;
|
import core.stdc.stdlib;
|
||||||
|
import core.stdc.string;
|
||||||
import elna.elf;
|
import elna.elf;
|
||||||
import elna.ir;
|
import elna.ir;
|
||||||
import elna.extended;
|
import elna.extended;
|
||||||
@ -17,7 +18,7 @@ import tanya.container.array;
|
|||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
import tanya.memory.allocator;
|
import tanya.memory.allocator;
|
||||||
|
|
||||||
private Nullable!String readSource(string source) @nogc
|
private char* readSource(string source) @nogc
|
||||||
{
|
{
|
||||||
enum size_t bufferSize = 255;
|
enum size_t bufferSize = 255;
|
||||||
auto sourceFilename = String(source);
|
auto sourceFilename = String(source);
|
||||||
@ -25,31 +26,39 @@ private Nullable!String readSource(string source) @nogc
|
|||||||
return readFile(sourceFilename).match!(
|
return readFile(sourceFilename).match!(
|
||||||
(ErrorCode errorCode) {
|
(ErrorCode errorCode) {
|
||||||
perror(sourceFilename.toStringz);
|
perror(sourceFilename.toStringz);
|
||||||
return Nullable!String();
|
return null;
|
||||||
},
|
},
|
||||||
(Array!ubyte contents) => nullable(String(cast(char[]) contents.get))
|
(Array!ubyte contents) {
|
||||||
|
char* cString = cast(char*) malloc(contents.length + 1);
|
||||||
|
memcpy(cString, contents.get.ptr, contents.length);
|
||||||
|
cString[contents.length] = '\0';
|
||||||
|
|
||||||
|
return cString;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int generate(string inFile, ref String outputFilename) @nogc
|
int generate(string inFile, ref String outputFilename) @nogc
|
||||||
{
|
{
|
||||||
auto sourceText = readSource(inFile);
|
auto sourceText = readSource(inFile);
|
||||||
if (sourceText.isNull)
|
if (sourceText is null)
|
||||||
{
|
{
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
auto tokens = lex(sourceText.get.get);
|
CompileError compileError = void;
|
||||||
if (!tokens.valid)
|
size_t tokensCount;
|
||||||
|
auto tokens = lex(sourceText, &compileError, &tokensCount);
|
||||||
|
free(sourceText);
|
||||||
|
if (tokens is null)
|
||||||
{
|
{
|
||||||
auto compileError = tokens.error.get;
|
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
|
||||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
auto ast = parse(tokens.result);
|
auto ast = parse(tokens[0 .. tokensCount]);
|
||||||
if (!ast.valid)
|
if (!ast.valid)
|
||||||
{
|
{
|
||||||
auto compileError = ast.error.get;
|
compileError = ast.error.get;
|
||||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
|
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
auto transformVisitor = cast(TransformVisitor) malloc(__traits(classInstanceSize, TransformVisitor));
|
auto transformVisitor = cast(TransformVisitor) malloc(__traits(classInstanceSize, TransformVisitor));
|
||||||
@ -85,18 +94,18 @@ int generate(string inFile, ref String outputFilename) @nogc
|
|||||||
|
|
||||||
final switch (reference.target)
|
final switch (reference.target)
|
||||||
{
|
{
|
||||||
case Reference.Target.text:
|
case Target.text:
|
||||||
relocationEntry.r_info = R_RISCV_CALL;
|
relocationEntry.r_info = R_RISCV_CALL;
|
||||||
break;
|
break;
|
||||||
case Reference.Target.high20:
|
case Target.high20:
|
||||||
relocationEntry.r_info = R_RISCV_HI20;
|
relocationEntry.r_info = R_RISCV_HI20;
|
||||||
break;
|
break;
|
||||||
case Reference.Target.lower12i:
|
case Target.lower12i:
|
||||||
relocationEntry.r_info = R_RISCV_LO12_I;
|
relocationEntry.r_info = R_RISCV_LO12_I;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
elf.relocate(reference.name, relocationEntry, relocationSub);
|
elf.relocate(String(reference.name[0 .. strlen(reference.name)]), relocationEntry, relocationSub);
|
||||||
}
|
}
|
||||||
|
|
||||||
elf.finish();
|
elf.finish();
|
||||||
|
106
source/elna/ir.d
106
source/elna/ir.d
@ -1,5 +1,6 @@
|
|||||||
module elna.ir;
|
module elna.ir;
|
||||||
|
|
||||||
|
import core.stdc.stdlib;
|
||||||
import parser = elna.parser;
|
import parser = elna.parser;
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.hashtable;
|
import tanya.container.hashtable;
|
||||||
@ -14,7 +15,6 @@ struct ASTMapping
|
|||||||
{
|
{
|
||||||
alias Node = .Node;
|
alias Node = .Node;
|
||||||
alias Definition = .Definition;
|
alias Definition = .Definition;
|
||||||
alias VariableDeclaration = .VariableDeclaration;
|
|
||||||
alias Statement = .Operand;
|
alias Statement = .Operand;
|
||||||
alias BangStatement = .Operand;
|
alias BangStatement = .Operand;
|
||||||
alias Block = .Definition;
|
alias Block = .Definition;
|
||||||
@ -28,20 +28,21 @@ struct ASTMapping
|
|||||||
/**
|
/**
|
||||||
* IR visitor.
|
* IR visitor.
|
||||||
*/
|
*/
|
||||||
interface IRVisitor
|
extern(C++, "elna", "ir")
|
||||||
|
abstract class IRVisitor
|
||||||
{
|
{
|
||||||
abstract void visit(Node) @nogc;
|
abstract void visit(Node) @nogc;
|
||||||
abstract void visit(Definition) @nogc;
|
abstract void visit(Definition) @nogc;
|
||||||
abstract void visit(Operand) @nogc;
|
abstract void visit(Operand) @nogc;
|
||||||
abstract void visit(BinaryExpression) @nogc;
|
abstract void visit(BinaryExpression) @nogc;
|
||||||
abstract void visit(Variable) @nogc;
|
abstract void visit(Variable) @nogc;
|
||||||
abstract void visit(VariableDeclaration) @nogc;
|
|
||||||
abstract void visit(Number) @nogc;
|
abstract void visit(Number) @nogc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AST node.
|
* AST node.
|
||||||
*/
|
*/
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
abstract class Node
|
abstract class Node
|
||||||
{
|
{
|
||||||
abstract void accept(IRVisitor) @nogc;
|
abstract void accept(IRVisitor) @nogc;
|
||||||
@ -50,99 +51,69 @@ abstract class Node
|
|||||||
/**
|
/**
|
||||||
* Definition.
|
* Definition.
|
||||||
*/
|
*/
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
class Definition : Node
|
class Definition : Node
|
||||||
{
|
{
|
||||||
char[] identifier;
|
BinaryExpression* statements;
|
||||||
Array!BinaryExpression statements;
|
size_t statementsLength;
|
||||||
Array!VariableDeclaration variableDeclarations;
|
|
||||||
Operand result;
|
Operand result;
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
abstract class Statement : Node
|
abstract class Statement : Node
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
abstract class Operand : Node
|
abstract class Operand : Node
|
||||||
{
|
{
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
class Number : Operand
|
class Number : Operand
|
||||||
{
|
{
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
class Variable : Operand
|
class Variable : Operand
|
||||||
{
|
{
|
||||||
size_t counter;
|
size_t counter;
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VariableDeclaration : Node
|
|
||||||
{
|
|
||||||
String identifier;
|
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
class BinaryExpression : Statement
|
class BinaryExpression : Statement
|
||||||
{
|
{
|
||||||
Operand lhs, rhs;
|
Operand lhs, rhs;
|
||||||
BinaryOperator operator;
|
BinaryOperator operator;
|
||||||
|
|
||||||
this(Operand lhs, Operand rhs, BinaryOperator operator)
|
this(Operand lhs, Operand rhs, BinaryOperator operator) @nogc;
|
||||||
@nogc
|
|
||||||
{
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
this.lhs = lhs;
|
|
||||||
this.rhs = rhs;
|
|
||||||
this.operator = operator;
|
|
||||||
}
|
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna", "ir")
|
||||||
class BangExpression : Statement
|
class BangExpression : Statement
|
||||||
{
|
{
|
||||||
Operand operand;
|
Operand operand;
|
||||||
|
|
||||||
this(Operand operand)
|
this(Operand operand);
|
||||||
{
|
|
||||||
this.operand = operand;
|
|
||||||
}
|
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc;
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
||||||
{
|
{
|
||||||
private HashTable!(String, int) constants;
|
private HashTable!(String, int) constants;
|
||||||
private Array!BinaryExpression statements;
|
private BinaryExpression* statements;
|
||||||
|
private size_t statementsLength;
|
||||||
|
|
||||||
ASTMapping.Node visit(parser.Node node) @nogc
|
ASTMapping.Node visit(parser.Node node) @nogc
|
||||||
{
|
{
|
||||||
@ -154,11 +125,6 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
assert(false, "Not implemented");
|
assert(false, "Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTMapping.VariableDeclaration visit(parser.VariableDeclaration declaration) @nogc
|
|
||||||
{
|
|
||||||
assert(false, "Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
||||||
{
|
{
|
||||||
return statement.expression.accept(this);
|
return statement.expression.accept(this);
|
||||||
@ -171,8 +137,7 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
|
|
||||||
target.result = block.statement.accept(this);
|
target.result = block.statement.accept(this);
|
||||||
target.statements = this.statements;
|
target.statements = this.statements;
|
||||||
|
target.statementsLength = this.statementsLength;
|
||||||
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
|
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@ -217,10 +182,12 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
binaryExpression.rhs.accept(this),
|
binaryExpression.rhs.accept(this),
|
||||||
binaryExpression.operator
|
binaryExpression.operator
|
||||||
);
|
);
|
||||||
statements.insertBack(target);
|
this.statements = cast(BinaryExpression*)
|
||||||
|
realloc(this.statements, (this.statementsLength + 1) * BinaryExpression.sizeof);
|
||||||
|
this.statements[this.statementsLength++] = target;
|
||||||
|
|
||||||
auto newVariable = defaultAllocator.make!Variable;
|
auto newVariable = defaultAllocator.make!Variable;
|
||||||
newVariable.counter = statements.length;
|
newVariable.counter = this.statementsLength;
|
||||||
|
|
||||||
return newVariable;
|
return newVariable;
|
||||||
}
|
}
|
||||||
@ -250,19 +217,4 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
|
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.VariableDeclaration) variableDeclarations)
|
|
||||||
@nogc
|
|
||||||
{
|
|
||||||
typeof(return) variables;
|
|
||||||
|
|
||||||
foreach (ref variableDeclaration; variableDeclarations)
|
|
||||||
{
|
|
||||||
auto newDeclaration = defaultAllocator.make!VariableDeclaration;
|
|
||||||
newDeclaration.identifier = variableDeclaration.identifier;
|
|
||||||
variables.insertBack(newDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,28 +8,29 @@ import std.range;
|
|||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
struct Token
|
struct Token
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type : ushort
|
||||||
{
|
{
|
||||||
number,
|
number = 0,
|
||||||
operator,
|
operator = 1,
|
||||||
let,
|
let = 2,
|
||||||
identifier,
|
identifier = 3,
|
||||||
equals,
|
equals = 4,
|
||||||
var,
|
var = 5,
|
||||||
semicolon,
|
semicolon = 6,
|
||||||
leftParen,
|
leftParen = 7,
|
||||||
rightParen,
|
rightParen = 8,
|
||||||
bang,
|
bang = 9,
|
||||||
dot,
|
dot = 10,
|
||||||
comma,
|
comma = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
union Value
|
union Value
|
||||||
{
|
{
|
||||||
int number;
|
int number;
|
||||||
String identifier;
|
const(char)* identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type type;
|
private Type type;
|
||||||
@ -38,62 +39,21 @@ struct Token
|
|||||||
|
|
||||||
@disable this();
|
@disable this();
|
||||||
|
|
||||||
this(Type type, Position position) @nogc nothrow pure @safe
|
this(Type type, Position position) @nogc nothrow pure @safe;
|
||||||
{
|
this(Type type, int value, Position position) @nogc nothrow pure @trusted;
|
||||||
this.type = type;
|
this(Type type, const(char)* value, Position position) @nogc nothrow;
|
||||||
this.position_ = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
this(Type type, int value, Position position) @nogc nothrow pure @trusted
|
|
||||||
in (type == Type.number)
|
|
||||||
{
|
|
||||||
this(type, position);
|
|
||||||
this.value_.number = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this()(Type type, auto ref String value, Position position)
|
|
||||||
@nogc nothrow pure @trusted
|
|
||||||
in (type == Type.identifier || type == Type.operator)
|
|
||||||
{
|
|
||||||
this(type, position);
|
|
||||||
this.value_.identifier = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params:
|
* Returns: Expected token type.
|
||||||
* type = Expected type.
|
|
||||||
*
|
|
||||||
* Returns: Whether this token is of the expected type.
|
|
||||||
*/
|
*/
|
||||||
bool ofType(Type type) const @nogc nothrow pure @safe
|
Type of() const @nogc nothrow pure @safe;
|
||||||
{
|
const(char)* identifier() const @nogc nothrow pure;
|
||||||
return this.type == type;
|
int number() const @nogc nothrow pure;
|
||||||
}
|
|
||||||
|
|
||||||
@property auto value(Type type)() @nogc nothrow pure @trusted
|
|
||||||
in (ofType(type))
|
|
||||||
{
|
|
||||||
static if (type == Type.number)
|
|
||||||
{
|
|
||||||
return this.value_.number;
|
|
||||||
}
|
|
||||||
else static if (type == Type.identifier || type == Type.operator)
|
|
||||||
{
|
|
||||||
return this.value_.identifier;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static assert(false, "This type doesn't have a value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: The token position in the source text.
|
* Returns: The token position in the source text.
|
||||||
*/
|
*/
|
||||||
@property const(Position) position() const @nogc nothrow pure @safe
|
@property const(Position) position() const @nogc nothrow pure @safe;
|
||||||
{
|
|
||||||
return this.position_;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,10 +61,10 @@ struct Token
|
|||||||
*/
|
*/
|
||||||
struct Source
|
struct Source
|
||||||
{
|
{
|
||||||
char[] buffer;
|
const(char)* buffer;
|
||||||
Position position;
|
Position position;
|
||||||
|
|
||||||
this(char[] buffer) @nogc nothrow pure @safe
|
this(const(char)* buffer) @nogc nothrow pure @safe
|
||||||
{
|
{
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
}
|
}
|
||||||
@ -113,7 +73,7 @@ struct Source
|
|||||||
|
|
||||||
bool empty() @nogc nothrow pure @safe
|
bool empty() @nogc nothrow pure @safe
|
||||||
{
|
{
|
||||||
return this.length == 0;
|
return this.buffer is null || this.buffer[0] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
char front() @nogc nothrow pure @safe
|
char front() @nogc nothrow pure @safe
|
||||||
@ -122,133 +82,36 @@ struct Source
|
|||||||
return this.buffer[0];
|
return this.buffer[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void popFront() @nogc nothrow pure @safe
|
void popFront() @nogc nothrow pure
|
||||||
in (!empty)
|
in (!empty)
|
||||||
{
|
{
|
||||||
this.buffer = buffer[1 .. $];
|
++this.buffer;
|
||||||
++this.position.column;
|
++this.position.column;
|
||||||
}
|
}
|
||||||
|
|
||||||
void breakLine() @nogc nothrow pure @safe
|
void breakLine() @nogc nothrow pure
|
||||||
in (!empty)
|
in (!empty)
|
||||||
{
|
{
|
||||||
this.buffer = buffer[1 .. $];
|
++this.buffer;
|
||||||
++this.position.line;
|
++this.position.line;
|
||||||
this.position.column = 1;
|
this.position.column = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property size_t length() const @nogc nothrow pure @safe
|
@property size_t length() const @nogc nothrow pure
|
||||||
{
|
{
|
||||||
return this.buffer.length;
|
return strlen(this.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
char opIndex(size_t index) @nogc nothrow pure @safe
|
char opIndex(size_t index) @nogc nothrow pure
|
||||||
in (index < length)
|
|
||||||
{
|
{
|
||||||
return this.buffer[index];
|
return this.buffer[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
|
const(char)[] opSlice(size_t i, size_t j) @nogc nothrow pure
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(i <= j);
|
|
||||||
assert(j <= length);
|
|
||||||
}
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
return this.buffer[i .. j];
|
return this.buffer[i .. j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result!(Array!Token) lex(char[] buffer) @nogc
|
extern(C++, "elna")
|
||||||
{
|
Token* lex(const(char)* buffer, CompileError* compileError, size_t* length) @nogc;
|
||||||
Array!Token tokens;
|
|
||||||
auto source = Source(buffer);
|
|
||||||
|
|
||||||
while (!source.empty)
|
|
||||||
{
|
|
||||||
if (source.front == ' ')
|
|
||||||
{
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front >= '0' && source.front <= '9') // Multi-digit.
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.number, source.front - '0', source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == '=')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.equals, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == '(')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.leftParen, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == ')')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.rightParen, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == ';')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.semicolon, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == ',')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.comma, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == '!')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.bang, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == '.')
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.dot, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (isalpha(source.front))
|
|
||||||
{
|
|
||||||
size_t i = 1;
|
|
||||||
while (i < source.length && isalpha(source[i]))
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
if (source[0 .. i] == "const")
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.let, source.position));
|
|
||||||
}
|
|
||||||
else if (source[0 .. i] == "var")
|
|
||||||
{
|
|
||||||
tokens.insertBack(Token(Token.Type.var, source.position));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto identifier = String(source[0 .. i]);
|
|
||||||
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
|
|
||||||
}
|
|
||||||
source.popFrontN(i);
|
|
||||||
}
|
|
||||||
else if (source.front == '+' || source.front == '-')
|
|
||||||
{
|
|
||||||
String operator;
|
|
||||||
|
|
||||||
operator.insertBack(source.front);
|
|
||||||
tokens.insertBack(Token(Token.Type.operator, operator, source.position));
|
|
||||||
source.popFront;
|
|
||||||
}
|
|
||||||
else if (source.front == '\n')
|
|
||||||
{
|
|
||||||
source.breakLine;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return typeof(return)("Unexptected next character", source.position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return typeof(return)(tokens);
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
module elna.parser;
|
module elna.parser;
|
||||||
|
|
||||||
|
import core.stdc.string;
|
||||||
import elna.lexer;
|
import elna.lexer;
|
||||||
import elna.result;
|
import elna.result;
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
import tanya.memory.allocator;
|
import tanya.memory.allocator;
|
||||||
|
import std.array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parser visitor.
|
* Parser visitor.
|
||||||
@ -14,7 +15,6 @@ interface ParserVisitor(Mapping)
|
|||||||
{
|
{
|
||||||
Mapping.Node visit(Node) @nogc;
|
Mapping.Node visit(Node) @nogc;
|
||||||
Mapping.Definition visit(Definition) @nogc;
|
Mapping.Definition visit(Definition) @nogc;
|
||||||
Mapping.VariableDeclaration visit(VariableDeclaration) @nogc;
|
|
||||||
Mapping.Statement visit(Statement) @nogc;
|
Mapping.Statement visit(Statement) @nogc;
|
||||||
Mapping.BangStatement visit(BangStatement) @nogc;
|
Mapping.BangStatement visit(BangStatement) @nogc;
|
||||||
Mapping.Block visit(Block) @nogc;
|
Mapping.Block visit(Block) @nogc;
|
||||||
@ -49,19 +49,6 @@ class Definition : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Variable declaration.
|
|
||||||
*/
|
|
||||||
class VariableDeclaration : Node
|
|
||||||
{
|
|
||||||
String identifier;
|
|
||||||
|
|
||||||
Mapping.VariableDeclaration accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
|
||||||
{
|
|
||||||
return visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Statement : Node
|
abstract class Statement : Node
|
||||||
{
|
{
|
||||||
Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||||
@ -83,7 +70,6 @@ class BangStatement : Statement
|
|||||||
class Block : Node
|
class Block : Node
|
||||||
{
|
{
|
||||||
Array!Definition definitions;
|
Array!Definition definitions;
|
||||||
Array!VariableDeclaration variableDeclarations;
|
|
||||||
Statement statement;
|
Statement statement;
|
||||||
|
|
||||||
Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||||
@ -120,6 +106,7 @@ class Variable : Expression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum BinaryOperator
|
enum BinaryOperator
|
||||||
{
|
{
|
||||||
sum,
|
sum,
|
||||||
@ -155,24 +142,24 @@ class BinaryExpression : Expression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!Expression parseFactor(ref Array!Token.Range tokens) @nogc
|
private Result!Expression parseFactor(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected factor, got end of stream")
|
in (!tokens.empty, "Expected factor, got end of stream")
|
||||||
{
|
{
|
||||||
if (tokens.front.ofType(Token.Type.identifier))
|
if (tokens.front.of() == Token.Type.identifier)
|
||||||
{
|
{
|
||||||
auto variable = defaultAllocator.make!Variable;
|
auto variable = defaultAllocator.make!Variable;
|
||||||
variable.identifier = tokens.front.value!(Token.Type.identifier);
|
variable.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())];
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
return Result!Expression(variable);
|
return Result!Expression(variable);
|
||||||
}
|
}
|
||||||
else if (tokens.front.ofType(Token.Type.number))
|
else if (tokens.front.of() == Token.Type.number)
|
||||||
{
|
{
|
||||||
auto number = defaultAllocator.make!Number;
|
auto number = defaultAllocator.make!Number;
|
||||||
number.value = tokens.front.value!(Token.Type.number);
|
number.value = tokens.front.number();
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
return Result!Expression(number);
|
return Result!Expression(number);
|
||||||
}
|
}
|
||||||
else if (tokens.front.ofType(Token.Type.leftParen))
|
else if (tokens.front.of() == Token.Type.leftParen)
|
||||||
{
|
{
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
|
|
||||||
@ -184,20 +171,20 @@ in (!tokens.empty, "Expected factor, got end of stream")
|
|||||||
return Result!Expression("Expected a factor", tokens.front.position);
|
return Result!Expression("Expected a factor", tokens.front.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!Expression parseTerm(ref Array!(Token).Range tokens) @nogc
|
private Result!Expression parseTerm(ref Token[] tokens) @nogc
|
||||||
{
|
{
|
||||||
return parseFactor(tokens);
|
return parseFactor(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
|
private Result!Expression parseExpression(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected expression, got end of stream")
|
in (!tokens.empty, "Expected expression, got end of stream")
|
||||||
{
|
{
|
||||||
auto term = parseTerm(tokens);
|
auto term = parseTerm(tokens);
|
||||||
if (!term.valid || tokens.empty || !tokens.front.ofType(Token.Type.operator))
|
if (!term.valid || tokens.empty || tokens.front.of() != Token.Type.operator)
|
||||||
{
|
{
|
||||||
return term;
|
return term;
|
||||||
}
|
}
|
||||||
auto operator = tokens.front.value!(Token.Type.operator);
|
auto operator = String(tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]);
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
|
|
||||||
auto expression = parseExpression(tokens);
|
auto expression = parseExpression(tokens);
|
||||||
@ -215,19 +202,19 @@ in (!tokens.empty, "Expected expression, got end of stream")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc
|
private Result!Definition parseDefinition(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected definition, got end of stream")
|
in (!tokens.empty, "Expected definition, got end of stream")
|
||||||
{
|
{
|
||||||
auto definition = defaultAllocator.make!Definition;
|
auto definition = defaultAllocator.make!Definition;
|
||||||
definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy.
|
definition.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]; // Copy.
|
||||||
|
|
||||||
tokens.popFront();
|
tokens.popFront();
|
||||||
tokens.popFront(); // Skip the equals sign.
|
tokens.popFront(); // Skip the equals sign.
|
||||||
|
|
||||||
if (tokens.front.ofType(Token.Type.number))
|
if (tokens.front.of() == Token.Type.number)
|
||||||
{
|
{
|
||||||
auto number = defaultAllocator.make!Number;
|
auto number = defaultAllocator.make!Number;
|
||||||
number.value = tokens.front.value!(Token.Type.number);
|
number.value = tokens.front.number();
|
||||||
definition.number = number;
|
definition.number = number;
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
return Result!Definition(definition);
|
return Result!Definition(definition);
|
||||||
@ -235,10 +222,10 @@ in (!tokens.empty, "Expected definition, got end of stream")
|
|||||||
return Result!Definition("Expected a number", tokens.front.position);
|
return Result!Definition("Expected a number", tokens.front.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!Statement parseStatement(ref Array!Token.Range tokens) @nogc
|
private Result!Statement parseStatement(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected block, got end of stream")
|
in (!tokens.empty, "Expected block, got end of stream")
|
||||||
{
|
{
|
||||||
if (tokens.front.ofType(Token.Type.bang))
|
if (tokens.front.of() == Token.Type.bang)
|
||||||
{
|
{
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
auto statement = defaultAllocator.make!BangStatement;
|
auto statement = defaultAllocator.make!BangStatement;
|
||||||
@ -256,7 +243,7 @@ in (!tokens.empty, "Expected block, got end of stream")
|
|||||||
return Result!Statement("Expected ! statement", tokens.front.position);
|
return Result!Statement("Expected ! statement", tokens.front.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!(Array!Definition) parseDefinitions(ref Array!Token.Range tokens) @nogc
|
private Result!(Array!Definition) parseDefinitions(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected definition, got end of stream")
|
in (!tokens.empty, "Expected definition, got end of stream")
|
||||||
{
|
{
|
||||||
tokens.popFront; // Skip const.
|
tokens.popFront; // Skip const.
|
||||||
@ -271,11 +258,11 @@ in (!tokens.empty, "Expected definition, got end of stream")
|
|||||||
return typeof(return)(definition.error.get);
|
return typeof(return)(definition.error.get);
|
||||||
}
|
}
|
||||||
definitions.insertBack(definition.result);
|
definitions.insertBack(definition.result);
|
||||||
if (tokens.front.ofType(Token.Type.semicolon))
|
if (tokens.front.of() == Token.Type.semicolon)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (tokens.front.ofType(Token.Type.comma))
|
if (tokens.front.of() == Token.Type.comma)
|
||||||
{
|
{
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
}
|
}
|
||||||
@ -284,49 +271,11 @@ in (!tokens.empty, "Expected definition, got end of stream")
|
|||||||
return typeof(return)(definitions);
|
return typeof(return)(definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result!(Array!VariableDeclaration) parseVariableDeclarations(ref Array!Token.Range tokens) @nogc
|
private Result!Block parseBlock(ref Token[] tokens) @nogc
|
||||||
in (!tokens.empty, "Expected variable declarations, got end of stream")
|
|
||||||
{
|
|
||||||
tokens.popFront; // Skip var.
|
|
||||||
|
|
||||||
Array!VariableDeclaration variableDeclarations;
|
|
||||||
|
|
||||||
while (!tokens.empty)
|
|
||||||
{
|
|
||||||
auto currentToken = tokens.front;
|
|
||||||
if (currentToken.ofType(Token.Type.identifier))
|
|
||||||
{
|
|
||||||
auto variableDeclaration = defaultAllocator.make!VariableDeclaration;
|
|
||||||
variableDeclaration.identifier = currentToken.value!(Token.Type.identifier);
|
|
||||||
variableDeclarations.insertBack(variableDeclaration);
|
|
||||||
tokens.popFront;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return typeof(return)("Expected variable name", tokens.front.position);
|
|
||||||
}
|
|
||||||
if (tokens.empty)
|
|
||||||
{
|
|
||||||
return typeof(return)("Expected \";\" or \",\" name", currentToken.position);
|
|
||||||
}
|
|
||||||
if (tokens.front.ofType(Token.Type.semicolon))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (tokens.front.ofType(Token.Type.comma))
|
|
||||||
{
|
|
||||||
tokens.popFront;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeof(return)(variableDeclarations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc
|
|
||||||
in (!tokens.empty, "Expected block, got end of stream")
|
in (!tokens.empty, "Expected block, got end of stream")
|
||||||
{
|
{
|
||||||
auto block = defaultAllocator.make!Block;
|
auto block = defaultAllocator.make!Block;
|
||||||
if (tokens.front.ofType(Token.Type.let))
|
if (tokens.front.of() == Token.Type.let)
|
||||||
{
|
{
|
||||||
auto constDefinitions = parseDefinitions(tokens);
|
auto constDefinitions = parseDefinitions(tokens);
|
||||||
if (constDefinitions.valid)
|
if (constDefinitions.valid)
|
||||||
@ -339,19 +288,6 @@ in (!tokens.empty, "Expected block, got end of stream")
|
|||||||
}
|
}
|
||||||
tokens.popFront;
|
tokens.popFront;
|
||||||
}
|
}
|
||||||
if (tokens.front.ofType(Token.Type.var))
|
|
||||||
{
|
|
||||||
auto variableDeclarations = parseVariableDeclarations(tokens);
|
|
||||||
if (variableDeclarations.valid)
|
|
||||||
{
|
|
||||||
block.variableDeclarations = variableDeclarations.result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Result!Block(variableDeclarations.error.get);
|
|
||||||
}
|
|
||||||
tokens.popFront;
|
|
||||||
}
|
|
||||||
auto statement = parseStatement(tokens);
|
auto statement = parseStatement(tokens);
|
||||||
if (statement.valid)
|
if (statement.valid)
|
||||||
{
|
{
|
||||||
@ -365,7 +301,7 @@ in (!tokens.empty, "Expected block, got end of stream")
|
|||||||
return Result!Block(block);
|
return Result!Block(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result!Block parse(ref Array!Token tokenStream) @nogc
|
Result!Block parse(Token[] tokenStream) @nogc
|
||||||
{
|
{
|
||||||
auto tokens = tokenStream[];
|
auto tokens = tokenStream[];
|
||||||
return parseBlock(tokens);
|
return parseBlock(tokens);
|
||||||
|
@ -7,6 +7,7 @@ import tanya.container.string;
|
|||||||
/**
|
/**
|
||||||
* Position in the source text.
|
* Position in the source text.
|
||||||
*/
|
*/
|
||||||
|
extern(C++, "elna")
|
||||||
struct Position
|
struct Position
|
||||||
{
|
{
|
||||||
/// Line.
|
/// Line.
|
||||||
@ -16,9 +17,10 @@ struct Position
|
|||||||
size_t column = 1;
|
size_t column = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
struct CompileError
|
struct CompileError
|
||||||
{
|
{
|
||||||
private string message_;
|
private const(char)* message_;
|
||||||
|
|
||||||
private Position position_;
|
private Position position_;
|
||||||
|
|
||||||
@ -29,29 +31,16 @@ struct CompileError
|
|||||||
* message = Error text.
|
* message = Error text.
|
||||||
* position = Error position in the source text.
|
* position = Error position in the source text.
|
||||||
*/
|
*/
|
||||||
this(string message, Position position) @nogc nothrow pure @safe
|
this(const(char)* message, const Position position) @nogc nothrow pure @safe;
|
||||||
{
|
|
||||||
this.message_ = message;
|
|
||||||
this.position_ = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error text.
|
/// Error text.
|
||||||
@property string message() const @nogc nothrow pure @safe
|
@property const(char)* what() const @nogc nothrow pure @safe;
|
||||||
{
|
|
||||||
return this.message_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error line in the source text.
|
/// Error line in the source text.
|
||||||
@property size_t line() const @nogc nothrow pure @safe
|
@property size_t line() const @nogc nothrow pure @safe;
|
||||||
{
|
|
||||||
return this.position_.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error column in the source text.
|
/// Error column in the source text.
|
||||||
@property size_t column() const @nogc nothrow pure @safe
|
@property size_t column() const @nogc nothrow pure @safe;
|
||||||
{
|
|
||||||
return this.position_.column;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Result(T)
|
struct Result(T)
|
||||||
@ -65,7 +54,7 @@ struct Result(T)
|
|||||||
this.error = typeof(this.error).init;
|
this.error = typeof(this.error).init;
|
||||||
}
|
}
|
||||||
|
|
||||||
this(string message, Position position)
|
this(const(char)* message, Position position)
|
||||||
{
|
{
|
||||||
this.result = T.init;
|
this.result = T.init;
|
||||||
this.error = CompileError(message, position);
|
this.error = CompileError(message, position);
|
||||||
@ -85,8 +74,7 @@ struct Result(T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Reference
|
extern(C++, "elna")
|
||||||
{
|
|
||||||
enum Target
|
enum Target
|
||||||
{
|
{
|
||||||
text,
|
text,
|
||||||
@ -94,7 +82,10 @@ struct Reference
|
|||||||
lower12i
|
lower12i
|
||||||
}
|
}
|
||||||
|
|
||||||
String name;
|
extern(C++, "elna")
|
||||||
|
struct Reference
|
||||||
|
{
|
||||||
|
const(char)* name;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
Target target;
|
Target target;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
module elna.riscv;
|
module elna.riscv;
|
||||||
|
|
||||||
import core.stdc.stdlib;
|
import core.stdc.stdlib;
|
||||||
import elna.extended;
|
|
||||||
import elna.ir;
|
import elna.ir;
|
||||||
import elna.result;
|
import elna.result;
|
||||||
import std.algorithm;
|
|
||||||
import std.typecons;
|
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum XRegister : ubyte
|
enum XRegister : ubyte
|
||||||
{
|
{
|
||||||
zero = 0,
|
zero = 0,
|
||||||
@ -45,6 +43,7 @@ enum XRegister : ubyte
|
|||||||
t6 = 31,
|
t6 = 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum Funct3 : ubyte
|
enum Funct3 : ubyte
|
||||||
{
|
{
|
||||||
addi = 0b000,
|
addi = 0b000,
|
||||||
@ -92,18 +91,21 @@ enum Funct3 : ubyte
|
|||||||
jalr = 0b000,
|
jalr = 0b000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum Funct12 : ubyte
|
enum Funct12 : ubyte
|
||||||
{
|
{
|
||||||
ecall = 0b000000000000,
|
ecall = 0b000000000000,
|
||||||
ebreak = 0b000000000001,
|
ebreak = 0b000000000001,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum Funct7 : ubyte
|
enum Funct7 : ubyte
|
||||||
{
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
sub = 0b0100000
|
sub = 0b0100000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
enum BaseOpcode : ubyte
|
enum BaseOpcode : ubyte
|
||||||
{
|
{
|
||||||
opImm = 0b0010011,
|
opImm = 0b0010011,
|
||||||
@ -119,243 +121,61 @@ enum BaseOpcode : ubyte
|
|||||||
system = 0b1110011,
|
system = 0b1110011,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
struct Instruction
|
struct Instruction
|
||||||
{
|
{
|
||||||
private uint instruction;
|
private uint instruction;
|
||||||
|
|
||||||
this(BaseOpcode opcode) @nogc
|
this(BaseOpcode opcode) @nogc;
|
||||||
{
|
|
||||||
this.instruction = opcode;
|
|
||||||
}
|
|
||||||
@disable this();
|
@disable this();
|
||||||
|
|
||||||
ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
|
ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
|
||||||
return scope @nogc
|
return scope @nogc;
|
||||||
{
|
|
||||||
this.instruction |= (rd << 7)
|
|
||||||
| (funct3 << 12)
|
|
||||||
| (rs1 << 15)
|
|
||||||
| (immediate << 20);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
|
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
|
||||||
return scope @nogc
|
return scope @nogc;
|
||||||
{
|
|
||||||
this.instruction |= ((imm1 & 0b11111) << 7)
|
|
||||||
| (funct3 << 12)
|
|
||||||
| (rs1 << 15)
|
|
||||||
| (rs2 << 20)
|
|
||||||
| ((imm1 & 0b111111100000) << 20);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
|
ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
|
||||||
return scope @nogc
|
return scope @nogc;
|
||||||
{
|
|
||||||
this.instruction |= (rd << 7)
|
|
||||||
| (funct3 << 12)
|
|
||||||
| (rs1 << 15)
|
|
||||||
| (rs2 << 20)
|
|
||||||
| (funct7 << 25);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref Instruction u(XRegister rd, uint imm)
|
ref Instruction u(XRegister rd, uint imm)
|
||||||
return scope @nogc
|
return scope @nogc;
|
||||||
{
|
|
||||||
this.instruction |= (rd << 7) | (imm << 12);
|
|
||||||
|
|
||||||
return this;
|
ubyte* encode() return scope @nogc;
|
||||||
}
|
|
||||||
|
|
||||||
ubyte* encode() return scope @nogc
|
|
||||||
{
|
|
||||||
return cast(ubyte*) (&this.instruction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++, "elna")
|
||||||
class RiscVVisitor : IRVisitor
|
class RiscVVisitor : IRVisitor
|
||||||
{
|
{
|
||||||
Array!Instruction instructions;
|
Instruction *instructions;
|
||||||
|
size_t instructionsLength;
|
||||||
bool registerInUse;
|
bool registerInUse;
|
||||||
uint variableCounter = 1;
|
uint variableCounter = 1;
|
||||||
Array!Reference references;
|
Reference[3] references;
|
||||||
|
|
||||||
override void visit(Node) @nogc
|
override void visit(Node) @nogc;
|
||||||
{
|
override void visit(Definition definition) @nogc;
|
||||||
}
|
override void visit(Operand operand) @nogc;
|
||||||
|
override void visit(Variable variable) @nogc;
|
||||||
override void visit(Definition definition) @nogc
|
override void visit(Number number) @nogc;
|
||||||
{
|
override void visit(BinaryExpression expression) @nogc;
|
||||||
const uint stackSize = cast(uint) (definition.statements.length * 4 + 12);
|
|
||||||
|
|
||||||
// Prologue.
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm)
|
|
||||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, -stackSize)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.store)
|
|
||||||
.s(stackSize - 4, Funct3.sw, XRegister.sp, XRegister.s0)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.store)
|
|
||||||
.s(stackSize - 8, Funct3.sw, XRegister.sp, XRegister.ra)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm)
|
|
||||||
.i(XRegister.s0, Funct3.addi, XRegister.sp, stackSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (statement; definition.statements[])
|
|
||||||
{
|
|
||||||
statement.accept(this);
|
|
||||||
}
|
|
||||||
foreach (variableDeclaration; definition.variableDeclarations[])
|
|
||||||
{
|
|
||||||
variableDeclaration.accept(this);
|
|
||||||
}
|
|
||||||
this.registerInUse = true;
|
|
||||||
definition.result.accept(this);
|
|
||||||
this.registerInUse = false;
|
|
||||||
|
|
||||||
// Print the result.
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm)
|
|
||||||
.i(XRegister.a1, Funct3.addi, XRegister.a0, 0)
|
|
||||||
);
|
|
||||||
this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.high20));
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.lui).u(XRegister.a5, 0)
|
|
||||||
);
|
|
||||||
this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.lower12i));
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm).i(XRegister.a0, Funct3.addi, XRegister.a5, 0)
|
|
||||||
);
|
|
||||||
this.references.insertBack(Reference(String("printf"), instructions.length * 4, Reference.Target.text));
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.auipc).u(XRegister.ra, 0)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.jalr)
|
|
||||||
.i(XRegister.ra, Funct3.jalr, XRegister.ra, 0)
|
|
||||||
);
|
|
||||||
// Set the return value (0).
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.op)
|
|
||||||
.r(XRegister.a0, Funct3.and, XRegister.zero, XRegister.zero)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Epilogue.
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.load)
|
|
||||||
.i(XRegister.s0, Funct3.lw, XRegister.sp, stackSize - 4)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.load)
|
|
||||||
.i(XRegister.ra, Funct3.lw, XRegister.sp, stackSize - 8)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm)
|
|
||||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, stackSize)
|
|
||||||
);
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.jalr)
|
|
||||||
.i(XRegister.zero, Funct3.jalr, XRegister.ra, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(Operand operand) @nogc
|
|
||||||
{
|
|
||||||
if ((cast(Variable) operand) !is null)
|
|
||||||
{
|
|
||||||
return (cast(Variable) operand).accept(this);
|
|
||||||
}
|
|
||||||
if ((cast(Number) operand) !is null)
|
|
||||||
{
|
|
||||||
return (cast(Number) operand).accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(Variable variable) @nogc
|
|
||||||
{
|
|
||||||
const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
|
|
||||||
|
|
||||||
// movl -x(%rbp), %eax; where x is a number.
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.load)
|
|
||||||
.i(freeRegister, Funct3.lw, XRegister.sp,
|
|
||||||
cast(byte) (variable.counter * 4))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(VariableDeclaration) @nogc
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(Number number) @nogc
|
|
||||||
{
|
|
||||||
const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
|
|
||||||
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.opImm) // movl $x, %eax; where $x is a number.
|
|
||||||
.i(freeRegister, Funct3.addi, XRegister.zero, number.value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(BinaryExpression expression) @nogc
|
|
||||||
{
|
|
||||||
this.registerInUse = true;
|
|
||||||
expression.lhs.accept(this);
|
|
||||||
this.registerInUse = false;
|
|
||||||
expression.rhs.accept(this);
|
|
||||||
|
|
||||||
// Calculate the result and assign it to a variable on the stack.
|
|
||||||
final switch (expression.operator)
|
|
||||||
{
|
|
||||||
case BinaryOperator.sum:
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.op)
|
|
||||||
.r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case BinaryOperator.subtraction:
|
|
||||||
this.instructions.insertBack(
|
|
||||||
Instruction(BaseOpcode.op)
|
|
||||||
.r(XRegister.a0, Funct3.sub, XRegister.a0, XRegister.t0, Funct7.sub)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.instructions.insertBack( // movl %eax, -x(%rbp); where x is a number.
|
|
||||||
Instruction(BaseOpcode.store)
|
|
||||||
.s(cast(uint) (this.variableCounter * 4), Funct3.sw, XRegister.sp, XRegister.a0)
|
|
||||||
);
|
|
||||||
|
|
||||||
++this.variableCounter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol writeNext(Definition ast) @nogc
|
Symbol writeNext(Definition ast) @nogc
|
||||||
{
|
{
|
||||||
Array!Instruction instructions;
|
Array!Instruction instructions;
|
||||||
Array!Reference references;
|
|
||||||
auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
|
auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
|
||||||
(cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
|
(cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
|
||||||
scope (exit)
|
scope (exit)
|
||||||
{
|
{
|
||||||
visitor.__xdtor();
|
|
||||||
free(cast(void*) visitor);
|
free(cast(void*) visitor);
|
||||||
}
|
}
|
||||||
visitor.visit(ast);
|
visitor.visit(ast);
|
||||||
|
|
||||||
auto program = Symbol(String("main"));
|
auto program = Symbol(String("main"));
|
||||||
|
|
||||||
program.symbols = move(visitor.references);
|
program.symbols = Array!Reference(visitor.references[]);
|
||||||
foreach (ref instruction; visitor.instructions)
|
foreach (ref instruction; visitor.instructions[0 .. visitor.instructionsLength])
|
||||||
{
|
{
|
||||||
program.text.insertBack(instruction.encode[0 .. uint.sizeof]);
|
program.text.insertBack(instruction.encode[0 .. uint.sizeof]);
|
||||||
}
|
}
|
||||||
|
53
source/ir.cpp
Normal file
53
source/ir.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "elna/ir.hpp"
|
||||||
|
|
||||||
|
namespace elna::ir
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* AST node.
|
||||||
|
*/
|
||||||
|
void Node::accept(IRVisitor *)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Definition::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Operand::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Number::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Variable::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryExpression::BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator)
|
||||||
|
{
|
||||||
|
this->lhs = lhs;
|
||||||
|
this->rhs = rhs;
|
||||||
|
this->_operator = _operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryExpression::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
BangExpression::BangExpression(Operand *operand)
|
||||||
|
{
|
||||||
|
this->operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BangExpression::accept(IRVisitor *visitor)
|
||||||
|
{
|
||||||
|
visitor->visit(this);
|
||||||
|
}
|
||||||
|
}
|
189
source/lexer.cpp
189
source/lexer.cpp
@ -14,18 +14,18 @@ namespace elna
|
|||||||
|
|
||||||
source::const_iterator source::end() const
|
source::const_iterator source::end() const
|
||||||
{
|
{
|
||||||
source_position end_position{ 0, 0 };
|
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 source_position start_position)
|
const Position start_position)
|
||||||
: m_buffer(buffer), m_position(start_position)
|
: m_buffer(buffer), m_position(start_position)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const source_position& source::const_iterator::position() const noexcept
|
const Position& source::const_iterator::position() const noexcept
|
||||||
{
|
{
|
||||||
return this->m_position;
|
return this->m_position;
|
||||||
}
|
}
|
||||||
@ -73,29 +73,109 @@ namespace elna
|
|||||||
return !(*this == that);
|
return !(*this == that);
|
||||||
}
|
}
|
||||||
|
|
||||||
token::token(const type of, source::const_iterator begin, source::const_iterator end)
|
Token::Token(const Type of, const char *value, Position position)
|
||||||
: m_type(of), m_value(begin, end)
|
: m_type(of), m_position(position)
|
||||||
|
{
|
||||||
|
std::size_t value_length = strlen(value);
|
||||||
|
char *buffer = reinterpret_cast<char *>(malloc(value_length + 1));
|
||||||
|
|
||||||
|
std::memcpy(buffer, value, value_length);
|
||||||
|
buffer[value_length] = 0;
|
||||||
|
|
||||||
|
m_value.identifier = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Token(const Type of, std::int32_t number, Position position)
|
||||||
|
: m_type(of), m_position(position)
|
||||||
|
{
|
||||||
|
m_value.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Token(const Type of, Position position)
|
||||||
|
: m_type(of), m_position(position)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
token::type token::of() const noexcept
|
Token::Token(const Token& that)
|
||||||
|
: m_type(that.of()), m_position(that.position())
|
||||||
|
{
|
||||||
|
*this = that;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Token(Token&& that)
|
||||||
|
: m_type(that.of()), m_position(that.position())
|
||||||
|
{
|
||||||
|
*this = std::move(that);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::~Token()
|
||||||
|
{
|
||||||
|
if (m_type == TOKEN_IDENTIFIER || m_type == TOKEN_OPERATOR)
|
||||||
|
{
|
||||||
|
std::free(const_cast<char*>(m_value.identifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token& Token::operator=(const Token& that)
|
||||||
|
{
|
||||||
|
m_type = that.of();
|
||||||
|
m_position = that.position();
|
||||||
|
if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR)
|
||||||
|
{
|
||||||
|
std::size_t value_length = strlen(that.identifier());
|
||||||
|
char *buffer = reinterpret_cast<char *>(malloc(value_length + 1));
|
||||||
|
|
||||||
|
std::memcpy(buffer, that.identifier(), value_length);
|
||||||
|
buffer[value_length] = 0;
|
||||||
|
|
||||||
|
m_value.identifier = buffer;
|
||||||
|
}
|
||||||
|
else if (that.of() == TOKEN_NUMBER)
|
||||||
|
{
|
||||||
|
m_value.number = that.number();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token& Token::operator=(Token&& that)
|
||||||
|
{
|
||||||
|
m_type = that.of();
|
||||||
|
m_position = that.position();
|
||||||
|
if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR)
|
||||||
|
{
|
||||||
|
m_value.identifier = that.identifier();
|
||||||
|
that.m_value.identifier = nullptr;
|
||||||
|
}
|
||||||
|
else if (that.of() == TOKEN_NUMBER)
|
||||||
|
{
|
||||||
|
m_value.number = that.number();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Type Token::of() const noexcept
|
||||||
{
|
{
|
||||||
return m_type;
|
return m_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token::value& token::identifier() const noexcept
|
const char *Token::identifier() const noexcept
|
||||||
{
|
{
|
||||||
return m_value;
|
return m_value.identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source_position& token::position() const noexcept
|
std::int32_t Token::number() const noexcept
|
||||||
|
{
|
||||||
|
return m_value.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Position& Token::position() const noexcept
|
||||||
{
|
{
|
||||||
return m_position;
|
return m_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
result<std::vector<token>> lex(const std::string& buffer)
|
Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length)
|
||||||
{
|
{
|
||||||
std::vector<token> tokens;
|
std::vector<Token> tokens;
|
||||||
source input{ buffer };
|
source input{ buffer };
|
||||||
|
|
||||||
for (auto iterator = input.begin(); iterator != input.end();)
|
for (auto iterator = input.begin(); iterator != input.end();)
|
||||||
@ -103,23 +183,88 @@ namespace elna
|
|||||||
if (*iterator == ' ' || *iterator == '\n')
|
if (*iterator == ' ' || *iterator == '\n')
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
else if (std::isgraph(*iterator))
|
else if (std::isdigit(*iterator))
|
||||||
{
|
{
|
||||||
auto current_position = iterator;
|
tokens.emplace_back(
|
||||||
do
|
Token::TOKEN_NUMBER,
|
||||||
{
|
static_cast<std::int32_t>(*iterator - '0'),
|
||||||
++current_position;
|
iterator.position()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
while (current_position != input.end() && std::isgraph(*current_position));
|
else if (*iterator == '=')
|
||||||
token new_token{ token::type::word, iterator, current_position };
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_EQUALS, iterator.position());
|
||||||
tokens.push_back(new_token);
|
}
|
||||||
iterator = current_position;
|
else if (*iterator == '(')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_LEFT_PAREN, iterator.position());
|
||||||
|
}
|
||||||
|
else if (*iterator == ')')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_RIGHT_PAREN, iterator.position());
|
||||||
|
}
|
||||||
|
else if (*iterator == ';')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_SEMICOLON, iterator.position());
|
||||||
|
}
|
||||||
|
else if (*iterator == ',')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_COMMA, iterator.position());
|
||||||
|
}
|
||||||
|
else if (*iterator == '!')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_BANG, iterator.position());
|
||||||
|
}
|
||||||
|
else if (*iterator == '.')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_DOT, iterator.position());
|
||||||
|
}
|
||||||
|
else if (std::isalpha(*iterator))
|
||||||
|
{
|
||||||
|
std::string word;
|
||||||
|
auto i = iterator;
|
||||||
|
while (i != input.end() && std::isalpha(*i))
|
||||||
|
{
|
||||||
|
word.push_back(*i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (word == "const")
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_LET, iterator.position());
|
||||||
|
}
|
||||||
|
else if (word == "var")
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_VAR, iterator.position());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tokens.emplace_back(Token::TOKEN_IDENTIFIER, word.c_str(), iterator.position());
|
||||||
|
}
|
||||||
|
iterator = i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (*iterator == '+' || *iterator == '-')
|
||||||
|
{
|
||||||
|
std::string _operator{ *iterator };
|
||||||
|
|
||||||
|
tokens.emplace_back(Token::TOKEN_OPERATOR, _operator.c_str(), iterator.position());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*compile_error = CompileError("Unexpected next character", iterator.position());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
++iterator;
|
++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 tokens;
|
return target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,23 @@
|
|||||||
|
|
||||||
namespace elna
|
namespace elna
|
||||||
{
|
{
|
||||||
compile_error::compile_error(const char *message, const source_position position) noexcept
|
CompileError::CompileError(const char *message, const Position position) noexcept
|
||||||
{
|
{
|
||||||
this->message = message;
|
this->message = message;
|
||||||
this->position = position;
|
this->position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *compile_error::what() const noexcept
|
char const *CompileError::what() const noexcept
|
||||||
{
|
{
|
||||||
return this->message;
|
return this->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t compile_error::line() const noexcept
|
std::size_t CompileError::line() const noexcept
|
||||||
{
|
{
|
||||||
return this->position.line;
|
return this->position.line;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t compile_error::column() const noexcept
|
std::size_t CompileError::column() const noexcept
|
||||||
{
|
{
|
||||||
return this->position.column;
|
return this->position.column;
|
||||||
}
|
}
|
191
source/riscv.cpp
Normal file
191
source/riscv.cpp
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#include "elna/parser.hpp"
|
||||||
|
#include "elna/riscv.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace elna
|
||||||
|
{
|
||||||
|
Instruction::Instruction(BaseOpcode opcode)
|
||||||
|
{
|
||||||
|
this->instruction = static_cast<std::underlying_type<BaseOpcode>::type>(opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction& Instruction::i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate)
|
||||||
|
{
|
||||||
|
this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7)
|
||||||
|
| (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
|
||||||
|
| (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
|
||||||
|
| (immediate << 20);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction& Instruction::s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
|
||||||
|
{
|
||||||
|
this->instruction |= ((imm1 & 0b11111) << 7)
|
||||||
|
| (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
|
||||||
|
| (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
|
||||||
|
| (static_cast<std::underlying_type<XRegister>::type>(rs2) << 20)
|
||||||
|
| ((imm1 & 0b111111100000) << 20);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction& Instruction::r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7)
|
||||||
|
{
|
||||||
|
this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7)
|
||||||
|
| (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
|
||||||
|
| (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
|
||||||
|
| (static_cast<std::underlying_type<XRegister>::type>(rs2) << 20)
|
||||||
|
| (static_cast<std::underlying_type<Funct7>::type>(funct7) << 25);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction& Instruction::u(XRegister rd, std::uint32_t imm)
|
||||||
|
{
|
||||||
|
this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7) | (imm << 12);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t *Instruction::encode()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::uint8_t *>(&this->instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::Node *)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::Definition *definition)
|
||||||
|
{
|
||||||
|
const uint stackSize = static_cast<std::uint32_t>(definition->statementsLength * 4 + 12);
|
||||||
|
|
||||||
|
this->instructionsLength += 4;
|
||||||
|
this->instructions = reinterpret_cast<Instruction *>(
|
||||||
|
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||||
|
|
||||||
|
// Prologue.
|
||||||
|
this->instructions[instructionsLength - 4] = Instruction(BaseOpcode::opImm)
|
||||||
|
.i(XRegister::sp, Funct3::addi, XRegister::sp, -stackSize);
|
||||||
|
this->instructions[instructionsLength - 3] = Instruction(BaseOpcode::store)
|
||||||
|
.s(stackSize - 4, Funct3::sw, XRegister::sp, XRegister::s0);
|
||||||
|
this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::store)
|
||||||
|
.s(stackSize - 8, Funct3::sw, XRegister::sp, XRegister::ra);
|
||||||
|
this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::opImm)
|
||||||
|
.i(XRegister::s0, Funct3::addi, XRegister::sp, stackSize);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < definition->statementsLength; ++i)
|
||||||
|
{
|
||||||
|
definition->statements[i]->accept(this);
|
||||||
|
}
|
||||||
|
this->registerInUse = true;
|
||||||
|
definition->result->accept(this);
|
||||||
|
this->registerInUse = false;
|
||||||
|
|
||||||
|
this->instructions = reinterpret_cast<Instruction*>(
|
||||||
|
realloc(this->instructions, (this->instructionsLength + 10) * sizeof(Instruction)));
|
||||||
|
|
||||||
|
// Print the result.
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
|
||||||
|
.i(XRegister::a1, Funct3::addi, XRegister::a0, 0);
|
||||||
|
this->references[0] = Reference();
|
||||||
|
this->references[0].name = ".CL0";
|
||||||
|
this->references[0].offset = instructionsLength * 4;
|
||||||
|
this->references[0].target = Target::high20;
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::lui).u(XRegister::a5, 0);
|
||||||
|
this->references[1] = Reference();
|
||||||
|
this->references[1].name = ".CL0";
|
||||||
|
this->references[1].offset = instructionsLength * 4;
|
||||||
|
this->references[1].target = Target::lower12i;
|
||||||
|
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
|
||||||
|
.i(XRegister::a0, Funct3::addi, XRegister::a5, 0);
|
||||||
|
this->references[2] = Reference();
|
||||||
|
this->references[2].name = "printf";
|
||||||
|
this->references[2].offset = instructionsLength * 4;
|
||||||
|
this->references[2].target = Target::text;
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::auipc).u(XRegister::ra, 0);
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::jalr)
|
||||||
|
.i(XRegister::ra, Funct3::jalr, XRegister::ra, 0);
|
||||||
|
// Set the return value (0).
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::op)
|
||||||
|
.r(XRegister::a0, Funct3::_and, XRegister::zero, XRegister::zero);
|
||||||
|
|
||||||
|
// Epilogue.
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::load)
|
||||||
|
.i(XRegister::s0, Funct3::lw, XRegister::sp, stackSize - 4);
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::load)
|
||||||
|
.i(XRegister::ra, Funct3::lw, XRegister::sp, stackSize - 8);
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
|
||||||
|
.i(XRegister::sp, Funct3::addi, XRegister::sp, stackSize);
|
||||||
|
this->instructions[instructionsLength++] = Instruction(BaseOpcode::jalr)
|
||||||
|
.i(XRegister::zero, Funct3::jalr, XRegister::ra, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::Operand *operand)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<ir::Variable *>(operand) != nullptr)
|
||||||
|
{
|
||||||
|
return dynamic_cast<ir::Variable *>(operand)->accept(this);
|
||||||
|
}
|
||||||
|
if (dynamic_cast<ir::Number *>(operand) != nullptr)
|
||||||
|
{
|
||||||
|
return dynamic_cast<ir::Number *>(operand)->accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::Variable *variable)
|
||||||
|
{
|
||||||
|
const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0;
|
||||||
|
|
||||||
|
++this->instructionsLength;
|
||||||
|
this->instructions = reinterpret_cast<Instruction *>(
|
||||||
|
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||||
|
// movl -x(%rbp), %eax; where x is a number.
|
||||||
|
this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::load)
|
||||||
|
.i(freeRegister, Funct3::lw, XRegister::sp,
|
||||||
|
static_cast<std::int8_t>(variable->counter * 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::Number *number)
|
||||||
|
{
|
||||||
|
const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0;
|
||||||
|
|
||||||
|
++this->instructionsLength;
|
||||||
|
this->instructions = reinterpret_cast<Instruction *>(
|
||||||
|
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||||
|
this->instructions[this->instructionsLength - 1] =
|
||||||
|
Instruction(BaseOpcode::opImm) // movl $x, %eax; where $x is a number.
|
||||||
|
.i(freeRegister, Funct3::addi, XRegister::zero, number->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RiscVVisitor::visit(ir::BinaryExpression *expression)
|
||||||
|
{
|
||||||
|
this->registerInUse = true;
|
||||||
|
expression->lhs->accept(this);
|
||||||
|
this->registerInUse = false;
|
||||||
|
expression->rhs->accept(this);
|
||||||
|
|
||||||
|
this->instructionsLength += 2;
|
||||||
|
this->instructions = reinterpret_cast<Instruction *>(
|
||||||
|
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||||
|
// Calculate the result and assign it to a variable on the stack.
|
||||||
|
switch (expression->_operator)
|
||||||
|
{
|
||||||
|
case BinaryOperator::sum:
|
||||||
|
this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::op)
|
||||||
|
.r(XRegister::a0, Funct3::add, XRegister::a0, XRegister::t0);
|
||||||
|
break;
|
||||||
|
case BinaryOperator::subtraction:
|
||||||
|
this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::op)
|
||||||
|
.r(XRegister::a0, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->instructions[instructionsLength - 1] = // movl %eax, -x(%rbp); where x is a number.
|
||||||
|
Instruction(BaseOpcode::store)
|
||||||
|
.s(static_cast<std::uint32_t>(this->variableCounter * 4), Funct3::sw, XRegister::sp, XRegister::a0);
|
||||||
|
|
||||||
|
++this->variableCounter;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user