Exemplo n.º 1
0
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
}