Grow stack automatically
This commit is contained in:
		
							
								
								
									
										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();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user