Grow stack automatically
This commit is contained in:
parent
df2494e145
commit
86d579e8d5
5
CMakeLists.txt
Normal file
5
CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(Elna)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
add_executable(tester tests/runner.cpp)
|
8
README
8
README
@ -35,3 +35,11 @@ factor = ident | number | "(" expression ")";
|
|||||||
"!" - Write a line.
|
"!" - Write a line.
|
||||||
"?" - Read user input.
|
"?" - Read user input.
|
||||||
"odd" - The only function, returns whether a number is odd.
|
"odd" - The only function, returns whether a number is odd.
|
||||||
|
|
||||||
|
# Build and test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -B build
|
||||||
|
make -C build
|
||||||
|
./build/bin/tester
|
||||||
|
```
|
||||||
|
33
Rakefile
33
Rakefile
@ -39,40 +39,9 @@ file BINARY => SOURCES do |t|
|
|||||||
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
|
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task default: TESTS
|
||||||
task default: BINARY
|
task default: BINARY
|
||||||
|
|
||||||
desc 'Run all tests and check the results'
|
|
||||||
task test: TESTS
|
|
||||||
task test: BINARY do
|
|
||||||
TESTS.each do |test|
|
|
||||||
expected = Pathname
|
|
||||||
.new(test)
|
|
||||||
.sub_ext('.txt')
|
|
||||||
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
|
|
||||||
.to_path
|
|
||||||
|
|
||||||
puts "Running #{test}"
|
|
||||||
if test.include? '/riscv/'
|
|
||||||
spike = [
|
|
||||||
'/opt/riscv/bin/spike',
|
|
||||||
'--isa=RV32IMAC',
|
|
||||||
'/opt/riscv/riscv32-unknown-elf/bin/pk',
|
|
||||||
test
|
|
||||||
]
|
|
||||||
diff = ['diff', '-Nur', '--color', expected, '-']
|
|
||||||
tail = ['tail', '-n', '1']
|
|
||||||
|
|
||||||
last_stdout, wait_threads = Open3.pipeline_r spike, tail, diff
|
|
||||||
else
|
|
||||||
raise 'Unsupported test platform'
|
|
||||||
end
|
|
||||||
print last_stdout.read
|
|
||||||
last_stdout.close
|
|
||||||
|
|
||||||
fail unless wait_threads.last.value.exitstatus.zero?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Run unittest blocks'
|
desc 'Run unittest blocks'
|
||||||
task unittest: SOURCES do |t|
|
task unittest: SOURCES do |t|
|
||||||
sh('dub', 'test', '--compiler=gdc-12')
|
sh('dub', 'test', '--compiler=gdc-12')
|
||||||
|
8
TODO
Normal file
8
TODO
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
- Mark all classes as extern(C++) to interface with C++.
|
||||||
|
- Some nodes assert(false, "Not implemented") because their logic is handled
|
||||||
|
in the parent nodes. Move this logic to the nodes themselves.
|
||||||
|
- Implement multiplication and division.
|
||||||
|
- Split tester.cpp into a header and implementation files.
|
||||||
|
- The generate function in backend.d contains the whole compilation logic.
|
||||||
|
It's not a backend task.
|
||||||
|
- Output assembly.
|
146
source/elna/ir.d
146
source/elna/ir.d
@ -15,34 +15,35 @@ struct ASTMapping
|
|||||||
alias Node = .Node;
|
alias Node = .Node;
|
||||||
alias Definition = .Definition;
|
alias Definition = .Definition;
|
||||||
alias VariableDeclaration = .VariableDeclaration;
|
alias VariableDeclaration = .VariableDeclaration;
|
||||||
alias Statement = Array!(.Statement);
|
alias Statement = .Operand;
|
||||||
alias BangStatement = .Expression;
|
alias BangStatement = .Operand;
|
||||||
alias Block = .Definition;
|
alias Block = .Definition;
|
||||||
alias Expression = .Expression;
|
alias Expression = .Operand;
|
||||||
alias Number = .Number;
|
alias Number = .Number;
|
||||||
alias Variable = .Variable;
|
alias Variable = .Number;
|
||||||
alias BinaryExpression = .BinaryExpression;
|
alias BinaryExpression = .Variable;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IR visitor.
|
* IR visitor.
|
||||||
*/
|
*/
|
||||||
|
extern(C++)
|
||||||
interface IRVisitor
|
interface IRVisitor
|
||||||
{
|
{
|
||||||
abstract void visit(Node) @nogc;
|
abstract void visit(Node) @nogc;
|
||||||
abstract void visit(Definition) @nogc;
|
abstract void visit(Definition) @nogc;
|
||||||
abstract void visit(Expression) @nogc;
|
abstract void visit(Operand) @nogc;
|
||||||
abstract void visit(Statement) @nogc;
|
abstract void visit(BinaryExpression) @nogc;
|
||||||
abstract void visit(Variable) @nogc;
|
abstract void visit(Variable) @nogc;
|
||||||
abstract void visit(VariableDeclaration) @nogc;
|
abstract void visit(VariableDeclaration) @nogc;
|
||||||
abstract void visit(Number) @nogc;
|
abstract void visit(Number) @nogc;
|
||||||
abstract void visit(BinaryExpression) @nogc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AST node.
|
* AST node.
|
||||||
*/
|
*/
|
||||||
|
extern(C++)
|
||||||
abstract class Node
|
abstract class Node
|
||||||
{
|
{
|
||||||
abstract void accept(IRVisitor) @nogc;
|
abstract void accept(IRVisitor) @nogc;
|
||||||
@ -51,11 +52,13 @@ abstract class Node
|
|||||||
/**
|
/**
|
||||||
* Definition.
|
* Definition.
|
||||||
*/
|
*/
|
||||||
|
extern(C++)
|
||||||
class Definition : Node
|
class Definition : Node
|
||||||
{
|
{
|
||||||
char[] identifier;
|
char[] identifier;
|
||||||
Array!Statement statements;
|
Array!BinaryExpression statements;
|
||||||
Array!VariableDeclaration variableDeclarations;
|
Array!VariableDeclaration variableDeclarations;
|
||||||
|
Operand result;
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc
|
||||||
{
|
{
|
||||||
@ -63,17 +66,13 @@ class Definition : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Statement : Node
|
extern(C++)
|
||||||
|
abstract class Statement : Node
|
||||||
{
|
{
|
||||||
BinaryExpression expression;
|
|
||||||
|
|
||||||
override void accept(IRVisitor visitor) @nogc
|
|
||||||
{
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Expression : Node
|
extern(C++)
|
||||||
|
abstract class Operand : Node
|
||||||
{
|
{
|
||||||
override void accept(IRVisitor visitor) @nogc
|
override void accept(IRVisitor visitor) @nogc
|
||||||
{
|
{
|
||||||
@ -81,7 +80,8 @@ abstract class Expression : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Number : Expression
|
extern(C++)
|
||||||
|
class Number : Operand
|
||||||
{
|
{
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
@ -91,7 +91,8 @@ class Number : Expression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Variable : Expression
|
extern(C++)
|
||||||
|
class Variable : Operand
|
||||||
{
|
{
|
||||||
size_t counter;
|
size_t counter;
|
||||||
|
|
||||||
@ -101,6 +102,7 @@ class Variable : Expression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++)
|
||||||
class VariableDeclaration : Node
|
class VariableDeclaration : Node
|
||||||
{
|
{
|
||||||
String identifier;
|
String identifier;
|
||||||
@ -111,12 +113,13 @@ class VariableDeclaration : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BinaryExpression : Node
|
extern(C++)
|
||||||
|
class BinaryExpression : Statement
|
||||||
{
|
{
|
||||||
Expression lhs, rhs;
|
Operand lhs, rhs;
|
||||||
BinaryOperator operator;
|
BinaryOperator operator;
|
||||||
|
|
||||||
this(Expression lhs, Expression rhs, BinaryOperator operator)
|
this(Operand lhs, Operand rhs, BinaryOperator operator)
|
||||||
@nogc
|
@nogc
|
||||||
{
|
{
|
||||||
this.lhs = lhs;
|
this.lhs = lhs;
|
||||||
@ -130,9 +133,26 @@ class BinaryExpression : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++)
|
||||||
|
class BangExpression : Statement
|
||||||
|
{
|
||||||
|
Operand operand;
|
||||||
|
|
||||||
|
this(Operand operand)
|
||||||
|
{
|
||||||
|
this.operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void accept(IRVisitor visitor) @nogc
|
||||||
|
{
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
||||||
{
|
{
|
||||||
private HashTable!(String, int) constants;
|
private HashTable!(String, int) constants;
|
||||||
|
private Array!BinaryExpression statements;
|
||||||
|
|
||||||
ASTMapping.Node visit(parser.Node node) @nogc
|
ASTMapping.Node visit(parser.Node node) @nogc
|
||||||
{
|
{
|
||||||
@ -151,7 +171,7 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
|
|
||||||
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
||||||
{
|
{
|
||||||
assert(false, "Not implemented");
|
return statement.expression.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTMapping.Block visit(parser.Block block) @nogc
|
ASTMapping.Block visit(parser.Block block) @nogc
|
||||||
@ -159,7 +179,9 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
auto target = defaultAllocator.make!Definition;
|
auto target = defaultAllocator.make!Definition;
|
||||||
this.constants = transformConstants(block.definitions);
|
this.constants = transformConstants(block.definitions);
|
||||||
|
|
||||||
target.statements = block.statement.accept(this);
|
target.result = block.statement.accept(this);
|
||||||
|
target.statements = this.statements;
|
||||||
|
|
||||||
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
|
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
@ -167,41 +189,45 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
|
|
||||||
ASTMapping.Expression visit(parser.Expression expression) @nogc
|
ASTMapping.Expression visit(parser.Expression expression) @nogc
|
||||||
{
|
{
|
||||||
assert(false, "Not implemented");
|
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
|
ASTMapping.Number visit(parser.Number number) @nogc
|
||||||
{
|
{
|
||||||
assert(false, "Not implemented");
|
auto numberExpression = defaultAllocator.make!Number;
|
||||||
|
numberExpression.value = number.value;
|
||||||
|
|
||||||
|
return numberExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTMapping.Variable visit(parser.Variable variable) @nogc
|
ASTMapping.Variable visit(parser.Variable variable) @nogc
|
||||||
{
|
{
|
||||||
assert(false, "Not implemented");
|
auto numberExpression = defaultAllocator.make!Number;
|
||||||
|
numberExpression.value = this.constants[variable.identifier];
|
||||||
|
|
||||||
|
return numberExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTMapping.BinaryExpression visit(parser.BinaryExpression) @nogc
|
ASTMapping.BinaryExpression visit(parser.BinaryExpression binaryExpression) @nogc
|
||||||
{
|
|
||||||
assert(false, "Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Number transformNumber(parser.Number number) @nogc
|
|
||||||
{
|
|
||||||
return defaultAllocator.make!Number(number.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Variable binaryExpression(parser.BinaryExpression binaryExpression,
|
|
||||||
ref Array!Statement statements) @nogc
|
|
||||||
{
|
{
|
||||||
auto target = defaultAllocator.make!BinaryExpression(
|
auto target = defaultAllocator.make!BinaryExpression(
|
||||||
expression(binaryExpression.lhs, statements),
|
binaryExpression.lhs.accept(this),
|
||||||
expression(binaryExpression.rhs, statements),
|
binaryExpression.rhs.accept(this),
|
||||||
binaryExpression.operator
|
binaryExpression.operator
|
||||||
);
|
);
|
||||||
|
statements.insertBack(target);
|
||||||
auto newStatement = defaultAllocator.make!Statement;
|
|
||||||
newStatement.expression = target;
|
|
||||||
statements.insertBack(newStatement);
|
|
||||||
|
|
||||||
auto newVariable = defaultAllocator.make!Variable;
|
auto newVariable = defaultAllocator.make!Variable;
|
||||||
newVariable.counter = statements.length;
|
newVariable.counter = statements.length;
|
||||||
@ -209,38 +235,18 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
|||||||
return newVariable;
|
return newVariable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression expression(parser.Expression expression,
|
private Number transformNumber(parser.Number number) @nogc
|
||||||
ref Array!Statement statements) @nogc
|
|
||||||
{
|
{
|
||||||
if ((cast(parser.Number) expression) !is null)
|
return defaultAllocator.make!Number(number.value);
|
||||||
{
|
|
||||||
auto numberExpression = defaultAllocator.make!Number;
|
|
||||||
numberExpression.value = (cast(parser.Number) expression).value;
|
|
||||||
|
|
||||||
return numberExpression;
|
|
||||||
}
|
|
||||||
if ((cast(parser.Variable) expression) !is null)
|
|
||||||
{
|
|
||||||
auto numberExpression = defaultAllocator.make!Number;
|
|
||||||
numberExpression.value = this.constants[(cast(parser.Variable) expression).identifier];
|
|
||||||
|
|
||||||
return numberExpression;
|
|
||||||
}
|
|
||||||
else if ((cast(parser.BinaryExpression) expression) !is null)
|
|
||||||
{
|
|
||||||
return binaryExpression(cast(parser.BinaryExpression) expression, statements);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override Array!Statement visit(parser.Statement statement) @nogc
|
override Operand visit(parser.Statement statement) @nogc
|
||||||
{
|
{
|
||||||
typeof(return) statements;
|
|
||||||
if ((cast(parser.BangStatement) statement) !is null)
|
if ((cast(parser.BangStatement) statement) !is null)
|
||||||
{
|
{
|
||||||
expression((cast(parser.BangStatement) statement).expression, statements);
|
return (cast(parser.BangStatement) statement).accept(this);
|
||||||
}
|
}
|
||||||
return statements;
|
assert(false, "Invalid statement type");
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
|
private HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
|
||||||
|
@ -8,6 +8,7 @@ import std.range;
|
|||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
|
|
||||||
|
extern(C++)
|
||||||
struct Token
|
struct Token
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type
|
||||||
@ -71,7 +72,7 @@ struct Token
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property auto value(Type type)() @nogc nothrow pure @trusted
|
@property auto value(Type type)() @nogc nothrow pure @trusted
|
||||||
in (ofType(type))
|
in (ofType(type), "Expected type: " ~ type.stringof)
|
||||||
{
|
{
|
||||||
static if (type == Type.number)
|
static if (type == Type.number)
|
||||||
{
|
{
|
||||||
@ -99,14 +100,17 @@ struct Token
|
|||||||
/**
|
/**
|
||||||
* Range over the source text that keeps track of the current position.
|
* Range over the source text that keeps track of the current position.
|
||||||
*/
|
*/
|
||||||
|
extern(C++)
|
||||||
struct Source
|
struct Source
|
||||||
{
|
{
|
||||||
char[] buffer;
|
char* buffer_;
|
||||||
|
size_t length_;
|
||||||
Position position;
|
Position position;
|
||||||
|
|
||||||
this(char[] buffer) @nogc nothrow pure @safe
|
this(char* buffer, const size_t length) @nogc nothrow pure
|
||||||
{
|
{
|
||||||
this.buffer = buffer;
|
this.buffer_ = buffer;
|
||||||
|
this.length_ = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@disable this();
|
@disable this();
|
||||||
@ -116,54 +120,50 @@ struct Source
|
|||||||
return this.length == 0;
|
return this.length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char front() @nogc nothrow pure @safe
|
char front() @nogc nothrow pure
|
||||||
in (!empty)
|
in (!empty)
|
||||||
{
|
{
|
||||||
return this.buffer[0];
|
return this.buffer_[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void popFront() @nogc nothrow pure @safe
|
void popFront() @nogc nothrow pure
|
||||||
in (!empty)
|
in (!empty)
|
||||||
{
|
{
|
||||||
this.buffer = buffer[1 .. $];
|
++this.buffer_;
|
||||||
|
--this.length_;
|
||||||
++this.position.column;
|
++this.position.column;
|
||||||
}
|
}
|
||||||
|
|
||||||
void breakLine() @nogc nothrow pure @safe
|
void breakLine() @nogc nothrow pure
|
||||||
in (!empty)
|
in (!empty)
|
||||||
{
|
{
|
||||||
this.buffer = buffer[1 .. $];
|
++this.buffer_;
|
||||||
|
--this.length_;
|
||||||
++this.position.line;
|
++this.position.line;
|
||||||
this.position.column = 1;
|
this.position.column = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property size_t length() const @nogc nothrow pure @safe
|
@property size_t length() const @nogc nothrow pure @safe
|
||||||
{
|
{
|
||||||
return this.buffer.length;
|
return this.length_;
|
||||||
}
|
}
|
||||||
|
|
||||||
char opIndex(size_t index) @nogc nothrow pure @safe
|
char opIndex(size_t index) @nogc nothrow pure
|
||||||
in (index < length)
|
in (index < length)
|
||||||
{
|
{
|
||||||
return this.buffer[index];
|
return this.buffer_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
|
char* buffer() @nogc nothrow pure
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(i <= j);
|
return this.buffer_;
|
||||||
assert(j <= length);
|
|
||||||
}
|
|
||||||
do
|
|
||||||
{
|
|
||||||
return this.buffer[i .. j];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result!(Array!Token) lex(char[] buffer) @nogc
|
Result!(Array!Token) lex(char[] buffer) @nogc
|
||||||
{
|
{
|
||||||
Array!Token tokens;
|
Array!Token tokens;
|
||||||
auto source = Source(buffer);
|
auto source = Source(buffer.ptr, buffer.length);
|
||||||
|
|
||||||
while (!source.empty)
|
while (!source.empty)
|
||||||
{
|
{
|
||||||
@ -218,17 +218,17 @@ Result!(Array!Token) lex(char[] buffer) @nogc
|
|||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
if (source[0 .. i] == "const")
|
if (source.buffer[0 .. i] == "const")
|
||||||
{
|
{
|
||||||
tokens.insertBack(Token(Token.Type.let, source.position));
|
tokens.insertBack(Token(Token.Type.let, source.position));
|
||||||
}
|
}
|
||||||
else if (source[0 .. i] == "var")
|
else if (source.buffer[0 .. i] == "var")
|
||||||
{
|
{
|
||||||
tokens.insertBack(Token(Token.Type.var, source.position));
|
tokens.insertBack(Token(Token.Type.var, source.position));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto identifier = String(source[0 .. i]);
|
auto identifier = String(source.buffer[0 .. i]);
|
||||||
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
|
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
|
||||||
}
|
}
|
||||||
source.popFrontN(i);
|
source.popFrontN(i);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module elna.riscv;
|
module elna.riscv;
|
||||||
|
|
||||||
|
import core.stdc.stdlib;
|
||||||
import elna.extended;
|
import elna.extended;
|
||||||
import elna.ir;
|
import elna.ir;
|
||||||
import elna.result;
|
import elna.result;
|
||||||
@ -7,7 +8,6 @@ import std.algorithm;
|
|||||||
import std.typecons;
|
import std.typecons;
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.string;
|
import tanya.container.string;
|
||||||
import tanya.memory.allocator;
|
|
||||||
|
|
||||||
enum XRegister : ubyte
|
enum XRegister : ubyte
|
||||||
{
|
{
|
||||||
@ -140,14 +140,14 @@ struct Instruction
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2, uint imm2 = 0)
|
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
|
||||||
return scope @nogc
|
return scope @nogc
|
||||||
{
|
{
|
||||||
this.instruction |= (imm1 << 7)
|
this.instruction |= ((imm1 & 0b11111) << 7)
|
||||||
| (funct3 << 12)
|
| (funct3 << 12)
|
||||||
| (rs1 << 15)
|
| (rs1 << 15)
|
||||||
| (rs2 << 20)
|
| (rs2 << 20)
|
||||||
| (imm2 << 25);
|
| ((imm1 & 0b111111100000) << 20);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -178,6 +178,7 @@ struct Instruction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C++)
|
||||||
class RiscVVisitor : IRVisitor
|
class RiscVVisitor : IRVisitor
|
||||||
{
|
{
|
||||||
Array!Instruction instructions;
|
Array!Instruction instructions;
|
||||||
@ -191,22 +192,24 @@ class RiscVVisitor : IRVisitor
|
|||||||
|
|
||||||
override void visit(Definition definition) @nogc
|
override void visit(Definition definition) @nogc
|
||||||
{
|
{
|
||||||
|
const uint stackSize = cast(uint) (definition.statements.length * 4 + 12);
|
||||||
|
|
||||||
// Prologue.
|
// Prologue.
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.opImm)
|
Instruction(BaseOpcode.opImm)
|
||||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32)
|
.i(XRegister.sp, Funct3.addi, XRegister.sp, -stackSize)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.store)
|
Instruction(BaseOpcode.store)
|
||||||
.s(28, Funct3.sw, XRegister.sp, XRegister.s0)
|
.s(stackSize - 4, Funct3.sw, XRegister.sp, XRegister.s0)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.store)
|
Instruction(BaseOpcode.store)
|
||||||
.s(24, Funct3.sw, XRegister.sp, XRegister.ra)
|
.s(stackSize - 8, Funct3.sw, XRegister.sp, XRegister.ra)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.opImm)
|
Instruction(BaseOpcode.opImm)
|
||||||
.i(XRegister.s0, Funct3.addi, XRegister.sp, 32)
|
.i(XRegister.s0, Funct3.addi, XRegister.sp, stackSize)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach (statement; definition.statements[])
|
foreach (statement; definition.statements[])
|
||||||
@ -217,6 +220,10 @@ class RiscVVisitor : IRVisitor
|
|||||||
{
|
{
|
||||||
variableDeclaration.accept(this);
|
variableDeclaration.accept(this);
|
||||||
}
|
}
|
||||||
|
this.registerInUse = true;
|
||||||
|
definition.result.accept(this);
|
||||||
|
this.registerInUse = false;
|
||||||
|
|
||||||
// Print the result.
|
// Print the result.
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.opImm)
|
Instruction(BaseOpcode.opImm)
|
||||||
@ -247,15 +254,15 @@ class RiscVVisitor : IRVisitor
|
|||||||
// Epilogue.
|
// Epilogue.
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.load)
|
Instruction(BaseOpcode.load)
|
||||||
.i(XRegister.s0, Funct3.lw, XRegister.sp, 28)
|
.i(XRegister.s0, Funct3.lw, XRegister.sp, stackSize - 4)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.load)
|
Instruction(BaseOpcode.load)
|
||||||
.i(XRegister.ra, Funct3.lw, XRegister.sp, 24)
|
.i(XRegister.ra, Funct3.lw, XRegister.sp, stackSize - 8)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.opImm)
|
Instruction(BaseOpcode.opImm)
|
||||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, 32)
|
.i(XRegister.sp, Funct3.addi, XRegister.sp, stackSize)
|
||||||
);
|
);
|
||||||
this.instructions.insertBack(
|
this.instructions.insertBack(
|
||||||
Instruction(BaseOpcode.jalr)
|
Instruction(BaseOpcode.jalr)
|
||||||
@ -263,13 +270,16 @@ class RiscVVisitor : IRVisitor
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(Expression) @nogc
|
override void visit(Operand operand) @nogc
|
||||||
{
|
{
|
||||||
}
|
if ((cast(Variable) operand) !is null)
|
||||||
|
{
|
||||||
override void visit(Statement statement) @nogc
|
return (cast(Variable) operand).accept(this);
|
||||||
{
|
}
|
||||||
statement.expression.accept(this);
|
if ((cast(Number) operand) !is null)
|
||||||
|
{
|
||||||
|
return (cast(Number) operand).accept(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(Variable variable) @nogc
|
override void visit(Variable variable) @nogc
|
||||||
@ -334,10 +344,12 @@ Symbol writeNext(Definition ast) @nogc
|
|||||||
{
|
{
|
||||||
Array!Instruction instructions;
|
Array!Instruction instructions;
|
||||||
Array!Reference references;
|
Array!Reference references;
|
||||||
auto visitor = defaultAllocator.make!RiscVVisitor;
|
auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
|
||||||
|
(cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
|
||||||
scope (exit)
|
scope (exit)
|
||||||
{
|
{
|
||||||
defaultAllocator.dispose(visitor);
|
visitor.__xdtor();
|
||||||
|
free(cast(void*) visitor);
|
||||||
}
|
}
|
||||||
visitor.visit(ast);
|
visitor.visit(ast);
|
||||||
|
|
||||||
|
1
tests/7_member_sum.eln
Normal file
1
tests/7_member_sum.eln
Normal file
@ -0,0 +1 @@
|
|||||||
|
! 3 + 4 + 5 + 1 + 2 + 4 + 3
|
1
tests/expectations/7_member_sum.txt
Normal file
1
tests/expectations/7_member_sum.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
22
|
1
tests/expectations/print_number.txt
Normal file
1
tests/expectations/print_number.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
3
|
2
tests/print_number.eln
Normal file
2
tests/print_number.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
! 3
|
||||||
|
.
|
103
tests/runner.cpp
Executable file
103
tests/runner.cpp
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#include <boost/process.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
class test_results final
|
||||||
|
{
|
||||||
|
std::uint32_t m_total{ 0 };
|
||||||
|
std::uint32_t m_passed{ 0 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
test_results() = default;
|
||||||
|
|
||||||
|
std::uint32_t total() const noexcept
|
||||||
|
{
|
||||||
|
return m_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t passed() const noexcept
|
||||||
|
{
|
||||||
|
return m_passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t failed() const noexcept
|
||||||
|
{
|
||||||
|
return m_total - m_passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exit_code() const noexcept
|
||||||
|
{
|
||||||
|
return m_total == m_passed ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_exit_code(const int exit_code) noexcept
|
||||||
|
{
|
||||||
|
++m_total;
|
||||||
|
if (exit_code == 0)
|
||||||
|
{
|
||||||
|
++m_passed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int run_test(const std::filesystem::directory_entry& test_entry)
|
||||||
|
{
|
||||||
|
const std::filesystem::path test_filename = test_entry.path().filename();
|
||||||
|
|
||||||
|
std::filesystem::path test_binary = "build/riscv" / test_filename;
|
||||||
|
test_binary.replace_extension();
|
||||||
|
|
||||||
|
std::filesystem::path expectation_path = test_entry.path().parent_path() / "expectations" / test_filename;
|
||||||
|
expectation_path.replace_extension(".txt");
|
||||||
|
|
||||||
|
std::cout << "Running " << test_binary << std::endl;
|
||||||
|
|
||||||
|
boost::process::pipe pipe_stream;
|
||||||
|
boost::process::child vm(
|
||||||
|
"/opt/riscv/bin/spike", "--isa=RV32IMAC",
|
||||||
|
"/opt/riscv/riscv32-unknown-elf/bin/pk",
|
||||||
|
test_binary.string(),
|
||||||
|
boost::process::std_out > pipe_stream
|
||||||
|
);
|
||||||
|
boost::process::child diff(
|
||||||
|
"/usr/bin/diff", "-Nur", "--color",
|
||||||
|
expectation_path.string(), "-",
|
||||||
|
boost::process::std_in < pipe_stream
|
||||||
|
);
|
||||||
|
vm.wait();
|
||||||
|
diff.wait();
|
||||||
|
|
||||||
|
return diff.exit_code();
|
||||||
|
}
|
||||||
|
|
||||||
|
test_results run_in_path(const std::filesystem::path test_directory)
|
||||||
|
{
|
||||||
|
test_results results;
|
||||||
|
|
||||||
|
for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
|
||||||
|
{
|
||||||
|
if (test_entry.path().extension() != ".eln" || !test_entry.is_regular_file())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
results.add_exit_code(run_test(test_entry));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
boost::process::system("rake");
|
||||||
|
|
||||||
|
std::cout << "Run all tests and check the results" << std::endl;
|
||||||
|
std::filesystem::path test_directory{ "tests" };
|
||||||
|
const auto results = run_in_path(test_directory);
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << results.total() << " tests run, "
|
||||||
|
<< results.passed() << " passed, "
|
||||||
|
<< results.failed() << " failed." << std::endl;
|
||||||
|
|
||||||
|
return results.exit_code();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user