diff --git a/src/core/Evaluate.go b/src/core/Evaluate.go index 216e480..81ffac0 100644 --- a/src/core/Evaluate.go +++ b/src/core/Evaluate.go @@ -27,8 +27,14 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { } f.Dependencies.Add(function) - v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern()) - v.SetSource(expr.Source) + + v := f.Append(&ssa.Function{ + UniqueName: function.UniqueName, + Typ: function.Type, + IsExtern: function.IsExtern(), + Source: ssa.Source(expr.Source), + }) + return v, nil } @@ -41,26 +47,33 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { return nil, err } - v := f.AppendInt(number) - v.SetSource(expr.Source) + v := f.Append(&ssa.Int{ + Int: number, + Source: ssa.Source(expr.Source), + }) + return v, nil case token.String: data := expr.Token.Bytes(f.File.Bytes) data = Unescape(data) - length := f.AppendInt(len(data)) - length.SetSource(expr.Source) + length := f.Append(&ssa.Int{ + Int: len(data), + Source: ssa.Source(expr.Source), + }) - pointer := f.AppendBytes(data) - pointer.SetSource(expr.Source) + pointer := f.Append(&ssa.Bytes{ + Bytes: data, + Source: ssa.Source(expr.Source), + }) v := f.Append(&ssa.Struct{ Arguments: []ssa.Value{pointer, length}, Typ: types.String, + Source: ssa.Source(expr.Source), }) - v.SetSource(expr.Source) return v, nil } @@ -75,17 +88,6 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { if children[0].Token.Kind == token.Identifier { funcName := children[0].String(f.File.Bytes) - if funcName == "len" { - identifier := children[1].String(f.File.Bytes) - length, exists := f.Identifiers[identifier+".len"] - - if !exists { - return nil, errors.New(&UnknownIdentifier{Name: identifier + ".len"}, f.File, expr.Token.Position) - } - - return length, nil - } - if funcName == "syscall" { children = children[1:] isSyscall = true @@ -111,47 +113,57 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { } return f.Append(syscall), nil - } else { - name := args[0].(*ssa.Function).UniqueName - fn := f.All.Functions[name] - parameters := args[1:] + } - if len(parameters) != len(fn.Input) { - return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position) - } + name := args[0].(*ssa.Function).UniqueName + fn := f.All.Functions[name] + parameters := args[1:] - for i, param := range slices.Backward(parameters) { - if !types.Is(param.Type(), fn.Input[i].Typ) { - _, isPointer := fn.Input[i].Typ.(*types.Pointer) + if len(parameters) != len(fn.Input) { + return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position) + } - if isPointer { - number, isInt := param.(*ssa.Int) + for i, param := range slices.Backward(parameters) { + if !types.Is(param.Type(), fn.Input[i].Typ) { + _, isPointer := fn.Input[i].Typ.(*types.Pointer) - if isInt && number.Int == 0 { - continue - } - } + if isPointer { + number, isInt := param.(*ssa.Int) - // Temporary hack to allow int64 -> uint32 conversion - if types.Is(param.Type(), types.AnyInt) && types.Is(fn.Input[i].Typ, types.AnyInt) { + if isInt && number.Int == 0 { continue } - - return nil, errors.New(&TypeMismatch{ - Encountered: param.Type().Name(), - Expected: fn.Input[i].Typ.Name(), - ParameterName: fn.Input[i].Name, - }, f.File, param.Start()) } - } - if fn.IsExtern() { - return f.Append(&ssa.CallExtern{Arguments: args, Source: ssa.Source(expr.Source)}), nil - } + // Temporary hack to allow int64 -> uint32 conversion + if types.Is(param.Type(), types.AnyInt) && types.Is(fn.Input[i].Typ, types.AnyInt) { + continue + } - return f.Append(&ssa.Call{Arguments: args, Source: ssa.Source(expr.Source)}), nil + return nil, errors.New(&TypeMismatch{ + Encountered: param.Type().Name(), + Expected: fn.Input[i].Typ.Name(), + ParameterName: fn.Input[i].Name, + }, f.File, param.Start()) + } } + if fn.IsExtern() { + v := f.Append(&ssa.CallExtern{ + Arguments: args, + Source: ssa.Source(expr.Source), + }) + + return v, nil + } + + v := f.Append(&ssa.Call{ + Arguments: args, + Source: ssa.Source(expr.Source), + }) + + return v, nil + case token.Dot: left := expr.Children[0] right := expr.Children[1] @@ -167,8 +179,12 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { return nil, errors.New(&UnknownStructField{StructName: structure.Name(), FieldName: rightText}, f.File, right.Token.Position) } - v := f.Append(&ssa.StructField{Struct: identifier, Field: field}) - v.SetSource(expr.Source) + v := f.Append(&ssa.StructField{ + Struct: identifier, + Field: field, + Source: ssa.Source(expr.Source), + }) + return v, nil } @@ -182,13 +198,19 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) { f.Dependencies.Add(function) } - v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern()) - v.SetSource(expr.Source) + v := f.Append(&ssa.Function{ + UniqueName: function.UniqueName, + Typ: function.Type, + IsExtern: function.IsExtern(), + Source: ssa.Source(expr.Source), + }) + return v, nil } return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position) - } - return nil, nil + default: + panic("not implemented") + } } \ No newline at end of file diff --git a/src/ssa/BinaryOp_test.go b/src/ssa/BinaryOp_test.go index 5994058..2eab37e 100644 --- a/src/ssa/BinaryOp_test.go +++ b/src/ssa/BinaryOp_test.go @@ -11,12 +11,12 @@ import ( func TestBinaryOp(t *testing.T) { fn := ssa.IR{} - a := fn.AppendInt(1) - b := fn.AppendInt(2) + a := fn.Append(&ssa.Int{Int: 1}) + b := fn.Append(&ssa.Int{Int: 2}) c := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: a, Right: b}) fn.AddBlock() - d := fn.AppendInt(3) - e := fn.AppendInt(4) + d := fn.Append(&ssa.Int{Int: 3}) + e := fn.Append(&ssa.Int{Int: 4}) f := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: d, Right: e}) assert.Equal(t, c.String(), "1 + 2") @@ -26,11 +26,11 @@ func TestBinaryOp(t *testing.T) { func TestBinaryOpEquals(t *testing.T) { fn := ssa.IR{} - one := fn.AppendInt(1) - two := fn.AppendInt(2) + one := fn.Append(&ssa.Int{Int: 1}) + two := fn.Append(&ssa.Int{Int: 2}) binOp := fn.Append(&ssa.BinaryOp{Op: token.Add, Left: one, Right: two}) - oneDup := fn.AppendInt(1) - twoDup := fn.AppendInt(2) + oneDup := fn.Append(&ssa.Int{Int: 1}) + twoDup := fn.Append(&ssa.Int{Int: 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}) diff --git a/src/ssa/Bytes_test.go b/src/ssa/Bytes_test.go index eee39a1..941927a 100644 --- a/src/ssa/Bytes_test.go +++ b/src/ssa/Bytes_test.go @@ -13,7 +13,7 @@ func TestBytes(t *testing.T) { 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) + one := fn.Append(&ssa.Int{Int: 1}) assert.False(t, hello.Equals(world)) assert.False(t, hello.Equals(one)) diff --git a/src/ssa/Call_test.go b/src/ssa/Call_test.go index 756e540..e77cff6 100644 --- a/src/ssa/Call_test.go +++ b/src/ssa/Call_test.go @@ -12,7 +12,7 @@ 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) + one := fn.Append(&ssa.Int{Int: 1}) call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{myfunc, one}}) assert.True(t, call.Type() == types.Void) @@ -31,8 +31,8 @@ func TestCallEquals(t *testing.T) { }, }) - one := fn.AppendInt(1) - two := fn.AppendInt(2) + one := fn.Append(&ssa.Int{Int: 1}) + two := fn.Append(&ssa.Int{Int: 2}) call1 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) call2 := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) @@ -51,8 +51,8 @@ func TestCallReturnType(t *testing.T) { }, }) - one := fn.AppendInt(1) - two := fn.AppendInt(2) + one := fn.Append(&ssa.Int{Int: 1}) + two := fn.Append(&ssa.Int{Int: 2}) call := fn.Append(&ssa.Call{Arguments: ssa.Arguments{sum, one, two}}) assert.Equal(t, call.String(), "sum(1, 2)") diff --git a/src/ssa/IR.go b/src/ssa/IR.go index c2022d2..fea735d 100644 --- a/src/ssa/IR.go +++ b/src/ssa/IR.go @@ -1,9 +1,5 @@ package ssa -import ( - "git.urbach.dev/cli/q/src/types" -) - // IR is a list of basic blocks. type IR struct { Blocks []*Block @@ -26,12 +22,10 @@ func (f *IR) Append(instr Value) Value { f.AddBlock() } - if instr.IsConst() { - for existing := range f.Values { - if existing.IsConst() && instr.Equals(existing) { - return existing - } - } + existing := f.FindExisting(instr) + + if existing != nil { + return existing } instr.SetID(f.nextId) @@ -39,24 +33,19 @@ func (f *IR) Append(instr Value) Value { return f.Blocks[len(f.Blocks)-1].Append(instr) } -// AppendInt adds a new integer value to the last block. -func (f *IR) AppendInt(x int) Value { - return f.Append(&Int{Int: x}) -} +// FindExisting returns an equal instruction that's already appended or `nil` if none could be found. +func (f *IR) FindExisting(instr Value) Value { + if !instr.IsConst() { + return nil + } -// AppendFunction adds a new function value to the last block. -func (f *IR) AppendFunction(name string, typ *types.Function, extern bool) Value { - return f.Append(&Function{UniqueName: name, Typ: typ, IsExtern: extern}) -} + for existing := range f.Values { + if existing.IsConst() && instr.Equals(existing) { + return existing + } + } -// AppendBytes adds a new byte slice value to the last block. -func (f *IR) AppendBytes(s []byte) Value { - return f.Append(&Bytes{Bytes: s}) -} - -// AppendString adds a new string value to the last block. -func (f *IR) AppendString(s string) Value { - return f.Append(&Bytes{Bytes: []byte(s)}) + return nil } // Values yields on each value. diff --git a/src/ssa/Int.go b/src/ssa/Int.go index e07603c..0488f51 100644 --- a/src/ssa/Int.go +++ b/src/ssa/Int.go @@ -1,7 +1,7 @@ package ssa import ( - "fmt" + "strconv" "git.urbach.dev/cli/q/src/types" ) @@ -36,7 +36,7 @@ func (v *Int) Debug(expand bool) string { } func (v *Int) String() string { - return fmt.Sprintf("%d", v.Int) + return strconv.Itoa(v.Int) } func (v *Int) Type() types.Type { diff --git a/src/ssa/Return_test.go b/src/ssa/Return_test.go index a789f83..3df47c8 100644 --- a/src/ssa/Return_test.go +++ b/src/ssa/Return_test.go @@ -11,9 +11,9 @@ import ( func TestReturn(t *testing.T) { fn := ssa.IR{} ret := fn.Append(&ssa.Return{}) - one := fn.AppendInt(1) + one := fn.Append(&ssa.Int{Int: 1}) ret2 := fn.Append(&ssa.Return{Arguments: ssa.Arguments{one}}) - two := fn.AppendInt(2) + two := fn.Append(&ssa.Int{Int: 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}}) diff --git a/src/ssa/Syscall_test.go b/src/ssa/Syscall_test.go index 63dab89..1f3bdb8 100644 --- a/src/ssa/Syscall_test.go +++ b/src/ssa/Syscall_test.go @@ -11,9 +11,9 @@ import ( func TestSyscall(t *testing.T) { fn := ssa.IR{} syscall := fn.Append(&ssa.Syscall{}) - one := fn.AppendInt(1) + one := fn.Append(&ssa.Int{Int: 1}) syscall2 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one}}) - two := fn.AppendInt(2) + two := fn.Append(&ssa.Int{Int: 2}) syscall3 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}}) syscall4 := fn.Append(&ssa.Syscall{Arguments: ssa.Arguments{one, two}})