runtime.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package interpreter
  11. import (
  12. "fmt"
  13. "strconv"
  14. "strings"
  15. "devt.de/krotik/eliasdb/eql/parser"
  16. "devt.de/krotik/eliasdb/graph"
  17. "devt.de/krotik/eliasdb/graph/data"
  18. )
  19. /*
  20. allowMultiEval allows multiple calls to eval of runtime components without
  21. resetting state (used for testing)
  22. */
  23. var allowMultiEval = false
  24. // Special flags which can be set by with statements
  25. type withFlags struct {
  26. ordering []byte // Result ordering
  27. orderingCol []int // Columns which should be ordered
  28. notnullCol []int // Columns which must not be null
  29. uniqueCol []int // Columns which will only contain unique values
  30. uniqueColCnt []bool // Flag if unique values should be counted
  31. }
  32. const (
  33. withOrderingAscending = 0x1
  34. withOrderingDescending = 0x2
  35. )
  36. /*
  37. GroupNodeKind is a special group node kind
  38. */
  39. const GroupNodeKind = "group"
  40. // General runtime provider
  41. // ========================
  42. /*
  43. eqlRuntimeProvider defines the main interpreter
  44. datastructure and all functions for general evaluation.
  45. */
  46. type eqlRuntimeProvider struct {
  47. name string // Name to identify the input
  48. part string // Graph partition to query
  49. gm *graph.Manager // GraphManager to operate on
  50. ni NodeInfo // NodeInfo to use for formatting
  51. groupScope string // Group scope for query
  52. allowNilTraversal bool // Flag if empty traversals should be included in the result
  53. withFlags *withFlags // Special flags which can be set by with statements
  54. primaryKind string // Primary node kind
  55. nextStartKey func() (string, error) // Function to get the next start key
  56. traversals []*parser.ASTNode // Array of all top level query traversals
  57. where *parser.ASTNode // First where clause
  58. show *parser.ASTNode // Show clause node
  59. specs []string // Flat list of traversals of this query
  60. attrsNodes []map[string]string // Attributes for nodes to query on each traversal
  61. attrsEdges []map[string]string // Attributes for nodes to query on each traversal
  62. rowNode []data.Node // Current row of nodes which is evaluated
  63. rowEdge []data.Edge // Current row of edges which is evaluated
  64. colLabels []string // Labels for columns
  65. colFormat []string // Format for columns
  66. colData []string // Data for columns
  67. colFunc []FuncShow // Function to transform column value
  68. _attrsNodesFetch [][]string // Internal copy of attrsNodes better suited for fetchPart calls
  69. _attrsEdgesFetch [][]string // Internal copy of attrsEdges better suited for fetchPart calls
  70. }
  71. /*
  72. Initialise and validate data structures.
  73. */
  74. func (p *eqlRuntimeProvider) init(startKind string,
  75. rootChildren []*parser.ASTNode) error {
  76. // By default we don't include empty traversals in the result
  77. p.allowNilTraversal = false
  78. // Clear any with flags
  79. p.withFlags = &withFlags{make([]byte, 0), make([]int, 0), make([]int, 0),
  80. make([]int, 0), make([]bool, 0)}
  81. // Reinitialise datastructures
  82. p.groupScope = ""
  83. p.traversals = make([]*parser.ASTNode, 0)
  84. p.where = nil
  85. p.show = nil
  86. p.specs = make([]string, 0)
  87. p.attrsNodes = make([]map[string]string, 0)
  88. p.attrsEdges = make([]map[string]string, 0)
  89. p.rowNode = nil
  90. p.rowEdge = nil
  91. p._attrsNodesFetch = nil
  92. p._attrsEdgesFetch = nil
  93. p.colLabels = make([]string, 0)
  94. p.colFormat = make([]string, 0)
  95. p.colData = make([]string, 0)
  96. p.colFunc = make([]FuncShow, 0)
  97. p.primaryKind = ""
  98. p.specs = append(p.specs, startKind)
  99. p.attrsNodes = append(p.attrsNodes, make(map[string]string))
  100. p.attrsEdges = append(p.attrsEdges, make(map[string]string))
  101. // With clause is interpreted straight after finishing the columns
  102. var withChild *parser.ASTNode
  103. // Go through the children, check if they are valid and initialise them
  104. for _, child := range rootChildren {
  105. if child.Name == parser.NodeWHERE {
  106. // Check if the show clause or some traversals are already populated
  107. if p.show != nil || len(p.traversals) > 0 {
  108. return p.newRuntimeError(ErrInvalidConstruct,
  109. "condition must be before show clause and traversals", child)
  110. }
  111. // Reset state of where and store it
  112. if err := child.Runtime.Validate(); err != nil {
  113. return err
  114. }
  115. p.where = child
  116. } else if child.Name == parser.NodeTRAVERSE {
  117. // Check if show clause or where clause is already populated
  118. if p.show != nil {
  119. return p.newRuntimeError(ErrInvalidConstruct,
  120. "traversals must be before show clause", child)
  121. }
  122. // Reset state of traversal and add it to the traversal list
  123. if err := child.Runtime.Validate(); err != nil {
  124. return err
  125. }
  126. p.traversals = append(p.traversals, child)
  127. } else if child.Name == parser.NodeSHOW {
  128. p.show = child
  129. } else if child.Name == parser.NodeFROM {
  130. // Set the group state
  131. p.groupScope = child.Children[0].Children[0].Token.Val
  132. } else if child.Name == parser.NodePRIMARY {
  133. pk := child.Children[0].Token.Val
  134. for _, nk := range p.gm.NodeKinds() {
  135. if nk == pk {
  136. p.primaryKind = pk
  137. }
  138. }
  139. if p.primaryKind == "" {
  140. return p.newRuntimeError(ErrUnknownNodeKind, pk, child.Children[0])
  141. }
  142. } else if child.Name == parser.NodeWITH {
  143. withChild = child
  144. } else {
  145. return p.newRuntimeError(ErrInvalidConstruct, child.Name, child)
  146. }
  147. }
  148. // Populate column related attributes
  149. nodeKindPos, edgeKindPos, err := p.initCols()
  150. if err != nil {
  151. return err
  152. }
  153. // Interpret with clause straight after populating the columns
  154. if withChild != nil {
  155. if err := p.initWithFlags(withChild, nodeKindPos, edgeKindPos); err != nil {
  156. return err
  157. }
  158. }
  159. if p.primaryKind == "" {
  160. p.primaryKind = startKind
  161. }
  162. return nil
  163. }
  164. /*
  165. initWithFlags populates the withFlags datastructure. It is assumed that the
  166. columns have been populated before calling this function.
  167. */
  168. func (p *eqlRuntimeProvider) initWithFlags(withNode *parser.ASTNode,
  169. nodeKindPos map[string][]int, edgeKindPos map[string][]int) error {
  170. // Helper function to find a specified column
  171. findColumn := func(colData string, node *parser.ASTNode) (int, error) {
  172. col := -1
  173. colDataSplit := strings.SplitN(colData, ":", 3)
  174. switch len(colDataSplit) {
  175. case 1:
  176. // Find the first column which displays the given attribute
  177. for i, cd := range p.colData {
  178. cds := strings.SplitN(cd, ":", 3)
  179. if cds[2] == colDataSplit[0] {
  180. col = i
  181. }
  182. }
  183. case 2:
  184. // Search for first kind / attribute occurrence
  185. kind := colDataSplit[0]
  186. attr := colDataSplit[1]
  187. searchColData := func(pos int, t string) {
  188. cstr := fmt.Sprint(pos+1, ":", t, ":", attr)
  189. for i, c := range p.colData {
  190. if c == cstr {
  191. col = i
  192. }
  193. }
  194. }
  195. if poslist, ok := nodeKindPos[kind]; ok {
  196. searchColData(poslist[0], "n")
  197. } else if poslist, ok := edgeKindPos[kind]; ok {
  198. searchColData(poslist[0], "e")
  199. } else {
  200. return -1, p.newRuntimeError(ErrInvalidConstruct,
  201. "Cannot determine column for with term: "+colData, node)
  202. }
  203. case 3:
  204. // Search for exact specification
  205. for i, c := range p.colData {
  206. if c == colData {
  207. col = i
  208. }
  209. }
  210. }
  211. if col == -1 {
  212. return -1, p.newRuntimeError(ErrInvalidConstruct,
  213. "Cannot determine column for with term: "+colData, node)
  214. }
  215. return col, nil
  216. }
  217. // Go through all children and initialise the withFlags data structure
  218. for _, child := range withNode.Children {
  219. if child.Name == parser.NodeNULLTRAVERSAL && child.Children[0].Name == parser.NodeTRUE {
  220. p.allowNilTraversal = true
  221. } else if child.Name == parser.NodeFILTERING {
  222. for _, child := range child.Children {
  223. if child.Name == parser.NodeISNOTNULL || child.Name == parser.NodeUNIQUE || child.Name == parser.NodeUNIQUECOUNT {
  224. c, err := findColumn(child.Children[0].Token.Val, child)
  225. if err != nil {
  226. return err
  227. }
  228. if child.Name == parser.NodeISNOTNULL {
  229. p.withFlags.notnullCol = append(p.withFlags.notnullCol, c)
  230. } else if child.Name == parser.NodeUNIQUE {
  231. p.withFlags.uniqueCol = append(p.withFlags.uniqueCol, c)
  232. p.withFlags.uniqueColCnt = append(p.withFlags.uniqueColCnt, false)
  233. } else if child.Name == parser.NodeUNIQUECOUNT {
  234. p.withFlags.uniqueCol = append(p.withFlags.uniqueCol, c)
  235. p.withFlags.uniqueColCnt = append(p.withFlags.uniqueColCnt, true)
  236. }
  237. } else {
  238. return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)
  239. }
  240. }
  241. } else if child.Name == parser.NodeORDERING {
  242. for _, child := range child.Children {
  243. if child.Name == parser.NodeASCENDING || child.Name == parser.NodeDESCENDING {
  244. c, err := findColumn(child.Children[0].Token.Val, child)
  245. if err != nil {
  246. return err
  247. }
  248. if child.Name == parser.NodeASCENDING {
  249. p.withFlags.ordering = append(p.withFlags.ordering, withOrderingAscending)
  250. } else {
  251. p.withFlags.ordering = append(p.withFlags.ordering, withOrderingDescending)
  252. }
  253. p.withFlags.orderingCol = append(p.withFlags.orderingCol, c)
  254. } else {
  255. return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)
  256. }
  257. }
  258. } else {
  259. return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)
  260. }
  261. }
  262. return nil
  263. }
  264. /*
  265. initCols populates the column related attributes. This function assumes that
  266. specs is filled with all necessary traversals.
  267. The following formats for a show term are allowed:
  268. <step>:<type>:<attr> - Attribute from whatever is at the given traversal step
  269. <kind>:<attr> - First matching kind in a row provides the attribute
  270. <attr> - Show attribute from root node kind
  271. */
  272. func (p *eqlRuntimeProvider) initCols() (map[string][]int, map[string][]int, error) {
  273. // Fill lookup maps for traversal kind positions
  274. // Show term match by kind uses these
  275. nodeKindPos := make(map[string][]int)
  276. edgeKindPos := make(map[string][]int)
  277. addPos := func(kmap map[string][]int, kind string, pos int) {
  278. if l, ok := kmap[kind]; ok {
  279. kmap[kind] = append(l, pos)
  280. } else {
  281. kmap[kind] = []int{pos}
  282. }
  283. }
  284. for i, spec := range p.specs {
  285. if i == 0 {
  286. addPos(nodeKindPos, spec, i)
  287. } else {
  288. sspec := strings.Split(spec, ":")
  289. if sspec[1] != "" {
  290. addPos(edgeKindPos, sspec[1], i)
  291. }
  292. if sspec[3] != "" {
  293. addPos(nodeKindPos, sspec[3], i)
  294. }
  295. }
  296. }
  297. // Fill up column lists
  298. if p.show == nil || len(p.show.Children) == 0 {
  299. // If no show clause is defined ask the NodeInfo to provide a summary list
  300. for i, spec := range p.specs {
  301. sspec := strings.Split(spec, ":")
  302. kind := sspec[len(sspec)-1]
  303. for _, attr := range p.ni.SummaryAttributes(kind) {
  304. // Make sure the attribute is in attrsNodes
  305. p.attrsNodes[i][attr] = ""
  306. // Fill col attributes (we only show nodes)
  307. p.colLabels = append(p.colLabels, p.ni.AttributeDisplayString(kind, attr))
  308. p.colFormat = append(p.colFormat, "auto")
  309. p.colData = append(p.colData, fmt.Sprintf("%v:n:%s", i+1, attr))
  310. p.colFunc = append(p.colFunc, nil)
  311. }
  312. }
  313. } else {
  314. var err error
  315. var attr, label, colData string
  316. var pos int
  317. var isNode bool
  318. var colFunc FuncShow
  319. // Go through the elements of the provided show clause
  320. for _, col := range p.show.Children {
  321. if col.Name != parser.NodeSHOWTERM {
  322. return nil, nil, p.newRuntimeError(ErrInvalidConstruct, col.Name, col)
  323. }
  324. // Reset label value
  325. label = ""
  326. colFunc = nil
  327. // Create the correct colData value
  328. if col.Token.ID == parser.TokenAT {
  329. // We have a function get the attribute which it operates on
  330. funcName := col.Children[0].Children[0].Token.Val
  331. funcInst, ok := showFunc[funcName]
  332. if !ok {
  333. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  334. "Unknown function: "+funcName, col)
  335. }
  336. colFunc, colData, label, err = funcInst(col.Children[0], p)
  337. if err != nil {
  338. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  339. err.Error(), col)
  340. }
  341. } else {
  342. colData = col.Token.Val
  343. }
  344. colDataSplit := strings.SplitN(colData, ":", 3)
  345. switch len(colDataSplit) {
  346. case 1:
  347. // Show attribute from root node kind
  348. attr = colDataSplit[0]
  349. pos = 0
  350. isNode = true
  351. colData = "1:n:" + attr
  352. if label == "" {
  353. label = p.ni.AttributeDisplayString(p.specs[0], attr)
  354. }
  355. case 2:
  356. // First matching kind in a row provides the attribute
  357. kind := colDataSplit[0]
  358. if poslist, ok := nodeKindPos[kind]; ok {
  359. attr = colDataSplit[1]
  360. pos = poslist[0]
  361. isNode = true
  362. colData = fmt.Sprint(pos+1) + ":n:" + attr
  363. } else if poslist, ok := edgeKindPos[kind]; ok {
  364. attr = colDataSplit[1]
  365. pos = poslist[0]
  366. isNode = false
  367. colData = fmt.Sprint(pos+1) + ":e:" + attr
  368. } else {
  369. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  370. "Cannot determine data position for kind: "+kind, col)
  371. }
  372. if label == "" {
  373. label = p.ni.AttributeDisplayString(kind, attr)
  374. }
  375. case 3:
  376. // Attribute from whatever is at the given traversal step
  377. attr = colDataSplit[2]
  378. pos, err = strconv.Atoi(colDataSplit[0])
  379. if err != nil {
  380. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  381. "Invalid data index: "+colData+" ("+err.Error()+")", col)
  382. } else if pos < 1 {
  383. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  384. "Invalid data index: "+colData+" (index must be greater than 0)", col)
  385. }
  386. pos--
  387. if colDataSplit[1] == "n" {
  388. isNode = true
  389. } else if colDataSplit[1] == "e" {
  390. isNode = false
  391. } else {
  392. return nil, nil, p.newRuntimeError(ErrInvalidConstruct,
  393. "Invalid data source '"+colDataSplit[1]+"' (either n - Node or e - Edge)", col)
  394. }
  395. if label == "" {
  396. label = p.ni.AttributeDisplayString("", attr)
  397. }
  398. }
  399. if pos >= len(p.attrsNodes) {
  400. return nil, nil, p.newRuntimeError(ErrInvalidColData,
  401. fmt.Sprintf("Data index out of range: %v", pos+1), col)
  402. }
  403. // Determine label and format
  404. colLabel := label
  405. colFormat := "auto"
  406. for _, t := range col.Children {
  407. if t.Name == parser.NodeAS {
  408. colLabel = t.Children[0].Token.Val
  409. } else if t.Name == parser.NodeFORMAT {
  410. colFormat = t.Children[0].Token.Val
  411. } else if t.Name != parser.NodeFUNC {
  412. return nil, nil, p.newRuntimeError(ErrInvalidConstruct, t.Name, t)
  413. }
  414. }
  415. // Fill col attributes
  416. p.colLabels = append(p.colLabels, colLabel)
  417. p.colFormat = append(p.colFormat, colFormat)
  418. p.colData = append(p.colData, colData)
  419. p.colFunc = append(p.colFunc, colFunc)
  420. // Populate attrsNodes and attrsEdges
  421. if isNode {
  422. p.attrsNodes[pos][attr] = ""
  423. } else {
  424. p.attrsEdges[pos][attr] = ""
  425. }
  426. }
  427. }
  428. return nodeKindPos, edgeKindPos, nil
  429. }
  430. /*
  431. next advances to the next query row. Returns false if no more rows are available.
  432. It is assumed that all traversal specs and query attrs have been filled.
  433. */
  434. func (p *eqlRuntimeProvider) next() (bool, error) {
  435. // Create fetch lists if it is the first next() call
  436. if p._attrsNodesFetch == nil {
  437. makeFetchList := func(attrs []map[string]string, isEdge bool) [][]string {
  438. var fetchlist [][]string
  439. for _, attrs := range attrs {
  440. var attrsFetch []string
  441. for attr := range attrs {
  442. // Condition needs to be different for nodes and edges
  443. if !isEdge && attr != "" && attr != data.NodeKey && attr != data.NodeKind {
  444. attrsFetch = append(attrsFetch, attr)
  445. } else if attr != "" && attr != data.NodeKey && attr != data.NodeKind &&
  446. attr != data.EdgeEnd1Key && attr != data.EdgeEnd1Kind &&
  447. attr != data.EdgeEnd1Role && attr != data.EdgeEnd1Cascading &&
  448. attr != data.EdgeEnd2Key && attr != data.EdgeEnd2Kind &&
  449. attr != data.EdgeEnd2Role && attr != data.EdgeEnd2Cascading {
  450. attrsFetch = append(attrsFetch, attr)
  451. }
  452. }
  453. fetchlist = append(fetchlist, attrsFetch)
  454. }
  455. return fetchlist
  456. }
  457. p._attrsNodesFetch = makeFetchList(p.attrsNodes, false)
  458. p._attrsEdgesFetch = makeFetchList(p.attrsEdges, true)
  459. }
  460. // Make sure we have the row and rowEdge arrays
  461. if p.rowNode == nil {
  462. p.rowNode = make([]data.Node, 0)
  463. p.rowEdge = make([]data.Edge, 0)
  464. }
  465. // Check if a traversal can handle the call
  466. for _, child := range p.traversals {
  467. childRuntime := child.Runtime.(*traversalRuntime)
  468. if childRuntime.hasMoreNodes() {
  469. _, err := childRuntime.Eval()
  470. return err == nil, err
  471. }
  472. }
  473. // Get next root node
  474. startKey, err := p.nextStartKey()
  475. if err != nil || startKey == "" {
  476. return false, err
  477. }
  478. // Fetch node - always require the key attribute
  479. // to make sure we get a node back if it exists
  480. node, err := p.gm.FetchNodePart(p.part, startKey, p.specs[0],
  481. append(p._attrsNodesFetch[0], "key"))
  482. if err != nil || node == nil {
  483. return false, err
  484. }
  485. // Decide if this node should be added
  486. addNode := true
  487. if p.where != nil {
  488. res, err := p.where.Runtime.(CondRuntime).CondEval(node, nil)
  489. if err != nil {
  490. return false, err
  491. }
  492. addNode = res.(bool)
  493. }
  494. if addNode {
  495. // Add node and the first traversal
  496. if len(p.rowNode) == 0 {
  497. p.rowNode = append(p.rowNode, node)
  498. p.rowEdge = append(p.rowEdge, nil)
  499. } else {
  500. // Clear out the row
  501. for i := range p.rowNode {
  502. p.rowNode[i] = nil
  503. p.rowEdge[i] = nil
  504. }
  505. // Fill in the first node
  506. p.rowNode[0] = node
  507. p.rowEdge[0] = nil
  508. }
  509. // Give the new source to the children and let them evaluate
  510. for _, child := range p.traversals {
  511. childRuntime := child.Runtime.(*traversalRuntime)
  512. if err := childRuntime.newSource(node); err == ErrEmptyTraversal {
  513. // If an empty traversal error comes back advance until
  514. // there is an element or the end
  515. p.rowNode[0] = nil
  516. p.rowEdge[0] = nil
  517. return p.next()
  518. } else if err != nil {
  519. return false, err
  520. }
  521. }
  522. } else {
  523. // Recursively call next until there is a condition-matching node or
  524. // there are no more start keys available
  525. return p.next()
  526. }
  527. return true, nil
  528. }
  529. /*
  530. Instance function for general components
  531. */
  532. type generalInst func(*eqlRuntimeProvider, *parser.ASTNode) parser.Runtime
  533. /*
  534. Runtime map for general components
  535. */
  536. var generalProviderMap = map[string]generalInst{
  537. parser.NodeEOF: invalidRuntimeInst,
  538. parser.NodeVALUE: valueRuntimeInst,
  539. parser.NodeTRUE: valueRuntimeInst,
  540. parser.NodeFALSE: valueRuntimeInst,
  541. parser.NodeNULL: valueRuntimeInst,
  542. parser.NodeLIST: valueRuntimeInst,
  543. parser.NodeFUNC: valueRuntimeInst,
  544. parser.NodeTRAVERSE: traversalRuntimeInst,
  545. parser.NodeWHERE: whereRuntimeInst,
  546. // Condition components
  547. // ====================
  548. parser.NodeEQ: equalRuntimeInst,
  549. parser.NodeNEQ: notEqualRuntimeInst,
  550. parser.NodeLT: lessThanRuntimeInst,
  551. parser.NodeLEQ: lessThanEqualsRuntimeInst,
  552. parser.NodeGT: greaterThanRuntimeInst,
  553. parser.NodeGEQ: greaterThanEqualsRuntimeInst,
  554. parser.NodeNOT: notRuntimeInst,
  555. parser.NodeAND: andRuntimeInst,
  556. parser.NodeOR: orRuntimeInst,
  557. // Simple arithmetic expressions
  558. parser.NodePLUS: plusRuntimeInst,
  559. parser.NodeMINUS: minusRuntimeInst,
  560. parser.NodeTIMES: timesRuntimeInst,
  561. parser.NodeDIV: divRuntimeInst,
  562. parser.NodeMODINT: modIntRuntimeInst,
  563. parser.NodeDIVINT: divIntRuntimeInst,
  564. // List operations
  565. parser.NodeIN: inRuntimeInst,
  566. parser.NodeNOTIN: notInRuntimeInst,
  567. // String operations
  568. parser.NodeLIKE: likeRuntimeInst,
  569. parser.NodeCONTAINS: containsRuntimeInst,
  570. parser.NodeCONTAINSNOT: containsNotRuntimeInst,
  571. parser.NodeBEGINSWITH: beginsWithRuntimeInst,
  572. parser.NodeENDSWITH: endsWithRuntimeInst,
  573. }