walker.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. package ast
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. "github.com/opencost/opencost/pkg/filter21/util"
  7. "golang.org/x/text/cases"
  8. "golang.org/x/text/language"
  9. )
  10. // used to apply a title to pipeline
  11. var titleCaser cases.Caser = cases.Title(language.Und, cases.NoLower)
  12. var lowerCaser cases.Caser = cases.Lower(language.Und)
  13. // TraversalState represents the state of the current leaf node in a traversal
  14. // of the filter Any grouping ops will include an Enter on their first
  15. // occurence, and an Exit when leaving the op state.
  16. type TraversalState int
  17. const (
  18. // TraversalStateNone is used whenever a binary op leaf node is traversed.
  19. TraversalStateNone TraversalState = iota
  20. // TraversalStateEnter is used when a group op leaf node is traversed (and, or, not)
  21. TraversalStateEnter
  22. // TraversalStateExit is used wwhen a group op leaf node is popped (and, or, not).
  23. TraversalStateExit
  24. )
  25. // TransformLeaves produces a new tree, leaving non-leaf nodes (e.g. And, Or)
  26. // intact and replacing leaf nodes (e.g. Equals, Contains) with the result of
  27. // calling leafTransformer(node).
  28. func TransformLeaves(node FilterNode, transformer func(FilterNode) FilterNode) FilterNode {
  29. if node == nil {
  30. return nil
  31. }
  32. // For group ops, we need to execute the callback with an Enter,
  33. // recursively call traverse, then execute the callback with an Exit.
  34. switch n := node.(type) {
  35. case *NotOp:
  36. return &NotOp{
  37. Operand: TransformLeaves(n.Operand, transformer),
  38. }
  39. case *AndOp:
  40. var newOperands []FilterNode
  41. for _, o := range n.Operands {
  42. newOperands = append(newOperands, TransformLeaves(o, transformer))
  43. }
  44. return &AndOp{
  45. Operands: newOperands,
  46. }
  47. case *OrOp:
  48. var newOperands []FilterNode
  49. for _, o := range n.Operands {
  50. newOperands = append(newOperands, TransformLeaves(o, transformer))
  51. }
  52. return &OrOp{
  53. Operands: newOperands,
  54. }
  55. // Remaining nodes are assumed to be leaves
  56. default:
  57. return transformer(node)
  58. }
  59. }
  60. // PreOrderTraversal accepts a root `FilterNode` and calls the f callback on
  61. // each leaf node it traverses. When entering "group" leaf nodes (leaf nodes
  62. // which contain other leaf nodes), a TraversalStateEnter/Exit will be includes
  63. // to denote each depth. In short, the callback will be executed twice for each
  64. // "group" op, once before entering, and once bofore exiting.
  65. func PreOrderTraversal(node FilterNode, f func(FilterNode, TraversalState)) {
  66. if node == nil {
  67. return
  68. }
  69. // For group ops, we need to execute the callback with an Enter,
  70. // recursively call traverse, then execute the callback with an Exit.
  71. switch n := node.(type) {
  72. case *NotOp:
  73. f(node, TraversalStateEnter)
  74. PreOrderTraversal(n.Operand, f)
  75. f(node, TraversalStateExit)
  76. case *AndOp:
  77. f(node, TraversalStateEnter)
  78. for _, o := range n.Operands {
  79. PreOrderTraversal(o, f)
  80. }
  81. f(node, TraversalStateExit)
  82. case *OrOp:
  83. f(node, TraversalStateEnter)
  84. for _, o := range n.Operands {
  85. PreOrderTraversal(o, f)
  86. }
  87. f(node, TraversalStateExit)
  88. // Otherwise, we just linearly traverse
  89. default:
  90. f(node, TraversalStateNone)
  91. }
  92. }
  93. // ToPreOrderString runs a PreOrderTraversal and generates an indented tree structure string
  94. // format for the provided tree root.
  95. func ToPreOrderString(node FilterNode) string {
  96. var sb strings.Builder
  97. indent := 0
  98. printNode := func(n FilterNode, action TraversalState) {
  99. if action == TraversalStateEnter {
  100. sb.WriteString(OpStringFor(n, action, indent))
  101. indent++
  102. } else if action == TraversalStateExit {
  103. indent--
  104. sb.WriteString(OpStringFor(n, action, indent))
  105. } else {
  106. sb.WriteString(OpStringFor(n, action, indent))
  107. }
  108. }
  109. PreOrderTraversal(node, printNode)
  110. return sb.String()
  111. }
  112. // ToPreOrderShortString runs a PreOrderTraversal and generates a condensed tree structure string
  113. // format for the provided tree root.
  114. func ToPreOrderShortString(node FilterNode) string {
  115. var sb strings.Builder
  116. printNode := func(n FilterNode, action TraversalState) {
  117. if action == TraversalStateEnter {
  118. sb.WriteString(ShortOpStringFor(n, action))
  119. } else if action == TraversalStateExit {
  120. sb.WriteString(ShortOpStringFor(n, action))
  121. } else {
  122. sb.WriteString(ShortOpStringFor(n, action))
  123. }
  124. }
  125. PreOrderTraversal(node, printNode)
  126. return sb.String()
  127. }
  128. // OpStringFor returns a string for the provided leaf node, traversal state, and current
  129. // depth.
  130. func OpStringFor(node FilterNode, traversalState TraversalState, depth int) string {
  131. prefix := indent(depth)
  132. if traversalState == TraversalStateExit {
  133. return prefix + "}\n"
  134. }
  135. if traversalState == TraversalStateEnter {
  136. return prefix + titleCaser.String(string(node.Op())) + " {\n"
  137. }
  138. open := prefix + titleCaser.String(string(node.Op())) + " { "
  139. switch n := node.(type) {
  140. case *VoidOp:
  141. open += ")"
  142. case *EqualOp:
  143. open += fmt.Sprintf("Left: %s, Right: %s }\n", n.Left.String(), n.Right)
  144. case *ContainsOp:
  145. open += fmt.Sprintf("Left: %s, Right: %s }\n", n.Left.String(), n.Right)
  146. case *ContainsPrefixOp:
  147. open += fmt.Sprintf("Left: %s, Right: %s }\n", n.Left.String(), n.Right)
  148. case *ContainsSuffixOp:
  149. open += fmt.Sprintf("Left: %s, Right: %s }\n", n.Left.String(), n.Right)
  150. default:
  151. open += "}\n"
  152. }
  153. return open
  154. }
  155. // ShortOpStringFor returns a condensed string for the provided leaf node, traversal state, and current
  156. // depth.
  157. func ShortOpStringFor(node FilterNode, traversalState TraversalState) string {
  158. if traversalState == TraversalStateExit {
  159. return ")"
  160. }
  161. if traversalState == TraversalStateEnter {
  162. return lowerCaser.String(string(node.Op())) + "("
  163. }
  164. open := lowerCaser.String(string(node.Op())) + "("
  165. switch n := node.(type) {
  166. case *VoidOp:
  167. open += ")"
  168. case *EqualOp:
  169. open += fmt.Sprintf("%s,%s)", condenseIdent(n.Left), n.Right)
  170. case *ContainsOp:
  171. open += fmt.Sprintf("%s,%s)", condenseIdent(n.Left), n.Right)
  172. case *ContainsPrefixOp:
  173. open += fmt.Sprintf("%s,%s)", condenseIdent(n.Left), n.Right)
  174. case *ContainsSuffixOp:
  175. open += fmt.Sprintf("%s,%s)", condenseIdent(n.Left), n.Right)
  176. default:
  177. open += ")"
  178. }
  179. return open
  180. }
  181. // condenses an identifier string
  182. func condenseIdent(ident Identifier) string {
  183. s := condense(ident.Field.Name)
  184. if ident.Key != "" {
  185. s += "[" + ident.Key + "]"
  186. }
  187. return s
  188. }
  189. func condense(s string) string {
  190. lc := lowerCaser.String(s)
  191. if len(lc) > 2 {
  192. return lc[:2]
  193. }
  194. return lc
  195. }
  196. // Clone deep copies and returns the AST parameter.
  197. func Clone(filter FilterNode) FilterNode {
  198. var result FilterNode = &VoidOp{}
  199. var currentOps *util.Stack[FilterGroup] = util.NewStack[FilterGroup]()
  200. PreOrderTraversal(filter, func(fn FilterNode, state TraversalState) {
  201. if fn == nil {
  202. return
  203. }
  204. switch n := fn.(type) {
  205. case *AndOp:
  206. if state == TraversalStateEnter {
  207. currentOps.Push(&AndOp{})
  208. } else if state == TraversalStateExit {
  209. if currentOps.Length() > 1 {
  210. current := currentOps.Pop()
  211. currentOps.Top().Add(current)
  212. } else {
  213. result = currentOps.Pop()
  214. }
  215. }
  216. case *OrOp:
  217. if state == TraversalStateEnter {
  218. currentOps.Push(&OrOp{})
  219. } else if state == TraversalStateExit {
  220. if currentOps.Length() > 1 {
  221. current := currentOps.Pop()
  222. currentOps.Top().Add(current)
  223. } else {
  224. result = currentOps.Pop()
  225. }
  226. }
  227. case *NotOp:
  228. if state == TraversalStateEnter {
  229. currentOps.Push(&NotOp{})
  230. } else if state == TraversalStateExit {
  231. if currentOps.Length() > 1 {
  232. current := currentOps.Pop()
  233. currentOps.Top().Add(current)
  234. } else {
  235. result = currentOps.Pop()
  236. }
  237. }
  238. case *ContradictionOp:
  239. if currentOps.Length() == 0 {
  240. result = &ContradictionOp{}
  241. } else {
  242. currentOps.Top().Add(&ContradictionOp{})
  243. }
  244. case *EqualOp:
  245. var field Field
  246. if n.Left.Field != nil {
  247. field = *n.Left.Field
  248. }
  249. sm := &EqualOp{
  250. Left: Identifier{
  251. Field: &field,
  252. Key: n.Left.Key,
  253. },
  254. Right: n.Right,
  255. }
  256. if currentOps.Length() == 0 {
  257. result = sm
  258. } else {
  259. currentOps.Top().Add(sm)
  260. }
  261. case *ContainsOp:
  262. var field Field
  263. if n.Left.Field != nil {
  264. field = *n.Left.Field
  265. }
  266. sm := &ContainsOp{
  267. Left: Identifier{
  268. Field: &field,
  269. Key: n.Left.Key,
  270. },
  271. Right: n.Right,
  272. }
  273. if currentOps.Length() == 0 {
  274. result = sm
  275. } else {
  276. currentOps.Top().Add(sm)
  277. }
  278. case *ContainsPrefixOp:
  279. var field Field
  280. if n.Left.Field != nil {
  281. field = *n.Left.Field
  282. }
  283. sm := &ContainsPrefixOp{
  284. Left: Identifier{
  285. Field: &field,
  286. Key: n.Left.Key,
  287. },
  288. Right: n.Right,
  289. }
  290. if currentOps.Length() == 0 {
  291. result = sm
  292. } else {
  293. currentOps.Top().Add(sm)
  294. }
  295. case *ContainsSuffixOp:
  296. var field Field
  297. if n.Left.Field != nil {
  298. field = *n.Left.Field
  299. }
  300. sm := &ContainsSuffixOp{
  301. Left: Identifier{
  302. Field: &field,
  303. Key: n.Left.Key,
  304. },
  305. Right: n.Right,
  306. }
  307. if currentOps.Length() == 0 {
  308. result = sm
  309. } else {
  310. currentOps.Top().Add(sm)
  311. }
  312. }
  313. })
  314. return result
  315. }
  316. // returns an 2-space indention for each depth
  317. func indent(depth int) string {
  318. if depth <= 0 {
  319. return ""
  320. }
  321. return strings.Repeat(" ", depth)
  322. }
  323. func Fields(filter FilterNode) []Field {
  324. fields := map[Field]bool{}
  325. PreOrderTraversal(filter, func(fn FilterNode, state TraversalState) {
  326. if fn == nil {
  327. return
  328. }
  329. switch n := fn.(type) {
  330. case *EqualOp:
  331. if n.Left.Field != nil {
  332. fields[*n.Left.Field] = true
  333. }
  334. case *ContainsOp:
  335. if n.Left.Field != nil {
  336. fields[*n.Left.Field] = true
  337. }
  338. case *ContainsPrefixOp:
  339. if n.Left.Field != nil {
  340. fields[*n.Left.Field] = true
  341. }
  342. case *ContainsSuffixOp:
  343. if n.Left.Field != nil {
  344. fields[*n.Left.Field] = true
  345. }
  346. }
  347. })
  348. response := make([]Field, 0, len(fields))
  349. for field := range fields {
  350. response = append(response, field)
  351. }
  352. sort.Slice(response, func(i, j int) bool {
  353. return response[i].Name < response[j].Name
  354. })
  355. return response
  356. }
  357. func ToString(filter FilterNode) string {
  358. // TODO make this return a parseable filter string, if possible...
  359. return ToPreOrderShortString(filter)
  360. }