Simplified compilation of function calls
This commit is contained in:
parent
c3054369e3
commit
ea233d789d
@ -11,6 +11,7 @@ import (
|
||||
func Compile(constants <-chan *core.Constant, files <-chan *fs.File, functions <-chan *core.Function, structs <-chan *types.Struct, errs <-chan error) (Result, error) {
|
||||
all := core.Environment{
|
||||
Files: make([]*fs.File, 0, 8),
|
||||
Extern: make(map[string]struct{}, 0),
|
||||
Functions: make(map[string]*core.Function, 32),
|
||||
Structs: make(map[string]*types.Struct, 8),
|
||||
Constants: make(map[string]*core.Constant, 8),
|
||||
@ -29,6 +30,10 @@ func Compile(constants <-chan *core.Constant, files <-chan *fs.File, functions <
|
||||
function.All = &all
|
||||
all.Functions[function.UniqueName] = function
|
||||
|
||||
if function.IsExtern() {
|
||||
all.Extern[function.Package] = struct{}{}
|
||||
}
|
||||
|
||||
case structure, ok := <-structs:
|
||||
if !ok {
|
||||
structs = nil
|
||||
|
@ -3,7 +3,9 @@ package core
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/errors"
|
||||
"git.urbach.dev/cli/q/src/eval"
|
||||
"git.urbach.dev/cli/q/src/expression"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
@ -12,17 +14,8 @@ import (
|
||||
// Registers that are in use must be saved if they are modified by the function.
|
||||
// After the function call, they must be restored in reverse order.
|
||||
func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) {
|
||||
var (
|
||||
pkg = f.Package
|
||||
pkgNode *expression.Expression
|
||||
name string
|
||||
nameNode = root.Children[0]
|
||||
fn *Function
|
||||
exists bool
|
||||
)
|
||||
|
||||
if nameNode.IsLeaf() {
|
||||
name = nameNode.Token.Text(f.File.Bytes)
|
||||
if root.Children[0].Token.Kind == token.Identifier {
|
||||
name := root.Children[0].Token.Text(f.File.Bytes)
|
||||
|
||||
switch name {
|
||||
case "len":
|
||||
@ -37,57 +30,38 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error
|
||||
case "store":
|
||||
return nil, f.CompileMemoryStore(root)
|
||||
}
|
||||
} else {
|
||||
pkgNode = nameNode.Children[0]
|
||||
nameNode = nameNode.Children[1]
|
||||
|
||||
pkg = pkgNode.Token.Text(f.File.Bytes)
|
||||
name = nameNode.Token.Text(f.File.Bytes)
|
||||
}
|
||||
|
||||
if f.UniqueName == "core.init" && pkg == "main" && name == "main" {
|
||||
value, err := f.Evaluate(root.Children[0])
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parameters := root.Children[1:]
|
||||
registers := f.CPU.Input[:len(parameters)]
|
||||
|
||||
switch value := value.(type) {
|
||||
case *eval.Label:
|
||||
fn, exists := f.All.Functions[value.Label]
|
||||
|
||||
if !exists {
|
||||
if value.Label == "main.main" && f.UniqueName == "core.init" {
|
||||
f.Label(asm.CALL, "main.main")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fn, exists = f.All.Functions[pkg+"."+name]
|
||||
|
||||
if !exists {
|
||||
variable := f.VariableByName(name)
|
||||
|
||||
if variable != nil {
|
||||
f.Register(asm.CALL, variable.Value.Register)
|
||||
return nil, nil
|
||||
return nil, errors.New(&errors.UnknownIdentifier{Name: value.Label}, f.File, root.Children[0].Token.Position)
|
||||
}
|
||||
|
||||
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
|
||||
}
|
||||
|
||||
if pkg != f.File.Package && !fn.IsExtern() {
|
||||
if f.File.Imports == nil {
|
||||
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, pkgNode.Token.Position)
|
||||
}
|
||||
|
||||
imp, exists := f.File.Imports[pkg]
|
||||
|
||||
if !exists {
|
||||
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, pkgNode.Token.Position)
|
||||
}
|
||||
|
||||
imp.Used = true
|
||||
}
|
||||
|
||||
parameters := root.Children[1:]
|
||||
|
||||
if len(parameters) != len(fn.Input) {
|
||||
return nil, errors.New(&errors.ParameterCountMismatch{Function: fn.Name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, nameNode.Token.End())
|
||||
return nil, errors.New(&errors.ParameterCountMismatch{Function: fn.Name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, root.Children[0].Token.End())
|
||||
}
|
||||
|
||||
if fn.IsExtern() {
|
||||
return f.CallExtern(fn, parameters)
|
||||
}
|
||||
|
||||
registers := f.CPU.Input[:len(parameters)]
|
||||
err := f.ExpressionsToRegisters(parameters, registers, fn.Input, true)
|
||||
|
||||
if err != nil {
|
||||
@ -96,4 +70,16 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error
|
||||
|
||||
f.CallSafe(fn, registers)
|
||||
return fn.OutputTypes, nil
|
||||
|
||||
case *eval.Register:
|
||||
f.Register(asm.CALL, value.Register)
|
||||
|
||||
case *eval.Memory:
|
||||
tmp := f.NewRegister()
|
||||
f.MemoryRegister(asm.LOAD, value.Memory, tmp)
|
||||
f.Register(asm.CALL, tmp)
|
||||
f.FreeRegister(tmp)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
// Environment holds information about the entire build.
|
||||
type Environment struct {
|
||||
Constants map[string]*Constant
|
||||
Extern map[string]struct{}
|
||||
Functions map[string]*Function
|
||||
Structs map[string]*types.Struct
|
||||
Files []*fs.File
|
||||
|
@ -59,19 +59,24 @@ func (f *Function) EvaluateDot(expr *expression.Expression) (eval.Value, error)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
uniqueName := fmt.Sprintf("%s.%s", leftText, rightText)
|
||||
function, exists := f.All.Functions[uniqueName]
|
||||
if leftText != "main" {
|
||||
imp, exists := f.File.Imports[leftText]
|
||||
|
||||
if exists {
|
||||
f.File.Imports[leftText].Used = true
|
||||
imp.Used = true
|
||||
} else {
|
||||
_, exists := f.All.Extern[leftText]
|
||||
|
||||
if !exists {
|
||||
return nil, errors.New(&errors.UnknownPackage{Name: leftText}, f.File, left.Token.Position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value := &eval.Label{
|
||||
Typ: types.AnyPointer,
|
||||
Label: function.UniqueName,
|
||||
Label: fmt.Sprintf("%s.%s", leftText, rightText),
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
return nil, errors.New(&errors.UnknownIdentifier{Name: uniqueName}, f.File, left.Token.Position)
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UnknownFunction represents unknown function errors.
|
||||
type UnknownFunction struct {
|
||||
Name string
|
||||
CorrectName string
|
||||
}
|
||||
|
||||
// Error generates the string representation.
|
||||
func (err *UnknownFunction) Error() string {
|
||||
if err.CorrectName != "" {
|
||||
return fmt.Sprintf("Unknown function '%s', did you mean '%s'?", err.Name, err.CorrectName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Unknown function '%s'", err.Name)
|
||||
}
|
3
tests/errors/MissingBlockStart2.q
Normal file
3
tests/errors/MissingBlockStart2.q
Normal file
@ -0,0 +1,3 @@
|
||||
main() {
|
||||
loop
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
main() {
|
||||
x := 1 + f(x)
|
||||
}
|
3
tests/errors/UnknownIdentifier5.q
Normal file
3
tests/errors/UnknownIdentifier5.q
Normal file
@ -0,0 +1,3 @@
|
||||
main() {
|
||||
x := 1 + unknown(x)
|
||||
}
|
@ -39,6 +39,7 @@ var errs = []struct {
|
||||
{"MissingBlockEnd.q", errors.MissingBlockEnd},
|
||||
{"MissingBlockEnd2.q", errors.MissingBlockEnd},
|
||||
{"MissingBlockStart.q", errors.MissingBlockStart},
|
||||
{"MissingBlockStart2.q", errors.MissingBlockStart},
|
||||
{"MissingExpression.q", errors.MissingExpression},
|
||||
{"MissingGroupEnd.q", errors.MissingGroupEnd},
|
||||
{"MissingGroupStart.q", errors.MissingGroupStart},
|
||||
@ -52,11 +53,11 @@ var errs = []struct {
|
||||
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*any", Encountered: "int", ParameterName: "p"}},
|
||||
{"TypeMismatch2.q", &errors.TypeMismatch{Expected: "[]any", Encountered: "int", ParameterName: "array"}},
|
||||
{"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}},
|
||||
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
||||
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
{"UnknownIdentifier3.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
{"UnknownIdentifier4.q", &errors.UnknownIdentifier{Name: "unknown"}},
|
||||
{"UnknownIdentifier5.q", &errors.UnknownIdentifier{Name: "unknown"}},
|
||||
{"UnknownPackage.q", &errors.UnknownPackage{Name: "sys"}},
|
||||
{"UnknownType.q", &errors.UnknownType{Name: "unknown"}},
|
||||
{"UnknownType2.q", &errors.UnknownType{Name: "unknown"}},
|
||||
|
11
tests/programs/function-pointer-field.q
Normal file
11
tests/programs/function-pointer-field.q
Normal file
@ -0,0 +1,11 @@
|
||||
import core
|
||||
|
||||
struct Struct {
|
||||
func *any
|
||||
}
|
||||
|
||||
main() {
|
||||
s := new(Struct)
|
||||
s.func = core.exit
|
||||
s.func()
|
||||
}
|
21
tests/programs/loop-in-loop.q
Normal file
21
tests/programs/loop-in-loop.q
Normal file
@ -0,0 +1,21 @@
|
||||
import sys
|
||||
|
||||
main() {
|
||||
x := 0
|
||||
|
||||
loop {
|
||||
for 0..10 {
|
||||
x += 1
|
||||
}
|
||||
|
||||
assert x == 10
|
||||
|
||||
loop {
|
||||
x -= 1
|
||||
|
||||
if x == 0 {
|
||||
sys.exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -58,8 +58,9 @@ var programs = []struct {
|
||||
{"branch-save", 0},
|
||||
{"jump-near", 0},
|
||||
{"switch", 0},
|
||||
{"loop-infinite", 0},
|
||||
{"loop", 0},
|
||||
{"loop-lifetime", 0},
|
||||
{"loop-in-loop", 0},
|
||||
{"for", 0},
|
||||
{"memory-free", 0},
|
||||
{"out-of-memory", 0},
|
||||
@ -69,6 +70,7 @@ var programs = []struct {
|
||||
{"len", 0},
|
||||
{"cast", 0},
|
||||
{"function-pointer", 0},
|
||||
{"function-pointer-field", 0},
|
||||
}
|
||||
|
||||
func TestPrograms(t *testing.T) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user