Implemented package specific structs

This commit is contained in:
Eduard Urbach 2025-02-08 16:06:39 +01:00
parent 91bafc0867
commit 97cdcbd1cb
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
15 changed files with 121 additions and 78 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

36
src/types/ByName.go Normal file
View File

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

View File

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

View File

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