compiler.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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.ContradictionOp:
  92. if currentOps.Length() == 0 {
  93. result = &AllCut[T]{}
  94. } else {
  95. currentOps.Top().Add(&AllCut[T]{})
  96. }
  97. case *ast.EqualOp:
  98. sm := mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  99. if currentOps.Length() == 0 {
  100. result = sm
  101. } else {
  102. currentOps.Top().Add(sm)
  103. }
  104. case *ast.ContainsOp:
  105. f := n.Left.Field
  106. key := n.Left.Key
  107. var sm Matcher[T]
  108. if f.IsSlice() {
  109. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  110. } else if f.IsMap() && key == "" {
  111. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  112. } else {
  113. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  114. }
  115. if currentOps.Length() == 0 {
  116. result = sm
  117. } else {
  118. currentOps.Top().Add(sm)
  119. }
  120. case *ast.ContainsPrefixOp:
  121. f := n.Left.Field
  122. key := n.Left.Key
  123. var sm Matcher[T]
  124. if f.IsSlice() {
  125. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  126. } else if f.IsMap() && key == "" {
  127. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  128. } else {
  129. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  130. }
  131. if currentOps.Length() == 0 {
  132. result = sm
  133. } else {
  134. currentOps.Top().Add(sm)
  135. }
  136. case *ast.ContainsSuffixOp:
  137. f := n.Left.Field
  138. key := n.Left.Key
  139. var sm Matcher[T]
  140. if f.IsSlice() {
  141. sm = mc.sliceMatcher.NewStringSliceMatcher(n.Op(), n.Left, n.Right)
  142. } else if f.IsMap() && key == "" {
  143. sm = mc.mapMatcher.NewStringMapMatcher(n.Op(), n.Left, n.Right)
  144. } else {
  145. sm = mc.stringMatcher.NewStringMatcher(n.Op(), n.Left, n.Right)
  146. }
  147. if currentOps.Length() == 0 {
  148. result = sm
  149. } else {
  150. currentOps.Top().Add(sm)
  151. }
  152. }
  153. }
  154. ast.PreOrderTraversal(filter, handleLeaf)
  155. if result == nil {
  156. return &AllPass[T]{}, nil
  157. }
  158. return result, nil
  159. }