compiler.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package matcher
  2. import (
  3. "github.com/opencost/opencost/pkg/filter21/ast"
  4. "github.com/opencost/opencost/pkg/filter21/transform"
  5. "github.com/opencost/opencost/pkg/filter21/util"
  6. )
  7. // FieldMapper is the adapter which can fetch actual T instance data of type U
  8. // leveraging the ast.Identifier definition.
  9. type FieldMapper[T any, U any] func(T, ast.Identifier) (U, error)
  10. // StringFieldMapper is the adapter which can fetch actual T instance data of type string
  11. // leveraging the ast.Identifier definition.
  12. type StringFieldMapper[T any] FieldMapper[T, string]
  13. // SliceFieldMapper is the adapter which can fetch actual T instance data of type []string
  14. // leveraging the ast.Identifier definition.
  15. type SliceFieldMapper[T any] FieldMapper[T, []string]
  16. // SliceFieldMapper is the adapter which can fetch actual T instance data of type map[string]string
  17. // leveraging the ast.Identifier definition.
  18. type MapFieldMapper[T any] FieldMapper[T, map[string]string]
  19. // MatchCompiler compiles an `ast.FilterNode` into a Matcher[T] implementation.
  20. type MatchCompiler[T any] struct {
  21. stringMatcher *StringMatcherFactory[T]
  22. sliceMatcher *StringSliceMatcherFactory[T]
  23. mapMatcher *StringMapMatcherFactory[T]
  24. passes []transform.CompilerPass
  25. }
  26. // NewMatchCompiler creates a new MatchCompiler for T instances provided the funcs which
  27. // can map ast.Identifier instances to a specific T field
  28. func NewMatchCompiler[T any](
  29. stringFieldMapper StringFieldMapper[T],
  30. sliceFieldMapper SliceFieldMapper[T],
  31. mapFieldMapper MapFieldMapper[T],
  32. passes ...transform.CompilerPass,
  33. ) *MatchCompiler[T] {
  34. return &MatchCompiler[T]{
  35. stringMatcher: NewStringMatcherFactory(stringFieldMapper),
  36. sliceMatcher: NewStringSliceMatcherFactory(sliceFieldMapper),
  37. mapMatcher: NewStringMapMatcherFactory(mapFieldMapper),
  38. passes: passes,
  39. }
  40. }
  41. // Compile accepts anb `ast.FilterNode` tree and compiles it into a `Matcher[T]` implementation
  42. // which can be used to match T instances dynamically.
  43. func (mc *MatchCompiler[T]) Compile(filter ast.FilterNode) (Matcher[T], error) {
  44. // apply compiler passes on parsed ast
  45. filter = transform.ApplyAll(filter, mc.passes)
  46. // if the root node is a void op, return an allpass
  47. if _, ok := filter.(*ast.VoidOp); ok {
  48. return &AllPass[T]{}, nil
  49. }
  50. var result Matcher[T]
  51. var currentOps *util.Stack[MatcherGroup[T]] = util.NewStack[MatcherGroup[T]]()
  52. // handle leaf is the ast walker func. group ops get pushed onto a stack on
  53. // the Enter state, and popped on the Exit state. Any ops between Enter and
  54. // Exit are added to the group. If there are no more groups on the stack after
  55. // an Exit state, we set the result to the final group.
  56. handleLeaf := func(leaf ast.FilterNode, state ast.TraversalState) {
  57. switch n := leaf.(type) {
  58. case *ast.AndOp:
  59. if state == ast.TraversalStateEnter {
  60. currentOps.Push(&And[T]{})
  61. } else if state == ast.TraversalStateExit {
  62. if currentOps.Length() > 1 {
  63. current := currentOps.Pop()
  64. currentOps.Top().Add(current)
  65. } else {
  66. result = currentOps.Pop()
  67. }
  68. }
  69. case *ast.OrOp:
  70. if state == ast.TraversalStateEnter {
  71. currentOps.Push(&Or[T]{})
  72. } else if state == ast.TraversalStateExit {
  73. if currentOps.Length() > 1 {
  74. current := currentOps.Pop()
  75. currentOps.Top().Add(current)
  76. } else {
  77. result = currentOps.Pop()
  78. }
  79. }
  80. case *ast.NotOp:
  81. if state == ast.TraversalStateEnter {
  82. currentOps.Push(&Not[T]{})
  83. } else if state == ast.TraversalStateExit {
  84. if currentOps.Length() > 1 {
  85. current := currentOps.Pop()
  86. currentOps.Top().Add(current)
  87. } else {
  88. result = currentOps.Pop()
  89. }
  90. }
  91. case *ast.EqualOp:
  92. sm := mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  93. if currentOps.Length() == 0 {
  94. result = sm
  95. } else {
  96. currentOps.Top().Add(sm)
  97. }
  98. case *ast.ContainsOp:
  99. f := n.Left.Field
  100. key := n.Left.Key
  101. var sm Matcher[T]
  102. if f.IsSlice() {
  103. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  104. } else if f.IsMap() && key == "" {
  105. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  106. } else {
  107. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  108. }
  109. if currentOps.Length() == 0 {
  110. result = sm
  111. } else {
  112. currentOps.Top().Add(sm)
  113. }
  114. case *ast.ContainsPrefixOp:
  115. f := n.Left.Field
  116. key := n.Left.Key
  117. var sm Matcher[T]
  118. if f.IsSlice() {
  119. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  120. } else if f.IsMap() && key == "" {
  121. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  122. } else {
  123. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  124. }
  125. if currentOps.Length() == 0 {
  126. result = sm
  127. } else {
  128. currentOps.Top().Add(sm)
  129. }
  130. case *ast.ContainsSuffixOp:
  131. f := n.Left.Field
  132. key := n.Left.Key
  133. var sm Matcher[T]
  134. if f.IsSlice() {
  135. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  136. } else if f.IsMap() && key == "" {
  137. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  138. } else {
  139. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  140. }
  141. if currentOps.Length() == 0 {
  142. result = sm
  143. } else {
  144. currentOps.Top().Add(sm)
  145. }
  146. }
  147. }
  148. ast.PreOrderTraversal(filter, handleLeaf)
  149. if result == nil {
  150. return &AllPass[T]{}, nil
  151. }
  152. return result, nil
  153. }