Files
aego/script/checker/call.go
T

352 lines
9.5 KiB
Go

package checker
import (
"math"
"strconv"
"aego/script/abi"
"aego/script/ast"
"aego/script/diag"
"aego/script/resolver"
"aego/script/source"
"aego/script/types"
)
func (c *checker) args(list []ast.Expr) {
for _, a := range list {
c.expr(a)
}
}
func (c *checker) noTypeArgs(e *ast.CallExpr) {
if len(e.TypeArgs) > 0 {
c.errorf(diag.BadTypeArg, e.TypeArgs[0].Span(), "type arguments are only allowed on Get and Has")
}
}
func (c *checker) argList(sp source.Span, args []ast.Expr, name string, params []*types.Type) {
if len(args) != len(params) {
c.errorf(diag.ArgCount, sp, "%s expects %d arguments, got %d", name, len(params), len(args))
c.args(args)
return
}
for i, a := range args {
c.assign(a, c.value(a), params[i], "argument to "+name)
}
}
func (c *checker) arguments(e *ast.CallExpr, name string, params []*types.Type) {
c.argList(e.Sp, e.Args, name, params)
}
func (c *checker) abiTypes(list []abi.TypeRef) []*types.Type {
out := make([]*types.Type, len(list))
for i, r := range list {
out[i] = c.u.FromABI(r, c.surface)
}
return out
}
func (c *checker) call(e *ast.CallExpr) operand {
switch fun := e.Fun.(type) {
case *ast.Ident:
obj := c.lookup(fun.Name)
if obj == nil {
c.errorf(diag.UndefinedName, fun.Sp, "undefined: %s", fun.Name)
c.args(e.Args)
return invalid
}
c.use(fun, obj)
return c.callObject(e, fun.Sp, obj)
case *ast.SelectorExpr:
if p := c.pkgOf(fun.X); p != nil {
return c.callPackage(e, fun, p)
}
x := c.expr(fun.X)
switch {
case x.mode == mSignal:
return c.callSignal(e, fun, x)
case x.mode != mValue:
case x.t.Kind == types.KNodeRef, x.t.Kind == types.KAssetRef:
return c.callMethod(e, fun, x)
case x.t.Kind == types.KScriptRef:
return c.callScriptMethod(e, fun, x)
case x.t.Kind == types.KComponentRef:
return c.callComponentMethod(e, fun, x)
case x.t.Kind == types.KCoroutine, x.t.Kind == types.KConnection:
return c.callHandle(e, fun, x)
}
if x.mode != mInvalid {
c.errorf(diag.NotCallable, fun.Sel.Sp, "%s is not a function", fun.Sel.Name)
}
c.args(e.Args)
return invalid
}
if x := c.expr(e.Fun); x.mode != mInvalid {
c.errorf(diag.NotCallable, e.Fun.Span(), "expression is not callable")
}
c.args(e.Args)
return invalid
}
func (c *checker) callObject(e *ast.CallExpr, sp source.Span, obj *resolver.Object) operand {
switch obj.Kind {
case resolver.ObjBuiltin:
return c.builtin(e, obj.Name)
case resolver.ObjType:
return c.conversion(e, obj.Type)
case resolver.ObjFunc:
f, ok := obj.Data.(*Func)
if !ok {
c.args(e.Args)
return invalid
}
kind := CallFunc
if f.Script != nil {
kind = CallMethod
}
return c.callFunc(e, f, kind)
}
if x := c.objectOperand(sp, obj); x.mode != mInvalid {
c.errorf(diag.NotCallable, sp, "%s is not a function", obj.Name)
}
c.args(e.Args)
return invalid
}
func (c *checker) callFunc(e *ast.CallExpr, f *Func, kind CallKind) operand {
c.noTypeArgs(e)
if f.Decl.Coroutine {
c.errorf(diag.CoroutineCall, e.Sp, "coroutine %s is called without start, write start %s(...)", f.Name, f.Name)
c.args(e.Args)
return invalid
}
if f.Script != nil && f.Script.IsEvent(f) {
c.errorf(diag.EventCall, e.Sp, "%s is an engine event handler and only the engine calls it, move the shared code into a plain method", f.Name)
c.args(e.Args)
return invalid
}
c.arguments(e, f.Name, funcParams(f))
c.a.Calls[e] = &Call{Kind: kind, Func: f}
return result(f.Result)
}
func (c *checker) callPackage(e *ast.CallExpr, sel *ast.SelectorExpr, p *resolver.Object) operand {
name := sel.Sel.Name
if p.Target == "" {
c.args(e.Args)
return invalid
}
if !p.Host {
obj := c.member(sel.Sel, p)
if obj == nil {
c.args(e.Args)
return invalid
}
c.a.Selections[sel] = &Selection{Kind: SelPackage, Object: obj}
return c.callObject(e, sel.Sel.Sp, obj)
}
if sig, ok := c.surface.Func(p.Target, name); ok {
c.noTypeArgs(e)
c.arguments(e, p.Name+"."+name, c.abiTypes(sig.Params))
c.a.Calls[e] = &Call{Kind: CallHost, Module: p.Target, Name: name, Sig: sig}
return result(c.u.FromABI(sig.Result, c.surface))
}
if en, ok := c.surface.Enum(p.Target, name); ok {
return c.conversion(e, c.u.HostEnum(en))
}
c.errorf(diag.UnknownHost, sel.Sel.Sp, "%s has no function %s", p.Target, name)
c.args(e.Args)
return invalid
}
func (c *checker) callMethod(e *ast.CallExpr, sel *ast.SelectorExpr, recv operand) operand {
name := sel.Sel.Name
if recv.t.Kind == types.KNodeRef {
switch name {
case "Get", "Has":
return c.intrinsic(e, sel, name)
case "GetScript":
return c.getScript(e, sel)
}
}
c.noTypeArgs(e)
sig, ok := c.surface.Method(types.ToABI(recv.t).Kind, name)
if !ok {
c.errorf(diag.UnknownHost, sel.Sel.Sp, "%s has no method %s", recv.t, name)
c.args(e.Args)
return invalid
}
c.arguments(e, name, c.abiTypes(sig.Params))
c.a.Calls[e] = &Call{Kind: CallHostMethod, Name: name, Sig: sig, Recv: sel.X}
return result(c.u.FromABI(sig.Result, c.surface))
}
func (c *checker) callComponentMethod(e *ast.CallExpr, sel *ast.SelectorExpr, recv operand) operand {
comp := recv.t.Component
name := sel.Sel.Name
c.noTypeArgs(e)
sig, ok := c.surface.ComponentMethod(comp.Name, name)
if !ok {
if _, field := comp.Field(name); field {
c.errorf(diag.NotCallable, sel.Sel.Sp, "%s is a field of %s, not a method", name, comp.Short)
} else {
c.errorf(diag.UnknownHost, sel.Sel.Sp, "%s has no method %s", comp.Short, name)
}
c.args(e.Args)
return invalid
}
c.arguments(e, comp.Short+"."+name, c.abiTypes(sig.Params))
c.a.Calls[e] = &Call{Kind: CallComponentMethod, Name: name, Sig: sig, Component: comp, Recv: sel.X}
return result(c.u.FromABI(sig.Result, c.surface))
}
func (c *checker) intrinsic(e *ast.CallExpr, sel *ast.SelectorExpr, name string) operand {
if len(e.TypeArgs) != 1 {
c.errorf(diag.BadTypeArg, e.Sp, "%s needs one component type argument, like %s<SpriteRenderer>()", name, name)
c.args(e.Args)
return invalid
}
if len(e.Args) != 0 {
c.errorf(diag.ArgCount, e.Sp, "%s takes no arguments", name)
c.args(e.Args)
}
nt, ok := e.TypeArgs[0].(*ast.NamedType)
if !ok {
c.errorf(diag.BadTypeArg, e.TypeArgs[0].Span(), "%s argument must be a component name", name)
return invalid
}
comp := c.component(nt)
if comp == nil {
return invalid
}
if name == "Has" {
c.a.Calls[e] = &Call{Kind: CallHasComponent, Component: comp, Recv: sel.X}
return val(types.Bool)
}
c.a.Calls[e] = &Call{Kind: CallGetComponent, Component: comp, Recv: sel.X}
return val(c.u.ComponentRef(comp))
}
func (c *checker) builtin(e *ast.CallExpr, name string) operand {
c.noTypeArgs(e)
switch name {
case "len":
if len(e.Args) != 1 {
c.errorf(diag.ArgCount, e.Sp, "len expects 1 argument, got %d", len(e.Args))
c.args(e.Args)
return invalid
}
x := c.value(e.Args[0])
switch {
case x.mode == mInvalid:
return invalid
case x.t.Kind == types.KSlice:
case x.t == types.String:
if x.c != nil {
return operand{mode: mValue, t: types.Int, c: types.IntConst(int64(len(x.c.Str)))}
}
default:
c.errorf(diag.InvalidOperation, e.Args[0].Span(), "len is not defined on %s", x.t)
return invalid
}
c.a.Calls[e] = &Call{Kind: CallLen}
return val(types.Int)
case "append":
if len(e.Args) < 2 {
c.errorf(diag.ArgCount, e.Sp, "append needs a slice and at least one value")
c.args(e.Args)
return invalid
}
s := c.value(e.Args[0])
if s.mode == mInvalid {
c.args(e.Args[1:])
return invalid
}
if s.t.Kind != types.KSlice {
c.errorf(diag.InvalidOperation, e.Args[0].Span(), "first argument to append must be a slice, not %s", s.t)
c.args(e.Args[1:])
return invalid
}
for _, a := range e.Args[1:] {
c.assign(a, c.value(a), s.t.Elem, "append")
}
c.a.Calls[e] = &Call{Kind: CallAppend, Type: s.t}
return val(s.t)
}
return invalid
}
func (c *checker) conversion(e *ast.CallExpr, to *types.Type) operand {
c.noTypeArgs(e)
if len(e.Args) != 1 {
c.errorf(diag.ArgCount, e.Sp, "conversion to %s needs exactly one argument", to)
c.args(e.Args)
return invalid
}
x := c.value(e.Args[0])
if x.mode == mInvalid {
return val(to)
}
from := x.t
ok := false
switch to.Kind {
case types.KInt:
ok = from.IsNumeric() || from.Kind == types.KEnum
case types.KFloat:
ok = from.IsNumeric()
case types.KString:
ok = from.IsNumeric() || from == types.Bool || from == types.String
case types.KEnum:
ok = from.IsInteger() || from == to
default:
ok = from == to
}
if !ok {
c.errorf(diag.BadConversion, e.Sp, "cannot convert %s to %s", from, to)
return invalid
}
if from.IsUntyped() {
c.record(e.Args[0], operand{mode: mValue, t: types.Default(from), c: x.c})
}
c.a.Calls[e] = &Call{Kind: CallConvert, Type: to}
if x.c != nil {
if k := convertConst(x.c, to); k != nil {
return operand{mode: mValue, t: to, c: k}
}
}
return val(to)
}
func convertConst(k *types.Const, to *types.Type) *types.Const {
switch to.Kind {
case types.KInt, types.KEnum:
switch k.Kind {
case types.CInt:
return k
case types.CFloat:
if k.Float >= -9.223372036854776e18 && k.Float < 9.223372036854776e18 {
return types.IntConst(int64(k.Float))
}
}
case types.KFloat:
if k.Kind == types.CInt || k.Kind == types.CFloat {
if v := k.Float64(); math.Abs(v) <= math.MaxFloat32 {
return types.FloatConst(float64(float32(v)))
}
}
case types.KString:
switch k.Kind {
case types.CInt:
return types.StringConst(strconv.FormatInt(k.Int, 10))
case types.CFloat:
return types.StringConst(strconv.FormatFloat(k.Float, 'g', -1, 32))
case types.CBool:
return types.StringConst(strconv.FormatBool(k.Bool))
case types.CString:
return k
}
}
return nil
}