Implemented lazy appending of struct fields
All checks were successful
/ test (push) Successful in 16s

This commit is contained in:
Eduard Urbach 2025-07-15 09:37:59 +02:00
parent c5588510e2
commit 7cc371070a
Signed by: ed
GPG key ID: 49226B848C78F6C8
11 changed files with 136 additions and 55 deletions

11
examples/winapi/winapi.q Normal file
View file

@ -0,0 +1,11 @@
main() {
title := "Title\0"
message := "Message\0"
user32.MessageBoxA(0, message.ptr, title.ptr, 0x240040)
}
extern {
user32 {
MessageBoxA(window *any, text *byte, title *byte, flags uint) -> (button int)
}
}

View file

@ -1,6 +1,8 @@
package codegen
import (
"slices"
"git.urbach.dev/cli/q/src/asm"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/ssa"
@ -86,7 +88,7 @@ func (f *Function) exec(step *step) {
args := instr.Arguments
var pushed []cpu.Register
for i, arg := range args {
for i, arg := range slices.Backward(args) {
if i >= len(f.CPU.ExternCall.In) {
pushed = append(pushed, f.ValueToStep[arg].Register)
continue
@ -122,6 +124,10 @@ func (f *Function) exec(step *step) {
})
case *ssa.Int:
if step.Register == -1 {
return
}
f.Assembler.Append(&asm.MoveRegisterNumber{
Destination: step.Register,
Number: instr.Int,

View file

@ -17,7 +17,7 @@ func (f *Function) checkDeadCode() error {
}
source := instr.(ssa.HasSource)
return errors.New(&UnusedValue{Value: string(f.File.Bytes[source.Start():source.End()])}, f.File, source.Start())
return errors.New(&UnusedValue{Value: instr.String()}, f.File, source.Start())
}
return nil

View file

@ -0,0 +1,15 @@
package core
import (
"git.urbach.dev/cli/q/src/expression"
)
// compileDefinition compiles a define instruction.
func (f *Function) compileDefinition(expr *expression.Expression) error {
left := expr.Children[0]
right := expr.Children[1]
name := left.String(f.File.Bytes)
value, err := f.eval(right)
f.Identifiers[name] = value
return err
}

View file

@ -1,6 +1,7 @@
package core
import (
"git.urbach.dev/cli/q/src/errors"
"git.urbach.dev/cli/q/src/expression"
"git.urbach.dev/cli/q/src/token"
)
@ -16,13 +17,11 @@ func (f *Function) compileInstruction(instr token.List) error {
expr := expression.Parse(instr)
if expr.Token.Kind == token.Define {
left := expr.Children[0]
right := expr.Children[1]
name := left.String(f.File.Bytes)
value, err := f.eval(right)
f.Identifiers[name] = value
return err
switch expr.Token.Kind {
case token.Define:
return f.compileDefinition(expr)
case token.String:
return errors.New(&UnusedValue{Value: expr.String(f.File.Bytes)}, f.File, expr.Token.Position)
}
_, err := f.eval(expr)

View file

@ -30,7 +30,7 @@ func (f *Function) decompose(nodes []*expression.Expression, typeCheck []*ssa.Pa
if isStruct {
for _, field := range structure.Arguments {
args = append(args, field)
args = append(args, f.Append(field))
}
continue

View file

@ -30,7 +30,8 @@ var errs = []struct {
{"UnknownIdentifier5.q", &core.UnknownIdentifier{Name: "unknown"}},
{"UnknownIdentifier6.q", &core.UnknownIdentifier{Name: "os"}},
{"UnknownIdentifier7.q", &core.UnknownIdentifier{Name: "os.unknown"}},
{"UnusedValue.q", &core.UnusedValue{Value: "2+3"}},
{"UnusedValue.q", &core.UnusedValue{Value: "2 + 3"}},
{"UnusedValue2.q", &core.UnusedValue{Value: "\"not used\""}},
}
func TestErrors(t *testing.T) {

View file

@ -18,13 +18,19 @@ func (f *Function) eval(expr *expression.Expression) (ssa.Value, error) {
name := expr.Token.String(f.File.Bytes)
value, exists := f.Identifiers[name]
if !exists {
function := f.All.Function(f.File.Package, name)
if exists {
return value, nil
}
if function == nil {
return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
}
_, exists = f.All.Packages[name]
if exists {
return &ssa.Package{Name: name}, nil
}
function := f.All.Function(f.File.Package, name)
if function != nil {
f.Dependencies.Add(function)
v := &ssa.Function{
@ -38,7 +44,7 @@ func (f *Function) eval(expr *expression.Expression) (ssa.Value, error) {
return v, nil
}
return value, nil
return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
case token.Number:
number, err := f.toNumber(expr.Token)
@ -58,15 +64,15 @@ func (f *Function) eval(expr *expression.Expression) (ssa.Value, error) {
data := expr.Token.Bytes(f.File.Bytes)
data = unescape(data)
length := f.Append(&ssa.Int{
length := &ssa.Int{
Int: len(data),
Source: ssa.Source(expr.Source),
})
}
pointer := f.Append(&ssa.Bytes{
pointer := &ssa.Bytes{
Bytes: data,
Source: ssa.Source(expr.Source),
})
}
v := &ssa.Struct{
Arguments: []ssa.Value{pointer, length},
@ -141,49 +147,59 @@ func (f *Function) eval(expr *expression.Expression) (ssa.Value, error) {
right := expr.Children[1]
leftText := left.String(f.File.Bytes)
rightText := right.String(f.File.Bytes)
fullName := fmt.Sprintf("%s.%s", leftText, rightText)
identifier, exists := f.Identifiers[fullName]
leftValue, err := f.eval(left)
if exists {
return identifier, nil
if err != nil {
return nil, err
}
pkg, exists := f.All.Packages[leftText]
switch leftValue := leftValue.(type) {
case *ssa.Package:
pkg := f.All.Packages[leftText]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: leftText}, f.File, left.Token.Position)
}
if !pkg.IsExtern && f != f.All.Init {
_, exists := f.File.Imports[leftText]
if !pkg.IsExtern && f != f.All.Init {
_, exists = f.File.Imports[leftText]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: leftText}, f.File, left.Token.Position)
}
}
function, exists := pkg.Functions[rightText]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: leftText}, f.File, left.Token.Position)
return nil, errors.New(&UnknownIdentifier{Name: fmt.Sprintf("%s.%s", pkg.Name, rightText)}, f.File, left.Token.Position)
}
if function.IsExtern() {
f.Assembler.Libraries.Append(function.Package, function.Name)
} else {
f.Dependencies.Add(function)
}
v := &ssa.Function{
Package: function.Package,
Name: function.Name,
Typ: function.Type,
IsExtern: function.IsExtern(),
Source: ssa.Source(expr.Source),
}
return v, nil
case *ssa.Struct:
field := leftValue.Typ.FieldByName(rightText)
if field == nil {
panic("unknown field")
}
return f.Append(leftValue.Arguments[field.Index]), nil
default:
panic("not implemented")
}
function, exists := pkg.Functions[rightText]
if !exists {
return nil, errors.New(&UnknownIdentifier{Name: fullName}, f.File, left.Token.Position)
}
if function.IsExtern() {
f.Assembler.Libraries.Append(function.Package, function.Name)
} else {
f.Dependencies.Add(function)
}
v := &ssa.Function{
Package: function.Package,
Name: function.Name,
Typ: function.Type,
IsExtern: function.IsExtern(),
Source: ssa.Source(expr.Source),
}
return v, nil
default:
if expr.Token.IsOperator() {
left := expr.Children[0]

View file

@ -19,6 +19,8 @@ func (f *Function) registerInputs() {
structType, isStructType := input.Typ.(*types.Struct)
if isStructType {
structure := &ssa.Struct{Typ: structType}
for _, field := range structType.Fields {
param := &ssa.Parameter{
Index: uint8(offset + i),
@ -27,12 +29,13 @@ func (f *Function) registerInputs() {
Source: input.Source,
}
f.Identifiers[param.Name] = param
f.Append(param)
structure.Arguments = append(structure.Arguments, param)
offset++
}
offset--
f.Identifiers[input.Name] = structure
continue
}

3
src/core/testdata/UnusedValue2.q vendored Normal file
View file

@ -0,0 +1,3 @@
main() {
"not used"
}

27
src/ssa/Package.go Normal file
View file

@ -0,0 +1,27 @@
package ssa
import (
"git.urbach.dev/cli/q/src/types"
)
type Package struct {
Name string
IsExtern bool
}
func (v *Package) AddUser(Value) { panic("package can not be used as a dependency") }
func (v *Package) Inputs() []Value { return nil }
func (v *Package) IsConst() bool { return true }
func (v *Package) String() string { return v.Name }
func (v *Package) Type() types.Type { return nil }
func (v *Package) Users() []Value { return nil }
func (a *Package) Equals(v Value) bool {
b, sameType := v.(*Package)
if !sameType {
return false
}
return a.Name == b.Name
}