visitor.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. v.newVar = false
  39. fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
  40. v.vars = append(v.vars, fullVar)
  41. v.properties = []string{}
  42. v.currentID = n.Value
  43. }
  44. } else if n, ok := (*node).(*ast.PropertyNode); ok {
  45. v.properties = append(v.properties, n.Property)
  46. }
  47. }
  48. /*
  49. Build reconstruct all the variables used in a filter (to display their content later).
  50. */
  51. func (v *visitor) Build(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
  52. var expressions []*expression
  53. ret := &ExprDebugger{
  54. filter: filter,
  55. }
  56. if filter == "" {
  57. log.Debugf("unable to create expr debugger with empty filter")
  58. return &ExprDebugger{}, nil
  59. }
  60. v.newVar = false
  61. tree, err := parser.Parse(filter)
  62. if err != nil {
  63. return nil, err
  64. }
  65. ast.Walk(&tree.Node, v)
  66. if v.currentID != "" && len(v.properties) > 0 { // if its a variable with property (eg. evt.Line.Labels)
  67. fullVar := fmt.Sprintf("%s.%s", v.currentID, strings.Join(v.properties, "."))
  68. v.vars = append(v.vars, fullVar)
  69. } else if v.currentID != "" && len(v.properties) == 0 { // if it's a variable without property
  70. fullVar := v.currentID
  71. v.vars = append(v.vars, fullVar)
  72. } else {
  73. log.Debugf("no variable in filter : '%s'", filter)
  74. }
  75. v.properties = []string{}
  76. v.currentID = ""
  77. for _, variable := range v.vars {
  78. debugFilter, err := expr.Compile(variable, exprEnv)
  79. if err != nil {
  80. return ret, fmt.Errorf("compilation of variable '%s' failed: %v", variable, err)
  81. }
  82. tmpExpression := &expression{
  83. variable,
  84. debugFilter,
  85. }
  86. expressions = append(expressions, tmpExpression)
  87. }
  88. ret.expression = expressions
  89. return ret, nil
  90. }
  91. // ExprDebugger contains the list of expression to be run when debugging an expression filter
  92. type ExprDebugger struct {
  93. filter string
  94. expression []*expression
  95. }
  96. // expression is the structure that represents the variable in string and compiled format
  97. type expression struct {
  98. Str string
  99. Compiled *vm.Program
  100. }
  101. /*
  102. Run display the content of each variable of a filter by evaluating them with expr,
  103. again the expr environment given in parameter
  104. */
  105. func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) {
  106. if len(e.expression) == 0 {
  107. logger.Debugf("no variable to eval for filter '%s'", e.filter)
  108. return
  109. }
  110. logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult)))
  111. logger.Debugf("eval variables:")
  112. for _, expression := range e.expression {
  113. debug, err := expr.Run(expression.Compiled, exprEnv)
  114. if err != nil {
  115. logger.Errorf("unable to print debug expression for '%s': %s", expression.Str, err)
  116. }
  117. logger.Debugf(" %s = '%s'", expression.Str, debug)
  118. }
  119. }
  120. // NewDebugger is the exported function that build the debuggers expressions
  121. func NewDebugger(filter string, exprEnv expr.Option) (*ExprDebugger, error) {
  122. visitor := &visitor{}
  123. exprDebugger, err := visitor.Build(filter, exprEnv)
  124. return exprDebugger, err
  125. }