func evalChain(p ast.Position, scope *stateful.Scope, stck *stack) error { r := stck.Pop() l := stck.Pop() // Resolve identifier if left, ok := l.(*ast.IdentifierNode); ok { var err error l, err = scope.Get(left.Ident) if err != nil { return err } } switch right := r.(type) { case unboundFunc: ret, err := right(l) if err != nil { return err } stck.Push(ret) case *ast.IdentifierNode: name := right.Ident //Lookup field by name of left object var describer SelfDescriber if d, ok := l.(SelfDescriber); ok { describer = d } else { var err error var extraChainMethods map[string]reflect.Value if pd, ok := l.(PartialDescriber); ok { extraChainMethods = pd.ChainMethods() } describer, err = NewReflectionDescriber(l, extraChainMethods) if err != nil { return wrapError(p, err) } } if describer.HasProperty(name) { stck.Push(describer.Property(name)) } else { return errorf(p, "object %T has no property %s", l, name) } default: return errorf(p, "invalid right operand of type %T to '.' operator", r) } return nil }
func evalDeclaration(node *ast.DeclarationNode, scope *stateful.Scope, stck *stack, predefinedVars, defaultVars map[string]Var) error { name := node.Left.Ident if v, _ := scope.Get(name); v != nil { return fmt.Errorf("attempted to redefine %s, vars are immutable", name) } value := stck.Pop() if i, ok := value.(*ast.IdentifierNode); ok { // Resolve identifier v, err := scope.Get(i.Ident) if err != nil { return err } value = v } actualType := ast.TypeOf(value) // Populate set of default vars if actualType != ast.InvalidType { desc := "" if node.Comment != nil { desc = node.Comment.CommentString() } v, err := convertValueToVar(value, actualType, desc) if err != nil { return err } defaultVars[name] = v } // Populate scope, first check for predefined var if predefinedValue, ok := predefinedVars[name]; ok { if predefinedValue.Type != actualType { return fmt.Errorf("invalid type supplied for %s, got %v exp %v", name, predefinedValue.Type, actualType) } v, err := convertVarToValue(Var{Value: predefinedValue.Value, Type: actualType}) if err != nil { return err } value = v } scope.Set(name, value) return nil }
// Resolve all identifiers immediately in the tree with their value from the scope. // This operation is performed in place. // Panics if the scope value does not exist or if the value cannot be expressed as a literal. func resolveIdents(n ast.Node, scope *stateful.Scope) (_ ast.Node, err error) { switch node := n.(type) { case *ast.IdentifierNode: v, err := scope.Get(node.Ident) if err != nil { return nil, err } lit, err := ast.ValueToLiteralNode(node, v) if err != nil { return nil, err } return lit, nil case *ast.UnaryNode: node.Node, err = resolveIdents(node.Node, scope) if err != nil { return nil, err } case *ast.BinaryNode: node.Left, err = resolveIdents(node.Left, scope) if err != nil { return nil, err } node.Right, err = resolveIdents(node.Right, scope) case *ast.FunctionNode: for i, arg := range node.Args { node.Args[i], err = resolveIdents(arg, scope) if err != nil { return nil, err } } case *ast.ProgramNode: for i, n := range node.Nodes { node.Nodes[i], err = resolveIdents(n, scope) if err != nil { return nil, err } } } return n, nil }
func evalUnary(p ast.Position, op ast.TokenType, scope *stateful.Scope, stck *stack) error { v := stck.Pop() switch op { case ast.TokenMinus: if ident, ok := v.(*ast.IdentifierNode); ok { value, err := scope.Get(ident.Ident) if err != nil { return err } v = value } switch num := v.(type) { case float64: stck.Push(-1 * num) case int64: stck.Push(-1 * num) case time.Duration: stck.Push(-1 * num) default: return errorf(p, "invalid arugument to '-' %v", v) } case ast.TokenNot: if ident, ok := v.(*ast.IdentifierNode); ok { value, err := scope.Get(ident.Ident) if err != nil { return err } v = value } if b, ok := v.(bool); ok { stck.Push(!b) } else { return errorf(p, "invalid arugument to '!' %v", v) } } return nil }
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 }
// Evaluate a node using a stack machine in a given scope func eval(n ast.Node, scope *stateful.Scope, stck *stack, predefinedVars, defaultVars map[string]Var, ignoreMissingVars bool) (err error) { switch node := n.(type) { case *ast.BoolNode: stck.Push(node.Bool) case *ast.NumberNode: if node.IsInt { stck.Push(node.Int64) } else { stck.Push(node.Float64) } case *ast.DurationNode: stck.Push(node.Dur) case *ast.StringNode: stck.Push(node.Literal) case *ast.RegexNode: stck.Push(node.Regex) case *ast.UnaryNode: err = eval(node.Node, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalUnary(node, node.Operator, scope, stck) if err != nil { return } case *ast.BinaryNode: // Switch over to using the stateful expressions for evaluating a BinaryNode n, err := resolveIdents(node, scope) if err != nil { return err } expr, err := stateful.NewExpression(n) if err != nil { return err } value, err := expr.Eval(stateful.NewScope()) if err != nil { return err } stck.Push(value) case *ast.LambdaNode: node.Expression, err = resolveIdents(node.Expression, scope) if err != nil { return } stck.Push(node) case *ast.ListNode: nodes := make([]interface{}, len(node.Nodes)) for i, n := range node.Nodes { err = eval(n, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } a := stck.Pop() switch typed := a.(type) { case *ast.IdentifierNode: // Resolve identifier a, err = scope.Get(typed.Ident) if err != nil { return err } } nodes[i] = a } stck.Push(nodes) case *ast.TypeDeclarationNode: err = evalTypeDeclaration(node, scope, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } case *ast.DeclarationNode: err = eval(node.Right, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalDeclaration(node, scope, stck, predefinedVars, defaultVars) if err != nil { return } case *ast.ChainNode: err = eval(node.Left, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = eval(node.Right, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } err = evalChain(node, scope, stck) if err != nil { return } case *ast.FunctionNode: args := make([]interface{}, len(node.Args)) for i, arg := range node.Args { err = eval(arg, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } a := stck.Pop() switch typed := a.(type) { case *ast.IdentifierNode: // Resolve identifier a, err = scope.Get(typed.Ident) if err != nil { return err } case unboundFunc: // Call global func a, err = typed(nil) if err != nil { return err } } args[i] = a } err = evalFunc(node, scope, stck, args) if err != nil { return } case *ast.ProgramNode: for _, n := range node.Nodes { err = eval(n, scope, stck, predefinedVars, defaultVars, ignoreMissingVars) if err != nil { return } // Pop unused result if stck.Len() > 0 { ret := stck.Pop() if f, ok := ret.(unboundFunc); ok { // Call global function _, err := f(nil) if err != nil { return err } } } } default: stck.Push(node) } return nil }