Implemented struct pointer types

This commit is contained in:
Eduard Urbach 2025-02-05 15:16:00 +01:00
parent 85568949a2
commit 5d38a4980a
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
17 changed files with 190 additions and 102 deletions

View File

@ -7,10 +7,19 @@ struct Point {
} }
main() { main() {
p := construct()
print(p)
delete(p)
}
construct() -> *Point {
p := new(Point) p := new(Point)
p.x = 1 p.x = 1
p.y = 2 p.y = 2
return p
}
print(p *Point) {
out := mem.alloc(8) out := mem.alloc(8)
out[0] = 'x' out[0] = 'x'
out[1] = ' ' out[1] = ' '
@ -21,7 +30,5 @@ main() {
out[6] = '0' + p.y out[6] = '0' + p.y
out[7] = '\n' out[7] = '\n'
sys.write(1, out, 8) sys.write(1, out, 8)
mem.free(out) mem.free(out, 8)
delete(p)
} }

View File

@ -14,7 +14,18 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
result := Result{} result := Result{}
allFiles := make([]*fs.File, 0, 8) allFiles := make([]*fs.File, 0, 8)
allFunctions := map[string]*core.Function{} allFunctions := map[string]*core.Function{}
allTypes := map[string]types.Type{}
allTypes := map[string]types.Type{
"Int": types.Int,
"Int64": types.Int64,
"Int32": types.Int32,
"Int16": types.Int16,
"Int8": types.Int8,
"Float": types.Float,
"Float64": types.Float64,
"Float32": types.Float32,
"Pointer": types.PointerAny,
}
for functions != nil || files != nil || errs != nil { for functions != nil || files != nil || errs != nil {
select { select {
@ -54,6 +65,15 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
} }
} }
// Resolve the types
for _, function := range allFunctions {
err := function.ResolveTypes()
if err != nil {
return result, err
}
}
// Start parallel compilation // Start parallel compilation
CompileFunctions(allFunctions) CompileFunctions(allFunctions)

View File

@ -31,11 +31,13 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
return nil, f.CompileSyscall(root) return nil, f.CompileSyscall(root)
case "new": case "new":
return &Function{ return &Function{
ReturnTypes: []types.Type{ Output: []*Output{
&types.Pointer{ {
Type: &types.Pointer{
To: f.Types[root.Children[1].Token.Text(f.File.Bytes)], To: f.Types[root.Children[1].Token.Text(f.File.Bytes)],
}, },
}, },
},
}, f.CompileNew(root) }, f.CompileNew(root)
case "delete": case "delete":
return nil, f.CompileDelete(root) return nil, f.CompileDelete(root)
@ -92,16 +94,16 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
return nil, err return nil, err
} }
if !types.Is(typ, fn.Parameters[i].Type) { if !types.Is(typ, fn.Input[i].Type) {
return nil, errors.New(&errors.TypeMismatch{ return nil, errors.New(&errors.TypeMismatch{
Encountered: typ.Name(), Encountered: typ.Name(),
Expected: fn.Parameters[i].Type.Name(), Expected: fn.Input[i].Type.Name(),
ParameterName: fn.Parameters[i].Name, ParameterName: fn.Input[i].Name,
}, f.File, parameters[i].Token.Position) }, f.File, parameters[i].Token.Position)
} }
} }
for _, register := range f.CPU.Output[:len(fn.ReturnTypes)] { for _, register := range f.CPU.Output[:len(fn.Output)] {
f.SaveRegister(register) f.SaveRegister(register)
} }

View File

@ -28,7 +28,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
variable.Type = typ variable.Type = typ
if variable.Type == nil { if variable.Type == nil {
return errors.New(errors.UnknownType, f.File, node.Expression.Token.End()) return errors.New(errors.CouldNotInferType, f.File, node.Expression.Token.End())
} }
f.AddVariable(variable) f.AddVariable(variable)
@ -54,7 +54,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
} }
if called != nil { if called != nil {
variable.Type = called.ReturnTypes[count] variable.Type = called.Output[count].Type
} }
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count]) f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])

View File

