diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index fa0ffce..a6e78c9 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -14,7 +14,7 @@ import ( // All call registers must hold the correct parameter values before the function invocation. // Registers that are in use must be saved if they are modified by the function. // After the function call, they must be restored in reverse order. -func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { +func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) { var ( pkg = f.Package pkgNode *expression.Expression @@ -29,25 +29,12 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { switch name { case "len": - return &Function{ - Output: []*Output{ - { - Type: types.Int, - }, - }, - }, f.CompileLen(root) + return _len.OutputTypes, f.CompileLen(root) case "syscall": return nil, f.CompileSyscall(root) case "new": typ, err := f.CompileNew(root) - - return &Function{ - Output: []*Output{ - { - Type: typ, - }, - }, - }, err + return []types.Type{typ}, err case "delete": return nil, f.CompileDelete(root) case "store": @@ -112,29 +99,16 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { } if !types.Is(typ, fn.Input[i].Type) { - if parameters[i].Token.Kind == token.Number && parameters[i].Token.Text(f.File.Bytes) == "0" { + _, expectsPointer := fn.Input[i].Type.(*types.Pointer) + + if expectsPointer && parameters[i].Token.Kind == token.Number && parameters[i].Token.Text(f.File.Bytes) == "0" { continue } - encountered := "" - expected := "" - - if typ != nil { - encountered = typ.Name() - } - - if fn.Input[i].Type != nil { - expected = fn.Input[i].Type.Name() - } - - return nil, errors.New(&errors.TypeMismatch{ - Encountered: encountered, - Expected: expected, - ParameterName: fn.Input[i].Name, - }, f.File, parameters[i].Token.Position) + return nil, errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: fn.Input[i].Type.Name(), ParameterName: fn.Input[i].Name}, f.File, parameters[i].Token.Position) } } f.CallSafe(fn, registers) - return fn, nil + return fn.OutputTypes, nil } diff --git a/src/core/CompileDefinition.go b/src/core/CompileDefinition.go index b50b3dd..0887e0f 100644 --- a/src/core/CompileDefinition.go +++ b/src/core/CompileDefinition.go @@ -40,7 +40,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error { } count := 0 - called, err := f.CompileCall(right) + types, err := f.CompileCall(right) if err != nil { return err @@ -53,8 +53,8 @@ func (f *Function) CompileDefinition(node *ast.Define) error { return err } - if called != nil { - variable.Type = called.Output[count].Type + if count < len(types) { + variable.Type = types[count] } f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count]) diff --git a/src/core/CompileLen.go b/src/core/CompileLen.go index cb7cb44..e7264f3 100644 --- a/src/core/CompileLen.go +++ b/src/core/CompileLen.go @@ -4,17 +4,25 @@ import ( "math" "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/expression" + "git.akyoto.dev/cli/q/src/types" ) +var _len = Function{OutputTypes: []types.Type{types.Int}} + // CompileLen returns the length of a slice. func (f *Function) CompileLen(root *expression.Expression) error { - _, register, isTemporary, err := f.Evaluate(root.Children[1]) + typ, register, isTemporary, err := f.Evaluate(root.Children[1]) if err != nil { return err } + if !types.Is(typ, types.AnyArray) { + return errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: types.AnyArray.Name(), ParameterName: "array"}, f.File, root.Children[1].Token.Position) + } + f.SaveRegister(f.CPU.Output[0]) f.MemoryRegister(asm.LOAD, asm.Memory{Base: register, Offset: -8, OffsetRegister: math.MaxUint8, Length: 8}, f.CPU.Output[0]) diff --git a/src/core/CompileReturn.go b/src/core/CompileReturn.go index f535936..19d7963 100644 --- a/src/core/CompileReturn.go +++ b/src/core/CompileReturn.go @@ -30,12 +30,7 @@ func (f *Function) CompileReturn(node *ast.Return) error { return nil } - return errors.New(&errors.TypeMismatch{ - Encountered: typ.Name(), - Expected: f.Output[i].Type.Name(), - ParameterName: "", - IsReturn: true, - }, f.File, node.Values[i].Token.Position) + return errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: f.Output[i].Type.Name(), ParameterName: "", IsReturn: true}, f.File, node.Values[i].Token.Position) } } diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index 4157a1d..4dfb2d7 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -24,17 +24,21 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp } if ast.IsFunctionCall(node) { - fn, err := f.CompileCall(node) + types, err := f.CompileCall(node) + + if err != nil { + return nil, err + } if register != f.CPU.Output[0] { f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0]) } - if fn == nil || len(fn.Output) == 0 { - return nil, err + if len(types) == 0 { + return nil, nil } - return fn.Output[0].Type, err + return types[0], err } if node.Token.Kind == token.Array { diff --git a/src/core/Function.go b/src/core/Function.go index a502ccb..d0a6098 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -11,19 +11,20 @@ import ( // Function represents the smallest unit of code. type Function struct { register.Machine - Package string - Name string - UniqueName string - File *fs.File - Body token.List - Input []*Input - Output []*Output - Functions map[string]*Function - Structs map[string]*types.Struct - DLLs dll.List - Err error - deferred []func() - count counter + Package string + Name string + UniqueName string + File *fs.File + Body token.List + Input []*Input + Output []*Output + OutputTypes []types.Type + Functions map[string]*Function + Structs map[string]*types.Struct + DLLs dll.List + Err error + deferred []func() + count counter } // counter stores how often a certain statement appeared so we can generate a unique label from it. diff --git a/src/core/ResolveTypes.go b/src/core/ResolveTypes.go index 5636fcd..1a395cc 100644 --- a/src/core/ResolveTypes.go +++ b/src/core/ResolveTypes.go @@ -40,6 +40,8 @@ func (f *Function) ResolveTypes() error { if param.Type == nil { return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position) } + + f.OutputTypes = append(f.OutputTypes, param.Type) } return nil diff --git a/tests/errors/TypeMismatch2.q b/tests/errors/TypeMismatch2.q new file mode 100644 index 0000000..bc1b199 --- /dev/null +++ b/tests/errors/TypeMismatch2.q @@ -0,0 +1,3 @@ +main() { + len(123) +} \ No newline at end of file diff --git a/tests/errors_test.go b/tests/errors_test.go index 92f12a1..db440a2 100644 --- a/tests/errors_test.go +++ b/tests/errors_test.go @@ -47,6 +47,7 @@ var errs = []struct { {"MissingType.q", errors.MissingType}, {"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}}, {"TypeMismatch.q", &errors.TypeMismatch{Expected: "*Any", Encountered: "Int64", ParameterName: "p"}}, + {"TypeMismatch2.q", &errors.TypeMismatch{Expected: "[]Any", Encountered: "Int64", ParameterName: "array"}}, {"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}}, {"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},