From b785147ded496ec16cdb2894b79947dae83d7dd7 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 15 Jun 2022 19:00:54 +0200 Subject: [PATCH] Implement subtraction --- source/elna/backend.d | 7 ++-- source/elna/ir.d | 51 ++++++++++++++++++------------ source/elna/lexer.d | 18 ++++++----- source/elna/parser.d | 50 +++++++++++++++++++++-------- source/elna/riscv.d | 35 ++++++++++++++------ tests/expectations/subtraction.txt | 1 + tests/subtraction.eln | 2 ++ 7 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 tests/expectations/subtraction.txt create mode 100644 tests/subtraction.eln diff --git a/source/elna/backend.d b/source/elna/backend.d index dc1fd0e..86afaed 100644 --- a/source/elna/backend.d +++ b/source/elna/backend.d @@ -37,12 +37,13 @@ int generate(string inFile, ref String outputFilename) @nogc return 3; } auto tokens = lex(sourceText.get.get); - if (tokens.length == 0) + if (!tokens.valid) { - printf("Lexical analysis failed.\n"); + auto compileError = tokens.error.get; + printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr); return 1; } - auto ast = parse(tokens); + auto ast = parse(tokens.result); if (!ast.valid) { auto compileError = ast.error.get; diff --git a/source/elna/ir.d b/source/elna/ir.d index 4fe8858..db6bae9 100644 --- a/source/elna/ir.d +++ b/source/elna/ir.d @@ -5,7 +5,7 @@ import tanya.container.array; import tanya.container.hashtable; import tanya.container.string; import tanya.memory.allocator; -import tanya.memory.mmappool; +public import elna.parser : BinaryOperator; /** * IR visitor. @@ -19,7 +19,7 @@ abstract class IRVisitor abstract void visit(Variable) @nogc; abstract void visit(VariableDeclaration) @nogc; abstract void visit(Number) @nogc; - abstract void visit(Subroutine) @nogc; + abstract void visit(BinaryExpression) @nogc; } /** @@ -47,7 +47,7 @@ class Definition : Node class Statement : Node { - Subroutine subroutine; + BinaryExpression expression; override void accept(IRVisitor visitor) @nogc { @@ -93,9 +93,18 @@ class VariableDeclaration : Node } } -class Subroutine : Node +class BinaryExpression : Node { Expression lhs, rhs; + BinaryOperator operator; + + this(Expression lhs, Expression rhs, BinaryOperator operator) + @nogc + { + this.lhs = lhs; + this.rhs = rhs; + this.operator = operator; + } override void accept(IRVisitor visitor) @nogc { @@ -105,48 +114,50 @@ class Subroutine : Node private Number transformNumber(parser.Number number) @nogc { - return MmapPool.instance.make!Number(number.value); + return defaultAllocator.make!Number(number.value); } -private Variable transformSubroutine(parser.Subroutine subroutine, +private Variable binaryExpression(parser.BinaryExpression binaryExpression, ref Array!Statement statements, ref HashTable!(String, int) constants) @nogc { - auto target = MmapPool.instance.make!Subroutine; - target.lhs = transformExpression(subroutine.lhs, statements, constants); - target.rhs = transformExpression(subroutine.rhs, statements, constants); + auto target = defaultAllocator.make!BinaryExpression( + expression(binaryExpression.lhs, statements, constants), + expression(binaryExpression.rhs, statements, constants), + binaryExpression.operator + ); - auto newStatement = MmapPool.instance.make!Statement; - newStatement.subroutine = target; + auto newStatement = defaultAllocator.make!Statement; + newStatement.expression = target; statements.insertBack(newStatement); - auto newVariable = MmapPool.instance.make!Variable; + auto newVariable = defaultAllocator.make!Variable; newVariable.counter = statements.length; return newVariable; } -private Expression transformExpression(parser.Expression expression, +private Expression expression(parser.Expression expression, ref Array!Statement statements, ref HashTable!(String, int) constants) @nogc { if ((cast(parser.Number) expression) !is null) { - auto numberExpression = MmapPool.instance.make!Number; + auto numberExpression = defaultAllocator.make!Number; numberExpression.value = (cast(parser.Number) expression).value; return numberExpression; } if ((cast(parser.Variable) expression) !is null) { - auto numberExpression = MmapPool.instance.make!Number; + auto numberExpression = defaultAllocator.make!Number; numberExpression.value = constants[(cast(parser.Variable) expression).identifier]; return numberExpression; } - else if ((cast(parser.Subroutine) expression) !is null) + else if ((cast(parser.BinaryExpression) expression) !is null) { - return transformSubroutine(cast(parser.Subroutine) expression, statements, constants); + return binaryExpression(cast(parser.BinaryExpression) expression, statements, constants); } return null; } @@ -157,7 +168,7 @@ Expression transformStatement(parser.Statement statement, { if ((cast(parser.BangStatement) statement) !is null) { - return transformExpression((cast(parser.BangStatement) statement).expression, statements, constants); + return expression((cast(parser.BangStatement) statement).expression, statements, constants); } return null; } @@ -181,7 +192,7 @@ Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.Variab foreach (ref variableDeclaration; variableDeclarations) { - auto newDeclaration = MmapPool.instance.make!VariableDeclaration; + auto newDeclaration = defaultAllocator.make!VariableDeclaration; newDeclaration.identifier = variableDeclaration.identifier; variables.insertBack(newDeclaration); } @@ -191,7 +202,7 @@ Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.Variab Definition transform(parser.Block block) @nogc { - auto target = MmapPool.instance.make!Definition; + auto target = defaultAllocator.make!Definition; auto constants = transformConstants(block.definitions); transformStatement(block.statement, target.statements, constants); diff --git a/source/elna/lexer.d b/source/elna/lexer.d index 69ae141..83fb873 100644 --- a/source/elna/lexer.d +++ b/source/elna/lexer.d @@ -7,7 +7,6 @@ import elna.result; import std.range; import tanya.container.array; import tanya.container.string; -import tanya.memory.mmappool; struct Token { @@ -54,7 +53,7 @@ struct Token this()(Type type, auto ref String value, Position position) @nogc nothrow pure @trusted - in (type == Type.identifier) + in (type == Type.identifier || type == Type.operator) { this(type, position); this.value_.identifier = value; @@ -78,7 +77,7 @@ struct Token { return this.value_.number; } - else static if (type == Type.identifier) + else static if (type == Type.identifier || type == Type.operator) { return this.value_.identifier; } @@ -161,7 +160,7 @@ struct Source } } -Array!Token lex(char[] buffer) @nogc +Result!(Array!Token) lex(char[] buffer) @nogc { Array!Token tokens; auto source = Source(buffer); @@ -234,9 +233,12 @@ Array!Token lex(char[] buffer) @nogc } source.popFrontN(i); } - else if (source.front == '+') // Multi-character, random special characters. + else if (source.front == '+' || source.front == '-') { - tokens.insertBack(Token(Token.Type.operator, source.position)); + String operator; + + operator.insertBack(source.front); + tokens.insertBack(Token(Token.Type.operator, operator, source.position)); source.popFront; } else if (source.front == '\n') @@ -245,8 +247,8 @@ Array!Token lex(char[] buffer) @nogc } else { - return typeof(tokens)(); // Error. + return typeof(return)("Unexptected next character", source.position); } } - return tokens; + return typeof(return)(tokens); } diff --git a/source/elna/parser.d b/source/elna/parser.d index f1f1299..3c14bda 100644 --- a/source/elna/parser.d +++ b/source/elna/parser.d @@ -5,7 +5,6 @@ import elna.result; import tanya.container.array; import tanya.container.string; import tanya.memory.allocator; -import tanya.memory.mmappool; /** * Constant definition. @@ -54,9 +53,34 @@ class Variable : Expression String identifier; } -class Subroutine : Expression +enum BinaryOperator +{ + sum, + subtraction +} + +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"); + } + } } private Result!Expression parseFactor(ref Array!Token.Range tokens) @nogc @@ -64,14 +88,14 @@ in (!tokens.empty, "Expected factor, got end of stream") { if (tokens.front.ofType(Token.Type.identifier)) { - auto variable = MmapPool.instance.make!Variable; + auto variable = defaultAllocator.make!Variable; variable.identifier = tokens.front.value!(Token.Type.identifier); tokens.popFront; return Result!Expression(variable); } else if (tokens.front.ofType(Token.Type.number)) { - auto number = MmapPool.instance.make!Number; + auto number = defaultAllocator.make!Number; number.value = tokens.front.value!(Token.Type.number); tokens.popFront; return Result!Expression(number); @@ -101,17 +125,17 @@ in (!tokens.empty, "Expected expression, got end of stream") { return term; } + auto operator = tokens.front.value!(Token.Type.operator); tokens.popFront; - auto operator = MmapPool.instance.make!Subroutine; auto expression = parseExpression(tokens); if (expression.valid) { - operator.lhs = term.result; - operator.rhs = expression.result; + auto binaryExpression = defaultAllocator + .make!BinaryExpression(term.result, expression.result, operator); - return Result!Expression(operator); + return Result!Expression(binaryExpression); } else { @@ -122,7 +146,7 @@ in (!tokens.empty, "Expected expression, got end of stream") private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc in (!tokens.empty, "Expected definition, got end of stream") { - auto definition = MmapPool.instance.make!Definition; + auto definition = defaultAllocator.make!Definition; definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy. tokens.popFront(); @@ -130,7 +154,7 @@ in (!tokens.empty, "Expected definition, got end of stream") if (tokens.front.ofType(Token.Type.number)) { - auto number = MmapPool.instance.make!Number; + auto number = defaultAllocator.make!Number; number.value = tokens.front.value!(Token.Type.number); definition.number = number; tokens.popFront; @@ -145,7 +169,7 @@ in (!tokens.empty, "Expected block, got end of stream") if (tokens.front.ofType(Token.Type.bang)) { tokens.popFront; - auto statement = MmapPool.instance.make!BangStatement; + auto statement = defaultAllocator.make!BangStatement; auto expression = parseExpression(tokens); if (expression.valid) { @@ -200,7 +224,7 @@ in (!tokens.empty, "Expected variable declarations, got end of stream") auto currentToken = tokens.front; if (currentToken.ofType(Token.Type.identifier)) { - auto variableDeclaration = MmapPool.instance.make!VariableDeclaration; + auto variableDeclaration = defaultAllocator.make!VariableDeclaration; variableDeclaration.identifier = currentToken.value!(Token.Type.identifier); variableDeclarations.insertBack(variableDeclaration); tokens.popFront; @@ -229,7 +253,7 @@ in (!tokens.empty, "Expected variable declarations, got end of stream") private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc in (!tokens.empty, "Expected block, got end of stream") { - auto block = MmapPool.instance.make!Block; + auto block = defaultAllocator.make!Block; if (tokens.front.ofType(Token.Type.let)) { auto constDefinitions = parseDefinitions(tokens); diff --git a/source/elna/riscv.d b/source/elna/riscv.d index 0319b0c..8fcfb2d 100644 --- a/source/elna/riscv.d +++ b/source/elna/riscv.d @@ -98,6 +98,12 @@ enum Funct12 : ubyte ebreak = 0b000000000001, } +enum Funct7 : ubyte +{ + none = 0, + sub = 0b0100000 +} + enum BaseOpcode : ubyte { opImm = 0b0010011, @@ -146,7 +152,7 @@ struct Instruction return this; } - ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, ubyte funct7 = 0) + ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none) return scope @nogc { this.instruction |= (rd << 7) @@ -263,7 +269,7 @@ class RiscVVisitor : IRVisitor override void visit(Statement statement) @nogc { - statement.subroutine.accept(this); + statement.expression.accept(this); } override void visit(Variable variable) @nogc @@ -292,18 +298,29 @@ class RiscVVisitor : IRVisitor ); } - override void visit(Subroutine subroutine) @nogc + override void visit(BinaryExpression expression) @nogc { this.registerInUse = true; - subroutine.lhs.accept(this); + expression.lhs.accept(this); this.registerInUse = false; - subroutine.rhs.accept(this); + expression.rhs.accept(this); // Calculate the result and assign it to a variable on the stack. - this.instructions.insertBack( - Instruction(BaseOpcode.op) - .r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0) - ); + final switch (expression.operator) + { + case BinaryOperator.sum: + this.instructions.insertBack( + Instruction(BaseOpcode.op) + .r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0) + ); + break; + case BinaryOperator.subtraction: + this.instructions.insertBack( + Instruction(BaseOpcode.op) + .r(XRegister.a0, Funct3.sub, XRegister.a0, XRegister.t0, Funct7.sub) + ); + break; + } this.instructions.insertBack( // movl %eax, -x(%rbp); where x is a number. Instruction(BaseOpcode.store) .s(cast(uint) (this.variableCounter * 4), Funct3.sw, XRegister.sp, XRegister.a0) diff --git a/tests/expectations/subtraction.txt b/tests/expectations/subtraction.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/expectations/subtraction.txt @@ -0,0 +1 @@ +1 diff --git a/tests/subtraction.eln b/tests/subtraction.eln new file mode 100644 index 0000000..00917c6 --- /dev/null +++ b/tests/subtraction.eln @@ -0,0 +1,2 @@ +! 5 - 4 +.