added some tests

This commit is contained in:
Robin 2025-01-25 22:02:02 +01:00
parent cefa2698c6
commit 1dae344e09
9 changed files with 199 additions and 64 deletions

View File

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

20
asm/amd64/binary_test.txt Normal file
View File

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

View File

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

View File

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

View File

@ -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: ""},
},

View File

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

BIN
test

Binary file not shown.

View File

@ -1 +1 @@
fn main() = true != true;
fn main() = 3 + 3;

View File

@ -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"}},
}},
},
},
})
}