From 1dae344e0941e751a8063d54dd62e4e2b6dbd9e6 Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 25 Jan 2025 22:02:02 +0100 Subject: [PATCH] added some tests --- asm/amd64/amd64_test.go | 142 +++++++++++++++++++++++--------------- asm/amd64/binary_test.txt | 20 ++++++ asm/amd64/codegen.go | 4 +- cmd/cmd.go | 6 +- lexer/lexer_test.go | 4 +- parser/parser_test.go | 48 +++++++++++++ test | Bin 180 -> 0 bytes test.tt | 2 +- ttir/ttir_test.go | 37 ++++++++++ 9 files changed, 199 insertions(+), 64 deletions(-) create mode 100644 asm/amd64/binary_test.txt delete mode 100755 test diff --git a/asm/amd64/amd64_test.go b/asm/amd64/amd64_test.go index 6dc9771..55ff2d3 100644 --- a/asm/amd64/amd64_test.go +++ b/asm/amd64/amd64_test.go @@ -5,66 +5,10 @@ import ( "strings" "testing" + "robaertschi.xyz/robaertschi/tt/ast" "robaertschi.xyz/robaertschi/tt/ttir" ) -func TestOperands(t *testing.T) { - var op Operand - - op = AX - if str := op.OperandString(One); str != "al" { - t.Errorf("The register AX should be \"al\" but got %q", str) - } - - op = Imm(3) - if str := op.OperandString(One); str != "3" { - t.Errorf("The immediate value 3 should be \"3\" but got %q", str) - } -} - -//go:embed basic_test.txt -var basicTest string - -func TestCodegen(t *testing.T) { - program := &ttir.Program{ - Functions: []ttir.Function{ - { - Name: "main", - Instructions: []ttir.Instruction{ - &ttir.Ret{Op: &ttir.Constant{Value: 0}}, - }, - }, - }, - } - - expectedProgram := Program{ - Functions: []Function{ - { - Name: "main", - Instructions: []Instruction{ - &SimpleInstruction{ - Opcode: Mov, - Lhs: AX, - Rhs: Imm(0), - }, - &SimpleInstruction{ - Opcode: Ret, - }, - }, - }, - }, - } - - actualProgram := CgProgram(program) - expectProgram(t, expectedProgram, actualProgram) - - actual := actualProgram.Emit() - expected := basicTest - if strings.Trim(actual, " \n\t") != strings.Trim(expected, " \n\t") { - t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", expected, actual) - } -} - func expectProgram(t *testing.T, expected Program, actual Program) { t.Helper() if len(expected.Functions) != len(actual.Functions) { @@ -179,3 +123,87 @@ func expectOperand(t *testing.T, expected Operand, actual Operand) { t.Errorf("Unknown operand type %T", expected) } } + +func TestOperands(t *testing.T) { + var op Operand + + op = AX + if str := op.OperandString(One); str != "al" { + t.Errorf("The register AX should be \"al\" but got %q", str) + } + + op = Imm(3) + if str := op.OperandString(One); str != "3" { + t.Errorf("The immediate value 3 should be \"3\" but got %q", str) + } +} + +//go:embed basic_test.txt +var basicTest string + +func TestCodegen(t *testing.T) { + program := &ttir.Program{ + Functions: []ttir.Function{ + { + Name: "main", + Instructions: []ttir.Instruction{ + &ttir.Ret{Op: &ttir.Constant{Value: 0}}, + }, + }, + }, + } + + expectedProgram := Program{ + Functions: []Function{ + { + Name: "main", + Instructions: []Instruction{ + &SimpleInstruction{ + Opcode: Mov, + Lhs: AX, + Rhs: Imm(0), + }, + &SimpleInstruction{ + Opcode: Ret, + }, + }, + }, + }, + } + + actualProgram := CgProgram(program) + expectProgram(t, expectedProgram, *actualProgram) + + actual := actualProgram.Emit() + expected := basicTest + if strings.Trim(actual, " \n\t") != strings.Trim(expected, " \n\t") { + t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", expected, actual) + } +} + +//go:embed binary_test.txt +var binaryTest string + +func TestBinary(t *testing.T) { + program := &ttir.Program{ + Functions: []ttir.Function{ + { + Name: "main", + Instructions: []ttir.Instruction{ + &ttir.Binary{ + Lhs: &ttir.Constant{Value: 3}, + Rhs: &ttir.Constant{Value: 3}, + Operator: ast.Add, + Dst: &ttir.Var{Value: "temp.1"}, + }, + &ttir.Ret{Op: &ttir.Var{Value: "temp.1"}}, + }, + }, + }, + } + + actual := CgProgram(program).Emit() + if strings.Trim(actual, " \n\t") != strings.Trim(binaryTest, " \n\t") { + t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", binaryTest, actual) + } +} diff --git a/asm/amd64/binary_test.txt b/asm/amd64/binary_test.txt new file mode 100644 index 0000000..0639b40 --- /dev/null +++ b/asm/amd64/binary_test.txt @@ -0,0 +1,20 @@ +format ELF64 executable +segment readable executable +entry _start +_start: + call main + mov rdi, rax + mov rax, 60 + syscall +main: + push rbp + mov rbp, rsp + add rsp, -4 + mov qword [rsp -4], 3 + add qword [rsp -4], 3 + mov rax, qword [rsp -4] + mov rsp, rbp + pop rbp + ret + + diff --git a/asm/amd64/codegen.go b/asm/amd64/codegen.go index fa8e6a9..11cfd63 100644 --- a/asm/amd64/codegen.go +++ b/asm/amd64/codegen.go @@ -18,7 +18,7 @@ func toAsmOperand(op ttir.Operand) Operand { } } -func CgProgram(prog *ttir.Program) Program { +func CgProgram(prog *ttir.Program) *Program { funcs := make([]Function, 0) for _, f := range prog.Functions { @@ -32,7 +32,7 @@ func CgProgram(prog *ttir.Program) Program { newProgram = replacePseudo(newProgram) newProgram = instructionFixup(newProgram) - return newProgram + return &newProgram } func cgFunction(f ttir.Function) Function { diff --git a/cmd/cmd.go b/cmd/cmd.go index 3605e6f..ff1fe8d 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -123,7 +123,7 @@ func Compile(args Arguments) { os.Exit(1) } if (args.ToPrint & PrintAst) != 0 { - fmt.Printf("AST:\n%s\n", program.String()) + fmt.Printf("AST:\n%s\n%+#v\n", program.String(), program) } tprogram, err := typechecker.New().CheckProgram(program) @@ -132,12 +132,12 @@ func Compile(args Arguments) { os.Exit(1) } if (args.ToPrint & PrintTAst) != 0 { - fmt.Printf("TAST:\n%s\n", tprogram.String()) + fmt.Printf("TAST:\n%s\n%+#v\n", tprogram.String(), tprogram) } ir := ttir.EmitProgram(tprogram) if (args.ToPrint & PrintIr) != 0 { - fmt.Printf("TTIR:\n%s\n", ir.String()) + fmt.Printf("TTIR:\n%s\n%+#v\n", ir.String(), ir) } asm := amd64.CgProgram(ir) diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index fa444ed..cbd3ce2 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -40,7 +40,7 @@ func runLexerTest(t *testing.T, test lexerTest) { func TestBasicFunctionality(t *testing.T) { runLexerTest(t, lexerTest{ - input: "fn main() = 0 + 3;", + input: "fn main() = 0 + 3 == 3;", expectedToken: []token.Token{ {Type: token.Fn, Literal: "fn"}, {Type: token.Ident, Literal: "main"}, @@ -50,6 +50,8 @@ func TestBasicFunctionality(t *testing.T) { {Type: token.Int, Literal: "0"}, {Type: token.Plus, Literal: "+"}, {Type: token.Int, Literal: "3"}, + {Type: token.DoubleEqual, Literal: "=="}, + {Type: token.Int, Literal: "3"}, {Type: token.Semicolon, Literal: ";"}, {Type: token.Eof, Literal: ""}, }, diff --git a/parser/parser_test.go b/parser/parser_test.go index 4f670a6..a9d3360 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -87,6 +87,30 @@ func expectExpression(t *testing.T, expected ast.Expression, actual ast.Expressi if integerExpr.Value != expected.Value { t.Errorf("expected integer value %d, got %d", expected.Value, integerExpr.Value) } + case *ast.BinaryExpression: + binaryExpr, ok := actual.(*ast.BinaryExpression) + if !ok { + t.Errorf("expected %T, got %T", expected, actual) + return + } + + if binaryExpr.Operator != expected.Operator { + t.Errorf("expected %q operator for binary expression, got %q", expected.Operator.SymbolString(), binaryExpr.Operator.SymbolString()) + } + expectExpression(t, expected.Lhs, binaryExpr.Lhs) + expectExpression(t, expected.Rhs, binaryExpr.Rhs) + case *ast.BooleanExpression: + booleanExpr, ok := actual.(*ast.BooleanExpression) + if !ok { + t.Errorf("expected %T, got %T", expected, actual) + return + } + + if booleanExpr.Value != expected.Value { + t.Errorf("expected boolean %v, got %v", expected.Value, booleanExpr.Value) + } + default: + t.Fatalf("unknown expression type %T", expected) } } @@ -104,3 +128,27 @@ func TestFunctionDeclaration(t *testing.T) { } runParserTest(test, t) } + +func TestBinaryExpressions(t *testing.T) { + test := parserTest{ + input: "fn main() = true == true == true;", + expectedProgram: ast.Program{ + Declarations: []ast.Declaration{ + &ast.FunctionDeclaration{ + Name: "main", + Body: &ast.BinaryExpression{ + Lhs: &ast.BinaryExpression{ + Lhs: &ast.BooleanExpression{Value: true}, + Rhs: &ast.BooleanExpression{Value: true}, + Operator: ast.Equal, + }, + Rhs: &ast.BooleanExpression{Value: true}, + Operator: ast.Equal, + }, + }, + }, + }, + } + + runParserTest(test, t) +} diff --git a/test b/test deleted file mode 100755 index a637ba9b39771cdca669810185b0bbec65316134..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmb<-^>JfjWMqH=CI&kO5U+y40W1U|!Av;ez+eGX1Car#WrfRt7!FV#gx&(>!Dt2n zh`|ud+(qRNNICyhAj6{@M0CE1 GJq!RaOdGcV diff --git a/test.tt b/test.tt index 2ff1b9d..b854b64 100644 --- a/test.tt +++ b/test.tt @@ -1 +1 @@ -fn main() = true != true; +fn main() = 3 + 3; diff --git a/ttir/ttir_test.go b/ttir/ttir_test.go index 69d4ce5..3704a29 100644 --- a/ttir/ttir_test.go +++ b/ttir/ttir_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "robaertschi.xyz/robaertschi/tt/ast" "robaertschi.xyz/robaertschi/tt/lexer" "robaertschi.xyz/robaertschi/tt/parser" "robaertschi.xyz/robaertschi/tt/token" @@ -86,6 +87,17 @@ func expectInstruction(t *testing.T, inst Instruction, actual Instruction) { } expectOperand(t, inst.Op, ret.Op) + case *Binary: + binary, ok := actual.(*Binary) + + if !ok { + t.Errorf("expected inst to be %T, but got %T", inst, binary) + return + } + + expectOperand(t, inst.Lhs, binary.Lhs) + expectOperand(t, inst.Rhs, binary.Rhs) + expectOperand(t, inst.Dst, binary.Dst) } } @@ -104,6 +116,16 @@ func expectOperand(t *testing.T, expected Operand, actual Operand) { if expected.Value != constant.Value { t.Errorf("expected *Constant.Value to be %d, but got %d", expected.Value, constant.Value) } + case *Var: + v, ok := actual.(*Var) + + if !ok { + t.Errorf("expected operand to be %T, but got %T", expected, actual) + return + } + if expected.Value != v.Value { + t.Errorf("expected var to be %q, but got %q", expected.Value, v.Value) + } } } @@ -124,3 +146,18 @@ func TestBasicFunction(t *testing.T) { }, }) } + +func TestBinaryExpression(t *testing.T) { + runTTIREmitterTest(t, ttirEmitterTest{ + input: "fn main() = 3 + 3 + 3;", + expected: Program{ + Functions: []Function{ + {Name: "main", Instructions: []Instruction{ + &Binary{Operator: ast.Add, Lhs: &Constant{Value: 3}, Rhs: &Constant{Value: 3}, Dst: &Var{Value: "temp.1"}}, + &Binary{Operator: ast.Add, Lhs: &Var{Value: "temp.1"}, Rhs: &Constant{Value: 3}, Dst: &Var{Value: "temp.2"}}, + &Ret{Op: &Var{Value: "temp.2"}}, + }}, + }, + }, + }) +}