func evalFunc(f *ast.FunctionNode, scope *stateful.Scope, stck *stack, args []interface{}) error { // If the first and only arg is a list use it as the list of args if len(args) == 1 { if a, ok := args[0].([]interface{}); ok { args = a } } rec := func(obj interface{}, errp *error) { e := recover() if e != nil { *errp = fmt.Errorf("line %d char %d: error calling func %q on obj %T: %v", f.Line(), f.Char(), f.Func, obj, e) if strings.Contains((*errp).Error(), "*ast.ReferenceNode") && strings.Contains((*errp).Error(), "type string") { *errp = fmt.Errorf("line %d char %d: cannot assign *ast.ReferenceNode to type string, did you use double quotes instead of single quotes?", f.Line(), f.Char()) } } } fnc := unboundFunc(func(obj interface{}) (_ interface{}, err error) { //Setup recover method if there is a panic during the method call defer rec(obj, &err) if f.Type == ast.GlobalFunc { if obj != nil { return nil, fmt.Errorf("line %d char %d: calling global function on object %T", f.Line(), f.Char(), obj) } // Object is nil, check for func in scope fnc, _ := scope.Get(f.Func) if fnc == nil { return nil, fmt.Errorf("line %d char %d: no global function %q defined", f.Line(), f.Char(), f.Func) } method := reflect.ValueOf(fnc) o, err := callMethodReflection(method, args) return o, wrapError(f, err) } // Get SelfDescriber name := f.Func var describer SelfDescriber if d, ok := obj.(SelfDescriber); ok { describer = d } else { var err error var extraChainMethods map[string]reflect.Value if pd, ok := obj.(PartialDescriber); ok { extraChainMethods = pd.ChainMethods() } describer, err = NewReflectionDescriber(obj, extraChainMethods) if err != nil { return nil, wrapError(f, err) } } // Call correct type of function switch f.Type { case ast.ChainFunc: if describer.HasChainMethod(name) { o, err := describer.CallChainMethod(name, args...) return o, wrapError(f, err) } if describer.HasProperty(name) { return nil, errorf(f, "no chaining method %q on %T, but property does exist. Use '.' operator instead: 'node.%s(..)'.", name, obj, name) } if dm := scope.DynamicMethod(name); dm != nil { return nil, errorf(f, "no chaining method %q on %T, but dynamic method does exist. Use '@' operator instead: 'node@%s(..)'.", name, obj, name) } case ast.PropertyFunc: if describer.HasProperty(name) { o, err := describer.SetProperty(name, args...) return o, wrapError(f, err) } if describer.HasChainMethod(name) { return nil, errorf(f, "no property method %q on %T, but chaining method does exist. Use '|' operator instead: 'node|%s(..)'.", name, obj, name) } if dm := scope.DynamicMethod(name); dm != nil { return nil, errorf(f, "no property method %q on %T, but dynamic method does exist. Use '@' operator instead: 'node@%s(..)'.", name, obj, name) } case ast.DynamicFunc: // Check for dynamic method. if dm := scope.DynamicMethod(name); dm != nil { ret, err := dm(obj, args...) if err != nil { return nil, err } return ret, nil } if describer.HasProperty(name) { return nil, errorf(f, "no dynamic method %q on %T, but property does exist. Use '.' operator instead: 'node.%s(..)'.", name, obj, name) } if describer.HasChainMethod(name) { return nil, errorf(f, "no dynamic method %q on %T, but chaining method does exist. Use '|' operator instead: 'node|%s(..)'.", name, obj, name) } default: return nil, errorf(f, "unknown function type %v on function %T.%s", f.Type, obj, name) } // Ran out of options... return nil, errorf(f, "no method or property %q on %T", name, obj) }) stck.Push(fnc) return nil }