compiler.go 5.5 KB

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