expression.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. package workflow
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. )
  9. // ExpressionEvaluator evaluates workflow expressions.
  10. type ExpressionEvaluator struct {
  11. context ContextAccessor
  12. }
  13. // NewExpressionEvaluator creates a new expression evaluator
  14. func NewExpressionEvaluator(ctx ContextAccessor) *ExpressionEvaluator {
  15. return &ExpressionEvaluator{
  16. context: ctx,
  17. }
  18. }
  19. // EvaluateValue evaluates an arbitrary JSON value using the = prefix convention:
  20. // - Non-string types (bool, number, etc.): returned as-is (literal value)
  21. // - String not starting with "=": returned as literal string
  22. // - String starting with "=": remainder evaluated as expression (e.g. "=$name")
  23. // - String starting with "==": leading "=" removed, rest returned as literal string (e.g. "==foo" → "=foo")
  24. func (e *ExpressionEvaluator) EvaluateValue(value interface{}) (interface{}, error) {
  25. str, ok := value.(string)
  26. if !ok {
  27. // Non-string types are literal values
  28. return value, nil
  29. }
  30. if !strings.HasPrefix(str, "=") {
  31. // No = prefix: literal string
  32. return str, nil
  33. }
  34. if strings.HasPrefix(str, "==") {
  35. // == prefix: escaped literal, strip one leading =
  36. return str[1:], nil
  37. }
  38. // Single = prefix: evaluate the rest as expression
  39. return e.Evaluate(str[1:])
  40. }
  41. // Evaluate evaluates an expression string and returns the result
  42. func (e *ExpressionEvaluator) Evaluate(expr string) (interface{}, error) {
  43. if expr == "" {
  44. return nil, nil
  45. }
  46. expr = strings.TrimSpace(expr)
  47. // Handle string literals (must be a simple quoted string, not a complex expression)
  48. if isSimpleStringLiteral(expr) {
  49. return expr[1 : len(expr)-1], nil
  50. }
  51. // Handle boolean literals
  52. if expr == "true" {
  53. return true, nil
  54. }
  55. if expr == "false" {
  56. return false, nil
  57. }
  58. // Handle null/nil
  59. if expr == "null" || expr == "nil" {
  60. return nil, nil
  61. }
  62. // Handle numeric literals
  63. if num, err := strconv.ParseInt(expr, 10, 64); err == nil {
  64. return num, nil
  65. }
  66. if num, err := strconv.ParseFloat(expr, 64); err == nil {
  67. return num, nil
  68. }
  69. // Handle JSON literal arrays and objects
  70. if strings.HasPrefix(expr, "[") || strings.HasPrefix(expr, "{") {
  71. var parsed interface{}
  72. if err := json.Unmarshal([]byte(expr), &parsed); err == nil {
  73. return parsed, nil
  74. }
  75. }
  76. // Handle logical operators (check before comparison operators)
  77. if strings.Contains(expr, "&&") {
  78. return e.evaluateLogicalAnd(expr)
  79. }
  80. if strings.Contains(expr, "||") {
  81. return e.evaluateLogicalOr(expr)
  82. }
  83. // Handle comparison operators (check before variable references)
  84. if strings.Contains(expr, "==") {
  85. return e.evaluateBinaryOp(expr, "==")
  86. }
  87. if strings.Contains(expr, "!=") {
  88. return e.evaluateBinaryOp(expr, "!=")
  89. }
  90. if strings.Contains(expr, ">=") {
  91. return e.evaluateBinaryOp(expr, ">=")
  92. }
  93. if strings.Contains(expr, "<=") {
  94. return e.evaluateBinaryOp(expr, "<=")
  95. }
  96. if strings.Contains(expr, ">") {
  97. return e.evaluateBinaryOp(expr, ">")
  98. }
  99. if strings.Contains(expr, "<") {
  100. return e.evaluateBinaryOp(expr, "<")
  101. }
  102. // Handle negation
  103. if strings.HasPrefix(expr, "!") {
  104. val, err := e.Evaluate(strings.TrimSpace(expr[1:]))
  105. if err != nil {
  106. return nil, err
  107. }
  108. return !toBool(val), nil
  109. }
  110. // Handle arithmetic operators
  111. if e.findOperatorIndex(expr, "+") != -1 {
  112. return e.evaluateArithmetic(expr, "+")
  113. }
  114. if e.findOperatorIndex(expr, "-") != -1 && !e.isSimpleNegativeNumber(expr) {
  115. return e.evaluateArithmetic(expr, "-")
  116. }
  117. if strings.Contains(expr, "*") {
  118. return e.evaluateArithmetic(expr, "*")
  119. }
  120. if strings.Contains(expr, "/") {
  121. return e.evaluateArithmetic(expr, "/")
  122. }
  123. // Handle variable references
  124. if strings.HasPrefix(expr, "$") || strings.HasPrefix(expr, "_") || strings.HasPrefix(expr, "SYSVAR.") {
  125. return e.evaluateVariableReference(expr)
  126. }
  127. // Try to resolve as a path (could be param reference or variable reference)
  128. return e.evaluateVariableReference(expr)
  129. }
  130. // isSimpleStringLiteral checks if expr is a simple quoted string (not a complex expression)
  131. func isSimpleStringLiteral(expr string) bool {
  132. if len(expr) < 2 {
  133. return false
  134. }
  135. quote := expr[0]
  136. if quote != '"' && quote != '\'' {
  137. return false
  138. }
  139. if expr[len(expr)-1] != quote {
  140. return false
  141. }
  142. // Check that there are no unescaped quotes in the middle
  143. for i := 1; i < len(expr)-1; i++ {
  144. if expr[i] == quote && (i == 0 || expr[i-1] != '\\') {
  145. return false
  146. }
  147. }
  148. return true
  149. }
  150. // isSimpleNegativeNumber checks if expr is just a negative number like "-5"
  151. func (e *ExpressionEvaluator) isSimpleNegativeNumber(expr string) bool {
  152. if !strings.HasPrefix(expr, "-") {
  153. return false
  154. }
  155. rest := strings.TrimSpace(expr[1:])
  156. if _, err := strconv.ParseInt(rest, 10, 64); err == nil {
  157. return true
  158. }
  159. if _, err := strconv.ParseFloat(rest, 64); err == nil {
  160. return true
  161. }
  162. return false
  163. }
  164. // evaluateVariableReference resolves variable references and paths
  165. func (e *ExpressionEvaluator) evaluateVariableReference(path string) (interface{}, error) {
  166. parts := e.parsePath(path)
  167. if len(parts) == 0 {
  168. return nil, fmt.Errorf("empty variable path")
  169. }
  170. // Get the root variable
  171. var root interface{}
  172. var err error
  173. startIndex := 1
  174. rootName := parts[0]
  175. if strings.HasPrefix(rootName, "$") {
  176. // Global variable
  177. root, err = e.getGlobalVariable(rootName)
  178. } else if rootName == "SYSVAR" {
  179. // System variable - combine SYSVAR with next part for lookup
  180. if len(parts) < 2 {
  181. return nil, fmt.Errorf("incomplete SYSVAR reference")
  182. }
  183. sysvarKey := "SYSVAR." + parts[1]
  184. root, err = e.getSystemVariable(sysvarKey)
  185. startIndex = 2
  186. } else if strings.HasPrefix(rootName, "_") {
  187. // Local variable
  188. root, err = e.getLocalVariable(rootName)
  189. } else {
  190. // Try as parameter reference
  191. root, err = e.getParameter(rootName)
  192. if err != nil {
  193. return nil, err
  194. }
  195. }
  196. if err != nil {
  197. return nil, err
  198. }
  199. // Navigate the path
  200. current := root
  201. for i := startIndex; i < len(parts); i++ {
  202. part := parts[i]
  203. current, err = e.navigatePath(current, part)
  204. if err != nil {
  205. return nil, err
  206. }
  207. }
  208. return current, nil
  209. }
  210. // parsePath parses a variable path into segments
  211. // Handles: $var.field, $var[index], $var["literal"], SYSVAR.xxx
  212. func (e *ExpressionEvaluator) parsePath(path string) []string {
  213. var parts []string
  214. current := ""
  215. inBracket := false
  216. bracketContent := ""
  217. for i := 0; i < len(path); i++ {
  218. ch := path[i]
  219. switch ch {
  220. case '.':
  221. if inBracket {
  222. bracketContent += string(ch)
  223. } else {
  224. if current != "" {
  225. parts = append(parts, current)
  226. current = ""
  227. }
  228. }
  229. case '[':
  230. if current != "" {
  231. parts = append(parts, current)
  232. current = ""
  233. }
  234. inBracket = true
  235. bracketContent = ""
  236. case ']':
  237. if inBracket {
  238. // Evaluate bracket content as expression
  239. val, err := e.Evaluate(bracketContent)
  240. if err == nil {
  241. parts = append(parts, fmt.Sprintf("[%v]", val))
  242. }
  243. inBracket = false
  244. bracketContent = ""
  245. }
  246. default:
  247. if inBracket {
  248. bracketContent += string(ch)
  249. } else {
  250. current += string(ch)
  251. }
  252. }
  253. }
  254. if current != "" {
  255. parts = append(parts, current)
  256. }
  257. return parts
  258. }
  259. // navigatePath navigates one level in a nested structure
  260. func (e *ExpressionEvaluator) navigatePath(obj interface{}, key string) (interface{}, error) {
  261. if obj == nil {
  262. return nil, fmt.Errorf("cannot navigate path on nil object")
  263. }
  264. // Handle .length property on arrays, slices, and strings
  265. if key == "length" {
  266. val := reflect.ValueOf(obj)
  267. switch val.Kind() {
  268. case reflect.Slice, reflect.Array:
  269. return int64(val.Len()), nil
  270. case reflect.String:
  271. // Return the number of Unicode code points (runes), not bytes
  272. return int64(len([]rune(val.String()))), nil
  273. }
  274. // Fall through to map lookup so user-defined "length" keys still work
  275. }
  276. // Handle array/slice index: [index]
  277. if strings.HasPrefix(key, "[") && strings.HasSuffix(key, "]") {
  278. indexStr := key[1 : len(key)-1]
  279. index, err := strconv.Atoi(indexStr)
  280. if err != nil {
  281. return nil, fmt.Errorf("invalid array index: %s", indexStr)
  282. }
  283. val := reflect.ValueOf(obj)
  284. if val.Kind() == reflect.Slice || val.Kind() == reflect.Array {
  285. if index < 0 || index >= val.Len() {
  286. return nil, fmt.Errorf("index out of range: %d", index)
  287. }
  288. return val.Index(index).Interface(), nil
  289. }
  290. return nil, fmt.Errorf("cannot index non-array type")
  291. }
  292. // Handle map/struct field access
  293. val := reflect.ValueOf(obj)
  294. if val.Kind() == reflect.Map {
  295. mapVal := val.MapIndex(reflect.ValueOf(key))
  296. if mapVal.IsValid() {
  297. return mapVal.Interface(), nil
  298. }
  299. return nil, nil
  300. }
  301. if val.Kind() == reflect.Struct {
  302. field := val.FieldByName(key)
  303. if field.IsValid() {
  304. return field.Interface(), nil
  305. }
  306. return nil, fmt.Errorf("field not found: %s", key)
  307. }
  308. return nil, fmt.Errorf("cannot access field on type: %T", obj)
  309. }
  310. // getParameter retrieves a parameter
  311. func (e *ExpressionEvaluator) getParameter(name string) (interface{}, error) {
  312. if val, ok := e.context.GetParam(name); ok {
  313. return val, nil
  314. }
  315. return nil, fmt.Errorf("parameter not found: %s", name)
  316. }
  317. // getGlobalVariable retrieves a global variable
  318. func (e *ExpressionEvaluator) getGlobalVariable(name string) (interface{}, error) {
  319. if val, ok := e.context.GetVariable(name); ok {
  320. return val, nil
  321. }
  322. return nil, nil // Undefined variables return nil
  323. }
  324. // getSystemVariable retrieves a system variable
  325. func (e *ExpressionEvaluator) getSystemVariable(name string) (interface{}, error) {
  326. if val, ok := e.context.GetSystemVar(name); ok {
  327. return val, nil
  328. }
  329. return nil, fmt.Errorf("system variable not found: %s", name)
  330. }
  331. // getLocalVariable retrieves a local variable
  332. func (e *ExpressionEvaluator) getLocalVariable(name string) (interface{}, error) {
  333. if val, ok := e.context.GetLocalVar(name); ok {
  334. return val, nil
  335. }
  336. return nil, fmt.Errorf("local variable not found: %s", name)
  337. }
  338. // evaluateBinaryOp evaluates binary comparison operations
  339. func (e *ExpressionEvaluator) evaluateBinaryOp(expr string, op string) (interface{}, error) {
  340. parts := strings.SplitN(expr, op, 2)
  341. if len(parts) != 2 {
  342. return nil, fmt.Errorf("invalid binary operation: %s", expr)
  343. }
  344. left, err := e.Evaluate(strings.TrimSpace(parts[0]))
  345. if err != nil {
  346. return nil, err
  347. }
  348. right, err := e.Evaluate(strings.TrimSpace(parts[1]))
  349. if err != nil {
  350. return nil, err
  351. }
  352. return e.compare(left, right, op)
  353. }
  354. // compare compares two values based on the operator
  355. func (e *ExpressionEvaluator) compare(left, right interface{}, op string) (bool, error) {
  356. switch op {
  357. case "==":
  358. return e.equals(left, right), nil
  359. case "!=":
  360. return !e.equals(left, right), nil
  361. case ">", ">=", "<", "<=":
  362. return e.compareNumeric(left, right, op)
  363. default:
  364. return false, fmt.Errorf("unknown operator: %s", op)
  365. }
  366. }
  367. // equals compares two values for equality, normalizing numeric types
  368. func (e *ExpressionEvaluator) equals(left, right interface{}) bool {
  369. // Handle nil cases
  370. if left == nil && right == nil {
  371. return true
  372. }
  373. if left == nil || right == nil {
  374. return false
  375. }
  376. // Normalize numeric types for comparison
  377. if isNumeric(left) && isNumeric(right) {
  378. return toFloat64(left) == toFloat64(right)
  379. }
  380. return reflect.DeepEqual(left, right)
  381. }
  382. func isNumeric(val interface{}) bool {
  383. switch val.(type) {
  384. case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
  385. return true
  386. default:
  387. return false
  388. }
  389. }
  390. // compareNumeric compares numeric values
  391. func (e *ExpressionEvaluator) compareNumeric(left, right interface{}, op string) (bool, error) {
  392. l := toFloat64(left)
  393. r := toFloat64(right)
  394. switch op {
  395. case ">":
  396. return l > r, nil
  397. case ">=":
  398. return l >= r, nil
  399. case "<":
  400. return l < r, nil
  401. case "<=":
  402. return l <= r, nil
  403. default:
  404. return false, fmt.Errorf("unknown numeric operator: %s", op)
  405. }
  406. }
  407. // evaluateLogicalAnd evaluates logical AND
  408. func (e *ExpressionEvaluator) evaluateLogicalAnd(expr string) (interface{}, error) {
  409. parts := strings.Split(expr, "&&")
  410. for _, part := range parts {
  411. val, err := e.Evaluate(strings.TrimSpace(part))
  412. if err != nil {
  413. return nil, err
  414. }
  415. if !toBool(val) {
  416. return false, nil
  417. }
  418. }
  419. return true, nil
  420. }
  421. // evaluateLogicalOr evaluates logical OR
  422. func (e *ExpressionEvaluator) evaluateLogicalOr(expr string) (interface{}, error) {
  423. parts := strings.Split(expr, "||")
  424. for _, part := range parts {
  425. val, err := e.Evaluate(strings.TrimSpace(part))
  426. if err != nil {
  427. return nil, err
  428. }
  429. if toBool(val) {
  430. return true, nil
  431. }
  432. }
  433. return false, nil
  434. }
  435. // evaluateArithmetic evaluates arithmetic operations
  436. func (e *ExpressionEvaluator) evaluateArithmetic(expr string, op string) (interface{}, error) {
  437. // Find the operator (avoiding string literals)
  438. opIndex := e.findOperatorIndex(expr, op)
  439. if opIndex == -1 {
  440. return nil, fmt.Errorf("operator not found: %s", op)
  441. }
  442. leftExpr := strings.TrimSpace(expr[:opIndex])
  443. rightExpr := strings.TrimSpace(expr[opIndex+len(op):])
  444. left, err := e.Evaluate(leftExpr)
  445. if err != nil {
  446. return nil, err
  447. }
  448. right, err := e.Evaluate(rightExpr)
  449. if err != nil {
  450. return nil, err
  451. }
  452. // Handle string concatenation with +
  453. if op == "+" {
  454. if isString(left) || isString(right) {
  455. return toString(left) + toString(right), nil
  456. }
  457. }
  458. // Numeric operations
  459. l := toFloat64(left)
  460. r := toFloat64(right)
  461. switch op {
  462. case "+":
  463. return l + r, nil
  464. case "-":
  465. return l - r, nil
  466. case "*":
  467. return l * r, nil
  468. case "/":
  469. if r == 0 {
  470. return nil, fmt.Errorf("division by zero")
  471. }
  472. return l / r, nil
  473. default:
  474. return nil, fmt.Errorf("unknown arithmetic operator: %s", op)
  475. }
  476. }
  477. // findOperatorIndex finds the index of an operator, avoiding string literals
  478. func (e *ExpressionEvaluator) findOperatorIndex(expr string, op string) int {
  479. inString := false
  480. stringChar := rune(0)
  481. for i := 0; i < len(expr)-len(op)+1; i++ {
  482. ch := rune(expr[i])
  483. if ch == '"' || ch == '\'' {
  484. if !inString {
  485. inString = true
  486. stringChar = ch
  487. } else if ch == stringChar {
  488. inString = false
  489. }
  490. }
  491. if !inString && strings.HasPrefix(expr[i:], op) {
  492. return i
  493. }
  494. }
  495. return -1
  496. }
  497. // Helper functions
  498. func toBool(val interface{}) bool {
  499. if val == nil {
  500. return false
  501. }
  502. switch v := val.(type) {
  503. case bool:
  504. return v
  505. case int, int64, float64:
  506. return toFloat64(v) != 0
  507. case string:
  508. return v != ""
  509. default:
  510. return true
  511. }
  512. }
  513. func toFloat64(val interface{}) float64 {
  514. if val == nil {
  515. return 0
  516. }
  517. switch v := val.(type) {
  518. case int:
  519. return float64(v)
  520. case int64:
  521. return float64(v)
  522. case float64:
  523. return v
  524. case float32:
  525. return float64(v)
  526. case string:
  527. f, _ := strconv.ParseFloat(v, 64)
  528. return f
  529. default:
  530. return 0
  531. }
  532. }
  533. func toString(val interface{}) string {
  534. if val == nil {
  535. return ""
  536. }
  537. return fmt.Sprintf("%v", val)
  538. }
  539. func isString(val interface{}) bool {
  540. _, ok := val.(string)
  541. return ok
  542. }
  543. // SetVariable sets a variable value using a path expression
  544. func (e *ExpressionEvaluator) SetVariable(path string, value interface{}) error {
  545. parts := e.parsePath(path)
  546. if len(parts) == 0 {
  547. return fmt.Errorf("empty variable path")
  548. }
  549. rootName := parts[0]
  550. // Check if trying to set a parameter (read-only)
  551. if !strings.HasPrefix(rootName, "$") && !strings.HasPrefix(rootName, "_") {
  552. // This is a parameter reference - check if it exists
  553. if _, ok := e.context.GetParam(rootName); ok {
  554. return fmt.Errorf("cannot modify parameter: %s (parameters are read-only)", rootName)
  555. }
  556. }
  557. // Simple case: direct assignment
  558. if len(parts) == 1 {
  559. if strings.HasPrefix(rootName, "$") {
  560. // Apply type conversion if needed
  561. convertedValue, err := e.applyTypeConversion(rootName, value)
  562. if err != nil {
  563. return err
  564. }
  565. e.context.SetVariable(rootName, convertedValue)
  566. return nil
  567. }
  568. return fmt.Errorf("cannot set non-global variable: %s", rootName)
  569. }
  570. // Special case: $var[index] - direct slice index assignment with auto-grow
  571. if len(parts) == 2 && strings.HasPrefix(rootName, "$") {
  572. lastKey := parts[1]
  573. if strings.HasPrefix(lastKey, "[") && strings.HasSuffix(lastKey, "]") {
  574. indexStr := lastKey[1 : len(lastKey)-1]
  575. index, err := strconv.Atoi(indexStr)
  576. if err == nil {
  577. // Use atomic SetArrayIndex to avoid race conditions
  578. e.context.SetArrayIndex(rootName, index, value)
  579. return nil
  580. }
  581. }
  582. }
  583. // Deep set with auto-creation of intermediates
  584. if !strings.HasPrefix(rootName, "$") {
  585. return fmt.Errorf("cannot set non-global variable: %s", rootName)
  586. }
  587. root, _ := e.context.GetVariable(rootName)
  588. if root == nil {
  589. // Auto-create root: slice if next part is [N], map otherwise
  590. if isArrayIndex(parts[1]) {
  591. root = make([]interface{}, 0)
  592. } else {
  593. root = make(map[string]interface{})
  594. }
  595. e.context.SetVariable(rootName, root)
  596. }
  597. // Navigate to parent of the final field, auto-creating intermediates.
  598. // We track (parent, key) so we can propagate slice growth back up.
  599. current := root
  600. for i := 1; i < len(parts)-1; i++ {
  601. key := parts[i]
  602. // Determine what type the next level should be
  603. nextIsArray := i+1 < len(parts) && isArrayIndex(parts[i+1])
  604. if isArrayIndex(key) {
  605. idx, _ := strconv.Atoi(key[1 : len(key)-1])
  606. slice, ok := current.([]interface{})
  607. if !ok {
  608. return fmt.Errorf("cannot index non-array type %T at path segment %s", current, key)
  609. }
  610. // Auto-grow slice
  611. if idx >= len(slice) {
  612. grown := make([]interface{}, idx+1)
  613. copy(grown, slice)
  614. slice = grown
  615. // Propagate grown slice back: if root level, update variable
  616. if i == 1 {
  617. e.context.SetVariable(rootName, slice)
  618. }
  619. }
  620. // Auto-create element if nil
  621. if slice[idx] == nil {
  622. if nextIsArray {
  623. slice[idx] = make([]interface{}, 0)
  624. } else {
  625. slice[idx] = make(map[string]interface{})
  626. }
  627. }
  628. current = slice[idx]
  629. } else {
  630. m, ok := current.(map[string]interface{})
  631. if !ok {
  632. return fmt.Errorf("cannot access field %s on type %T", key, current)
  633. }
  634. if m[key] == nil {
  635. if nextIsArray {
  636. m[key] = make([]interface{}, 0)
  637. } else {
  638. m[key] = make(map[string]interface{})
  639. }
  640. }
  641. current = m[key]
  642. }
  643. }
  644. // Set the final field
  645. lastKey := parts[len(parts)-1]
  646. return e.setField(current, lastKey, value)
  647. }
  648. // setField sets a field on an object
  649. func (e *ExpressionEvaluator) setField(obj interface{}, key string, value interface{}) error {
  650. if obj == nil {
  651. return fmt.Errorf("cannot set field on nil object")
  652. }
  653. // Handle map
  654. if m, ok := obj.(map[string]interface{}); ok {
  655. m[key] = value
  656. return nil
  657. }
  658. // Handle slice index assignment (key is "[N]" format)
  659. if strings.HasPrefix(key, "[") && strings.HasSuffix(key, "]") {
  660. indexStr := key[1 : len(key)-1]
  661. index, err := strconv.Atoi(indexStr)
  662. if err != nil {
  663. return fmt.Errorf("invalid array index: %s", indexStr)
  664. }
  665. rv := reflect.ValueOf(obj)
  666. if rv.Kind() == reflect.Slice {
  667. // Grow slice if necessary
  668. if index >= rv.Len() {
  669. // We need to get the pointer to the slice in the parent to grow it
  670. // For now, return an error if index is out of bounds
  671. return fmt.Errorf("array index %d out of bounds (length %d)", index, rv.Len())
  672. }
  673. rv.Index(index).Set(reflect.ValueOf(value))
  674. return nil
  675. }
  676. }
  677. return fmt.Errorf("cannot set field on type: %T", obj)
  678. }
  679. // isArrayIndex returns true if the path segment is an array index like "[0]", "[2]"
  680. func isArrayIndex(segment string) bool {
  681. return strings.HasPrefix(segment, "[") && strings.HasSuffix(segment, "]")
  682. }
  683. // EvaluateDeep recursively evaluates expressions in nested structures (maps, arrays)
  684. // It walks through the entire structure and evaluates any string values that look like expressions
  685. func (e *ExpressionEvaluator) EvaluateDeep(value interface{}) (interface{}, error) {
  686. switch v := value.(type) {
  687. case string:
  688. // Evaluate string values using EvaluateValue (handles = prefix convention)
  689. return e.EvaluateValue(v)
  690. case map[string]interface{}:
  691. // Recursively evaluate all values in the map
  692. result := make(map[string]interface{})
  693. for key, val := range v {
  694. evaluated, err := e.EvaluateDeep(val)
  695. if err != nil {
  696. return nil, fmt.Errorf("failed to evaluate map key %s: %w", key, err)
  697. }
  698. result[key] = evaluated
  699. }
  700. return result, nil
  701. case []interface{}:
  702. // Recursively evaluate all elements in the array
  703. result := make([]interface{}, len(v))
  704. for i, val := range v {
  705. evaluated, err := e.EvaluateDeep(val)
  706. if err != nil {
  707. return nil, fmt.Errorf("failed to evaluate array index %d: %w", i, err)
  708. }
  709. result[i] = evaluated
  710. }
  711. return result, nil
  712. default:
  713. // For other types (int, bool, float, etc.), return as-is
  714. return value, nil
  715. }
  716. }
  717. // applyTypeConversion applies type conversion based on variable type declaration
  718. // If the variable is declared as OBJECT and the value is a string, it parses the string as JSON
  719. // If the variable is declared as an array type (e.g., [OBJECT], [STRING]) and the value is a string, it parses as JSON array
  720. // If the variable is declared as STRING and the value is not a string, it marshals the value to JSON
  721. func (e *ExpressionEvaluator) applyTypeConversion(varName string, value interface{}) (interface{}, error) {
  722. // Get base context to access VarTypes
  723. baseCtx := e.context.GetBaseContext()
  724. if baseCtx.VarTypes == nil {
  725. return value, nil
  726. }
  727. // Check if variable has a type declaration
  728. varType, ok := baseCtx.VarTypes[varName]
  729. if !ok {
  730. return value, nil
  731. }
  732. // Check if type is an array type like [OBJECT], [STRING], etc.
  733. if strings.HasPrefix(varType, "[") && strings.HasSuffix(varType, "]") {
  734. // Array type
  735. if str, ok := value.(string); ok {
  736. var result []interface{}
  737. if err := json.Unmarshal([]byte(str), &result); err != nil {
  738. return nil, fmt.Errorf("failed to convert string to %s for variable %s (original string: %q): %w", varType, varName, str, err)
  739. }
  740. return result, nil
  741. }
  742. } else if varType == "OBJECT" {
  743. // If type is OBJECT and value is string, parse as JSON
  744. if str, ok := value.(string); ok {
  745. var result map[string]interface{}
  746. if err := json.Unmarshal([]byte(str), &result); err != nil {
  747. return nil, fmt.Errorf("failed to convert string to OBJECT for variable %s (original string: %q): %w", varName, str, err)
  748. }
  749. return result, nil
  750. }
  751. } else if varType == "INT" {
  752. // If type is INT and value is a string, parse as integer
  753. if str, ok := value.(string); ok {
  754. if n, err := strconv.ParseInt(str, 10, 64); err == nil {
  755. return n, nil
  756. }
  757. // Also try parsing as float then truncating
  758. if f, err := strconv.ParseFloat(str, 64); err == nil {
  759. return int64(f), nil
  760. }
  761. return nil, fmt.Errorf("failed to convert string %q to INT for variable %s", str, varName)
  762. }
  763. // If value is float64, convert to int64
  764. if f, ok := value.(float64); ok {
  765. return int64(f), nil
  766. }
  767. } else if varType == "FLOAT" {
  768. // If type is FLOAT and value is a string, parse as float
  769. if str, ok := value.(string); ok {
  770. if f, err := strconv.ParseFloat(str, 64); err == nil {
  771. return f, nil
  772. }
  773. return nil, fmt.Errorf("failed to convert string %q to FLOAT for variable %s", str, varName)
  774. }
  775. // If value is int64, convert to float64
  776. if n, ok := value.(int64); ok {
  777. return float64(n), nil
  778. }
  779. } else if varType == "STRING" {
  780. // If type is STRING and value is not a string, marshal to JSON
  781. if _, ok := value.(string); !ok {
  782. // For non-string values (objects, arrays, etc.), marshal to JSON string
  783. jsonBytes, err := json.Marshal(value)
  784. if err != nil {
  785. return nil, fmt.Errorf("failed to convert %T to STRING for variable %s: %w", value, varName, err)
  786. }
  787. return string(jsonBytes), nil
  788. }
  789. }
  790. return value, nil
  791. }