From 69a5fdc70368e829c02b8874caa5cdfd2a68badf Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 8 Aug 2024 12:55:25 +0200 Subject: [PATCH] Improved type system --- lib/sys/linux.q | 2 +- src/core/CompileCall.go | 19 ++++++------------- src/core/CompileDefinition.go | 3 +-- src/core/CompileReturn.go | 6 +++--- src/core/Evaluate.go | 2 +- src/core/ExpressionToMemory.go | 6 +++--- src/core/ExpressionToRegister.go | 10 +++++----- src/core/Function.go | 2 +- src/core/TokenToRegister.go | 8 ++++---- src/scanner/scanFile.go | 4 ++-- src/scope/Variable.go | 2 +- src/types/Base.go | 16 ++++++++++++++++ src/types/Check.go | 6 ++++++ src/types/Field.go | 11 +++++++++++ src/types/New.go | 6 ------ src/types/NewList.go | 16 ---------------- src/types/Parse.go | 27 +++++++++++++++++++++++++++ src/types/ParseList.go | 16 ++++++++++++++++ src/types/Type.go | 14 ++++++-------- tests/errors_test.go | 2 +- 20 files changed, 111 insertions(+), 67 deletions(-) create mode 100644 src/types/Base.go create mode 100644 src/types/Check.go create mode 100644 src/types/Field.go delete mode 100644 src/types/New.go delete mode 100644 src/types/NewList.go create mode 100644 src/types/Parse.go create mode 100644 src/types/ParseList.go diff --git a/lib/sys/linux.q b/lib/sys/linux.q index e70ffee..c28f77d 100644 --- a/lib/sys/linux.q +++ b/lib/sys/linux.q @@ -22,7 +22,7 @@ munmap(address Pointer, length Int) -> Int { return syscall(11, address, length) } -clone(flags Int, stack Pointer) -> ThreadID { +clone(flags Int, stack Pointer) -> Int { return syscall(56, flags, stack) } diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 5036f1f..14ae685 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -1,11 +1,10 @@ package core import ( - "strings" - "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" ) // CompileCall executes a function call. @@ -18,7 +17,6 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { nameNode = root.Children[0] fn *Function name string - fullName string exists bool ) @@ -47,12 +45,7 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { imp.Used = true } - tmp := strings.Builder{} - tmp.WriteString(pkg) - tmp.WriteString(".") - tmp.WriteString(name) - fullName = tmp.String() - fn, exists = f.Functions[fullName] + fn, exists = f.Functions[pkg+"."+name] if !exists { return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position) @@ -68,10 +61,10 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { return nil, err } - if typ != fn.Parameters[i].Type { + if !types.Check(typ, fn.Parameters[i].Type) { return nil, errors.New(&errors.TypeMismatch{ - Encountered: string(typ), - Expected: string(fn.Parameters[i].Type), + Encountered: typ.Name, + Expected: fn.Parameters[i].Type.Name, ParameterName: fn.Parameters[i].Name, }, f.File, parameters[i].Token.Position) } @@ -87,7 +80,7 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { } } - f.Call(fullName) + f.Call(fn.UniqueName) for _, register := range registers { if register == f.CPU.Output[0] && root.Parent != nil { diff --git a/src/core/CompileDefinition.go b/src/core/CompileDefinition.go index 90993d9..5782978 100644 --- a/src/core/CompileDefinition.go +++ b/src/core/CompileDefinition.go @@ -5,7 +5,6 @@ import ( "git.akyoto.dev/cli/q/src/ast" "git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/expression" - "git.akyoto.dev/cli/q/src/types" ) // CompileDefinition compiles a variable definition. @@ -28,7 +27,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error { variable.Type = typ - if variable.Type == types.Invalid { + if variable.Type == nil { return errors.New(errors.UnknownType, f.File, node.Expression.Token.End()) } diff --git a/src/core/CompileReturn.go b/src/core/CompileReturn.go index 754b4b8..ab901f2 100644 --- a/src/core/CompileReturn.go +++ b/src/core/CompileReturn.go @@ -21,10 +21,10 @@ func (f *Function) CompileReturn(node *ast.Return) error { return err } - if typ != types.Any && typ != f.ReturnTypes[i] { + if !types.Check(typ, f.ReturnTypes[i]) { return errors.New(&errors.TypeMismatch{ - Encountered: string(typ), - Expected: string(f.ReturnTypes[i]), + Encountered: typ.Name, + Expected: f.ReturnTypes[i].Name, ParameterName: "", IsReturn: true, }, f.File, node.Values[i].Token.Position) diff --git a/src/core/Evaluate.go b/src/core/Evaluate.go index ed23b7d..235f917 100644 --- a/src/core/Evaluate.go +++ b/src/core/Evaluate.go @@ -8,7 +8,7 @@ import ( ) // Evaluate evaluates an expression and returns a register that contains the value of the expression. -func (f *Function) Evaluate(expr *expression.Expression) (types.Type, cpu.Register, error) { +func (f *Function) Evaluate(expr *expression.Expression) (*types.Type, cpu.Register, error) { if expr.Token.Kind == token.Identifier { name := expr.Token.Text(f.File.Bytes) variable := f.VariableByName(name) diff --git a/src/core/ExpressionToMemory.go b/src/core/ExpressionToMemory.go index f39e734..9077a1e 100644 --- a/src/core/ExpressionToMemory.go +++ b/src/core/ExpressionToMemory.go @@ -9,18 +9,18 @@ import ( ) // ExpressionToMemory puts the result of an expression into the specified memory address. -func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) (types.Type, error) { +func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) (*types.Type, error) { if node.IsLeaf() && node.Token.IsNumeric() { number, err := f.Number(node.Token) if err != nil { - return types.Invalid, err + return nil, err } size := byte(sizeof.Signed(int64(number))) if size != memory.Length { - return types.Invalid, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position) + return nil, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position) } f.MemoryNumber(asm.STORE, memory, number) diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index 8b4fa20..7c5b31f 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -11,7 +11,7 @@ import ( ) // ExpressionToRegister puts the result of an expression into the specified register. -func (f *Function) ExpressionToRegister(node *expression.Expression, register cpu.Register) (types.Type, error) { +func (f *Function) ExpressionToRegister(node *expression.Expression, register cpu.Register) (*types.Type, error) { f.SaveRegister(register) if node.IsFolded { @@ -31,7 +31,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp } if fn == nil || len(fn.ReturnTypes) == 0 { - return types.Any, err + return nil, err } return fn.ReturnTypes[0], err @@ -46,7 +46,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp if len(node.Children) == 1 { if !node.Token.IsUnaryOperator() { - return types.Invalid, errors.New(errors.MissingOperand, f.File, node.Token.End()) + return nil, errors.New(errors.MissingOperand, f.File, node.Token.End()) } typ, err := f.ExpressionToRegister(node.Children[0], register) @@ -69,10 +69,10 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp typ, err := f.ExpressionToRegister(left, register) if err != nil { - return types.Invalid, err + return nil, err } - if typ == types.Pointer && (node.Token.Kind == token.Add || node.Token.Kind == token.Sub) && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.Pointer { + if typ == types.Pointer && right.Token.Kind == token.Identifier && f.VariableByName(right.Token.Text(f.File.Bytes)).Type == types.Pointer { typ = types.Int } diff --git a/src/core/Function.go b/src/core/Function.go index 4ff2c4d..121ae8c 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -17,7 +17,7 @@ type Function struct { File *fs.File Body token.List Parameters []*scope.Variable - ReturnTypes []types.Type + ReturnTypes []*types.Type Functions map[string]*Function Err error deferred []func() diff --git a/src/core/TokenToRegister.go b/src/core/TokenToRegister.go index d33c1a7..117d8db 100644 --- a/src/core/TokenToRegister.go +++ b/src/core/TokenToRegister.go @@ -10,14 +10,14 @@ import ( // TokenToRegister moves a token into a register. // It only works with identifiers, numbers and strings. -func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.Type, error) { +func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (*types.Type, error) { switch t.Kind { case token.Identifier: name := t.Text(f.File.Bytes) variable := f.VariableByName(name) if variable == nil { - return types.Invalid, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) + return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) } f.UseVariable(variable) @@ -29,7 +29,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types. number, err := f.Number(t) if err != nil { - return types.Invalid, err + return nil, err } f.SaveRegister(register) @@ -45,6 +45,6 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types. return types.Pointer, nil default: - return types.Invalid, errors.New(errors.InvalidExpression, f.File, t.Position) + return nil, errors.New(errors.InvalidExpression, f.File, t.Position) } } diff --git a/src/scanner/scanFile.go b/src/scanner/scanFile.go index 46f25ff..1b9c795 100644 --- a/src/scanner/scanFile.go +++ b/src/scanner/scanFile.go @@ -233,7 +233,7 @@ func (s *Scanner) scanFile(path string, pkg string) error { typeEnd-- } - function.ReturnTypes = types.NewList(tokens[typeStart:typeEnd], contents) + function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], contents) } parameters := tokens[paramsStart:paramsEnd] @@ -245,7 +245,7 @@ func (s *Scanner) scanFile(path string, pkg string) error { } name := tokens[0].Text(contents) - dataType := types.New(tokens[1:].Text(contents)) + dataType := types.Parse(tokens[1:].Text(contents)) register := x64.InputRegisters[count] uses := token.Count(function.Body, contents, token.Identifier, name) diff --git a/src/scope/Variable.go b/src/scope/Variable.go index 7c6081e..d64ddca 100644 --- a/src/scope/Variable.go +++ b/src/scope/Variable.go @@ -7,8 +7,8 @@ import ( // Variable represents a named register. type Variable struct { + Type *types.Type Name string - Type types.Type Alive uint8 Register cpu.Register } diff --git a/src/types/Base.go b/src/types/Base.go new file mode 100644 index 0000000..f28b5dd --- /dev/null +++ b/src/types/Base.go @@ -0,0 +1,16 @@ +package types + +var ( + Float64 = &Type{Name: "Float64", Size: 8} + Float32 = &Type{Name: "Float32", Size: 4} + Int64 = &Type{Name: "Int64", Size: 8} + Int32 = &Type{Name: "Int32", Size: 4} + Int16 = &Type{Name: "Int16", Size: 2} + Int8 = &Type{Name: "Int8", Size: 1} + Pointer = &Type{Name: "Pointer", Size: 8} +) + +var ( + Float = Float64 + Int = Int64 +) diff --git a/src/types/Check.go b/src/types/Check.go new file mode 100644 index 0000000..dbf9808 --- /dev/null +++ b/src/types/Check.go @@ -0,0 +1,6 @@ +package types + +// Check returns true if the first type can be converted into the second type. +func Check(a *Type, b *Type) bool { + return a == nil || a == b +} diff --git a/src/types/Field.go b/src/types/Field.go new file mode 100644 index 0000000..aa0d554 --- /dev/null +++ b/src/types/Field.go @@ -0,0 +1,11 @@ +package types + +import "git.akyoto.dev/cli/q/src/token" + +// Field is a field in a data structure. +type Field struct { + Type *Type + Name string + Position token.Position + Offset uint8 +} diff --git a/src/types/New.go b/src/types/New.go deleted file mode 100644 index ae20817..0000000 --- a/src/types/New.go +++ /dev/null @@ -1,6 +0,0 @@ -package types - -// New creates a new type from a list of tokens. -func New(name string) Type { - return Type(name) -} diff --git a/src/types/NewList.go b/src/types/NewList.go deleted file mode 100644 index c315c8d..0000000 --- a/src/types/NewList.go +++ /dev/null @@ -1,16 +0,0 @@ -package types - -import "git.akyoto.dev/cli/q/src/token" - -// NewList generates a list of types from comma separated tokens. -func NewList(tokens token.List, source []byte) []Type { - var list []Type - - tokens.Split(func(parameter token.List) error { - typ := New(parameter.Text(source)) - list = append(list, typ) - return nil - }) - - return list -} diff --git a/src/types/Parse.go b/src/types/Parse.go new file mode 100644 index 0000000..0cfc305 --- /dev/null +++ b/src/types/Parse.go @@ -0,0 +1,27 @@ +package types + +// Parse creates a new type from a list of tokens. +func Parse(name string) *Type { + switch name { + case "Int": + return Int + case "Int64": + return Int64 + case "Int32": + return Int32 + case "Int16": + return Int16 + case "Int8": + return Int8 + case "Float": + return Float + case "Float64": + return Float64 + case "Float32": + return Float32 + case "Pointer": + return Pointer + default: + panic("Unknown type " + name) + } +} diff --git a/src/types/ParseList.go b/src/types/ParseList.go new file mode 100644 index 0000000..56a8592 --- /dev/null +++ b/src/types/ParseList.go @@ -0,0 +1,16 @@ +package types + +import "git.akyoto.dev/cli/q/src/token" + +// ParseList generates a list of types from comma separated tokens. +func ParseList(tokens token.List, source []byte) []*Type { + var list []*Type + + tokens.Split(func(parameter token.List) error { + typ := Parse(parameter.Text(source)) + list = append(list, typ) + return nil + }) + + return list +} diff --git a/src/types/Type.go b/src/types/Type.go index 76c04a9..d9ce347 100644 --- a/src/types/Type.go +++ b/src/types/Type.go @@ -1,10 +1,8 @@ package types -type Type string - -const ( - Invalid = "" - Any = "Any" - Int = "Int" - Pointer = "Pointer" -) +// Type represents a type in the type system. +type Type struct { + Name string + Fields []*Field + Size uint8 +} diff --git a/tests/errors_test.go b/tests/errors_test.go index 484cf94..71dbdf9 100644 --- a/tests/errors_test.go +++ b/tests/errors_test.go @@ -36,7 +36,7 @@ var errs = []struct { {"MissingOperand.q", errors.MissingOperand}, {"MissingOperand2.q", errors.MissingOperand}, {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, - {"TypeMismatch.q", &errors.TypeMismatch{Expected: "Pointer", Encountered: "Int", ParameterName: "p"}}, + {"TypeMismatch.q", &errors.TypeMismatch{Expected: "Pointer", Encountered: "Int64", ParameterName: "p"}}, {"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}}, {"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},