mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
added some tests
This commit is contained in:
parent
cefa2698c6
commit
1dae344e09
@ -5,66 +5,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"robaertschi.xyz/robaertschi/tt/ast"
|
||||||
"robaertschi.xyz/robaertschi/tt/ttir"
|
"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) {
|
func expectProgram(t *testing.T, expected Program, actual Program) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(expected.Functions) != len(actual.Functions) {
|
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)
|
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
20
asm/amd64/binary_test.txt
Normal 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
|
||||||
|
|
||||||
|
|
@ -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)
|
funcs := make([]Function, 0)
|
||||||
|
|
||||||
for _, f := range prog.Functions {
|
for _, f := range prog.Functions {
|
||||||
@ -32,7 +32,7 @@ func CgProgram(prog *ttir.Program) Program {
|
|||||||
newProgram = replacePseudo(newProgram)
|
newProgram = replacePseudo(newProgram)
|
||||||
newProgram = instructionFixup(newProgram)
|
newProgram = instructionFixup(newProgram)
|
||||||
|
|
||||||
return newProgram
|
return &newProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
func cgFunction(f ttir.Function) Function {
|
func cgFunction(f ttir.Function) Function {
|
||||||
|
@ -123,7 +123,7 @@ func Compile(args Arguments) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if (args.ToPrint & PrintAst) != 0 {
|
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)
|
tprogram, err := typechecker.New().CheckProgram(program)
|
||||||
@ -132,12 +132,12 @@ func Compile(args Arguments) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if (args.ToPrint & PrintTAst) != 0 {
|
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)
|
ir := ttir.EmitProgram(tprogram)
|
||||||
if (args.ToPrint & PrintIr) != 0 {
|
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)
|
asm := amd64.CgProgram(ir)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func runLexerTest(t *testing.T, test lexerTest) {
|
|||||||
|
|
||||||
func TestBasicFunctionality(t *testing.T) {
|
func TestBasicFunctionality(t *testing.T) {
|
||||||
runLexerTest(t, lexerTest{
|
runLexerTest(t, lexerTest{
|
||||||
input: "fn main() = 0 + 3;",
|
input: "fn main() = 0 + 3 == 3;",
|
||||||
expectedToken: []token.Token{
|
expectedToken: []token.Token{
|
||||||
{Type: token.Fn, Literal: "fn"},
|
{Type: token.Fn, Literal: "fn"},
|
||||||
{Type: token.Ident, Literal: "main"},
|
{Type: token.Ident, Literal: "main"},
|
||||||
@ -50,6 +50,8 @@ func TestBasicFunctionality(t *testing.T) {
|
|||||||
{Type: token.Int, Literal: "0"},
|
{Type: token.Int, Literal: "0"},
|
||||||
{Type: token.Plus, Literal: "+"},
|
{Type: token.Plus, Literal: "+"},
|
||||||
{Type: token.Int, Literal: "3"},
|
{Type: token.Int, Literal: "3"},
|
||||||
|
{Type: token.DoubleEqual, Literal: "=="},
|
||||||
|
{Type: token.Int, Literal: "3"},
|
||||||
{Type: token.Semicolon, Literal: ";"},
|
{Type: token.Semicolon, Literal: ";"},
|
||||||
{Type: token.Eof, Literal: ""},
|
{Type: token.Eof, Literal: ""},
|
||||||
},
|
},
|
||||||
|
@ -87,6 +87,30 @@ func expectExpression(t *testing.T, expected ast.Expression, actual ast.Expressi
|
|||||||
if integerExpr.Value != expected.Value {
|
if integerExpr.Value != expected.Value {
|
||||||
t.Errorf("expected integer value %d, got %d", expected.Value, integerExpr.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)
|
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)
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"robaertschi.xyz/robaertschi/tt/ast"
|
||||||
"robaertschi.xyz/robaertschi/tt/lexer"
|
"robaertschi.xyz/robaertschi/tt/lexer"
|
||||||
"robaertschi.xyz/robaertschi/tt/parser"
|
"robaertschi.xyz/robaertschi/tt/parser"
|
||||||
"robaertschi.xyz/robaertschi/tt/token"
|
"robaertschi.xyz/robaertschi/tt/token"
|
||||||
@ -86,6 +87,17 @@ func expectInstruction(t *testing.T, inst Instruction, actual Instruction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expectOperand(t, inst.Op, ret.Op)
|
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 {
|
if expected.Value != constant.Value {
|
||||||
t.Errorf("expected *Constant.Value to be %d, but got %d", 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"}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user