@divmain/jdm-asm 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/.github/workflows/ci.yml +53 -0
  2. package/.oxfmtrc.json +16 -0
  3. package/.oxlintrc.json +183 -0
  4. package/AGENTS.md +81 -0
  5. package/README.md +769 -0
  6. package/asconfig.json +23 -0
  7. package/benchmarks/fixtures.ts +111 -0
  8. package/benchmarks/input-fixtures.ts +80 -0
  9. package/benchmarks/run.ts +913 -0
  10. package/benchmarks/worker-pool.ts +223 -0
  11. package/benchmarks/worker.ts +374 -0
  12. package/dist/index.d.ts +996 -0
  13. package/dist/index.js +12239 -0
  14. package/dist/index.js.map +1 -0
  15. package/package.json +49 -0
  16. package/scripts/run-all-tests.ts +220 -0
  17. package/src/compiler/EXPRESSION_SUBSETS.md +228 -0
  18. package/src/compiler/asc-compiler.ts +315 -0
  19. package/src/compiler/ast-types.ts +215 -0
  20. package/src/compiler/build.ts +56 -0
  21. package/src/compiler/cache.ts +414 -0
  22. package/src/compiler/code-generators.ts +211 -0
  23. package/src/compiler/codegen/index.ts +15 -0
  24. package/src/compiler/codegen/js-marshal.ts +999 -0
  25. package/src/compiler/codegen/js-validation.ts +243 -0
  26. package/src/compiler/codegen.ts +19 -0
  27. package/src/compiler/compile-time-validation.ts +507 -0
  28. package/src/compiler/cst-visitor.ts +434 -0
  29. package/src/compiler/errors.ts +227 -0
  30. package/src/compiler/expression-parser.ts +536 -0
  31. package/src/compiler/graph.ts +197 -0
  32. package/src/compiler/index.ts +199 -0
  33. package/src/compiler/input-validation.ts +33 -0
  34. package/src/compiler/marshal-gen.ts +21 -0
  35. package/src/compiler/nodes/context-resolvers.ts +197 -0
  36. package/src/compiler/nodes/decision-table.ts +507 -0
  37. package/src/compiler/nodes/decision.ts +292 -0
  38. package/src/compiler/nodes/expression-compiler.ts +526 -0
  39. package/src/compiler/nodes/expression.ts +425 -0
  40. package/src/compiler/nodes/function.ts +316 -0
  41. package/src/compiler/nodes/input.ts +60 -0
  42. package/src/compiler/nodes/switch.ts +547 -0
  43. package/src/compiler/optimizer.ts +948 -0
  44. package/src/compiler/orchestrator.ts +352 -0
  45. package/src/compiler/parser.ts +115 -0
  46. package/src/compiler/result-selection.ts +161 -0
  47. package/src/compiler/runtime/index.ts +26 -0
  48. package/src/compiler/runtime-codegen.ts +211 -0
  49. package/src/compiler/runtime-validation-codegen.ts +294 -0
  50. package/src/compiler/runtime.ts +452 -0
  51. package/src/compiler/schema.ts +245 -0
  52. package/src/compiler/switch-branch-detection.ts +92 -0
  53. package/src/compiler/types.ts +136 -0
  54. package/src/compiler/unary-ast-transforms.ts +148 -0
  55. package/src/compiler/unary-parser.ts +301 -0
  56. package/src/compiler/unary-transform.ts +161 -0
  57. package/src/compiler/utils.ts +27 -0
  58. package/src/compiler/virtual-fs.ts +90 -0
  59. package/src/compiler/wasm-instantiate.ts +127 -0
  60. package/src/index.ts +1 -0
  61. package/src/runtime/arrays.ts +579 -0
  62. package/src/runtime/context.ts +189 -0
  63. package/src/runtime/expressions.ts +1811 -0
  64. package/src/runtime/index.ts +8 -0
  65. package/src/runtime/memory.ts +607 -0
  66. package/src/runtime/strings.ts +260 -0
  67. package/src/runtime/tables.ts +96 -0
  68. package/src/runtime/tsconfig.json +4 -0
  69. package/src/runtime/values.ts +209 -0
  70. package/test-data/README.md +83 -0
  71. package/test-data/decision-tables/basic/8k.json +87992 -0
  72. package/test-data/decision-tables/basic/affiliate-commission-calculator.json +228 -0
  73. package/test-data/decision-tables/basic/airline-loyalty-points-calculations.json +285 -0
  74. package/test-data/decision-tables/basic/airline-upgrade-eligibility.json +466 -0
  75. package/test-data/decision-tables/basic/auto-insurance-premium-calculator.json +412 -0
  76. package/test-data/decision-tables/basic/booking-personalization-system.json +553 -0
  77. package/test-data/decision-tables/basic/care-team-assignment-system.json +585 -0
  78. package/test-data/decision-tables/basic/claim-validation-system.json +307 -0
  79. package/test-data/decision-tables/basic/clinical-lab-result-interpreter.json +433 -0
  80. package/test-data/decision-tables/basic/clinical-treatment-protocol.json +474 -0
  81. package/test-data/decision-tables/basic/credit-limit-adjustment.json +479 -0
  82. package/test-data/decision-tables/basic/customer-eligibility-engine.json +551 -0
  83. package/test-data/decision-tables/basic/customer-lifetime-value.json +200 -0
  84. package/test-data/decision-tables/basic/customer-onboarding-kyc-verification.json +611 -0
  85. package/test-data/decision-tables/basic/customer-service-escalation.json +191 -0
  86. package/test-data/decision-tables/basic/decision-table-discounts.json +168 -0
  87. package/test-data/decision-tables/basic/decision-table-shipping.json +398 -0
  88. package/test-data/decision-tables/basic/delivery-route-optimizer.json +271 -0
  89. package/test-data/decision-tables/basic/device-compatibility-checker.json +303 -0
  90. package/test-data/decision-tables/basic/disaster-relief-fund-allocation.json +296 -0
  91. package/test-data/decision-tables/basic/dynamic-fx-rate-pricing-system.json +237 -0
  92. package/test-data/decision-tables/basic/dynamic-marketplace-comission-calculator.json +242 -0
  93. package/test-data/decision-tables/basic/dynamic-shipping-cost-calculator.json +378 -0
  94. package/test-data/decision-tables/basic/dynamic-tarrif-engine.json +289 -0
  95. package/test-data/decision-tables/basic/dynamic-ticket-pricing.json +325 -0
  96. package/test-data/decision-tables/basic/empty-column-with-space.json +100 -0
  97. package/test-data/decision-tables/basic/empty-column-without-space.json +100 -0
  98. package/test-data/decision-tables/basic/environment-compliance-assessment.json +386 -0
  99. package/test-data/decision-tables/basic/expression-table-map.json +313 -0
  100. package/test-data/decision-tables/basic/flash-sale-eligibility.json +366 -0
  101. package/test-data/decision-tables/basic/flight-dispatch-decision-system.json +455 -0
  102. package/test-data/decision-tables/basic/flight-rebooking-fee-calculator.json +406 -0
  103. package/test-data/decision-tables/basic/government-assistance.json +299 -0
  104. package/test-data/decision-tables/basic/grant-funding-distribution.json +307 -0
  105. package/test-data/decision-tables/basic/hazardous-materials-management-system.json +414 -0
  106. package/test-data/decision-tables/basic/immigration-eligibility-evaluator.json +765 -0
  107. package/test-data/decision-tables/basic/import-duties-calculator.json +318 -0
  108. package/test-data/decision-tables/basic/insurance-agent-commission.json +228 -0
  109. package/test-data/decision-tables/basic/insurance-coverage-calculator.json +362 -0
  110. package/test-data/decision-tables/basic/insurance-underwriting-risk.json +321 -0
  111. package/test-data/decision-tables/basic/international-roaming-policy-manager.json +199 -0
  112. package/test-data/decision-tables/basic/legacy-plan-management.json +434 -0
  113. package/test-data/decision-tables/basic/marketplace-listing-verification-system.json +334 -0
  114. package/test-data/decision-tables/basic/medication-dosage-calculator.json +318 -0
  115. package/test-data/decision-tables/basic/merch-bags.json +171 -0
  116. package/test-data/decision-tables/basic/municipal-permit-evaluation-system.json +364 -0
  117. package/test-data/decision-tables/basic/mvno-partner-enablement.json +313 -0
  118. package/test-data/decision-tables/basic/partner-revenue-sharing.json +244 -0
  119. package/test-data/decision-tables/basic/payment-routing-and-fee-calculator.json +475 -0
  120. package/test-data/decision-tables/basic/policy-discount-calculator.json +307 -0
  121. package/test-data/decision-tables/basic/policy-eligibility-analyzer.json +299 -0
  122. package/test-data/decision-tables/basic/product-listing-scoring.json +358 -0
  123. package/test-data/decision-tables/basic/realtime-fraud-detection.json +235 -0
  124. package/test-data/decision-tables/basic/regional-compliance-manager.json +278 -0
  125. package/test-data/decision-tables/basic/returns-and-refund-policy.json +366 -0
  126. package/test-data/decision-tables/basic/returns-processing-system.json +448 -0
  127. package/test-data/decision-tables/basic/school-district-resource-allocation.json +282 -0
  128. package/test-data/decision-tables/basic/seat-map-optimization.json +325 -0
  129. package/test-data/decision-tables/basic/seller-fee-calculator.json +307 -0
  130. package/test-data/decision-tables/basic/service-level-agreement-enforcement.json +575 -0
  131. package/test-data/decision-tables/basic/smart-financial-product-matcher.json +249 -0
  132. package/test-data/decision-tables/basic/supply-chain-risk.json +316 -0
  133. package/test-data/decision-tables/basic/table-loop.json +93 -0
  134. package/test-data/decision-tables/basic/table.json +76 -0
  135. package/test-data/decision-tables/basic/traffic-violation-penalty-calculator.json +436 -0
  136. package/test-data/decision-tables/basic/transaction-compliance-classifier.json +525 -0
  137. package/test-data/decision-tables/basic/vehicle-claims-resolution.json +310 -0
  138. package/test-data/decision-tables/basic/warehouse-storage-location.json +345 -0
  139. package/test-data/decision-tables/hit-policy-collect/collect-multiple-matches.json +127 -0
  140. package/test-data/decision-tables/hit-policy-collect/collect-no-match.json +95 -0
  141. package/test-data/decision-tables/hit-policy-first/first-match.json +103 -0
  142. package/test-data/decision-tables/hit-policy-first/no-match.json +95 -0
  143. package/test-data/decision-tables/hit-policy-output-order/output-order-respected.json +94 -0
  144. package/test-data/decision-tables/hit-policy-output-order/string-output-order.json +94 -0
  145. package/test-data/decision-tables/hit-policy-priority/priority-respected.json +86 -0
  146. package/test-data/decision-tables/hit-policy-rule-order/rule-order-respected.json +94 -0
  147. package/test-data/decision-tables/hit-policy-unique/all-match-error.json +89 -0
  148. package/test-data/decision-tables/hit-policy-unique/multiple-match-error.json +89 -0
  149. package/test-data/decision-tables/hit-policy-unique/no-match.json +88 -0
  150. package/test-data/decision-tables/hit-policy-unique/unique-match.json +99 -0
  151. package/test-data/expressions/arithmetic/error-cyclic.json +114 -0
  152. package/test-data/expressions/arithmetic/error-missing-input.json +54 -0
  153. package/test-data/expressions/arithmetic/error-missing-output.json +54 -0
  154. package/test-data/expressions/arithmetic/expression-default.json +93 -0
  155. package/test-data/expressions/arithmetic/expression-fields.json +94 -0
  156. package/test-data/expressions/arithmetic/expression-loop.json +94 -0
  157. package/test-data/expressions/arithmetic/expression-passthrough.json +108 -0
  158. package/test-data/expressions/arithmetic/expression.json +69 -0
  159. package/test-data/expressions/arithmetic/nested-request.json +125 -0
  160. package/test-data/expressions/arithmetic/number-function.json +58 -0
  161. package/test-data/expressions/arithmetic/test-number-functions.json +68 -0
  162. package/test-data/expressions/functions/all.json +149 -0
  163. package/test-data/expressions/functions/avg.json +89 -0
  164. package/test-data/expressions/functions/filter.json +109 -0
  165. package/test-data/expressions/functions/flat.json +167 -0
  166. package/test-data/expressions/functions/map-strings.json +65 -0
  167. package/test-data/expressions/functions/map.json +73 -0
  168. package/test-data/expressions/functions/reduce.json +49 -0
  169. package/test-data/expressions/functions/some.json +175 -0
  170. package/test-data/expressions/functions/sort-strings.json +97 -0
  171. package/test-data/expressions/functions/sort.json +97 -0
  172. package/test-data/expressions/logical/logical-and.json +116 -0
  173. package/test-data/expressions/logical/logical-complex.json +260 -0
  174. package/test-data/expressions/logical/logical-not.json +111 -0
  175. package/test-data/expressions/logical/logical-or.json +123 -0
  176. package/test-data/expressions/string/string-comparison.json +128 -0
  177. package/test-data/expressions/string/string-concat.json +106 -0
  178. package/test-data/expressions/string/string-contains.json +125 -0
  179. package/test-data/expressions/string/string-endsWith.json +113 -0
  180. package/test-data/expressions/string/string-indexOf.json +131 -0
  181. package/test-data/expressions/string/string-join.json +92 -0
  182. package/test-data/expressions/string/string-lower.json +94 -0
  183. package/test-data/expressions/string/string-replace.json +130 -0
  184. package/test-data/expressions/string/string-split.json +101 -0
  185. package/test-data/expressions/string/string-startsWith.json +113 -0
  186. package/test-data/expressions/string/string-substring.json +138 -0
  187. package/test-data/expressions/string/string-trim.json +100 -0
  188. package/test-data/expressions/string/string-upper.json +94 -0
  189. package/test-data/other/custom.json +51 -0
  190. package/test-data/other/customer-input-schema.json +34 -0
  191. package/test-data/other/customer-output-schema.json +34 -0
  192. package/test-data/other/passthrough.json +31 -0
  193. package/test-data/sub-decisions/basic/$nodes-child.json +31 -0
  194. package/test-data/sub-decisions/basic/$nodes-parent.json +49 -0
  195. package/test-data/sub-decisions/basic/recursive-table1.json +49 -0
  196. package/test-data/sub-decisions/basic/recursive-table2.json +49 -0
  197. package/test-data/sub-decisions/complex-multi/approval-decision.json +31 -0
  198. package/test-data/sub-decisions/complex-multi/complex-dag.json +175 -0
  199. package/test-data/sub-decisions/complex-multi/credit-check.json +31 -0
  200. package/test-data/sub-decisions/complex-multi/customer-segmentation.json +31 -0
  201. package/test-data/sub-decisions/complex-multi/discount-eligibility.json +31 -0
  202. package/test-data/sub-decisions/complex-multi/eligibility-check.json +31 -0
  203. package/test-data/sub-decisions/complex-multi/final-offer.json +31 -0
  204. package/test-data/sub-decisions/complex-multi/income-verification.json +31 -0
  205. package/test-data/sub-decisions/complex-multi/linear-chain.json +121 -0
  206. package/test-data/sub-decisions/complex-multi/pricing-calculation.json +31 -0
  207. package/test-data/sub-decisions/complex-multi/product-eligibility.json +31 -0
  208. package/test-data/sub-decisions/complex-multi/risk-assessment.json +31 -0
  209. package/test-data/sub-decisions/complex-multi/shared-validation.json +31 -0
  210. package/test-data/sub-decisions/complex-multi/validation.json +31 -0
  211. package/test-data/sub-decisions/diamond/decision-a.json +31 -0
  212. package/test-data/sub-decisions/diamond/decision-b.json +31 -0
  213. package/test-data/sub-decisions/diamond/decision-c.json +31 -0
  214. package/test-data/sub-decisions/diamond/decision-shared.json +31 -0
  215. package/test-data/sub-decisions/diamond/diamond-pattern.json +109 -0
  216. package/test-data/sub-decisions/error-propagation/parent-calls-error.json +44 -0
  217. package/test-data/sub-decisions/error-propagation/sub-decision-with-error.json +60 -0
  218. package/test-data/switch-nodes/basic/account-dormancy-management.json +245 -0
  219. package/test-data/switch-nodes/basic/application-risk-assessment.json +474 -0
  220. package/test-data/switch-nodes/basic/cellular-data-rollover-system.json +281 -0
  221. package/test-data/switch-nodes/basic/clinical-pathway-selection.json +454 -0
  222. package/test-data/switch-nodes/basic/insurance-prior-authorization.json +467 -0
  223. package/test-data/switch-nodes/basic/last-mile-delivery-assignment.json +373 -0
  224. package/test-data/switch-nodes/basic/loan-approval.json +469 -0
  225. package/test-data/switch-nodes/basic/multi-switch.json +498 -0
  226. package/test-data/switch-nodes/basic/online-checkin-eligibility.json +285 -0
  227. package/test-data/switch-nodes/basic/order-consolidation-system.json +493 -0
  228. package/test-data/switch-nodes/basic/seller-approval-workflow.json +383 -0
  229. package/test-data/switch-nodes/basic/set-fee.json +243 -0
  230. package/test-data/switch-nodes/basic/shipping-carrier-selector.json +379 -0
  231. package/test-data/switch-nodes/basic/switch-node.json +167 -0
  232. package/test-data/switch-nodes/basic/switch-performance-2.json +1307 -0
  233. package/test-data/switch-nodes/basic/switch-performance.json +691 -0
  234. package/test-data/switch-nodes/basic/tax-exemption.json +295 -0
  235. package/test-data/switch-nodes/basic/warehouse-cross-docking.json +313 -0
  236. package/test-data/switch-nodes/default-cases/switch-with-default.json +134 -0
  237. package/test-data/zen-reference/$nodes-child.json +69 -0
  238. package/test-data/zen-reference/$nodes-parent.json +34 -0
  239. package/test-data/zen-reference/8k.json +87992 -0
  240. package/test-data/zen-reference/credit-analysis.json +324 -0
  241. package/test-data/zen-reference/custom.json +51 -0
  242. package/test-data/zen-reference/customer-input-schema.json +34 -0
  243. package/test-data/zen-reference/customer-output-schema.json +34 -0
  244. package/test-data/zen-reference/error-cyclic.json +114 -0
  245. package/test-data/zen-reference/error-missing-input.json +54 -0
  246. package/test-data/zen-reference/error-missing-output.json +54 -0
  247. package/test-data/zen-reference/expression.json +69 -0
  248. package/test-data/zen-reference/function-v2.json +48 -0
  249. package/test-data/zen-reference/function.json +46 -0
  250. package/test-data/zen-reference/graphs/account-dormancy-management.json +245 -0
  251. package/test-data/zen-reference/graphs/affiliate-commission-calculator.json +228 -0
  252. package/test-data/zen-reference/graphs/airline-loyalty-points-calculations.json +285 -0
  253. package/test-data/zen-reference/graphs/airline-upgrade-eligibility.json +466 -0
  254. package/test-data/zen-reference/graphs/aml.json +537 -0
  255. package/test-data/zen-reference/graphs/application-risk-assessment.json +474 -0
  256. package/test-data/zen-reference/graphs/auto-insurance-premium-calculator.json +412 -0
  257. package/test-data/zen-reference/graphs/booking-personalization-system.json +553 -0
  258. package/test-data/zen-reference/graphs/care-team-assignment-system.json +585 -0
  259. package/test-data/zen-reference/graphs/cellular-data-rollover-system.json +281 -0
  260. package/test-data/zen-reference/graphs/claim-validation-system.json +307 -0
  261. package/test-data/zen-reference/graphs/clinical-lab-result-interpreter.json +433 -0
  262. package/test-data/zen-reference/graphs/clinical-pathway-selection.json +454 -0
  263. package/test-data/zen-reference/graphs/clinical-treatment-protocol.json +474 -0
  264. package/test-data/zen-reference/graphs/company-analysis.json +390 -0
  265. package/test-data/zen-reference/graphs/credit-limit-adjustment.json +479 -0
  266. package/test-data/zen-reference/graphs/customer-eligibility-engine.json +551 -0
  267. package/test-data/zen-reference/graphs/customer-lifetime-value.json +200 -0
  268. package/test-data/zen-reference/graphs/customer-onboarding-kyc-verification.json +611 -0
  269. package/test-data/zen-reference/graphs/customer-service-escalation.json +191 -0
  270. package/test-data/zen-reference/graphs/decision-table-discounts.json +168 -0
  271. package/test-data/zen-reference/graphs/decision-table-shipping.json +398 -0
  272. package/test-data/zen-reference/graphs/delivery-route-optimizer.json +271 -0
  273. package/test-data/zen-reference/graphs/device-compatibility-checker.json +303 -0
  274. package/test-data/zen-reference/graphs/disaster-relief-fund-allocation.json +296 -0
  275. package/test-data/zen-reference/graphs/dynamic-fx-rate-pricing-system.json +237 -0
  276. package/test-data/zen-reference/graphs/dynamic-marketplace-comission-calculator.json +242 -0
  277. package/test-data/zen-reference/graphs/dynamic-shipping-cost-calculator.json +378 -0
  278. package/test-data/zen-reference/graphs/dynamic-tarrif-engine.json +289 -0
  279. package/test-data/zen-reference/graphs/dynamic-ticket-pricing.json +325 -0
  280. package/test-data/zen-reference/graphs/empty-column-with-space.json +100 -0
  281. package/test-data/zen-reference/graphs/empty-column-without-space.json +100 -0
  282. package/test-data/zen-reference/graphs/environment-compliance-assessment.json +386 -0
  283. package/test-data/zen-reference/graphs/expression-default.json +93 -0
  284. package/test-data/zen-reference/graphs/expression-fields.json +94 -0
  285. package/test-data/zen-reference/graphs/expression-loop.json +94 -0
  286. package/test-data/zen-reference/graphs/expression-passthrough.json +108 -0
  287. package/test-data/zen-reference/graphs/expression-table-map.json +313 -0
  288. package/test-data/zen-reference/graphs/flash-sale-eligibility.json +366 -0
  289. package/test-data/zen-reference/graphs/flight-dispatch-decision-system.json +455 -0
  290. package/test-data/zen-reference/graphs/flight-rebooking-fee-calculator.json +406 -0
  291. package/test-data/zen-reference/graphs/government-assistance.json +299 -0
  292. package/test-data/zen-reference/graphs/grant-funding-distribution.json +307 -0
  293. package/test-data/zen-reference/graphs/hazardous-materials-management-system.json +414 -0
  294. package/test-data/zen-reference/graphs/immigration-eligibility-evaluator.json +765 -0
  295. package/test-data/zen-reference/graphs/import-duties-calculator.json +318 -0
  296. package/test-data/zen-reference/graphs/insurance-agent-commission.json +228 -0
  297. package/test-data/zen-reference/graphs/insurance-breakdown.json +421 -0
  298. package/test-data/zen-reference/graphs/insurance-coverage-calculator.json +362 -0
  299. package/test-data/zen-reference/graphs/insurance-prior-authorization.json +467 -0
  300. package/test-data/zen-reference/graphs/insurance-underwriting-risk.json +321 -0
  301. package/test-data/zen-reference/graphs/international-roaming-policy-manager.json +199 -0
  302. package/test-data/zen-reference/graphs/last-mile-delivery-assignment.json +373 -0
  303. package/test-data/zen-reference/graphs/legacy-plan-management.json +434 -0
  304. package/test-data/zen-reference/graphs/loan-approval.json +469 -0
  305. package/test-data/zen-reference/graphs/marketplace-listing-verification-system.json +334 -0
  306. package/test-data/zen-reference/graphs/medication-dosage-calculator.json +318 -0
  307. package/test-data/zen-reference/graphs/merch-bags.json +171 -0
  308. package/test-data/zen-reference/graphs/multi-switch.json +498 -0
  309. package/test-data/zen-reference/graphs/municipal-permit-evaluation-system.json +364 -0
  310. package/test-data/zen-reference/graphs/mvno-partner-enablement.json +313 -0
  311. package/test-data/zen-reference/graphs/nested-request.json +125 -0
  312. package/test-data/zen-reference/graphs/online-checkin-eligibility.json +285 -0
  313. package/test-data/zen-reference/graphs/order-consolidation-system.json +493 -0
  314. package/test-data/zen-reference/graphs/partner-revenue-sharing.json +244 -0
  315. package/test-data/zen-reference/graphs/payment-routing-and-fee-calculator.json +475 -0
  316. package/test-data/zen-reference/graphs/policy-discount-calculator.json +307 -0
  317. package/test-data/zen-reference/graphs/policy-eligibility-analyzer.json +299 -0
  318. package/test-data/zen-reference/graphs/product-listing-scoring.json +358 -0
  319. package/test-data/zen-reference/graphs/realtime-fraud-detection.json +235 -0
  320. package/test-data/zen-reference/graphs/regional-compliance-manager.json +278 -0
  321. package/test-data/zen-reference/graphs/returns-and-refund-policy.json +366 -0
  322. package/test-data/zen-reference/graphs/returns-processing-system.json +448 -0
  323. package/test-data/zen-reference/graphs/school-district-resource-allocation.json +282 -0
  324. package/test-data/zen-reference/graphs/seat-map-optimization.json +325 -0
  325. package/test-data/zen-reference/graphs/seller-approval-workflow.json +383 -0
  326. package/test-data/zen-reference/graphs/seller-fee-calculator.json +307 -0
  327. package/test-data/zen-reference/graphs/service-level-agreement-enforcement.json +575 -0
  328. package/test-data/zen-reference/graphs/set-fee.json +243 -0
  329. package/test-data/zen-reference/graphs/shipping-carrier-selector.json +379 -0
  330. package/test-data/zen-reference/graphs/smart-financial-product-matcher.json +249 -0
  331. package/test-data/zen-reference/graphs/supply-chain-risk.json +316 -0
  332. package/test-data/zen-reference/graphs/table-loop.json +93 -0
  333. package/test-data/zen-reference/graphs/tax-exemption.json +295 -0
  334. package/test-data/zen-reference/graphs/traffic-violation-penalty-calculator.json +436 -0
  335. package/test-data/zen-reference/graphs/transaction-compliance-classifier.json +525 -0
  336. package/test-data/zen-reference/graphs/vehicle-claims-resolution.json +310 -0
  337. package/test-data/zen-reference/graphs/warehouse-cross-docking.json +313 -0
  338. package/test-data/zen-reference/graphs/warehouse-storage-location.json +345 -0
  339. package/test-data/zen-reference/http-function.json +34 -0
  340. package/test-data/zen-reference/infinite-function.json +46 -0
  341. package/test-data/zen-reference/js/imports.js +25 -0
  342. package/test-data/zen-reference/passthrough.json +31 -0
  343. package/test-data/zen-reference/recursive-table1.json +49 -0
  344. package/test-data/zen-reference/recursive-table2.json +49 -0
  345. package/test-data/zen-reference/sleep-function.json +34 -0
  346. package/test-data/zen-reference/switch-node.json +167 -0
  347. package/test-data/zen-reference/switch-performance-2.json +1307 -0
  348. package/test-data/zen-reference/switch-performance.json +691 -0
  349. package/test-data/zen-reference/table.json +76 -0
  350. package/tests/helpers/index.ts +73 -0
  351. package/tests/helpers/mock-context.ts +231 -0
  352. package/tests/helpers/round-trip.ts +398 -0
  353. package/tests/helpers/test-harness-comparison.ts +325 -0
  354. package/tests/helpers/test-harness-wasm.ts +710 -0
  355. package/tests/helpers/test-harness.ts +28 -0
  356. package/tests/helpers/wasm-test.ts +659 -0
  357. package/tests/integration/compilation-errors.test.ts +864 -0
  358. package/tests/integration/decision-tables.test.ts +531 -0
  359. package/tests/integration/edge-cases.test.ts +787 -0
  360. package/tests/integration/expressions.test.ts +513 -0
  361. package/tests/integration/function-node-integration.test.ts +182 -0
  362. package/tests/integration/sub-decisions.test.ts +108 -0
  363. package/tests/integration/switch-nodes.test.ts +399 -0
  364. package/tests/integration/unary-or-matching.test.ts +53 -0
  365. package/tests/integration/wasm-data-types.test.ts +398 -0
  366. package/tests/integration/wasm-errors.test.ts +199 -0
  367. package/tests/integration/wasm-execution.test.ts +348 -0
  368. package/tests/integration/wasm-memory.test.ts +228 -0
  369. package/tests/scripts/analyze-coverage.ts +166 -0
  370. package/tests/scripts/categorize-tests.ts +396 -0
  371. package/tests/scripts/coverage-analysis.ts +836 -0
  372. package/tests/unit/compiler/cache.test.ts +238 -0
  373. package/tests/unit/compiler/errors.test.ts +316 -0
  374. package/tests/unit/compiler/graph-scalability.test.ts +510 -0
  375. package/tests/unit/compiler/graph.test.ts +878 -0
  376. package/tests/unit/compiler/input-validation.test.ts +447 -0
  377. package/tests/unit/compiler/logical-and-parser.test.ts +143 -0
  378. package/tests/unit/compiler/logical-not-parser.test.ts +107 -0
  379. package/tests/unit/compiler/logical-or-parser.test.ts +236 -0
  380. package/tests/unit/compiler/marshal-gen/marshal-gen.test.ts +97 -0
  381. package/tests/unit/compiler/nodes/decision-table.test.ts +103 -0
  382. package/tests/unit/compiler/nodes/decision.test.ts +182 -0
  383. package/tests/unit/compiler/nodes/function-compile.test.ts +204 -0
  384. package/tests/unit/compiler/nodes/function.test.ts +176 -0
  385. package/tests/unit/compiler/nodes/input.test.ts +30 -0
  386. package/tests/unit/compiler/nodes/switch.test.ts +127 -0
  387. package/tests/unit/compiler/optimizer-cache.test.ts +327 -0
  388. package/tests/unit/compiler/optimizer-implementation.test.ts +625 -0
  389. package/tests/unit/compiler/parser.test.ts +508 -0
  390. package/tests/unit/compiler/runtime-error-cleanup.test.ts +426 -0
  391. package/tests/unit/compiler/runtime-validation.test.ts +303 -0
  392. package/tests/unit/compiler/runtime.test.ts +221 -0
  393. package/tests/unit/compiler/schema/schema.test.ts +248 -0
  394. package/tests/unit/compiler/unary-ast-transforms.test.ts +245 -0
  395. package/tsconfig.json +27 -0
  396. package/tsup.config.ts +11 -0
  397. package/vitest.config.ts +12 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Switch branch detection algorithm for JDM decisions.
