Remove the IR for know for simplicity
This commit is contained in:
136
source/cl.cpp
Normal file
136
source/cl.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "elna/cl.hpp"
|
||||
#include "elna/result.hpp"
|
||||
#include "elna/riscv.hpp"
|
||||
#include <cstddef>
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
char *readSource(const char *source)
|
||||
{
|
||||
const std::size_t bufferSize = 255;
|
||||
|
||||
std::ifstream input_stream{ source };
|
||||
std::stringstream buffer;
|
||||
buffer << input_stream.rdbuf();
|
||||
input_stream.close();
|
||||
std::string contents = buffer.str();
|
||||
char *result = reinterpret_cast<char *>(malloc(contents.size() + 1));
|
||||
std::copy(std::cbegin(contents), std::cend(contents), result);
|
||||
result[contents.size()] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int compile(const char *inFile, const char *outputFilename)
|
||||
{
|
||||
ELFIO::elfio writer;
|
||||
|
||||
// You can't proceed before this function call!
|
||||
writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB);
|
||||
|
||||
writer.set_os_abi(ELFIO::ELFOSABI_NONE);
|
||||
writer.set_type(ELFIO::ET_REL);
|
||||
writer.set_machine(ELFIO::EM_RISCV);
|
||||
|
||||
auto sourceText = readSource(inFile);
|
||||
if (sourceText == nullptr)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
CompileError *compileError = nullptr;
|
||||
size_t tokensCount{ 0 };
|
||||
auto tokens = lex(sourceText, compileError, &tokensCount);
|
||||
free(sourceText);
|
||||
if (tokens == nullptr)
|
||||
{
|
||||
printf("%lu:%lu: %s\n", compileError->line(), compileError->column(), compileError->what());
|
||||
return 1;
|
||||
}
|
||||
auto ast = parse(tokens, tokensCount);
|
||||
if (ast == nullptr)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
auto program = writeNext(ast);
|
||||
|
||||
// Create code section
|
||||
ELFIO::section* text_sec = writer.sections.add(".text");
|
||||
text_sec->set_type(ELFIO::SHT_PROGBITS);
|
||||
text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
|
||||
text_sec->set_addr_align(0x1);
|
||||
text_sec->set_data(reinterpret_cast<const char *>(program.text), program.length);
|
||||
|
||||
// Create string table section
|
||||
ELFIO::section* str_sec = writer.sections.add(".strtab");
|
||||
str_sec->set_type(ELFIO::SHT_STRTAB);
|
||||
|
||||
// Create string table writer
|
||||
ELFIO::string_section_accessor stra(str_sec);
|
||||
// Add label name
|
||||
ELFIO::Elf32_Word str_index = stra.add_string("msg");
|
||||
|
||||
// Create read only data section
|
||||
ELFIO::section* ro_sec = writer.sections.add(".rodata");
|
||||
ro_sec->set_type(ELFIO::SHT_PROGBITS);
|
||||
ro_sec->set_flags(ELFIO::SHF_ALLOC);
|
||||
ro_sec->set_addr_align(0x4);
|
||||
ro_sec->set_data("%d\n");
|
||||
|
||||
// Create symbol table section
|
||||
ELFIO::section* sym_sec = writer.sections.add(".symtab");
|
||||
sym_sec->set_type(ELFIO::SHT_SYMTAB);
|
||||
sym_sec->set_info(2);
|
||||
sym_sec->set_addr_align(0x4);
|
||||
sym_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
|
||||
sym_sec->set_link(str_sec->get_index());
|
||||
|
||||
// Create symbol table writer
|
||||
ELFIO::symbol_section_accessor syma(writer, sym_sec);
|
||||
auto label_sym = syma.add_symbol(stra, ".CL0", 0x00000000, strlen("%d\n") + 1,
|
||||
ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ro_sec->get_index());
|
||||
syma.add_symbol(stra, program.name, 0x00000000, program.length,
|
||||
ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text_sec->get_index());
|
||||
auto printf_sym = syma.add_symbol(stra, "printf", 0x00000000, 0,
|
||||
ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF);
|
||||
|
||||
// Create relocation table section
|
||||
ELFIO::section* rel_sec = writer.sections.add(".rel.text");
|
||||
rel_sec->set_type(ELFIO::SHT_REL);
|
||||
rel_sec->set_info(text_sec->get_index());
|
||||
rel_sec->set_addr_align(0x4);
|
||||
rel_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_REL));
|
||||
rel_sec->set_link(sym_sec->get_index());
|
||||
rel_sec->set_flags(ELFIO::SHF_ALLOC);
|
||||
|
||||
// Create relocation table writer
|
||||
ELFIO::relocation_section_accessor rela(writer, rel_sec);
|
||||
// Add relocation entry (adjust address at offset 11)
|
||||
rela.add_entry(program.symbols[0].offset, label_sym, 26 /* ELFIO::R_RISCV_HI20 */);
|
||||
rela.add_entry(program.symbols[0].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
rela.add_entry(program.symbols[1].offset, label_sym, 27 /* ELFIO::R_RISCV_LO12_I */);
|
||||
rela.add_entry(program.symbols[1].offset, label_sym, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
rela.add_entry(program.symbols[2].offset, printf_sym, 18 /* ELFIO::R_RISCV_CALL */);
|
||||
rela.add_entry(program.symbols[2].offset, printf_sym, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
|
||||
// Another method to add the same relocation entry at one step is:
|
||||
// rela.add_entry( stra, "msg",
|
||||
// syma, 29, 0,
|
||||
// ELF_ST_INFO( STB_GLOBAL, STT_OBJECT ), 0,
|
||||
// text_sec->get_index(),
|
||||
// place_to_adjust, (unsigned char)R_386_RELATIVE );
|
||||
|
||||
/* We don't use local symbols here. There is no need to rearrange them.
|
||||
// But, for the completeness, we do this just prior 'save'
|
||||
syma.arrange_local_symbols([&](ELFIO::Elf_Xword first, ELFIO::Elf_Xword second) {
|
||||
rela.swap_symbols( first, second );
|
||||
}); */
|
||||
|
||||
// Create ELF object file
|
||||
writer.save(outputFilename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* Argument parsing.
|
||||
*/
|
||||
module elna.arguments;
|
||||
|
||||
import std.algorithm;
|
||||
import std.range;
|
||||
import std.sumtype;
|
||||
|
||||
struct ArgumentError
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
expectedOutputFile,
|
||||
noInput,
|
||||
superfluousArguments,
|
||||
}
|
||||
|
||||
private Type type_;
|
||||
private string argument_;
|
||||
|
||||
@property Type type() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.type_;
|
||||
}
|
||||
|
||||
@property string argument() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.argument_;
|
||||
}
|
||||
|
||||
void toString(OR)(OR range)
|
||||
if (isOutputRage!OR)
|
||||
{
|
||||
final switch (Type)
|
||||
{
|
||||
case Type.expectedOutputFile:
|
||||
put(range, "Expected an output filename after -o");
|
||||
break;
|
||||
case Type.noInput:
|
||||
put(range, "No input files specified");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported compiler arguments.
|
||||
*/
|
||||
struct Arguments
|
||||
{
|
||||
private bool assembler_;
|
||||
private string output_;
|
||||
private string inFile_;
|
||||
|
||||
@property string inFile() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.inFile_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether to generate assembly instead of an object file.
|
||||
*/
|
||||
@property bool assembler() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.assembler_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Output file.
|
||||
*/
|
||||
@property string output() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.output_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse command line arguments.
|
||||
*
|
||||
* The first argument is expected to be the program name (and it is
|
||||
* ignored).
|
||||
*
|
||||
* Params:
|
||||
* arguments = Command line arguments.
|
||||
*
|
||||
* Returns: Parsed arguments or an error.
|
||||
*/
|
||||
static SumType!(ArgumentError, Arguments) parse(string[] arguments)
|
||||
@nogc nothrow pure @safe
|
||||
{
|
||||
if (!arguments.empty)
|
||||
{
|
||||
arguments.popFront;
|
||||
}
|
||||
alias ReturnType = typeof(return);
|
||||
|
||||
return parseArguments(arguments).match!(
|
||||
(Arguments parsed) {
|
||||
if (parsed.inFile is null)
|
||||
{
|
||||
return ReturnType(ArgumentError(ArgumentError.Type.noInput));
|
||||
}
|
||||
else if (!arguments.empty)
|
||||
{
|
||||
return ReturnType(ArgumentError(
|
||||
ArgumentError.Type.superfluousArguments,
|
||||
arguments.front
|
||||
));
|
||||
}
|
||||
return ReturnType(parsed);
|
||||
},
|
||||
(ArgumentError argumentError) => ReturnType(argumentError)
|
||||
);
|
||||
}
|
||||
|
||||
private static SumType!(ArgumentError, Arguments) parseArguments(ref string[] arguments)
|
||||
@nogc nothrow pure @safe
|
||||
{
|
||||
Arguments parsed;
|
||||
|
||||
while (!arguments.empty)
|
||||
{
|
||||
if (arguments.front == "-s")
|
||||
{
|
||||
parsed.assembler_ = true;
|
||||
}
|
||||
else if (arguments.front == "-o")
|
||||
{
|
||||
if (arguments.empty)
|
||||
{
|
||||
return typeof(return)(ArgumentError(
|
||||
ArgumentError.Type.expectedOutputFile,
|
||||
arguments.front
|
||||
));
|
||||
}
|
||||
arguments.popFront;
|
||||
parsed.output_ = arguments.front;
|
||||
}
|
||||
else if (arguments.front == "--")
|
||||
{
|
||||
arguments.popFront;
|
||||
parsed.inFile_ = arguments.front;
|
||||
arguments.popFront;
|
||||
break;
|
||||
}
|
||||
else if (!arguments.front.startsWith("-"))
|
||||
{
|
||||
parsed.inFile_ = arguments.front;
|
||||
}
|
||||
arguments.popFront;
|
||||
}
|
||||
return typeof(return)(parsed);
|
||||
}
|
||||
}
|
@ -4,9 +4,7 @@ import core.stdc.stdio;
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.string;
|
||||
import elna.elf;
|
||||
import elna.ir;
|
||||
import elna.extended;
|
||||
import elna.riscv;
|
||||
import elna.lexer;
|
||||
import elna.parser;
|
||||
import elna.result;
|
||||
@ -18,27 +16,16 @@ import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
|
||||
private char* readSource(string source) @nogc
|
||||
{
|
||||
enum size_t bufferSize = 255;
|
||||
auto sourceFilename = String(source);
|
||||
extern(C++, "elna")
|
||||
Symbol writeNext(Block ast) @nogc;
|
||||
|
||||
return readFile(sourceFilename).match!(
|
||||
(ErrorCode errorCode) {
|
||||
perror(sourceFilename.toStringz);
|
||||
return null;
|
||||
},
|
||||
(Array!ubyte contents) {
|
||||
char* cString = cast(char*) malloc(contents.length + 1);
|
||||
memcpy(cString, contents.get.ptr, contents.length);
|
||||
cString[contents.length] = '\0';
|
||||
extern(C++, "elna")
|
||||
char* readSource(const(char)* source) @nogc;
|
||||
|
||||
return cString;
|
||||
}
|
||||
);
|
||||
}
|
||||
extern(C++, "elna")
|
||||
int compile(const(char)* inFile, const(char)* outputFilename) @nogc;
|
||||
|
||||
int generate(string inFile, ref String outputFilename) @nogc
|
||||
int generate(const(char)* inFile, const(char)* outputFilename) @nogc
|
||||
{
|
||||
auto sourceText = readSource(inFile);
|
||||
if (sourceText is null)
|
||||
@ -54,32 +41,24 @@ int generate(string inFile, ref String outputFilename) @nogc
|
||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
|
||||
return 1;
|
||||
}
|
||||
auto ast = parse(tokens[0 .. tokensCount]);
|
||||
if (!ast.valid)
|
||||
auto ast = parse(tokens, tokensCount);
|
||||
if (ast is null)
|
||||
{
|
||||
compileError = ast.error.get;
|
||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
|
||||
return 2;
|
||||
}
|
||||
auto transformVisitor = cast(TransformVisitor) malloc(__traits(classInstanceSize, TransformVisitor));
|
||||
(cast(void*) transformVisitor)[0 .. __traits(classInstanceSize, TransformVisitor)] = __traits(initSymbol, TransformVisitor)[];
|
||||
auto handle = File.open(outputFilename, BitFlags!(File.Mode)(File.Mode.truncate));
|
||||
|
||||
auto ir = transformVisitor.visit(ast.result);
|
||||
|
||||
transformVisitor.__xdtor();
|
||||
free(cast(void*) transformVisitor);
|
||||
|
||||
auto handle = File.open(outputFilename.toStringz, BitFlags!(File.Mode)(File.Mode.truncate));
|
||||
if (!handle.valid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
auto program = writeNext(ir);
|
||||
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(program.name, program.text);
|
||||
elf.addCode(String(program.name[0 .. strlen(program.name)]), text);
|
||||
|
||||
elf.addExternSymbol(String("printf"));
|
||||
foreach (ref reference; program.symbols)
|
||||
|
@ -297,10 +297,10 @@ struct File
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL File.read)
|
||||
*/
|
||||
SumType!(ErrorCode, Array!ubyte) readFile(String sourceFilename) @nogc
|
||||
SumType!(ErrorCode, Array!ubyte) readFile(const(char)* sourceFilename) @nogc
|
||||
{
|
||||
enum size_t bufferSize = 255;
|
||||
auto sourceFile = File.open(sourceFilename.toStringz, BitFlags!(File.Mode)(File.Mode.read));
|
||||
auto sourceFile = File.open(sourceFilename, BitFlags!(File.Mode)(File.Mode.read));
|
||||
|
||||
if (!sourceFile.valid)
|
||||
{
|
||||
|
220
source/elna/ir.d
220
source/elna/ir.d
@ -1,220 +0,0 @@
|
||||
module elna.ir;
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import parser = elna.parser;
|
||||
import tanya.container.array;
|
||||
import tanya.container.hashtable;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
public import elna.parser : BinaryOperator;
|
||||
|
||||
/**
|
||||
* Mapping between the parser and IR AST.
|
||||
*/
|
||||
struct ASTMapping
|
||||
{
|
||||
alias Node = .Node;
|
||||
alias Definition = .Definition;
|
||||
alias Statement = .Operand;
|
||||
alias BangStatement = .Operand;
|
||||
alias Block = .Definition;
|
||||
alias Expression = .Operand;
|
||||
alias Number = .Number;
|
||||
alias Variable = .Number;
|
||||
alias BinaryExpression = .Variable;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* IR visitor.
|
||||
*/
|
||||
extern(C++, "elna", "ir")
|
||||
abstract class IRVisitor
|
||||
{
|
||||
abstract void visit(Node) @nogc;
|
||||
abstract void visit(Definition) @nogc;
|
||||
abstract void visit(Operand) @nogc;
|
||||
abstract void visit(BinaryExpression) @nogc;
|
||||
abstract void visit(Variable) @nogc;
|
||||
abstract void visit(Number) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
extern(C++, "elna", "ir")
|
||||
abstract class Node
|
||||
{
|
||||
abstract void accept(IRVisitor) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition.
|
||||
*/
|
||||
extern(C++, "elna", "ir")
|
||||
class Definition : Node
|
||||
{
|
||||
BinaryExpression* statements;
|
||||
size_t statementsLength;
|
||||
Operand result;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
abstract class Statement : Node
|
||||
{
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
abstract class Operand : Node
|
||||
{
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
class Number : Operand
|
||||
{
|
||||
int value;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
class Variable : Operand
|
||||
{
|
||||
size_t counter;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
class BinaryExpression : Statement
|
||||
{
|
||||
Operand lhs, rhs;
|
||||
BinaryOperator operator;
|
||||
|
||||
this(Operand lhs, Operand rhs, BinaryOperator operator) @nogc;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna", "ir")
|
||||
class BangExpression : Statement
|
||||
{
|
||||
Operand operand;
|
||||
|
||||
this(Operand operand);
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
||||
{
|
||||
private HashTable!(String, int) constants;
|
||||
private BinaryExpression* statements;
|
||||
private size_t statementsLength;
|
||||
|
||||
ASTMapping.Node visit(parser.Node node) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.Definition visit(parser.Definition definition) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
||||
{
|
||||
return statement.expression.accept(this);
|
||||
}
|
||||
|
||||
ASTMapping.Block visit(parser.Block block) @nogc
|
||||
{
|
||||
auto target = defaultAllocator.make!Definition;
|
||||
this.constants = transformConstants(block.definitions);
|
||||
|
||||
target.result = block.statement.accept(this);
|
||||
target.statements = this.statements;
|
||||
target.statementsLength = this.statementsLength;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
ASTMapping.Expression visit(parser.Expression expression) @nogc
|
||||
{
|
||||
if ((cast(parser.Number) expression) !is null)
|
||||
{
|
||||
return (cast(parser.Number) expression).accept(this);
|
||||
}
|
||||
if ((cast(parser.Variable) expression) !is null)
|
||||
{
|
||||
return (cast(parser.Variable) expression).accept(this);
|
||||
}
|
||||
else if ((cast(parser.BinaryExpression) expression) !is null)
|
||||
{
|
||||
return (cast(parser.BinaryExpression) expression).accept(this);
|
||||
}
|
||||
assert(false, "Invalid expression type");
|
||||
}
|
||||
|
||||
ASTMapping.Number visit(parser.Number number) @nogc
|
||||
{
|
||||
auto numberExpression = defaultAllocator.make!Number;
|
||||
numberExpression.value = number.value;
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
|
||||
ASTMapping.Variable visit(parser.Variable variable) @nogc
|
||||
{
|
||||
auto numberExpression = defaultAllocator.make!Number;
|
||||
numberExpression.value = this.constants[variable.identifier];
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
|
||||
ASTMapping.BinaryExpression visit(parser.BinaryExpression binaryExpression) @nogc
|
||||
{
|
||||
auto target = defaultAllocator.make!BinaryExpression(
|
||||
binaryExpression.lhs.accept(this),
|
||||
binaryExpression.rhs.accept(this),
|
||||
binaryExpression.operator
|
||||
);
|
||||
this.statements = cast(BinaryExpression*)
|
||||
realloc(this.statements, (this.statementsLength + 1) * BinaryExpression.sizeof);
|
||||
this.statements[this.statementsLength++] = target;
|
||||
|
||||
auto newVariable = defaultAllocator.make!Variable;
|
||||
newVariable.counter = this.statementsLength;
|
||||
|
||||
return newVariable;
|
||||
}
|
||||
|
||||
private Number transformNumber(parser.Number number) @nogc
|
||||
{
|
||||
return defaultAllocator.make!Number(number.value);
|
||||
}
|
||||
|
||||
override Operand visit(parser.Statement statement) @nogc
|
||||
{
|
||||
if ((cast(parser.BangStatement) statement) !is null)
|
||||
{
|
||||
return (cast(parser.BangStatement) statement).accept(this);
|
||||
}
|
||||
assert(false, "Invalid statement type");
|
||||
}
|
||||
|
||||
private HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
|
||||
{
|
||||
typeof(return) constants;
|
||||
|
||||
foreach (definition; definitions[])
|
||||
{
|
||||
constants[definition.identifier] = definition.number.value;
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
module elna.lexer;
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.ctype;
|
||||
import core.stdc.string;
|
||||
import elna.result;
|
||||
import std.range;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
|
||||
extern(C++, "elna")
|
||||
struct Token
|
||||
@ -56,62 +51,5 @@ struct Token
|
||||
@property const(Position) position() const @nogc nothrow pure @safe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Range over the source text that keeps track of the current position.
|
||||
*/
|
||||
struct Source
|
||||
{
|
||||
const(char)* buffer;
|
||||
Position position;
|
||||
|
||||
this(const(char)* buffer) @nogc nothrow pure @safe
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@disable this();
|
||||
|
||||
bool empty() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.buffer is null || this.buffer[0] == '\0';
|
||||
}
|
||||
|
||||
char front() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
return this.buffer[0];
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure
|
||||
in (!empty)
|
||||
{
|
||||
++this.buffer;
|
||||
++this.position.column;
|
||||
}
|
||||
|
||||
void breakLine() @nogc nothrow pure
|
||||
in (!empty)
|
||||
{
|
||||
++this.buffer;
|
||||
++this.position.line;
|
||||
this.position.column = 1;
|
||||
}
|
||||
|
||||
@property size_t length() const @nogc nothrow pure
|
||||
{
|
||||
return strlen(this.buffer);
|
||||
}
|
||||
|
||||
char opIndex(size_t index) @nogc nothrow pure
|
||||
{
|
||||
return this.buffer[index];
|
||||
}
|
||||
|
||||
const(char)[] opSlice(size_t i, size_t j) @nogc nothrow pure
|
||||
{
|
||||
return this.buffer[i .. j];
|
||||
}
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
Token* lex(const(char)* buffer, CompileError* compileError, size_t* length) @nogc;
|
||||
|
@ -1,109 +1,92 @@
|
||||
module elna.parser;
|
||||
|
||||
import core.stdc.string;
|
||||
import core.stdc.stdlib;
|
||||
import elna.lexer;
|
||||
import elna.result;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import std.array;
|
||||
|
||||
/**
|
||||
* Parser visitor.
|
||||
*/
|
||||
interface ParserVisitor(Mapping)
|
||||
extern(C++, "elna")
|
||||
abstract class ParserVisitor
|
||||
{
|
||||
Mapping.Node visit(Node) @nogc;
|
||||
Mapping.Definition visit(Definition) @nogc;
|
||||
Mapping.Statement visit(Statement) @nogc;
|
||||
Mapping.BangStatement visit(BangStatement) @nogc;
|
||||
Mapping.Block visit(Block) @nogc;
|
||||
Mapping.Expression visit(Expression) @nogc;
|
||||
Mapping.Number visit(Number) @nogc;
|
||||
Mapping.Variable visit(Variable) @nogc;
|
||||
Mapping.BinaryExpression visit(BinaryExpression) @nogc;
|
||||
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
|
||||
{
|
||||
Mapping.Node accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
abstract void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
abstract class Statement : Node
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
extern(C++, "elna")
|
||||
class Definition : Node
|
||||
{
|
||||
Number number;
|
||||
String identifier;
|
||||
const(char)* identifier;
|
||||
|
||||
Mapping.Definition accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Statement : Node
|
||||
{
|
||||
Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class BangStatement : Statement
|
||||
{
|
||||
Expression expression;
|
||||
|
||||
Mapping.BangStatement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class Block : Node
|
||||
{
|
||||
Array!Definition definitions;
|
||||
Definition* definitions;
|
||||
size_t definitionsLength;
|
||||
Statement statement;
|
||||
|
||||
Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
abstract class Expression : Node
|
||||
{
|
||||
Mapping.Expression accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class Number : Expression
|
||||
{
|
||||
int value;
|
||||
|
||||
Mapping.Number accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class Variable : Expression
|
||||
{
|
||||
String identifier;
|
||||
const(char)* identifier;
|
||||
|
||||
Mapping.Variable accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
@ -113,196 +96,15 @@ enum BinaryOperator
|
||||
subtraction
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class BinaryExpression : Expression
|
||||
{
|
||||
Expression lhs, rhs;
|
||||
BinaryOperator operator;
|
||||
|
||||
this(Expression lhs, Expression rhs, String operator) @nogc
|
||||
{
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
if (operator == "+")
|
||||
{
|
||||
this.operator = BinaryOperator.sum;
|
||||
}
|
||||
else if (operator == "-")
|
||||
{
|
||||
this.operator = BinaryOperator.subtraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, "Invalid binary operator");
|
||||
}
|
||||
}
|
||||
|
||||
Mapping.BinaryExpression accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
this(Expression lhs, Expression rhs, ubyte operator) @nogc;
|
||||
override void accept(ParserVisitor visitor) @nogc;
|
||||
}
|
||||
|
||||
private Result!Expression parseFactor(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected factor, got end of stream")
|
||||
{
|
||||
if (tokens.front.of() == Token.Type.identifier)
|
||||
{
|
||||
auto variable = defaultAllocator.make!Variable;
|
||||
variable.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())];
|
||||
tokens.popFront;
|
||||
return Result!Expression(variable);
|
||||
}
|
||||
else if (tokens.front.of() == Token.Type.number)
|
||||
{
|
||||
auto number = defaultAllocator.make!Number;
|
||||
number.value = tokens.front.number();
|
||||
tokens.popFront;
|
||||
return Result!Expression(number);
|
||||
}
|
||||
else if (tokens.front.of() == Token.Type.leftParen)
|
||||
{
|
||||
tokens.popFront;
|
||||
|
||||
auto expression = parseExpression(tokens);
|
||||
|
||||
tokens.popFront;
|
||||
return expression;
|
||||
}
|
||||
return Result!Expression("Expected a factor", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Expression parseTerm(ref Token[] tokens) @nogc
|
||||
{
|
||||
return parseFactor(tokens);
|
||||
}
|
||||
|
||||
private Result!Expression parseExpression(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected expression, got end of stream")
|
||||
{
|
||||
auto term = parseTerm(tokens);
|
||||
if (!term.valid || tokens.empty || tokens.front.of() != Token.Type.operator)
|
||||
{
|
||||
return term;
|
||||
}
|
||||
auto operator = String(tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]);
|
||||
tokens.popFront;
|
||||
|
||||
auto expression = parseExpression(tokens);
|
||||
|
||||
if (expression.valid)
|
||||
{
|
||||
auto binaryExpression = defaultAllocator
|
||||
.make!BinaryExpression(term.result, expression.result, operator);
|
||||
|
||||
return Result!Expression(binaryExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Expression("Expected right-hand side to be an expression", tokens.front.position);
|
||||
}
|
||||
}
|
||||
|
||||
private Result!Definition parseDefinition(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
auto definition = defaultAllocator.make!Definition;
|
||||
definition.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]; // Copy.
|
||||
|
||||
tokens.popFront();
|
||||
tokens.popFront(); // Skip the equals sign.
|
||||
|
||||
if (tokens.front.of() == Token.Type.number)
|
||||
{
|
||||
auto number = defaultAllocator.make!Number;
|
||||
number.value = tokens.front.number();
|
||||
definition.number = number;
|
||||
tokens.popFront;
|
||||
return Result!Definition(definition);
|
||||
}
|
||||
return Result!Definition("Expected a number", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Statement parseStatement(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
if (tokens.front.of() == Token.Type.bang)
|
||||
{
|
||||
tokens.popFront;
|
||||
auto statement = defaultAllocator.make!BangStatement;
|
||||
auto expression = parseExpression(tokens);
|
||||
if (expression.valid)
|
||||
{
|
||||
statement.expression = expression.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Statement(expression.error.get);
|
||||
}
|
||||
return Result!Statement(statement);
|
||||
}
|
||||
return Result!Statement("Expected ! statement", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!(Array!Definition) parseDefinitions(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
tokens.popFront; // Skip const.
|
||||
|
||||
Array!Definition definitions;
|
||||
|
||||
while (!tokens.empty)
|
||||
{
|
||||
auto definition = parseDefinition(tokens);
|
||||
if (!definition.valid)
|
||||
{
|
||||
return typeof(return)(definition.error.get);
|
||||
}
|
||||
definitions.insertBack(definition.result);
|
||||
if (tokens.front.of() == Token.Type.semicolon)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tokens.front.of() == Token.Type.comma)
|
||||
{
|
||||
tokens.popFront;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)(definitions);
|
||||
}
|
||||
|
||||
private Result!Block parseBlock(ref Token[] tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
auto block = defaultAllocator.make!Block;
|
||||
if (tokens.front.of() == Token.Type.let)
|
||||
{
|
||||
auto constDefinitions = parseDefinitions(tokens);
|
||||
if (constDefinitions.valid)
|
||||
{
|
||||
block.definitions = constDefinitions.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(constDefinitions.error.get);
|
||||
}
|
||||
tokens.popFront;
|
||||
}
|
||||
auto statement = parseStatement(tokens);
|
||||
if (statement.valid)
|
||||
{
|
||||
block.statement = statement.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(statement.error.get);
|
||||
}
|
||||
|
||||
return Result!Block(block);
|
||||
}
|
||||
|
||||
Result!Block parse(Token[] tokenStream) @nogc
|
||||
{
|
||||
auto tokens = tokenStream[];
|
||||
return parseBlock(tokens);
|
||||
}
|
||||
extern(C++, "elna")
|
||||
Block parse(Token* tokenStream, size_t length) @nogc;
|
||||
|
@ -90,9 +90,12 @@ struct Reference
|
||||
Target target;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
Array!ubyte text;
|
||||
Array!Reference symbols;
|
||||
this(const(char)* name) @nogc;
|
||||
const(char)* name;
|
||||
ubyte* text;
|
||||
size_t length;
|
||||
Reference[3] symbols;
|
||||
}
|
||||
|
@ -1,183 +0,0 @@
|
||||
module elna.riscv;
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import elna.ir;
|
||||
import elna.result;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
|
||||
extern(C++, "elna")
|
||||
enum XRegister : ubyte
|
||||
{
|
||||
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,
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
enum Funct3 : ubyte
|
||||
{
|
||||
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,
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
enum Funct12 : ubyte
|
||||
{
|
||||
ecall = 0b000000000000,
|
||||
ebreak = 0b000000000001,
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
enum Funct7 : ubyte
|
||||
{
|
||||
none = 0,
|
||||
sub = 0b0100000
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
enum BaseOpcode : ubyte
|
||||
{
|
||||
opImm = 0b0010011,
|
||||
lui = 0b0110111,
|
||||
auipc = 0b0010111,
|
||||
op = 0b0110011,
|
||||
jal = 0b1101111,
|
||||
jalr = 0b1100111,
|
||||
branch = 0b1100011,
|
||||
load = 0b0000011,
|
||||
store = 0b0100011,
|
||||
miscMem = 0b0001111,
|
||||
system = 0b1110011,
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
struct Instruction
|
||||
{
|
||||
private uint instruction;
|
||||
|
||||
this(BaseOpcode opcode) @nogc;
|
||||
@disable this();
|
||||
|
||||
ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
|
||||
return scope @nogc;
|
||||
|
||||
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
|
||||
return scope @nogc;
|
||||
|
||||
ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
|
||||
return scope @nogc;
|
||||
|
||||
ref Instruction u(XRegister rd, uint imm)
|
||||
return scope @nogc;
|
||||
|
||||
ubyte* encode() return scope @nogc;
|
||||
}
|
||||
|
||||
extern(C++, "elna")
|
||||
class RiscVVisitor : IRVisitor
|
||||
{
|
||||
Instruction *instructions;
|
||||
size_t instructionsLength;
|
||||
bool registerInUse;
|
||||
uint variableCounter = 1;
|
||||
Reference[3] references;
|
||||
|
||||
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(Number number) @nogc;
|
||||
override void visit(BinaryExpression expression) @nogc;
|
||||
}
|
||||
|
||||
Symbol writeNext(Definition ast) @nogc
|
||||
{
|
||||
Array!Instruction instructions;
|
||||
auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
|
||||
(cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
|
||||
scope (exit)
|
||||
{
|
||||
free(cast(void*) visitor);
|
||||
}
|
||||
visitor.visit(ast);
|
||||
|
||||
auto program = Symbol(String("main"));
|
||||
|
||||
program.symbols = Array!Reference(visitor.references[]);
|
||||
foreach (ref instruction; visitor.instructions[0 .. visitor.instructionsLength])
|
||||
{
|
||||
program.text.insertBack(instruction.encode[0 .. uint.sizeof]);
|
||||
}
|
||||
return program;
|
||||
}
|
@ -1,53 +1,46 @@
|
||||
#include "elna/ir.hpp"
|
||||
|
||||
namespace elna::ir
|
||||
#include <cassert>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
void Node::accept(IRVisitor *)
|
||||
void TransformVisitor::visit(Node *node)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void Definition::accept(IRVisitor *visitor)
|
||||
void TransformVisitor::visit(Definition *definition)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void Operand::accept(IRVisitor *visitor)
|
||||
void TransformVisitor::visit(BangStatement *statement)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void Number::accept(IRVisitor *visitor)
|
||||
void TransformVisitor::visit(Block *block)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void Variable::accept(IRVisitor *visitor)
|
||||
void TransformVisitor::visit(Expression *expression)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
BinaryExpression::BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator)
|
||||
void TransformVisitor::visit(Number *number)
|
||||
{
|
||||
this->lhs = lhs;
|
||||
this->rhs = rhs;
|
||||
this->_operator = _operator;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void BinaryExpression::accept(IRVisitor *visitor)
|
||||
void TransformVisitor::visit(Variable *variable)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
BangExpression::BangExpression(Operand *operand)
|
||||
void TransformVisitor::visit(BinaryExpression *binaryExpression)
|
||||
{
|
||||
this->operand = operand;
|
||||
}
|
||||
|
||||
void BangExpression::accept(IRVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,15 @@
|
||||
import elna.backend;
|
||||
import elna.ir;
|
||||
import elna.arguments;
|
||||
import std.path;
|
||||
import std.sumtype;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
int main(string[] args)
|
||||
extern(C)
|
||||
int main(int argc, char** args)
|
||||
{
|
||||
defaultAllocator = MmapPool.instance;
|
||||
|
||||
return Arguments.parse(args).match!(
|
||||
(ArgumentError argumentError) => 4,
|
||||
(Arguments arguments) {
|
||||
String outputFilename;
|
||||
if (arguments.output is null)
|
||||
{
|
||||
outputFilename = arguments
|
||||
.inFile
|
||||
.baseName
|
||||
.withExtension("o");
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFilename = String(arguments.output);
|
||||
}
|
||||
|
||||
return generate(arguments.inFile, outputFilename);
|
||||
}
|
||||
);
|
||||
// return generate(args[3], args[2]);
|
||||
return compile(args[3], args[2]);
|
||||
}
|
||||
|
240
source/parser.cpp
Normal file
240
source/parser.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
#include "elna/parser.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
void Node::accept(ParserVisitor *)
|
||||
{
|
||||
}
|
||||
|
||||
void Definition::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
void Block::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
void Expression::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
void Number::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
void Variable::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
BinaryExpression::BinaryExpression(Expression *lhs, Expression *rhs, unsigned char _operator)
|
||||
{
|
||||
this->lhs = lhs;
|
||||
this->rhs = rhs;
|
||||
|
||||
if (_operator == '+')
|
||||
{
|
||||
this->_operator = BinaryOperator::sum;
|
||||
}
|
||||
else if (_operator == '-')
|
||||
{
|
||||
this->_operator = BinaryOperator::subtraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error("Invalid binary operator");
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryExpression::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
void BangStatement::accept(ParserVisitor *visitor)
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
Block *parse(Token *tokenStream, std::size_t length)
|
||||
{
|
||||
return parseBlock(&tokenStream, &length);
|
||||
}
|
||||
|
||||
Expression *parseFactor(Token **tokens, size_t *length)
|
||||
{
|
||||
if ((*tokens)[0].of() == Token::TOKEN_IDENTIFIER)
|
||||
{
|
||||
auto variable = new Variable();
|
||||
variable->identifier = (*tokens)[0].identifier();
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
return variable;
|
||||
}
|
||||
else if ((*tokens)[0].of() == Token::TOKEN_NUMBER)
|
||||
{
|
||||
auto number = new Number();
|
||||
number->value = (*tokens)[0].number();
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
return number;
|
||||
}
|
||||
else if ((*tokens)[0].of() == Token::TOKEN_LEFT_PAREN)
|
||||
{
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
|
||||
auto expression = parseExpression(tokens, length);
|
||||
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
|
||||
return expression;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Expression *parseTerm(Token **tokens, size_t *length)
|
||||
{
|
||||
return parseFactor(tokens, length);
|
||||
}
|
||||
|
||||
Expression *parseExpression(Token **tokens, size_t *length)
|
||||
{
|
||||
auto term = parseTerm(tokens, length);
|
||||
if (term == nullptr || *length == 0 || (*tokens)[0].of() != Token::TOKEN_OPERATOR)
|
||||
{
|
||||
return term;
|
||||
}
|
||||
auto _operator = (*tokens)[0].identifier()[0];
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
|
||||
auto expression = parseExpression(tokens, length);
|
||||
|
||||
if (expression != nullptr)
|
||||
{
|
||||
auto binaryExpression = new BinaryExpression(term, expression, _operator);
|
||||
|
||||
return binaryExpression;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Definition *parseDefinition(Token **tokens, size_t *length)
|
||||
{
|
||||
auto definition = new Definition();
|
||||
definition->identifier = (*tokens)[0].identifier(); // Copy.
|
||||
|
||||
++(*tokens);
|
||||
++(*tokens); // Skip the equals sign.
|
||||
*length -= 2;
|
||||
|
||||
if ((*tokens)[0].of() == Token::TOKEN_NUMBER)
|
||||
{
|
||||
auto number = new Number();
|
||||
number->value = (*tokens)[0].number();
|
||||
definition->number = number;
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
return definition;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Statement *parseStatement(Token **tokens, std::size_t *length)
|
||||
{
|
||||
if ((*tokens)[0].of() == Token::TOKEN_BANG)
|
||||
{
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
auto statement = new BangStatement();
|
||||
auto expression = parseExpression(tokens, length);
|
||||
if (expression != nullptr)
|
||||
{
|
||||
statement->expression = expression;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Definition **parseDefinitions(Token **tokens, size_t *length, size_t *resultLength)
|
||||
{
|
||||
++(*tokens); // Skip const.
|
||||
--(*length);
|
||||
|
||||
Definition **definitions;
|
||||
*resultLength = 0;
|
||||
|
||||
while (*length != 0)
|
||||
{
|
||||
auto definition = parseDefinition(tokens, length);
|
||||
if (definition == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
definitions = reinterpret_cast<Definition **>(
|
||||
realloc(definitions, (*resultLength + 1) * sizeof(Definition*)));
|
||||
definitions[(*resultLength)++] = definition;
|
||||
|
||||
if ((*tokens)[0].of() == Token::TOKEN_SEMICOLON)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((*tokens)[0].of() == Token::TOKEN_COMMA)
|
||||
{
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
Block *parseBlock(Token **tokens, std::size_t *length)
|
||||
{
|
||||
auto block = new Block();
|
||||
if ((*tokens)[0].of() == Token::TOKEN_LET)
|
||||
{
|
||||
size_t length_ = 0;
|
||||
auto constDefinitions = parseDefinitions(tokens, length, &length_);
|
||||
if (constDefinitions != nullptr)
|
||||
{
|
||||
block->definitionsLength = length_;
|
||||
block->definitions = constDefinitions;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
++(*tokens);
|
||||
--(*length);
|
||||
}
|
||||
auto statement = parseStatement(tokens, length);
|
||||
if (statement != nullptr)
|
||||
{
|
||||
block->statement = statement;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
}
|
@ -22,4 +22,9 @@ namespace elna
|
||||
{
|
||||
return this->position.column;
|
||||
}
|
||||
|
||||
Symbol::Symbol(const char *name)
|
||||
{
|
||||
this->name = name;
|
||||
}
|
||||
}
|
||||
|
130
source/riscv.cpp
130
source/riscv.cpp
@ -1,5 +1,6 @@
|
||||
#include "elna/parser.hpp"
|
||||
#include "elna/riscv.hpp"
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace elna
|
||||
@ -53,35 +54,43 @@ namespace elna
|
||||
return reinterpret_cast<std::uint8_t *>(&this->instruction);
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::Node *)
|
||||
void RiscVVisitor::visit(Node *)
|
||||
{
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::Definition *definition)
|
||||
void RiscVVisitor::visit(Definition *definition)
|
||||
{
|
||||
const uint stackSize = static_cast<std::uint32_t>(definition->statementsLength * 4 + 12);
|
||||
++constCount;
|
||||
constNames = reinterpret_cast<const char **>(realloc(constNames, sizeof(const char *) * constCount));
|
||||
constValues = reinterpret_cast<std::int32_t *>(realloc(constValues, sizeof(std::int32_t) * constCount));
|
||||
|
||||
constNames[constCount - 1] = definition->identifier;
|
||||
constValues[constCount - 1] = definition->number->value;
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(Block *block)
|
||||
{
|
||||
for (std::size_t i = 0; i < block->definitionsLength; ++i)
|
||||
{
|
||||
block->definitions[i]->accept(this);
|
||||
}
|
||||
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);
|
||||
block->statement->accept(this);
|
||||
|
||||
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;
|
||||
// Prologue.
|
||||
const uint stackSize = static_cast<std::uint32_t>(variableCounter * 4 + 12);
|
||||
|
||||
this->instructions[0] = Instruction(BaseOpcode::opImm)
|
||||
.i(XRegister::sp, Funct3::addi, XRegister::sp, -stackSize);
|
||||
this->instructions[1] = Instruction(BaseOpcode::store)
|
||||
.s(stackSize - 4, Funct3::sw, XRegister::sp, XRegister::s0);
|
||||
this->instructions[2] = Instruction(BaseOpcode::store)
|
||||
.s(stackSize - 8, Funct3::sw, XRegister::sp, XRegister::ra);
|
||||
this->instructions[3] = Instruction(BaseOpcode::opImm)
|
||||
.i(XRegister::s0, Funct3::addi, XRegister::sp, stackSize);
|
||||
|
||||
this->instructions = reinterpret_cast<Instruction*>(
|
||||
realloc(this->instructions, (this->instructionsLength + 10) * sizeof(Instruction)));
|
||||
@ -123,32 +132,44 @@ namespace elna
|
||||
.i(XRegister::zero, Funct3::jalr, XRegister::ra, 0);
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::Operand *operand)
|
||||
void RiscVVisitor::visit(BangStatement *statement)
|
||||
{
|
||||
if (dynamic_cast<ir::Variable *>(operand) != nullptr)
|
||||
statement->expression->accept(this);
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(Expression *operand)
|
||||
{
|
||||
if (dynamic_cast<Variable *>(operand) != nullptr)
|
||||
{
|
||||
return dynamic_cast<ir::Variable *>(operand)->accept(this);
|
||||
return dynamic_cast<Variable *>(operand)->accept(this);
|
||||
}
|
||||
if (dynamic_cast<ir::Number *>(operand) != nullptr)
|
||||
if (dynamic_cast<Number *>(operand) != nullptr)
|
||||
{
|
||||
return dynamic_cast<ir::Number *>(operand)->accept(this);
|
||||
return dynamic_cast<Number *>(operand)->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::Variable *variable)
|
||||
void RiscVVisitor::visit(Variable *variable)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < constCount; ++i)
|
||||
{
|
||||
if (strcmp(variable->identifier, constNames[i]) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
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));
|
||||
this->instructions[this->instructionsLength - 1] =
|
||||
Instruction(BaseOpcode::opImm) // movl $x, %eax; where $x is a number.
|
||||
.i(freeRegister, Funct3::addi, XRegister::zero, constValues[i]);
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::Number *number)
|
||||
void RiscVVisitor::visit(Number *number)
|
||||
{
|
||||
const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0;
|
||||
|
||||
@ -160,32 +181,63 @@ namespace elna
|
||||
.i(freeRegister, Funct3::addi, XRegister::zero, number->value);
|
||||
}
|
||||
|
||||
void RiscVVisitor::visit(ir::BinaryExpression *expression)
|
||||
void RiscVVisitor::visit(BinaryExpression *expression)
|
||||
{
|
||||
const auto lhs_register = this->registerInUse ? XRegister::a0 : XRegister::t0;
|
||||
|
||||
this->registerInUse = true;
|
||||
expression->lhs->accept(this);
|
||||
|
||||
++this->instructionsLength;
|
||||
this->instructions = reinterpret_cast<Instruction *>(
|
||||
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||
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);
|
||||
auto lhs_stack_position = ++this->variableCounter;
|
||||
|
||||
this->registerInUse = false;
|
||||
expression->rhs->accept(this);
|
||||
|
||||
this->instructionsLength += 2;
|
||||
this->instructions = reinterpret_cast<Instruction *>(
|
||||
realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
|
||||
|
||||
this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::load)
|
||||
.i(XRegister::a0, Funct3::lw, XRegister::sp,
|
||||
static_cast<std::int8_t>((lhs_stack_position - 1) * 4));
|
||||
|
||||
// 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);
|
||||
this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op)
|
||||
.r(lhs_register, 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);
|
||||
this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::op)
|
||||
.r(lhs_register, 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;
|
||||
Symbol writeNext(Block *ast)
|
||||
{
|
||||
auto visitor = std::make_unique<RiscVVisitor>();
|
||||
visitor->visit(ast);
|
||||
|
||||
Symbol program{ "main" };
|
||||
|
||||
for (std::size_t i = 0; i < 3; ++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)
|
||||
{
|
||||
memcpy(program.text + program.length, visitor->instructions[i].encode(), sizeof(std::uint32_t));
|
||||
program.length += sizeof(std::uint32_t);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user