visitor.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package exprhelpers
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "github.com/antonmedv/expr/parser"
  7. "github.com/sirupsen/logrus"
  8. log "github.com/sirupsen/logrus"
  9. "github.com/antonmedv/expr"
  10. "github.com/antonmedv/expr/ast"
  11. "github.com/antonmedv/expr/vm"
  12. )
  13. /*
  14. Visitor is used to reconstruct variables with its property called in an expr filter
  15. Thus, we can debug expr filter by displaying all variables contents present in the filter
  16. */
  17. type visitor struct {
  18. newVar bool
  19. currentID string
  20. properties []string
  21. vars []string
  22. }
  23. /*
  24. Enter should be present for the interface but is never used
  25. */
  26. func (v *visitor) Enter(node *ast.Node) {}
  27. /*
  28. Exit is called when running ast.Walk(node, visitor), each time a node exit.
  29. So we have the node information and we can get the identifier (first level of the struct)
  30. and its properties to reconstruct the complete variable.
  31. */
  32. func (v *visitor) Exit(node *ast.Node) {
  33. if n, ok := (*node).(*ast.IdentifierNode); ok {
  34. if !v.newVar {
  35. v.newVar = true
  36. v.currentID = n.Value
  37. } else {
  38. fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
  39. v.vars = append(v.vars, fullVar)
  40. v.properties = []string{}
  41. v.currentID = n.Value
  42. }
  43. } else if n, ok := (*node).(*ast.PropertyNode); ok {
  44. v.properties = append(v.properties, n.Property)
  45. }
  46. }
  47. /*
  48. Build reconstruct all the variables used in a filter (to display their content later).
  49. */
  50. func (v *visitor) Build(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
  51. var expressions []*expression
  52. ret := &ExprDebugger{
  53. filter: filter,
  54. }
  55. if filter == "" {
  56. log.Debugf("unable to create expr debugger with empty filter")
  57. return &ExprDebugger{}, nil
  58. }
  59. v.newVar = false
  60. tree, err := parser.Parse(filter)
  61. if err != nil {
  62. return nil, err
  63. }
  64. ast.Walk(&tree.Node, v)
  65. if v.currentID != "" && len(v.properties) > 0 { // if its a variable with property (eg. evt.Line.Labels)
  66. fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
  67. v.vars = append(v.vars, fullVar)
  68. } else if v.currentID != "" && len(v.properties) == 0 { // if it's a variable without property
  69. fullVar := v.currentID
  70. v.vars = append(v.vars, fullVar)
  71. } else {
  72. log.Debugf("no variable in filter : '%s'", filter)
  73. }
  74. v.properties = []string{}
  75. v.currentID = ""
  76. for _, variable := range v.vars {
  77. debugFilter, err := expr.Compile(variable, exprEnv)
  78. if err != nil {
  79. return ret, fmt.Errorf("compilation of variable '%s' failed: %v", variable, err)
  80. }
  81. tmpExpression := &expression{
  82. variable,
  83. debugFilter,
  84. }
  85. expressions = append(expressions, tmpExpression)
  86. }
  87. ret.expression = expressions
  88. return ret, nil
  89. }
  90. // ExprDebugger contains the list of expression to be run when debugging an expression filter
  91. type ExprDebugger struct {
  92. filter string
  93. expression []*expression
  94. }
  95. // expression is the structure that represents the variable in string and compiled format
  96. type expression struct {
  97. Str string
  98. Compiled *vm.Program
  99. }
  100. /*
  101. Run display the content of each variable of a filter by evaluating them with expr,
  102. again the expr environment given in parameter
  103. */
  104. func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) {
  105. if len(e.expression) == 0 {
  106. logger.Tracef("no variable to eval for filter '%s'", e.filter)
  107. return
  108. }
  109. logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult)))
  110. logger.Debugf("eval variables:")
  111. for _, expression := range e.expression {
  112. debug, err := expr.Run(expression.Compiled, exprEnv)
  113. if err != nil {
  114. logger.Errorf("unable to print debug expression for '%s': %s", expression.Str, err)
  115. }
  116. logger.Debugf(" %s = '%v'", expression.Str, debug)
  117. }
  118. }
  119. // NewDebugger is the exported function that build the debuggers expressions
  120. func NewDebugger(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
  121. visitor := &visitor{}
  122. exprDebugger, err := visitor.Build(filter, exprEnv)
  123. return exprDebugger, err
  124. }