diff --git a/src/core/Function.go b/src/core/Function.go index 2270649..07555f2 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -36,7 +36,7 @@ func NewFunction(name string, file *fs.File) *Function { Name: name, File: file, UniqueName: fmt.Sprintf("%s.%s", file.Package, name), - Identifiers: make(map[string]ssa.Value), + Identifiers: make(map[string]ssa.Value, 8), IR: ssa.IR{ Blocks: []*ssa.Block{ {Instructions: make([]ssa.Value, 0, 8)}, diff --git a/src/ssa/BinaryOperation.go b/src/ssa/BinaryOp.go similarity index 63% rename from src/ssa/BinaryOperation.go rename to src/ssa/BinaryOp.go index bdeae32..401d8ce 100644 --- a/src/ssa/BinaryOperation.go +++ b/src/ssa/BinaryOp.go @@ -8,7 +8,7 @@ import ( "git.urbach.dev/cli/q/src/types" ) -type BinaryOperation struct { +type BinaryOp struct { Left Value Right Value Op token.Kind @@ -16,12 +16,12 @@ type BinaryOperation struct { Source } -func (v *BinaryOperation) Dependencies() []Value { +func (v *BinaryOp) Dependencies() []Value { return []Value{v.Left, v.Right} } -func (a *BinaryOperation) Equals(v Value) bool { - b, sameType := v.(*BinaryOperation) +func (a *BinaryOp) Equals(v Value) bool { + b, sameType := v.(*BinaryOp) if !sameType { return false @@ -38,14 +38,14 @@ func (a *BinaryOperation) Equals(v Value) bool { return true } -func (v *BinaryOperation) IsConst() bool { +func (v *BinaryOp) IsConst() bool { return true } -func (v *BinaryOperation) String() string { +func (v *BinaryOp) String() string { return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right) } -func (v *BinaryOperation) Type() types.Type { +func (v *BinaryOp) Type() types.Type { return v.Left.Type() } \ No newline at end of file diff --git a/src/ssa/BinaryOp_test.go b/src/ssa/BinaryOp_test.go new file mode 100644 index 0000000..5994058 --- /dev/null +++ b/src/ssa/BinaryOp_test.go @@ -0,0 +1,45 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/token" + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestBinaryOp(t *testing.T) { + fn := ssa.IR{} + a := fn.AppendInt(1) + b := fn.AppendInt(2) + c := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: a, Right: b}) + fn.AddBlock() + d := fn.AppendInt(3) + e := fn.AppendInt(4) + f := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: d, Right: e}) + + assert.Equal(t, c.String(), "1 + 2") + assert.Equal(t, f.String(), "3 + 4") + assert.True(t, c.Type() == types.AnyInt) +} + +func TestBinaryOpEquals(t *testing.T) { + fn := ssa.IR{} + one := fn.AppendInt(1) + two := fn.AppendInt(2) + binOp := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: one, Right: two}) + oneDup := fn.AppendInt(1) + twoDup := fn.AppendInt(2) + binOpDup := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: twoDup}) + binOpDiff := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: oneDup, Right: oneDup}) + + assert.False(t, one.Equals(two)) + assert.False(t, one.Equals(binOp)) + assert.True(t, one.Equals(oneDup)) + assert.False(t, two.Equals(one)) + assert.False(t, two.Equals(binOp)) + assert.True(t, two.Equals(twoDup)) + assert.False(t, binOp.Equals(binOpDiff)) + assert.True(t, binOp.Equals(binOpDup)) +} \ No newline at end of file diff --git a/src/ssa/Bytes_test.go b/src/ssa/Bytes_test.go new file mode 100644 index 0000000..9fae68d --- /dev/null +++ b/src/ssa/Bytes_test.go @@ -0,0 +1,23 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestBytes(t *testing.T) { + fn := ssa.IR{} + hello := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")}) + world := fn.Append(&ssa.Bytes{Bytes: []byte("World")}) + helloDup := fn.Append(&ssa.Bytes{Bytes: []byte("Hello")}) + one := fn.AppendInt(1) + + assert.False(t, hello.Equals(world)) + assert.False(t, hello.Equals(one)) + assert.True(t, hello.Equals(helloDup)) + assert.Equal(t, hello.String(), "\"Hello\"") + assert.True(t, types.Is(hello.Type(), types.String)) +} \ No newline at end of file diff --git a/src/ssa/Call.go b/src/ssa/Call.go index 717891d..f84631f 100644 --- a/src/ssa/Call.go +++ b/src/ssa/Call.go @@ -2,6 +2,7 @@ package ssa import ( "fmt" + "strings" "git.urbach.dev/cli/q/src/types" ) @@ -27,7 +28,13 @@ func (v *Call) IsConst() bool { } func (v *Call) String() string { - return fmt.Sprintf("%s(%v)", v.Arguments[0], v.Arguments[1:]) + args := make([]string, 0, len(v.Arguments)-1) + + for _, arg := range v.Arguments[1:] { + args = append(args, arg.String()) + } + + return fmt.Sprintf("%s(%s)", v.Arguments[0].String(), strings.Join(args, ", ")) } func (v *Call) Type() types.Type { diff --git a/src/ssa/Call_test.go b/src/ssa/Call_test.go new file mode 100644 index 0000000..756e540 --- /dev/null +++ b/src/ssa/Call_test.go @@ -0,0 +1,60 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestCall(t *testing.T) { + fn := ssa.IR{} + myfunc := fn.Append(&ssa.Function{UniqueName: "myfunc", Typ: &types.Function{}}) + call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc}}) + one := fn.AppendInt(1) + call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc, one}}) + + assert.True(t, call.Type() == types.Void) + assert.Equal(t, call.String(), "myfunc()") + assert.Equal(t, call2.String(), "myfunc(1)") +} + +func TestCallEquals(t *testing.T) { + fn := ssa.IR{} + + sum := fn.Append(&ssa.Function{ + UniqueName: "sum", + Typ: &types.Function{ + Input: []types.Type{types.Int, types.Int}, + Output: []types.Type{types.Int}, + }, + }) + + one := fn.AppendInt(1) + two := fn.AppendInt(2) + call1 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) + call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) + + assert.False(t, call1.Equals(one)) + assert.True(t, call1.Equals(call2)) +} + +func TestCallReturnType(t *testing.T) { + fn := ssa.IR{} + + sum := fn.Append(&ssa.Function{ + UniqueName: "sum", + Typ: &types.Function{ + Input: []types.Type{types.Int, types.Int}, + Output: []types.Type{types.Int}, + }, + }) + + one := fn.AppendInt(1) + two := fn.AppendInt(2) + call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) + + assert.Equal(t, call.String(), "sum(1, 2)") + assert.True(t, call.Type() == types.Int) +} \ No newline at end of file diff --git a/src/ssa/Return.go b/src/ssa/Return.go index 72a6010..e2e9a7c 100644 --- a/src/ssa/Return.go +++ b/src/ssa/Return.go @@ -2,6 +2,7 @@ package ssa import ( "fmt" + "strings" "git.urbach.dev/cli/q/src/types" ) @@ -39,7 +40,17 @@ func (v *Return) IsConst() bool { } func (v *Return) String() string { - return fmt.Sprintf("return %v", v.Arguments) + if len(v.Arguments) == 0 { + return "return" + } + + args := make([]string, 0, len(v.Arguments)) + + for _, arg := range v.Arguments { + args = append(args, arg.String()) + } + + return fmt.Sprintf("return %s", strings.Join(args, ", ")) } func (v *Return) Type() types.Type { diff --git a/src/ssa/Return_test.go b/src/ssa/Return_test.go new file mode 100644 index 0000000..a789f83 --- /dev/null +++ b/src/ssa/Return_test.go @@ -0,0 +1,30 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestReturn(t *testing.T) { + fn := ssa.IR{} + ret := fn.Append(&ssa.Return{}) + one := fn.AppendInt(1) + ret2 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one}}) + two := fn.AppendInt(2) + ret3 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}}) + ret4 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{two, one}}) + ret5 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one, two}}) + + assert.True(t, ret.Type() == types.Void) + assert.Equal(t, ret.String(), "return") + assert.Equal(t, ret2.String(), "return 1") + assert.Equal(t, ret3.String(), "return 1, 2") + assert.Equal(t, ret4.String(), "return 2, 1") + assert.False(t, ret5.Equals(one)) + assert.False(t, ret5.Equals(ret)) + assert.False(t, ret5.Equals(ret4)) + assert.True(t, ret5.Equals(ret3)) +} \ No newline at end of file diff --git a/src/ssa/Syscall.go b/src/ssa/Syscall.go index 7847c78..c13dfbc 100644 --- a/src/ssa/Syscall.go +++ b/src/ssa/Syscall.go @@ -2,6 +2,7 @@ package ssa import ( "fmt" + "strings" "git.urbach.dev/cli/q/src/types" ) @@ -27,7 +28,13 @@ func (v *Syscall) IsConst() bool { } func (v *Syscall) String() string { - return fmt.Sprintf("syscall(%v)", v.Arguments) + args := make([]string, 0, len(v.Arguments)) + + for _, arg := range v.Arguments { + args = append(args, arg.String()) + } + + return fmt.Sprintf("syscall(%s)", strings.Join(args, ", ")) } func (v *Syscall) Type() types.Type { diff --git a/src/ssa/Syscall_test.go b/src/ssa/Syscall_test.go new file mode 100644 index 0000000..63dab89 --- /dev/null +++ b/src/ssa/Syscall_test.go @@ -0,0 +1,27 @@ +package ssa_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestSyscall(t *testing.T) { + fn := ssa.IR{} + syscall := fn.Append(&ssa.Syscall{}) + one := fn.AppendInt(1) + syscall2 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one}}) + two := fn.AppendInt(2) + syscall3 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}}) + syscall4 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}}) + + assert.True(t, syscall.Type() == types.Any) + assert.Equal(t, syscall.String(), "syscall()") + assert.Equal(t, syscall2.String(), "syscall(1)") + assert.Equal(t, syscall3.String(), "syscall(1, 2)") + assert.False(t, syscall4.Equals(one)) + assert.False(t, syscall4.Equals(syscall)) + assert.True(t, syscall4.Equals(syscall3)) +} \ No newline at end of file diff --git a/src/ssa/ssa_test.go b/src/ssa/ssa_test.go deleted file mode 100644 index 09780e3..0000000 --- a/src/ssa/ssa_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package ssa_test - -import ( - "testing" - - "git.urbach.dev/cli/q/src/ssa" - "git.urbach.dev/cli/q/src/token" - "git.urbach.dev/go/assert" -) - -func TestFunction(t *testing.T) { - fn := ssa.IR{} - a := fn.AppendInt(1) - b := fn.AppendInt(2) - c := fn.Append(&ssa.BinaryOperation{Op: token.Add, Left: a, Right: b}) - fn.AddBlock() - d := fn.AppendInt(3) - e := fn.AppendInt(4) - f := fn.Append(&ssa.BinaryOperation{Op: token.Add, Left: d, Right: e}) - assert.Equal(t, c.String(), "1 + 2") - assert.Equal(t, f.String(), "3 + 4") -} \ No newline at end of file