q/src/core/CompileCall.go

108 lines
2.6 KiB
Go

package core
import (
"fmt"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/expression"
"git.akyoto.dev/cli/q/src/types"
"git.akyoto.dev/cli/q/src/x64"
)
// CompileCall executes a function call.
// All call registers must hold the correct parameter values before the function invocation.
// 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) (*Function, error) {
var (
pkg = f.Package
nameNode = root.Children[0]
fn *Function
name string
exists bool
)
if nameNode.IsLeaf() {
name = nameNode.Token.Text(f.File.Bytes)
switch name {
case "syscall":
return nil, f.CompileSyscall(root)
case "new":
return &Function{
Output: []*Output{
{
Type: &types.Pointer{
To: f.Types[root.Children[1].Token.Text(f.File.Bytes)],
},
},
},
}, f.CompileNew(root)
case "delete":
return nil, f.CompileDelete(root)
case "store":
return nil, f.CompileMemoryStore(root)
}
} else {
pkg = nameNode.Children[0].Token.Text(f.File.Bytes)
name = nameNode.Children[1].Token.Text(f.File.Bytes)
}
if pkg == "kernel32" || pkg == "user32" || pkg == "gdi32" || pkg == "comctl32" {
parameters := root.Children[1:]
registers := x64.WindowsInputRegisters[:len(parameters)]
for i := len(parameters) - 1; i >= 0; i-- {
_, err := f.ExpressionToRegister(parameters[i], registers[i])
if err != nil {
return nil, err
}
}
f.DLLs = f.DLLs.Append(pkg, name)
f.DLLCall(fmt.Sprintf("%s.%s", pkg, name))
return nil, nil
} else 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
}
fn, exists = f.Functions[pkg+"."+name]
if !exists {
return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position)
}
parameters := root.Children[1:]
registers := f.CPU.Input[:len(parameters)]
for i := len(parameters) - 1; i >= 0; i-- {
typ, err := f.ExpressionToRegister(parameters[i], registers[i])
if err != nil {
return nil, err
}
if !types.Is(typ, fn.Input[i].Type) {
return nil, errors.New(&errors.TypeMismatch{
Encountered: typ.Name(),
Expected: fn.Input[i].Type.Name(),
ParameterName: fn.Input[i].Name,
}, f.File, parameters[i].Token.Position)
}
}
f.CallSafe(fn, registers)
return fn, nil
}