From 97cdcbd1cba9371e70bcb93d9e8c60a4664aedc4 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sat, 8 Feb 2025 16:06:39 +0100 Subject: [PATCH] Implemented package specific structs --- examples/server/server.q | 9 +------- lib/sys/net_linux.q | 7 +++++++ src/build/Build.go | 4 ++-- src/compiler/Compile.go | 31 +++++++--------------------- src/core/CompileCall.go | 8 ++++---- src/core/CompileNew.go | 43 ++++++++++++++++++++++++++++++++++----- src/core/Function.go | 2 +- src/core/ResolveTypes.go | 5 +++-- src/core/TypeByName.go | 16 --------------- src/scanner/Scan.go | 8 ++++---- src/scanner/Scanner.go | 2 +- src/scanner/scanStruct.go | 4 ++-- src/types/ByName.go | 36 ++++++++++++++++++++++++++++++++ src/types/Struct.go | 20 +++++++++++------- src/types/types_test.go | 4 ++-- 15 files changed, 121 insertions(+), 78 deletions(-) create mode 100644 src/types/ByName.go diff --git a/examples/server/server.q b/examples/server/server.q index 852ae50..e9146c7 100644 --- a/examples/server/server.q +++ b/examples/server/server.q @@ -3,13 +3,6 @@ // [2] curl http://127.0.0.1:8080 import sys -struct sockaddr_in { - sin_family Int16 - sin_port Int16 - sin_addr Int64 - sin_zero Int64 -} - main() { socket := sys.socket(2, 1, 0) @@ -18,7 +11,7 @@ main() { sys.exit(1) } - addr := new(sockaddr_in) + addr := new(sys.sockaddr_in) addr.sin_family = 2 addr.sin_port = 0x901F addr.sin_addr = 0 diff --git a/lib/sys/net_linux.q b/lib/sys/net_linux.q index 00ed81c..91b0da6 100644 --- a/lib/sys/net_linux.q +++ b/lib/sys/net_linux.q @@ -1,3 +1,10 @@ +struct sockaddr_in { + sin_family Int16 + sin_port Int16 + sin_addr Int64 + sin_zero Int64 +} + socket(family Int, type Int, protocol Int) -> Int { return syscall(41, family, type, protocol) } diff --git a/src/build/Build.go b/src/build/Build.go index e3b620e..9784a97 100644 --- a/src/build/Build.go +++ b/src/build/Build.go @@ -23,8 +23,8 @@ func New(files ...string) *Build { // Run compiles the input files. func (build *Build) Run() (compiler.Result, error) { - files, functions, types, errors := scanner.Scan(build.Files) - return compiler.Compile(files, functions, types, errors) + files, functions, structs, errors := scanner.Scan(build.Files) + return compiler.Compile(files, functions, structs, errors) } // Executable returns the path to the executable. diff --git a/src/compiler/Compile.go b/src/compiler/Compile.go index 328c9f2..57ccb04 100644 --- a/src/compiler/Compile.go +++ b/src/compiler/Compile.go @@ -10,22 +10,11 @@ import ( ) // Compile waits for the scan to finish and compiles all functions. -func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan types.Type, errs <-chan error) (Result, error) { +func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Struct, errs <-chan error) (Result, error) { result := Result{} allFiles := make([]*fs.File, 0, 8) allFunctions := map[string]*core.Function{} - - 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, - } + allStructs := map[string]*types.Struct{} for functions != nil || files != nil || errs != nil { select { @@ -36,16 +25,16 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c } function.Functions = allFunctions - function.Types = allTypes + function.Structs = allStructs allFunctions[function.UniqueName] = function - case typ, ok := <-structs: + case structure, ok := <-structs: if !ok { structs = nil continue } - allTypes[typ.Name()] = typ + allStructs[structure.UniqueName] = structure case file, ok := <-files: if !ok { @@ -66,14 +55,8 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c } // Calculate size of structs - for _, typ := range allTypes { - structure, isStruct := typ.(*types.Struct) - - if !isStruct { - continue - } - - structure.Update(allTypes) + for _, structure := range allStructs { + structure.Update(allStructs) } // Resolve the types diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 9ac1b93..46b687c 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -30,15 +30,15 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { case "syscall": return nil, f.CompileSyscall(root) case "new": + typ, err := f.CompileNew(root) + return &Function{ Output: []*Output{ { - Type: &types.Pointer{ - To: f.Types[root.Children[1].Token.Text(f.File.Bytes)], - }, + Type: typ, }, }, - }, f.CompileNew(root) + }, err case "delete": return nil, f.CompileDelete(root) case "store": diff --git a/src/core/CompileNew.go b/src/core/CompileNew.go index d25818b..c0925b8 100644 --- a/src/core/CompileNew.go +++ b/src/core/CompileNew.go @@ -2,16 +2,49 @@ package core import ( "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" ) // CompileNew compiles a `new` function call which allocates a struct. -func (f *Function) CompileNew(root *expression.Expression) error { - parameters := root.Children[1:] - structName := parameters[0].Token.Text(f.File.Bytes) - typ := f.Types[structName] +func (f *Function) CompileNew(root *expression.Expression) (types.Type, error) { + var ( + parameters = root.Children[1:] + nameNode = parameters[0] + pkg = f.Package + name string + ) + + if nameNode.IsLeaf() { + name = nameNode.Token.Text(f.File.Bytes) + } else { + pkg = nameNode.Children[0].Token.Text(f.File.Bytes) + name = nameNode.Children[1].Token.Text(f.File.Bytes) + } + + if pkg != f.File.Package { + if f.File.Imports == nil { + return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position) + } + + imp, exists := f.File.Imports[pkg] + + if !exists { + return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position) + } + + imp.Used = true + } + + typ, exists := f.Structs[pkg+"."+name] + + if !exists { + return nil, errors.New(&errors.UnknownType{Name: name}, f.File, nameNode.Token.Position) + } + f.SaveRegister(f.CPU.Input[0]) f.RegisterNumber(asm.MOVE, f.CPU.Input[0], typ.Size()) f.CallSafe(f.Functions["mem.alloc"], f.CPU.Input[:1]) - return nil + return &types.Pointer{To: typ}, nil } diff --git a/src/core/Function.go b/src/core/Function.go index 7aeb189..a502ccb 100644 --- a/src/core/Function.go +++ b/src/core/Function.go @@ -19,7 +19,7 @@ type Function struct { Input []*Input Output []*Output Functions map[string]*Function - Types map[string]types.Type + Structs map[string]*types.Struct DLLs dll.List Err error deferred []func() diff --git a/src/core/ResolveTypes.go b/src/core/ResolveTypes.go index c779c13..5636fcd 100644 --- a/src/core/ResolveTypes.go +++ b/src/core/ResolveTypes.go @@ -4,6 +4,7 @@ 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/types" "git.akyoto.dev/cli/q/src/x86" ) @@ -12,7 +13,7 @@ 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) + param.Type = types.ByName(typeName, f.Package, f.Structs) if param.Type == nil { return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position) @@ -34,7 +35,7 @@ func (f *Function) ResolveTypes() error { for _, param := range f.Output { typeName := param.tokens.Text(f.File.Bytes) - param.Type = f.TypeByName(typeName) + param.Type = types.ByName(typeName, f.Package, f.Structs) if param.Type == nil { return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position) diff --git a/src/core/TypeByName.go b/src/core/TypeByName.go index fcad711..9a8bc95 100644 --- a/src/core/TypeByName.go +++ b/src/core/TypeByName.go @@ -1,17 +1 @@ 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] -} diff --git a/src/scanner/Scan.go b/src/scanner/Scan.go index a0ca92d..fdb3354 100644 --- a/src/scanner/Scan.go +++ b/src/scanner/Scan.go @@ -10,11 +10,11 @@ import ( ) // Scan scans the list of files. -func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan types.Type, <-chan error) { +func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan *types.Struct, <-chan error) { scanner := Scanner{ files: make(chan *fs.File), functions: make(chan *core.Function), - types: make(chan types.Type), + structs: make(chan *types.Struct), errors: make(chan error), } @@ -24,9 +24,9 @@ func Scan(files []string) (<-chan *fs.File, <-chan *core.Function, <-chan types. scanner.group.Wait() close(scanner.files) close(scanner.functions) - close(scanner.types) + close(scanner.structs) close(scanner.errors) }() - return scanner.files, scanner.functions, scanner.types, scanner.errors + return scanner.files, scanner.functions, scanner.structs, scanner.errors } diff --git a/src/scanner/Scanner.go b/src/scanner/Scanner.go index 13485f5..97847bd 100644 --- a/src/scanner/Scanner.go +++ b/src/scanner/Scanner.go @@ -12,7 +12,7 @@ import ( type Scanner struct { files chan *fs.File functions chan *core.Function - types chan types.Type + structs chan *types.Struct errors chan error queued sync.Map group sync.WaitGroup diff --git a/src/scanner/scanStruct.go b/src/scanner/scanStruct.go index d5fe98f..d471ce1 100644 --- a/src/scanner/scanStruct.go +++ b/src/scanner/scanStruct.go @@ -16,7 +16,7 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro } structName := tokens[i].Text(file.Bytes) - structure := types.NewStruct(structName) + structure := types.NewStruct(file.Package, structName) i++ @@ -54,6 +54,6 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position) } - s.types <- structure + s.structs <- structure return i, nil } diff --git a/src/types/ByName.go b/src/types/ByName.go new file mode 100644 index 0000000..1f402c1 --- /dev/null +++ b/src/types/ByName.go @@ -0,0 +1,36 @@ +package types + +import ( + "strings" +) + +// ByName returns the type with the given name or `nil` if it doesn't exist. +func ByName(name string, pkg string, structs map[string]*Struct) Type { + if strings.HasPrefix(name, "*") { + to := strings.TrimPrefix(name, "*") + return &Pointer{To: ByName(to, pkg, structs)} + } + + 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 + } + + return structs[pkg+"."+name] +} diff --git a/src/types/Struct.go b/src/types/Struct.go index ac3d20e..c1250a4 100644 --- a/src/types/Struct.go +++ b/src/types/Struct.go @@ -2,14 +2,20 @@ package types // Struct is a structure in memory whose regions are addressable with named fields. type Struct struct { - name string - fields []*Field - size int + Package string + UniqueName string + name string + fields []*Field + size int } // NewStruct creates a new struct. -func NewStruct(name string) *Struct { - return &Struct{name: name} +func NewStruct(pkg string, name string) *Struct { + return &Struct{ + Package: pkg, + UniqueName: pkg + "." + name, + name: name, + } } // AddField adds a new field to the end of the struct. @@ -39,11 +45,11 @@ func (s *Struct) Size() int { } // Update updates the offsets and structure size. -func (s *Struct) Update(types map[string]Type) { +func (s *Struct) Update(allTypes map[string]*Struct) { s.size = 0 for _, field := range s.fields { - field.Type = types[field.TypeName] + field.Type = ByName(field.TypeName, s.Package, allTypes) field.Offset = s.size s.size += field.Type.Size() } diff --git a/src/types/types_test.go b/src/types/types_test.go index d5b6ba8..e134ad1 100644 --- a/src/types/types_test.go +++ b/src/types/types_test.go @@ -24,12 +24,12 @@ func TestSize(t *testing.T) { } func TestStruct(t *testing.T) { - s := types.NewStruct("Test") + s := types.NewStruct("main", "Test") assert.Equal(t, s.Name(), "Test") assert.Equal(t, s.Size(), 0) field := &types.Field{Name: "TestField", TypeName: "Int8"} s.AddField(field) - s.Update(map[string]types.Type{"Int8": types.Int8}) + s.Update(nil) assert.Equal(t, s.Size(), 1) assert.Equal(t, s.FieldByName("TestField"), field) assert.Nil(t, s.FieldByName("does-not-exist"))