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) {
|
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{
|
all := core.Environment{
|
||||||
Files: make([]*fs.File, 0, 8),
|
Files: make([]*fs.File, 0, 8),
|
||||||
|
Extern: make(map[string]struct{}, 0),
|
||||||
Functions: make(map[string]*core.Function, 32),
|
Functions: make(map[string]*core.Function, 32),
|
||||||
Structs: make(map[string]*types.Struct, 8),
|
Structs: make(map[string]*types.Struct, 8),
|
||||||
Constants: make(map[string]*core.Constant, 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
|
function.All = &all
|
||||||
all.Functions[function.UniqueName] = function
|
all.Functions[function.UniqueName] = function
|
||||||
|
|
||||||
|
if function.IsExtern() {
|
||||||
|
all.Extern[function.Package] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
case structure, ok := <-structs:
|
case structure, ok := <-structs:
|
||||||
if !ok {
|
if !ok {
|
||||||
structs = nil
|
structs = nil
|
||||||
|
@ -3,7 +3,9 @@ package core
|
|||||||
import (
|
import (
|
||||||
"git.urbach.dev/cli/q/src/asm"
|
"git.urbach.dev/cli/q/src/asm"
|
||||||
"git.urbach.dev/cli/q/src/errors"
|
"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/expression"
|
||||||
|
"git.urbach.dev/cli/q/src/token"
|
||||||
"git.urbach.dev/cli/q/src/types"
|
"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.
|
// 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.
|
// After the function call, they must be restored in reverse order.
|
||||||
func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) {
|
func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) {
|
||||||
var (
|
if root.Children[0].Token.Kind == token.Identifier {
|
||||||
pkg = f.Package
|
name := root.Children[0].Token.Text(f.File.Bytes)
|
||||||
pkgNode *expression.Expression
|
|
||||||
name string
|
|
||||||
nameNode = root.Children[0]
|
|
||||||
fn *Function
|
|
||||||
exists bool
|
|
||||||
)
|
|
||||||
|
|
||||||
if nameNode.IsLeaf() {
|
|
||||||
name = nameNode.Token.Text(f.File.Bytes)
|
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case "len":
|
case "len":
|
||||||
@ -37,63 +30,56 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error
|
|||||||
case "store":
|
case "store":
|
||||||
return nil, f.CompileMemoryStore(root)
|
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])
|
||||||
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)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.CallSafe(fn, registers)
|
parameters := root.Children[1:]
|
||||||
return fn.OutputTypes, nil
|
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.
|
// Environment holds information about the entire build.
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
Constants map[string]*Constant
|
Constants map[string]*Constant
|
||||||
|
Extern map[string]struct{}
|
||||||
Functions map[string]*Function
|
Functions map[string]*Function
|
||||||
Structs map[string]*types.Struct
|
Structs map[string]*types.Struct
|
||||||
Files []*fs.File
|
Files []*fs.File
|
||||||
|
@ -59,19 +59,24 @@ func (f *Function) EvaluateDot(expr *expression.Expression) (eval.Value, error)
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uniqueName := fmt.Sprintf("%s.%s", leftText, rightText)
|
if leftText != "main" {
|
||||||
function, exists := f.All.Functions[uniqueName]
|
imp, exists := f.File.Imports[leftText]
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
f.File.Imports[leftText].Used = true
|
imp.Used = true
|
||||||
|
} else {
|
||||||
|
_, exists := f.All.Extern[leftText]
|
||||||
|
|
||||||
value := &eval.Label{
|
if !exists {
|
||||||
Typ: types.AnyPointer,
|
return nil, errors.New(&errors.UnknownPackage{Name: leftText}, f.File, left.Token.Position)
|
||||||
Label: function.UniqueName,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
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},
|
{"MissingBlockEnd.q", errors.MissingBlockEnd},
|
||||||
{"MissingBlockEnd2.q", errors.MissingBlockEnd},
|
{"MissingBlockEnd2.q", errors.MissingBlockEnd},
|
||||||
{"MissingBlockStart.q", errors.MissingBlockStart},
|
{"MissingBlockStart.q", errors.MissingBlockStart},
|
||||||
|
{"MissingBlockStart2.q", errors.MissingBlockStart},
|
||||||
{"MissingExpression.q", errors.MissingExpression},
|
{"MissingExpression.q", errors.MissingExpression},
|
||||||
{"MissingGroupEnd.q", errors.MissingGroupEnd},
|
{"MissingGroupEnd.q", errors.MissingGroupEnd},
|
||||||
{"MissingGroupStart.q", errors.MissingGroupStart},
|
{"MissingGroupStart.q", errors.MissingGroupStart},
|
||||||
@ -52,11 +53,11 @@ var errs = []struct {
|
|||||||
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
||||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*any", Encountered: "int", ParameterName: "p"}},
|
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*any", Encountered: "int", ParameterName: "p"}},
|
||||||
{"TypeMismatch2.q", &errors.TypeMismatch{Expected: "[]any", Encountered: "int", ParameterName: "array"}},
|
{"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"}},
|
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||||
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},
|
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||||
{"UnknownIdentifier3.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"}},
|
{"UnknownPackage.q", &errors.UnknownPackage{Name: "sys"}},
|
||||||
{"UnknownType.q", &errors.UnknownType{Name: "unknown"}},
|
{"UnknownType.q", &errors.UnknownType{Name: "unknown"}},
|
||||||
{"UnknownType2.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},
|
{"branch-save", 0},
|
||||||
{"jump-near", 0},
|
{"jump-near", 0},
|
||||||
{"switch", 0},
|
{"switch", 0},
|
||||||
{"loop-infinite", 0},
|
{"loop", 0},
|
||||||
{"loop-lifetime", 0},
|
{"loop-lifetime", 0},
|
||||||
|
{"loop-in-loop", 0},
|
||||||
{"for", 0},
|
{"for", 0},
|
||||||
{"memory-free", 0},
|
{"memory-free", 0},
|
||||||
{"out-of-memory", 0},
|
{"out-of-memory", 0},
|
||||||
@ -69,6 +70,7 @@ var programs = []struct {
|
|||||||
{"len", 0},
|
{"len", 0},
|
||||||
{"cast", 0},
|
{"cast", 0},
|
||||||
{"function-pointer", 0},
|
{"function-pointer", 0},
|
||||||
|
{"function-pointer-field", 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrograms(t *testing.T) {
|
func TestPrograms(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user