@ -14,8 +14,8 @@ func (f *Function) CompileReturn(node *ast.Return) error {
return nil return nil
} }
if len(node.Values) != len(f.ReturnTypes) { if len(node.Values) != len(f.Output) {
return errors.New(&errors.ReturnCountMismatch{Count: len(node.Values), ExpectedCount: len(f.ReturnTypes)}, f.File, node.Values[0].Token.Position) return errors.New(&errors.ReturnCountMismatch{Count: len(node.Values), ExpectedCount: len(f.Output)}, f.File, node.Values[0].Token.Position)
} }
for i := len(node.Values) - 1; i >= 0; i-- { for i := len(node.Values) - 1; i >= 0; i-- {
@ -25,10 +25,10 @@ func (f *Function) CompileReturn(node *ast.Return) error {
return err return err
} }
if !types.Is(typ, f.ReturnTypes[i]) { if !types.Is(typ, f.Output[i].Type) {
return errors.New(&errors.TypeMismatch{ return errors.New(&errors.TypeMismatch{
Encountered: typ.Name(), Encountered: typ.Name(),
Expected: f.ReturnTypes[i].Name(), Expected: f.Output[i].Type.Name(),
ParameterName: "", ParameterName: "",
IsReturn: true, IsReturn: true,
}, f.File, node.Values[i].Token.Position) }, f.File, node.Values[i].Token.Position)

View File

@ -30,11 +30,11 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0]) f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
} }
if fn == nil || len(fn.ReturnTypes) == 0 { if fn == nil || len(fn.Output) == 0 {
return nil, err return nil, err
} }
return fn.ReturnTypes[0], err return fn.Output[0].Type, err
} }
if node.Token.Kind == token.Array { if node.Token.Kind == token.Array {

View File

@ -4,7 +4,6 @@ import (
"git.akyoto.dev/cli/q/src/dll" "git.akyoto.dev/cli/q/src/dll"
"git.akyoto.dev/cli/q/src/fs" "git.akyoto.dev/cli/q/src/fs"
"git.akyoto.dev/cli/q/src/register" "git.akyoto.dev/cli/q/src/register"
"git.akyoto.dev/cli/q/src/scope"
"git.akyoto.dev/cli/q/src/token" "git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/types" "git.akyoto.dev/cli/q/src/types"
) )
@ -17,8 +16,8 @@ type Function struct {
UniqueName string UniqueName string
File *fs.File File *fs.File
Body token.List Body token.List
Parameters []*scope.Variable Input []*Input
ReturnTypes []types.Type Output []*Output
Functions map[string]*Function Functions map[string]*Function
Types map[string]types.Type Types map[string]types.Type
DLLs dll.List DLLs dll.List

16
src/core/Input.go Normal file
View File

@ -0,0 +1,16 @@
package core
import (
"git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/types"
)
type Input struct {
Name string
Type types.Type
tokens token.List
}
func NewInput(tokens token.List) *Input {
return &Input{tokens: tokens}
}

15
src/core/Output.go Normal file
View File

@ -0,0 +1,15 @@
package core
import (
"git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/types"
)
type Output struct {
Type types.Type
tokens token.List
}
func NewOutput(tokens token.List) *Output {
return &Output{tokens: tokens}
}

45
src/core/ResolveTypes.go Normal file
View File

@ -0,0 +1,45 @@
package core
import (
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/scope"
"git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/x64"
)
// ResolveTypes parses the input and output types.
func (f *Function) ResolveTypes() error {
for i, param := range f.Input {
param.Name = param.tokens[0].Text(f.File.Bytes)
typeName := param.tokens[1:].Text(f.File.Bytes)
param.Type = f.TypeByName(typeName)
if param.Type == nil {
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
}
uses := token.Count(f.Body, f.File.Bytes, token.Identifier, param.Name)
if uses == 0 && param.Name != "_" {
return errors.New(&errors.UnusedVariable{Name: param.Name}, f.File, param.tokens[0].Position)
}
f.AddVariable(&scope.Variable{
Name: param.Name,
Type: param.Type,
Register: x64.InputRegisters[i],
Alive: uses,
})
}
for _, param := range f.Output {
typeName := param.tokens.Text(f.File.Bytes)
param.Type = f.TypeByName(typeName)
if param.Type == nil {
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
}
}
return nil
}

17
src/core/TypeByName.go Normal file
View File

@ -0,0 +1,17 @@
package core
import (
"strings"
"git.akyoto.dev/cli/q/src/types"
)
// TypeByName returns the type with the given name or `nil` if it doesn't exist.
func (f *Function) TypeByName(name string) types.Type {
if strings.HasPrefix(name, "*") {
to := strings.TrimPrefix(name, "*")
return &types.Pointer{To: f.TypeByName(to)}
}
return f.Types[name]
}

View File

@ -1,6 +1,7 @@
package errors package errors
var ( var (
CouldNotInferType = &Base{"Couldn't infer type"}
EmptySwitch = &Base{"Empty switch"} EmptySwitch = &Base{"Empty switch"}
ExpectedFunctionName = &Base{"Expected function name"} ExpectedFunctionName = &Base{"Expected function name"}
ExpectedFunctionParameters = &Base{"Expected function parameters"} ExpectedFunctionParameters = &Base{"Expected function parameters"}
@ -21,5 +22,4 @@ var (
MissingParameter = &Base{"Missing parameter"} MissingParameter = &Base{"Missing parameter"}
MissingType = &Base{"Missing type"} MissingType = &Base{"Missing type"}
NotImplemented = &Base{"Not implemented"} NotImplemented = &Base{"Not implemented"}
UnknownType = &Base{"Unknown type"}
) )

18
src/errors/UnknownType.go Normal file
View File

@ -0,0 +1,18 @@
package errors
import "fmt"
// UnknownType represents unknown types.
type UnknownType struct {
Name string
CorrectName string
}
// Error generates the string representation.
func (err *UnknownType) Error() string {
if err.CorrectName != "" {
return fmt.Sprintf("Unknown type '%s', did you mean '%s'?", err.Name, err.CorrectName)
}
return fmt.Sprintf("Unknown type '%s'", err.Name)
}

View File

@ -4,10 +4,7 @@ import (
"git.akyoto.dev/cli/q/src/core" "git.akyoto.dev/cli/q/src/core"
"git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/fs" "git.akyoto.dev/cli/q/src/fs"
"git.akyoto.dev/cli/q/src/scope"
"git.akyoto.dev/cli/q/src/token" "git.akyoto.dev/cli/q/src/token"
"git.akyoto.dev/cli/q/src/types"
"git.akyoto.dev/cli/q/src/x64"
) )
// scanFunction scans a function. // scanFunction scans a function.
@ -157,11 +154,19 @@ func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, er
typeEnd-- typeEnd--
} }
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], file.Bytes) outputTokens := tokens[typeStart:typeEnd]
err := outputTokens.Split(func(tokens token.List) error {
function.Output = append(function.Output, core.NewOutput(tokens))
return nil
})
if err != nil {
return i, err
}
} }
parameters := tokens[paramsStart:paramsEnd] parameters := tokens[paramsStart:paramsEnd]
count := 0
err := parameters.Split(func(tokens token.List) error { err := parameters.Split(func(tokens token.List) error {
if len(tokens) == 0 { if len(tokens) == 0 {
@ -172,25 +177,7 @@ func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, er
return errors.New(errors.MissingType, file, tokens[0].End()) return errors.New(errors.MissingType, file, tokens[0].End())
} }
name := tokens[0].Text(file.Bytes) function.Input = append(function.Input, core.NewInput(tokens))
dataType := types.Parse(tokens[1:].Text(file.Bytes))
register := x64.InputRegisters[count]
uses := token.Count(function.Body, file.Bytes, token.Identifier, name)
if uses == 0 && name != "_" {
return errors.New(&errors.UnusedVariable{Name: name}, file, tokens[0].Position)
}
variable := &scope.Variable{
Name: name,
Type: dataType,
Register: register,
Alive: uses,
}
function.Parameters = append(function.Parameters, variable)
function.AddVariable(variable)
count++
return nil return nil
}) })

View File

@ -33,7 +33,12 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
fieldName := tokens[i].Text(file.Bytes) fieldName := tokens[i].Text(file.Bytes)
i++ i++
fieldTypeName := tokens[i].Text(file.Bytes) fieldTypeName := tokens[i].Text(file.Bytes)
fieldType := types.Parse(fieldTypeName) fieldType := types.Int
if fieldTypeName != "Int" {
panic("not implemented")
}
i++ i++
structure.AddField(&types.Field{ structure.AddField(&types.Field{

View File

@ -1,27 +0,0 @@
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 PointerAny
default:
panic("Unknown type " + name)
}
}

View File

@ -1,16 +0,0 @@
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
}