Remove the IR for know for simplicity

This commit is contained in:
2024-03-01 10:13:55 +01:00
parent 4d46fc6b4d
commit 223f54d38d
36 changed files with 7547 additions and 1079 deletions

136
source/cl.cpp Normal file
View 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,4 +22,9 @@ namespace elna
{
return this->position.column;
}
Symbol::Symbol(const char *name)
{
this->name = name;
}
}

View File

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