Example #1
0
func evalTypeDeclaration(node *ast.TypeDeclarationNode, scope *stateful.Scope, predefinedVars, defaultVars map[string]Var, ignoreMissingVars bool) error {
	var actualType ast.ValueType
	switch node.Type.Ident {
	case "int":
		actualType = ast.TInt
	case "float":
		actualType = ast.TFloat
	case "bool":
		actualType = ast.TBool
	case "string":
		actualType = ast.TString
	case "regex":
		actualType = ast.TRegex
	case "duration":
		actualType = ast.TDuration
	case "lambda":
		actualType = ast.TLambda
	case "list":
		actualType = ast.TList
	case "star":
		actualType = ast.TStar
	default:
		return fmt.Errorf("invalid var type %q", node.Type.Ident)
	}
	name := node.Node.Ident
	desc := ""
	if node.Comment != nil {
		desc = node.Comment.CommentString()
	}
	defaultVars[name] = Var{
		Type:        actualType,
		Value:       nil,
		Description: desc,
	}

	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
		}
		scope.Set(name, v)
	} else if ignoreMissingVars {
		// Set zero value on scope, so execution can continue
		scope.Set(name, ast.ZeroValue(actualType))
	} else {
		return fmt.Errorf("missing value for var %q.", name)
	}

	return nil
}
Example #2
0
// fillScope - given a scope and reference variables, we fill the exact variables from the now, fields and tags.
func fillScope(vars *stateful.Scope, referenceVariables []string, now time.Time, fields models.Fields, tags models.Tags) error {
	for _, refVariableName := range referenceVariables {
		if refVariableName == "time" {
			vars.Set("time", now.Local())
			continue
		}

		// Support the error with tags/fields collision
		var fieldValue interface{}
		var isFieldExists bool
		var tagValue interface{}
		var isTagExists bool

		if fieldValue, isFieldExists = fields[refVariableName]; isFieldExists {
			vars.Set(refVariableName, fieldValue)
		}

		if tagValue, isTagExists = tags[refVariableName]; isTagExists {
			if isFieldExists {
				return fmt.Errorf("cannot have field and tags with same name %q", refVariableName)
			}
			vars.Set(refVariableName, tagValue)
		}
		if !isFieldExists && !isTagExists {
			if !vars.Has(refVariableName) {
				return fmt.Errorf("no field or tag exists for %s", refVariableName)
			}
		}
	}

	return nil
}
Example #3
0
func createPipelineAndVars(
	script string,
	sourceEdge EdgeType,
	scope *stateful.Scope,
	deadman DeadmanService,
	predefinedVars map[string]tick.Var,
	ignoreMissingVars bool,
) (*Pipeline, map[string]tick.Var, error) {
	p := &Pipeline{
		deadman: deadman,
	}
	var src Node
	switch sourceEdge {
	case StreamEdge:
		src = newStreamNode()
		scope.Set("stream", src)
	case BatchEdge:
		src = newBatchNode()
		scope.Set("batch", src)
	default:
		return nil, nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
	}
	p.addSource(src)

	vars, err := tick.Evaluate(script, scope, predefinedVars, ignoreMissingVars)
	if err != nil {
		return nil, nil, err
	}
	if deadman.Global() {
		switch s := src.(type) {
		case *StreamNode:
			s.Deadman(deadman.Threshold(), deadman.Interval())
		case *BatchNode:
			s.Deadman(deadman.Threshold(), deadman.Interval())
		default:
			return nil, nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
		}
	}
	if err = p.Walk(
		func(n Node) error {
			return n.validate()
		}); err != nil {
		return nil, nil, err
	}
	return p, vars, nil
}
Example #4
0
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
}
Example #5
0
// 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
}
Example #6
0
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
}
Example #7
0
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
}
Example #8
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
}
Example #9
0
// 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
}