Improved type system checks

This commit is contained in:
Eduard Urbach 2025-02-10 14:09:27 +01:00
parent b2a9dc3aa7
commit 371059d08a
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
9 changed files with 49 additions and 61 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@ type Function struct {
Body token.List
Input []*Input
Output []*Output
OutputTypes []types.Type
Functions map[string]*Function
Structs map[string]*types.Struct
DLLs dll.List

View File

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

View File

@ -0,0 +1,3 @@
main() {
len(123)
}

View File

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