Simplified compilation of function calls
This commit is contained in:
@ -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,63 +30,56 @@ 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" {
|
||||
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.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())
|
||||
}
|
||||
|
||||
if fn.IsExtern() {
|
||||
return f.CallExtern(fn, parameters)
|
||||
}
|
||||
|
||||
registers := f.CPU.Input[:len(parameters)]
|
||||
err := f.ExpressionsToRegisters(parameters, registers, fn.Input, true)
|
||||
value, err := f.Evaluate(root.Children[0])
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.CallSafe(fn, registers)
|
||||
return fn.OutputTypes, nil
|
||||
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
|
||||
}
|
||||
|
||||
return nil, errors.New(&errors.UnknownIdentifier{Name: value.Label}, f.File, root.Children[0].Token.Position)
|
||||
}
|
||||
|
||||
if len(parameters) != len(fn.Input) {
|
||||
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)
|
||||
}
|
||||
|
||||
err := f.ExpressionsToRegisters(parameters, registers, fn.Input, true)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
if exists {
|
||||
imp.Used = true
|
||||
} else {
|
||||
_, exists := f.All.Extern[leftText]
|
||||
|
||||
value := &eval.Label{
|
||||
Typ: types.AnyPointer,
|
||||
Label: function.UniqueName,
|
||||
if !exists {
|
||||
return nil, errors.New(&errors.UnknownPackage{Name: leftText}, f.File, left.Token.Position)
|
||||
}
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
return nil, errors.New(&errors.UnknownIdentifier{Name: uniqueName}, f.File, left.Token.Position)
|
||||
value := &eval.Label{
|
||||
Typ: types.AnyPointer,
|
||||
Label: fmt.Sprintf("%s.%s", leftText, rightText),
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
Reference in New Issue
Block a user