3
+ *
4
+ * This module identifies nodes that are exclusively reachable through switch node branches.
5
+ * These nodes are marked as "switchBranchTargets" and should be skipped during the main
6
+ * evaluation loop because the switch node evaluates them inline when taking a branch.
7
+ *
8
+ * WHY: Prevent double-evaluation - switch nodes evaluate branches inline, so downstream
9
+ * nodes must be skipped in the main evaluation loop to avoid executing them twice.
10
+ *
11
+ * OPTIMIZATION: Uses a single forward traversal through the graph to mark all nodes
12
+ * reachable from switch branches. This is O(n + m) instead of O(s × b × (n + m))
13
+ * where s = switches, b = branches, n = nodes, m = edges.
14
+ */
15
+
16
+ import type { JDMDecision, JDMEdge } from './parser';
17
+
18
+ /**
19
+ * Build an adjacency map for fast edge lookups.
20
+ * Maps each source node ID to an array of target node IDs.
21
+ *
22
+ * @param edges - All edges in the decision graph
23
+ * @returns Map from source node ID to array of target node IDs
24
+ */
25
+ function buildAdjacencyMap(edges: JDMEdge[]): Map<string, string[]> {
26
+ const adjacencyMap = new Map<string, string[]>();
27
+
28
+ for (const edge of edges) {
29
+ const targets = adjacencyMap.get(edge.sourceId);
30
+ if (targets) {
31
+ targets.push(edge.targetId);
32
+ } else {
33
+ adjacencyMap.set(edge.sourceId, [edge.targetId]);
34
+ }
35
+ }
36
+
37
+ return adjacencyMap;
38
+ }
39
+
40
+ /**
41
+ * Find all nodes that are exclusively reachable through switch node branches.
42
+ *
43
+ * Optimized algorithm:
44
+ * 1. Build adjacency map once for O(1) edge lookups
45
+ * 2. Identify all direct targets of switch nodes
46
+ * 3. Use BFS with efficient queue operations to traverse from all switch targets
47
+ * 4. Marks all nodes reachable from switch branches in a single traversal
48
+ *
49
+ * Time complexity: O(n + m) where n = nodes, m = edges
50
+ * Space complexity: O(n + m) for adjacency map and visited set
51
+ *
52
+ * @param jdm - The parsed JDM decision
53
+ * @returns Set of node IDs that are switch branch targets
54
+ */
55
+ export function findSwitchBranchTargets(jdm: JDMDecision): Set<string> {
56
+ // Build adjacency map once for O(1) lookups instead of O(m) filtering
57
+ const adjacencyMap = buildAdjacencyMap(jdm.edges);
58
+
59
+ // Find all direct targets of switch nodes
60
+ const switchNodes = jdm.nodes.filter((n) => n.type === 'switchNode');
61
+ const switchBranchTargets = new Set<string>();
62
+
63
+ // Collect all direct switch targets to start BFS from
64
+ const bfsQueue: string[] = [];
65
+ for (const switchNode of switchNodes) {
66
+ const targets = adjacencyMap.get(switchNode.id) || [];
67
+ for (const targetId of targets) {
68
+ if (!switchBranchTargets.has(targetId)) {
69
+ switchBranchTargets.add(targetId);
70
+ bfsQueue.push(targetId);
71
+ }
72
+ }
73
+ }
74
+
75
+ // Single BFS traversal from all switch targets
76
+ // Use index-based iteration instead of shift() to avoid O(n) array operations
77
+ let queueIndex = 0;
78
+ while (queueIndex < bfsQueue.length) {
79
+ const currentId = bfsQueue[queueIndex++];
80
+
81
+ // Get all outgoing edges using O(1) map lookup
82
+ const targets = adjacencyMap.get(currentId) || [];
83
+ for (const targetId of targets) {
84
+ if (!switchBranchTargets.has(targetId)) {
85
+ switchBranchTargets.add(targetId);
86
+ bfsQueue.push(targetId);
87
+ }
88
+ }
89
+ }
90
+
91
+ return switchBranchTargets;
92
+ }
@@ -0,0 +1,136 @@
1
+ import type { TSchema } from '@sinclair/typebox';
2
+ import type { JDMDecision } from './parser';
3
+
4
+ /**
5
+ * No-match behavior options for decision tables and switch nodes.
6
+ *
7
+ * This is a discriminated union to ensure type safety:
8
+ * - returnNull and throwError don't require additional data
9
+ * - returnDefault requires a value at type level
10
+ */
11
+ export type NoMatchBehavior =
12
+ | { type: 'returnNull' }
13
+ | { type: 'throwError' }
14
+ | { type: 'returnDefault'; value: unknown };
15
+
16
+ /**
17
+ * Context passed through compilation phases.
18
+ */
19
+ export interface CompilationContext {
20
+ /** The parsed JDM */
21
+ jdm: JDMDecision;
22
+
23
+ /** Input schema (TypeBox) */
24
+ inputSchema: TSchema;
25
+
26
+ /** Output schema (TypeBox) */
27
+ outputSchema: TSchema;
28
+
29
+ /** Flattened input memory layout */
30
+ inputLayout: FlattenedLayout;
31
+
32
+ /** Flattened output memory layout */
33
+ outputLayout: FlattenedLayout;
34
+
35
+ /** Schema hash for runtime validation */
36
+ schemaHash: bigint;
37
+
38
+ /** Compiler options */
39
+ options: CompilerOptions;
40
+
41
+ /** Parse an expression string to AST */
42
+ parseExpression(expr: string): any;
43
+
44
+ /** Parse an expression in unary mode */
45
+ parseUnaryExpression(expr: string): any;
46
+
47
+ /** Get unique identifier for generated code */
48
+ getUniqueId(prefix: string): string;
49
+
50
+ /** Loader function to load sub-decision JDM files by reference key */
51
+ loadDecision(key: string): JDMDecision;
52
+
53
+ /** Cache of compiled sub-decisions (key -> compiled code) */
54
+ compiledSubDecisions: Map<string, AssemblyScriptCode>;
55
+
56
+ /** Track the current decision path for cycle detection */
57
+ decisionPath: string[];
58
+
59
+ /** Generated helper functions for higher-order operations */
60
+ helperFunctions: Map<string, string>;
61
+
62
+ /** Function node sources for JavaScript bridge (node ID -> JavaScript source code) */
63
+ functionSources?: Map<string, string>;
64
+ }
65
+
66
+ /**
67
+ * Flattened schema layout for direct memory access.
68
+ */
69
+ export interface FlattenedLayout {
70
+ fields: Array<{
71
+ path: string; // e.g., "customer.age"
72
+ flatName: string; // e.g., "customer_age"
73
+ type: 'f64' | 'i32' | 'i64' | 'bool' | 'string' | 'array';
74
+ offset: number; // Byte offset in struct
75
+ size: number; // Bytes (fixed types) or -1 (variable)
76
+ }>;
77
+ }
78
+
79
+ /**
80
+ * Compiler options.
81
+ */
82
+ export interface CompilerOptions {
83
+ optimize?: boolean;
84
+ debug?: boolean;
85
+ validate?: boolean;
86
+ /** How to handle cases where no rules match in decision tables/switches */
87
+ noMatchBehavior?: NoMatchBehavior;
88
+ /** Optional loader function to load sub-decision JDM files */
89
+ loadDecision?: (key: string) => JDMDecision;
90
+ /** Optional testDataRoot path for default loader to search */
91
+ testDataRoot?: string;
92
+ /**
93
+ * Optional path to write debug AssemblyScript files on compilation failure.
94
+ *
95
+ * When compilation fails, the generated AssemblyScript code is written to disk
96
+ * to aid debugging. This option controls where these files are written:
97
+ *
98
+ * - `undefined` (default): No debug files are written
99
+ * - `string`: Files are written to a secure temporary directory under this path
100
+ * - Environment variable `JDM_ASM_DEBUG_OUTPUT`: Falls back to this path if option not set
101
+ *
102
+ * **Filesystem Side Effects:**
103
+ * - Creates temporary directories with random names (e.g., `jdm-asm-debug-XXXXXX`)
104
+ * - Writes `.ts` files with 0o600 permissions (owner read/write only)
105
+ * - Files are NOT automatically cleaned up and may accumulate
106
+ *
107
+ * **Security:**
108
+ * - Uses `fs.mkdtempSync()` to create secure temporary directories
109
+ * - Files are created with restricted permissions (0o600)
110
+ * - Temporary directories have random names to prevent conflicts
111
+ */
112
+ debugOutputPath?: string;
113
+ }
114
+
115
+ /**
116
+ * Result of compilation.
117
+ */
118
+ export interface CompilationResult {
119
+ /** Compiled WASM binary */
120
+ wasm: Uint8Array;
121
+ /** Schema hash for runtime validation */
122
+ schemaHash: bigint;
123
+ /** Generated marshaling code (JS) */
124
+ marshalCode: string;
125
+ /** Generated validation code (JS) */
126
+ validationCode: string;
127
+ /** WAT text format (if debug enabled) */
128
+ wat?: string;
129
+ /** Generated AssemblyScript source (if debug enabled) */
130
+ assemblyScript?: string;
131
+ }
132
+
133
+ /**
134
+ * Alias for generated AssemblyScript code strings.
135
+ */
136
+ export type AssemblyScriptCode = string;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Unary AST transformation utilities.
3
+ *
4
+ * This module provides AST-level transformations specific to unary (decision table)
5
+ * context. These transformations are applied AFTER parsing to convert a standard AST
6
+ * into an AST that reflects the semantic meaning in unary context.
7
+ *
8
+ * Key transformations:
9
+ * - Bare identifiers → string literals (for implicit string matching)
10
+ * - Comparison without LHS → $ comparison (e.g., "> 100" → "$ > 100")
11
+ * - Multiple comma-separated values → OR chain
12
+ *
13
+ * This separates SEMANTIC transformations from SYNTACTIC parsing, keeping
14
+ * the parser focused solely on syntax-to-AST conversion.
15
+ */
16
+
17
+ import type { ExpressionAST, BinaryOperator, BinaryOp } from './ast-types';
18
+
19
+ /**
20
+ * Convert a bare Identifier node to a StringLiteral node.
21
+ *
22
+ * In decision table cells, unquoted words like "premium" are semantic values to match against,
23
+ * not variable references. This prevents errors when authors write intuitive cell values without
24
+ * quotes. This is a key DMN/JDM usability feature.
25
+ *
26
+ * This handles the common JDM/DMN convention where cell values like:
27
+ * - premium means match the string "premium" (identifier → string)
28
+ * - "premium" means match the string "premium" (already a string)
29
+ *
30
+ * We only convert top-level Identifiers. More complex expressions (member access,
31
+ * function calls, etc.) are left as-is since they likely involve actual variables.
32
+ *
33
+ * @param ast - The AST node that may be an Identifier
34
+ * @returns The AST node, with Identifier converted to StringLiteral if applicable
35
+ */
36
+ export function convertIdentifierToStringLiteral(ast: ExpressionAST): ExpressionAST {
37
+ if (ast.type === 'Identifier') {
38
+ return { type: 'StringLiteral', value: (ast as { type: 'Identifier'; name: string }).name };
39
+ }
40
+ return ast;
41
+ }
42
+
43
+ /**
44
+ * Wrap an AST value in a comparison with DollarRef (the implicit cell input).
45
+ *
46
+ * In unary context, expressions like "> 100" implicitly compare against the input value.
47
+ * This function creates the full comparison AST: "$ <op> <value>".
48
+ *
49
+ * @param ast - The right-hand side value AST
50
+ * @param op - The comparison operator
51
+ * @returns A BinaryOp AST with DollarRef on the left
52
+ */
53
+ export function wrapWithDollarComparison(ast: ExpressionAST, op: BinaryOperator): BinaryOp {
54
+ return {
55
+ type: 'BinaryOp',
56
+ op,
57
+ left: { type: 'DollarRef' },
58
+ right: ast,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Create an equality comparison with implicit identifier-to-string conversion.
64
+ *
65
+ * This is the default transformation for unary values - they become equality
66
+ * comparisons against the input value ($). For equality/inequality comparisons,
67
+ * bare identifiers are converted to string literals.
68
+ *
69
+ * @param ast - The value to compare against
70
+ * @param op - The equality operator (== or !=)
71
+ * @returns A BinaryOp AST representing the equality comparison
72
+ */
73
+ export function createEqualityComparison(ast: ExpressionAST, op: '==' | '!='): BinaryOp {
74
+ const value = convertIdentifierToStringLiteral(ast);
75
+ return wrapWithDollarComparison(value, op);
76
+ }
77
+
78
+ /**
79
+ * Create an interval membership check.
80
+ *
81
+ * In unary context, interval notation like "[1..10]" becomes "$ in [1..10]".
82
+ *
83
+ * @param intervalAst - The interval AST node
84
+ * @returns A BinaryOp AST representing the membership check
85
+ */
86
+ export function createIntervalCheck(intervalAst: ExpressionAST): BinaryOp {
87
+ return wrapWithDollarComparison(intervalAst, 'in');
88
+ }
89
+
90
+ /**
91
+ * Create an array membership check.
92
+ *
93
+ * In unary context, array literals like "['gold', 'platinum']" become
94
+ * "$ in ['gold', 'platinum']".
95
+ *
96
+ * @param arrayAst - The array literal AST node
97
+ * @returns A BinaryOp AST representing the membership check
98
+ */
99
+ export function createArrayMembershipCheck(arrayAst: ExpressionAST): BinaryOp {
100
+ return wrapWithDollarComparison(arrayAst, 'in');
101
+ }
102
+
103
+ /**
104
+ * Combine multiple expression ASTs into an OR chain.
105
+ *
106
+ * In unary context, comma-separated values represent OR semantics.
107
+ * For example, "a, b, c" becomes "$ == 'a' or $ == 'b' or $ == 'c'".
108
+ *
109
+ * @param exprs - Array of expression ASTs to combine
110
+ * @returns A single AST representing the OR of all expressions
111
+ */
112
+ export function combineWithOr(exprs: ExpressionAST[]): ExpressionAST {
113
+ if (exprs.length === 0) {
114
+ return { type: 'BooleanLiteral', value: true };
115
+ }
116
+ if (exprs.length === 1) {
117
+ return exprs[0];
118
+ }
119
+ return exprs.reduce((acc, expr) => ({
120
+ type: 'BinaryOp',
121
+ op: 'or',
122
+ left: acc,
123
+ right: expr,
124
+ }));
125
+ }
126
+
127
+ /**
128
+ * Combine multiple expression ASTs into an AND chain.
129
+ *
130
+ * Used for compound unary expressions like ">= 13 and < 15".
131
+ *
132
+ * @param exprs - Array of expression ASTs to combine
133
+ * @returns A single AST representing the AND of all expressions
134
+ */
135
+ export function combineWithAnd(exprs: ExpressionAST[]): ExpressionAST {
136
+ if (exprs.length === 0) {
137
+ return { type: 'BooleanLiteral', value: true };
138
+ }
139
+ if (exprs.length === 1) {
140
+ return exprs[0];
141
+ }
142
+ return exprs.reduce((acc, expr) => ({
143
+ type: 'BinaryOp',
144
+ op: 'and',
145
+ left: acc,
146
+ right: expr,
147
+ }));
148
+ }
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Unary expression parser for decision table cells.
3
+ *
4
+ * This module provides parsing for unary mode expressions, which are used in
5
+ * decision table cells. The parser is responsible for SYNTACTIC analysis only -
6
+ * converting text to AST nodes. Semantic transformations (like converting
7
+ * identifiers to string literals) are handled by the unary-ast-transforms module.
8
+ *
9
+ * Separation of Concerns:
10
+ * - This module: Tokenization, lexing, parsing (text → AST)
11
+ * - unary-ast-transforms: Semantic transformations (AST → transformed AST)
12
+ * - unary-transform: Dollar reference substitution for compilation (AST → compiled AST)
13
+ */
14
+
15
+ import { parser, lexer } from './expression-parser';
16
+ import { type ExpressionAST, type BinaryOperator } from './ast-types';
17
+ import { cstToAst } from './cst-visitor';
18
+ import { CompilationError, ErrorCode, type SourceLocation } from './errors';
19
+ import {
20
+ wrapWithDollarComparison,
21
+ createEqualityComparison,
22
+ createIntervalCheck,
23
+ createArrayMembershipCheck,
24
+ combineWithOr,
25
+ combineWithAnd,
26
+ } from './unary-ast-transforms';
27
+
28
+ /**
29
+ * Parse an expression in unary mode (for decision table cells).
30
+ *
31
+ * In DMN/JDM convention, decision table cells contain concise expressions that implicitly
32
+ * compare against the input column value ($). This allows table authors to write "premium"
33
+ * instead of "$ == 'premium'" or "> 100" instead of "$ > 100", making tables more readable.
34
+ *
35
+ * Unary mode transformations:
36
+ * - "admin" -> $ == "admin"
37
+ * - > 100 -> $ > 100
38
+ * - [1..10] -> $ in [1..10]
39
+ * - "a", "b", "c" -> $ == "a" or $ == "b" or $ == "c"
40
+ * - $ > 100 -> $ > 100 (explicit $ disables transformations)
41
+ *
42
+ * @param input - The unary expression to parse
43
+ * @returns The AST node
44
+ */
45
+ export function parseUnaryExpression(input: string): ExpressionAST {
46
+ const trimmed = input.trim();
47
+
48
+ // Empty cell = always matches
49
+ if (trimmed === '' || trimmed === '-') {
50
+ return { type: 'BooleanLiteral', value: true };
51
+ }
52
+
53
+ // Check if expression uses explicit $ - if so, parse as standard expression
54
+ if (trimmed.includes('$')) {
55
+ return parseStandardExpression(trimmed);
56
+ }
57
+
58
+ // Check for comma-separated values (OR semantics)
59
+ // But only if it's not an array literal
60
+ if (trimmed.includes(',') && !isArrayLiteral(trimmed)) {
61
+ const parts = splitByComma(trimmed);
62
+ const comparisons = parts.map((part) => transformUnaryPart(part.trim()));
63
+ return combineWithOr(comparisons);
64
+ }
65
+
66
+ // Check for compound expressions with 'and' or 'or' that need implicit $ expansion
67
+ // e.g., ">= 13 and < 15" should become "$ >= 13 and $ < 15"
68
+ const compoundResult = parseCompoundUnary(trimmed);
69
+ if (compoundResult) {
70
+ return compoundResult;
71
+ }
72
+
73
+ return transformUnaryPart(trimmed);
74
+ }
75
+
76
+ /**
77
+ * Parse a single unary part and apply semantic transformations.
78
+ *
79
+ * This function performs both parsing and transformation in a single step:
80
+ * 1. Parse the input using the standard expression parser
81
+ * 2. Apply unary-context semantic transformations (AST operations)
82
+ *
83
+ * @param input - The unary part to parse
84
+ * @returns The transformed AST node
85
+ */
86
+ function transformUnaryPart(input: string): ExpressionAST {
87
+ // Check for comparison operator at start
88
+ const comparisonMatch = input.match(/^(==|!=|<=|>=|<|>)\s*(.+)$/);
89
+ if (comparisonMatch) {
90
+ const [, op, valueExpr] = comparisonMatch;
91
+ const value = parseStandardExpression(valueExpr);
92
+ // For equality/inequality comparisons, convert bare identifiers to strings
93
+ if (op === '==' || op === '!=') {
94
+ return createEqualityComparison(value, op as '==' | '!=');
95
+ }
96
+ return wrapWithDollarComparison(value, op as BinaryOperator);
97
+ }
98
+
99
+ // Check for interval notation
100
+ if (input.match(/^[[].+\.\..*[\])]$/)) {
101
+ const intervalAst = parseStandardExpression(input);
102
+ return createIntervalCheck(intervalAst);
103
+ }
104
+
105
+ // Check for array literal - use 'in' for membership
106
+ // e.g., "['gold', 'platinum']" should become "$ in ['gold', 'platinum']"
107
+ if (isArrayLiteral(input)) {
108
+ const arrayAst = parseStandardExpression(input);
109
+ return createArrayMembershipCheck(arrayAst);
110
+ }
111
+
112
+ // Default: equality comparison
113
+ // Convert bare identifiers to string literals for implicit equality
114
+ const value = parseStandardExpression(input);
115
+ return createEqualityComparison(value, '==');
116
+ }
117
+
118
+ /**
119
+ * Parse compound unary expressions that use 'and' or 'or' with implicit $
120
+ * Examples:
121
+ * - ">= 13 and < 15" -> "$ >= 13 and $ < 15"
122
+ * - "> 0 or == null" -> "$ > 0 or $ == null"
123
+ *
124
+ * @param input - The expression to parse
125
+ * @returns The AST if compound, null otherwise
126
+ */
127
+ function parseCompoundUnary(input: string): ExpressionAST | null {
128
+ // Match patterns like: <op1> <val1> and/or <op2> <val2>
129
+ // We need to split by ' and ' or ' or ' while preserving quotes and brackets
130
+ const andParts = splitByLogicalOp(input, 'and');
131
+ if (andParts.length > 1) {
132
+ const comparisons = andParts.map((part) => transformUnaryPart(part.trim()));
133
+ return combineWithAnd(comparisons);
134
+ }
135
+
136
+ const orParts = splitByLogicalOp(input, 'or');
137
+ if (orParts.length > 1) {
138
+ const comparisons = orParts.map((part) => transformUnaryPart(part.trim()));
139
+ return combineWithOr(comparisons);
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * Split input by a logical operator (' and ' or ' or '), respecting quotes and brackets.
147
+ *
148
+ * This is a parsing helper that performs tokenization while respecting nested structures.
149
+ */
150
+ function splitByLogicalOp(input: string, op: 'and' | 'or'): string[] {
151
+ const pattern = ` ${op} `;
152
+ const parts: string[] = [];
153
+ let current = '';
154
+ let depth = 0;
155
+ let inString = false;
156
+ let stringChar = '';
157
+ let i = 0;
158
+
159
+ while (i < input.length) {
160
+ const char = input[i];
161
+
162
+ if (inString) {
163
+ current += char;
164
+ if (char === stringChar && input[i - 1] !== '\\') {
165
+ inString = false;
166
+ }
167
+ i++;
168
+ continue;
169
+ }
170
+
171
+ if (char === '"' || char === "'") {
172
+ inString = true;
173
+ stringChar = char;
174
+ current += char;
175
+ i++;
176
+ } else if (char === '[' || char === '(' || char === '{') {
177
+ depth++;
178
+ current += char;
179
+ i++;
180
+ } else if (char === ']' || char === ')' || char === '}') {
181
+ depth--;
182
+ current += char;
183
+ i++;
184
+ } else if (depth === 0 && input.substring(i, i + pattern.length).toLowerCase() === pattern) {
185
+ parts.push(current);
186
+ current = '';
187
+ i += pattern.length;
188
+ } else {
189
+ current += char;
190
+ i++;
191
+ }
192
+ }
193
+
194
+ if (current) {
195
+ parts.push(current);
196
+ }
197
+ return parts;
198
+ }
199
+
200
+ /**
201
+ * Check if a string represents an array literal (not an interval).
202
+ *
203
+ * Arrays and intervals both use bracket syntax but have different semantics.
204
+ * Arrays check membership ($ in [1,2,3]), intervals check ranges ($ in [1..10]).
205
+ * Must detect .. to parse correctly.
206
+ */
207
+ function isArrayLiteral(input: string): boolean {
208
+ return input.startsWith('[') && input.endsWith(']') && !input.includes('..');
209
+ }
210
+
211
+ /**
212
+ * Smart split by comma that respects brackets and strings.
213
+ *
214
+ * This is a parsing helper for tokenizing comma-separated unary values
215
+ * while respecting nested structures.
216
+ *
217
+ * Examples:
218
+ * - "a, b, c" -> ["a", "b", "c"]
219
+ * - "[1, 2], [3, 4]" -> ["[1, 2]", "[3, 4]"]
220
+ * - '"a, b", "c, d"' -> ['"a, b"', '"c, d"']
221
+ */
222
+ function splitByComma(input: string): string[] {
223
+ const parts: string[] = [];
224
+ let current = '';
225
+ let depth = 0;
226
+ let inString = false;
227
+ let stringChar = '';
228
+
229
+ for (let i = 0; i < input.length; i++) {
230
+ const char = input[i];
231
+
232
+ if (inString) {
233
+ current += char;
234
+ // Check if string ends (and handle escaped quotes)
235
+ if (char === stringChar && input[i - 1] !== '\\') {
236
+ inString = false;
237
+ }
238
+ continue;
239
+ }
240
+
241
+ if (char === '"' || char === "'") {
242
+ inString = true;
243
+ stringChar = char;
244
+ current += char;
245
+ } else if (char === '[' || char === '(') {
246
+ depth++;
247
+ current += char;
248
+ } else if (char === ']' || char === ')') {
249
+ depth--;
250
+ current += char;
251
+ } else if (char === ',' && depth === 0) {
252
+ parts.push(current);
253
+ current = '';
254
+ } else {
255
+ current += char;
256
+ }
257
+ }
258
+
259
+ if (current) {
260
+ parts.push(current);
261
+ }
262
+ return parts;
263
+ }
264
+
265
+ /**
266
+ * Parse a standard (non-unary) expression.
267
+ *
268
+ * This is the core parsing function that converts an expression string to an AST.
269
+ * It uses the Chevrotain lexer and parser defined in expression-parser.ts,
270
+ * then converts the CST to AST using cst-visitor.ts.
271
+ *
272
+ * @param input - The expression string to parse
273
+ * @param location - Optional source location for error reporting
274
+ * @returns The AST node
275
+ */
276
+ export function parseStandardExpression(input: string, location?: SourceLocation): ExpressionAST {
277
+ const lexingResult = lexer.tokenize(input);
278
+
279
+ if (lexingResult.errors.length > 0) {
280
+ throw new CompilationError(
281
+ `Lexer error: ${lexingResult.errors.map((e: any) => e.message).join(', ')}`,
282
+ ErrorCode.PARSE_ERROR,
283
+ location,
284
+ { expression: input },
285
+ );
286
+ }
287
+
288
+ parser.input = lexingResult.tokens;
289
+ const cst = parser.expression();
290
+
291
+ if (parser.errors.length > 0) {
292
+ throw new CompilationError(
293
+ `Parse error: ${parser.errors.map((e: any) => e.message).join(', ')}`,
294
+ ErrorCode.PARSE_ERROR,
295
+ location,
296
+ { expression: input },
297
+ );
298
+ }
299
+
300
+ return cstToAst(cst);
301
+ }