@divmain/jdm-asm 0.2.0 → 0.2.3
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.
- package/dist/index.js +2227 -29
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/.github/workflows/ci.yml +0 -53
- package/.oxfmtrc.json +0 -16
- package/.oxlintrc.json +0 -183
- package/AGENTS.md +0 -81
- package/asconfig.json +0 -23
- package/benchmarks/fixtures.ts +0 -111
- package/benchmarks/input-fixtures.ts +0 -80
- package/benchmarks/run.ts +0 -913
- package/benchmarks/worker-pool.ts +0 -223
- package/benchmarks/worker.ts +0 -374
- package/scripts/run-all-tests.ts +0 -220
- package/src/compiler/EXPRESSION_SUBSETS.md +0 -228
- package/src/compiler/asc-compiler.ts +0 -315
- package/src/compiler/ast-types.ts +0 -215
- package/src/compiler/build.ts +0 -56
- package/src/compiler/cache.ts +0 -414
- package/src/compiler/code-generators.ts +0 -211
- package/src/compiler/codegen/index.ts +0 -15
- package/src/compiler/codegen/js-marshal.ts +0 -999
- package/src/compiler/codegen/js-validation.ts +0 -243
- package/src/compiler/codegen.ts +0 -19
- package/src/compiler/compile-time-validation.ts +0 -507
- package/src/compiler/cst-visitor.ts +0 -434
- package/src/compiler/errors.ts +0 -227
- package/src/compiler/expression-parser.ts +0 -536
- package/src/compiler/graph.ts +0 -197
- package/src/compiler/index.ts +0 -199
- package/src/compiler/input-validation.ts +0 -33
- package/src/compiler/marshal-gen.ts +0 -21
- package/src/compiler/nodes/context-resolvers.ts +0 -197
- package/src/compiler/nodes/decision-table.ts +0 -507
- package/src/compiler/nodes/decision.ts +0 -292
- package/src/compiler/nodes/expression-compiler.ts +0 -526
- package/src/compiler/nodes/expression.ts +0 -425
- package/src/compiler/nodes/function.ts +0 -316
- package/src/compiler/nodes/input.ts +0 -60
- package/src/compiler/nodes/switch.ts +0 -547
- package/src/compiler/optimizer.ts +0 -948
- package/src/compiler/orchestrator.ts +0 -352
- package/src/compiler/parser.ts +0 -115
- package/src/compiler/result-selection.ts +0 -161
- package/src/compiler/runtime/index.ts +0 -26
- package/src/compiler/runtime-codegen.ts +0 -211
- package/src/compiler/runtime-validation-codegen.ts +0 -294
- package/src/compiler/runtime.ts +0 -452
- package/src/compiler/schema.ts +0 -245
- package/src/compiler/switch-branch-detection.ts +0 -92
- package/src/compiler/types.ts +0 -136
- package/src/compiler/unary-ast-transforms.ts +0 -148
- package/src/compiler/unary-parser.ts +0 -301
- package/src/compiler/unary-transform.ts +0 -161
- package/src/compiler/utils.ts +0 -27
- package/src/compiler/virtual-fs.ts +0 -90
- package/src/compiler/wasm-instantiate.ts +0 -127
- package/src/index.ts +0 -1
- package/src/runtime/arrays.ts +0 -579
- package/src/runtime/context.ts +0 -189
- package/src/runtime/expressions.ts +0 -1811
- package/src/runtime/index.ts +0 -8
- package/src/runtime/memory.ts +0 -607
- package/src/runtime/strings.ts +0 -260
- package/src/runtime/tables.ts +0 -96
- package/src/runtime/tsconfig.json +0 -4
- package/src/runtime/values.ts +0 -209
- package/test-data/README.md +0 -83
- package/test-data/decision-tables/basic/8k.json +0 -87992
- package/test-data/decision-tables/basic/affiliate-commission-calculator.json +0 -228
- package/test-data/decision-tables/basic/airline-loyalty-points-calculations.json +0 -285
- package/test-data/decision-tables/basic/airline-upgrade-eligibility.json +0 -466
- package/test-data/decision-tables/basic/auto-insurance-premium-calculator.json +0 -412
- package/test-data/decision-tables/basic/booking-personalization-system.json +0 -553
- package/test-data/decision-tables/basic/care-team-assignment-system.json +0 -585
- package/test-data/decision-tables/basic/claim-validation-system.json +0 -307
- package/test-data/decision-tables/basic/clinical-lab-result-interpreter.json +0 -433
- package/test-data/decision-tables/basic/clinical-treatment-protocol.json +0 -474
- package/test-data/decision-tables/basic/credit-limit-adjustment.json +0 -479
- package/test-data/decision-tables/basic/customer-eligibility-engine.json +0 -551
- package/test-data/decision-tables/basic/customer-lifetime-value.json +0 -200
- package/test-data/decision-tables/basic/customer-onboarding-kyc-verification.json +0 -611
- package/test-data/decision-tables/basic/customer-service-escalation.json +0 -191
- package/test-data/decision-tables/basic/decision-table-discounts.json +0 -168
- package/test-data/decision-tables/basic/decision-table-shipping.json +0 -398
- package/test-data/decision-tables/basic/delivery-route-optimizer.json +0 -271
- package/test-data/decision-tables/basic/device-compatibility-checker.json +0 -303
- package/test-data/decision-tables/basic/disaster-relief-fund-allocation.json +0 -296
- package/test-data/decision-tables/basic/dynamic-fx-rate-pricing-system.json +0 -237
- package/test-data/decision-tables/basic/dynamic-marketplace-comission-calculator.json +0 -242
- package/test-data/decision-tables/basic/dynamic-shipping-cost-calculator.json +0 -378
- package/test-data/decision-tables/basic/dynamic-tarrif-engine.json +0 -289
- package/test-data/decision-tables/basic/dynamic-ticket-pricing.json +0 -325
- package/test-data/decision-tables/basic/empty-column-with-space.json +0 -100
- package/test-data/decision-tables/basic/empty-column-without-space.json +0 -100
- package/test-data/decision-tables/basic/environment-compliance-assessment.json +0 -386
- package/test-data/decision-tables/basic/expression-table-map.json +0 -313
- package/test-data/decision-tables/basic/flash-sale-eligibility.json +0 -366
- package/test-data/decision-tables/basic/flight-dispatch-decision-system.json +0 -455
- package/test-data/decision-tables/basic/flight-rebooking-fee-calculator.json +0 -406
- package/test-data/decision-tables/basic/government-assistance.json +0 -299
- package/test-data/decision-tables/basic/grant-funding-distribution.json +0 -307
- package/test-data/decision-tables/basic/hazardous-materials-management-system.json +0 -414
- package/test-data/decision-tables/basic/immigration-eligibility-evaluator.json +0 -765
- package/test-data/decision-tables/basic/import-duties-calculator.json +0 -318
- package/test-data/decision-tables/basic/insurance-agent-commission.json +0 -228
- package/test-data/decision-tables/basic/insurance-coverage-calculator.json +0 -362
- package/test-data/decision-tables/basic/insurance-underwriting-risk.json +0 -321
- package/test-data/decision-tables/basic/international-roaming-policy-manager.json +0 -199
- package/test-data/decision-tables/basic/legacy-plan-management.json +0 -434
- package/test-data/decision-tables/basic/marketplace-listing-verification-system.json +0 -334
- package/test-data/decision-tables/basic/medication-dosage-calculator.json +0 -318
- package/test-data/decision-tables/basic/merch-bags.json +0 -171
- package/test-data/decision-tables/basic/municipal-permit-evaluation-system.json +0 -364
- package/test-data/decision-tables/basic/mvno-partner-enablement.json +0 -313
- package/test-data/decision-tables/basic/partner-revenue-sharing.json +0 -244
- package/test-data/decision-tables/basic/payment-routing-and-fee-calculator.json +0 -475
- package/test-data/decision-tables/basic/policy-discount-calculator.json +0 -307
- package/test-data/decision-tables/basic/policy-eligibility-analyzer.json +0 -299
- package/test-data/decision-tables/basic/product-listing-scoring.json +0 -358
- package/test-data/decision-tables/basic/realtime-fraud-detection.json +0 -235
- package/test-data/decision-tables/basic/regional-compliance-manager.json +0 -278
- package/test-data/decision-tables/basic/returns-and-refund-policy.json +0 -366
- package/test-data/decision-tables/basic/returns-processing-system.json +0 -448
- package/test-data/decision-tables/basic/school-district-resource-allocation.json +0 -282
- package/test-data/decision-tables/basic/seat-map-optimization.json +0 -325
- package/test-data/decision-tables/basic/seller-fee-calculator.json +0 -307
- package/test-data/decision-tables/basic/service-level-agreement-enforcement.json +0 -575
- package/test-data/decision-tables/basic/smart-financial-product-matcher.json +0 -249
- package/test-data/decision-tables/basic/supply-chain-risk.json +0 -316
- package/test-data/decision-tables/basic/table-loop.json +0 -93
- package/test-data/decision-tables/basic/table.json +0 -76
- package/test-data/decision-tables/basic/traffic-violation-penalty-calculator.json +0 -436
- package/test-data/decision-tables/basic/transaction-compliance-classifier.json +0 -525
- package/test-data/decision-tables/basic/vehicle-claims-resolution.json +0 -310
- package/test-data/decision-tables/basic/warehouse-storage-location.json +0 -345
- package/test-data/decision-tables/hit-policy-collect/collect-multiple-matches.json +0 -127
- package/test-data/decision-tables/hit-policy-collect/collect-no-match.json +0 -95
- package/test-data/decision-tables/hit-policy-first/first-match.json +0 -103
- package/test-data/decision-tables/hit-policy-first/no-match.json +0 -95
- package/test-data/decision-tables/hit-policy-output-order/output-order-respected.json +0 -94
- package/test-data/decision-tables/hit-policy-output-order/string-output-order.json +0 -94
- package/test-data/decision-tables/hit-policy-priority/priority-respected.json +0 -86
- package/test-data/decision-tables/hit-policy-rule-order/rule-order-respected.json +0 -94
- package/test-data/decision-tables/hit-policy-unique/all-match-error.json +0 -89
- package/test-data/decision-tables/hit-policy-unique/multiple-match-error.json +0 -89
- package/test-data/decision-tables/hit-policy-unique/no-match.json +0 -88
- package/test-data/decision-tables/hit-policy-unique/unique-match.json +0 -99
- package/test-data/expressions/arithmetic/error-cyclic.json +0 -114
- package/test-data/expressions/arithmetic/error-missing-input.json +0 -54
- package/test-data/expressions/arithmetic/error-missing-output.json +0 -54
- package/test-data/expressions/arithmetic/expression-default.json +0 -93
- package/test-data/expressions/arithmetic/expression-fields.json +0 -94
- package/test-data/expressions/arithmetic/expression-loop.json +0 -94
- package/test-data/expressions/arithmetic/expression-passthrough.json +0 -108
- package/test-data/expressions/arithmetic/expression.json +0 -69
- package/test-data/expressions/arithmetic/nested-request.json +0 -125
- package/test-data/expressions/arithmetic/number-function.json +0 -58
- package/test-data/expressions/arithmetic/test-number-functions.json +0 -68
- package/test-data/expressions/functions/all.json +0 -149
- package/test-data/expressions/functions/avg.json +0 -89
- package/test-data/expressions/functions/filter.json +0 -109
- package/test-data/expressions/functions/flat.json +0 -167
- package/test-data/expressions/functions/map-strings.json +0 -65
- package/test-data/expressions/functions/map.json +0 -73
- package/test-data/expressions/functions/reduce.json +0 -49
- package/test-data/expressions/functions/some.json +0 -175
- package/test-data/expressions/functions/sort-strings.json +0 -97
- package/test-data/expressions/functions/sort.json +0 -97
- package/test-data/expressions/logical/logical-and.json +0 -116
- package/test-data/expressions/logical/logical-complex.json +0 -260
- package/test-data/expressions/logical/logical-not.json +0 -111
- package/test-data/expressions/logical/logical-or.json +0 -123
- package/test-data/expressions/string/string-comparison.json +0 -128
- package/test-data/expressions/string/string-concat.json +0 -106
- package/test-data/expressions/string/string-contains.json +0 -125
- package/test-data/expressions/string/string-endsWith.json +0 -113
- package/test-data/expressions/string/string-indexOf.json +0 -131
- package/test-data/expressions/string/string-join.json +0 -92
- package/test-data/expressions/string/string-lower.json +0 -94
- package/test-data/expressions/string/string-replace.json +0 -130
- package/test-data/expressions/string/string-split.json +0 -101
- package/test-data/expressions/string/string-startsWith.json +0 -113
- package/test-data/expressions/string/string-substring.json +0 -138
- package/test-data/expressions/string/string-trim.json +0 -100
- package/test-data/expressions/string/string-upper.json +0 -94
- package/test-data/other/custom.json +0 -51
- package/test-data/other/customer-input-schema.json +0 -34
- package/test-data/other/customer-output-schema.json +0 -34
- package/test-data/other/passthrough.json +0 -31
- package/test-data/sub-decisions/basic/$nodes-child.json +0 -31
- package/test-data/sub-decisions/basic/$nodes-parent.json +0 -49
- package/test-data/sub-decisions/basic/recursive-table1.json +0 -49
- package/test-data/sub-decisions/basic/recursive-table2.json +0 -49
- package/test-data/sub-decisions/complex-multi/approval-decision.json +0 -31
- package/test-data/sub-decisions/complex-multi/complex-dag.json +0 -175
- package/test-data/sub-decisions/complex-multi/credit-check.json +0 -31
- package/test-data/sub-decisions/complex-multi/customer-segmentation.json +0 -31
- package/test-data/sub-decisions/complex-multi/discount-eligibility.json +0 -31
- package/test-data/sub-decisions/complex-multi/eligibility-check.json +0 -31
- package/test-data/sub-decisions/complex-multi/final-offer.json +0 -31
- package/test-data/sub-decisions/complex-multi/income-verification.json +0 -31
- package/test-data/sub-decisions/complex-multi/linear-chain.json +0 -121
- package/test-data/sub-decisions/complex-multi/pricing-calculation.json +0 -31
- package/test-data/sub-decisions/complex-multi/product-eligibility.json +0 -31
- package/test-data/sub-decisions/complex-multi/risk-assessment.json +0 -31
- package/test-data/sub-decisions/complex-multi/shared-validation.json +0 -31
- package/test-data/sub-decisions/complex-multi/validation.json +0 -31
- package/test-data/sub-decisions/diamond/decision-a.json +0 -31
- package/test-data/sub-decisions/diamond/decision-b.json +0 -31
- package/test-data/sub-decisions/diamond/decision-c.json +0 -31
- package/test-data/sub-decisions/diamond/decision-shared.json +0 -31
- package/test-data/sub-decisions/diamond/diamond-pattern.json +0 -109
- package/test-data/sub-decisions/error-propagation/parent-calls-error.json +0 -44
- package/test-data/sub-decisions/error-propagation/sub-decision-with-error.json +0 -60
- package/test-data/switch-nodes/basic/account-dormancy-management.json +0 -245
- package/test-data/switch-nodes/basic/application-risk-assessment.json +0 -474
- package/test-data/switch-nodes/basic/cellular-data-rollover-system.json +0 -281
- package/test-data/switch-nodes/basic/clinical-pathway-selection.json +0 -454
- package/test-data/switch-nodes/basic/insurance-prior-authorization.json +0 -467
- package/test-data/switch-nodes/basic/last-mile-delivery-assignment.json +0 -373
- package/test-data/switch-nodes/basic/loan-approval.json +0 -469
- package/test-data/switch-nodes/basic/multi-switch.json +0 -498
- package/test-data/switch-nodes/basic/online-checkin-eligibility.json +0 -285
- package/test-data/switch-nodes/basic/order-consolidation-system.json +0 -493
- package/test-data/switch-nodes/basic/seller-approval-workflow.json +0 -383
- package/test-data/switch-nodes/basic/set-fee.json +0 -243
- package/test-data/switch-nodes/basic/shipping-carrier-selector.json +0 -379
- package/test-data/switch-nodes/basic/switch-node.json +0 -167
- package/test-data/switch-nodes/basic/switch-performance-2.json +0 -1307
- package/test-data/switch-nodes/basic/switch-performance.json +0 -691
- package/test-data/switch-nodes/basic/tax-exemption.json +0 -295
- package/test-data/switch-nodes/basic/warehouse-cross-docking.json +0 -313
- package/test-data/switch-nodes/default-cases/switch-with-default.json +0 -134
- package/test-data/zen-reference/$nodes-child.json +0 -69
- package/test-data/zen-reference/$nodes-parent.json +0 -34
- package/test-data/zen-reference/8k.json +0 -87992
- package/test-data/zen-reference/credit-analysis.json +0 -324
- package/test-data/zen-reference/custom.json +0 -51
- package/test-data/zen-reference/customer-input-schema.json +0 -34
- package/test-data/zen-reference/customer-output-schema.json +0 -34
- package/test-data/zen-reference/error-cyclic.json +0 -114
- package/test-data/zen-reference/error-missing-input.json +0 -54
- package/test-data/zen-reference/error-missing-output.json +0 -54
- package/test-data/zen-reference/expression.json +0 -69
- package/test-data/zen-reference/function-v2.json +0 -48
- package/test-data/zen-reference/function.json +0 -46
- package/test-data/zen-reference/graphs/account-dormancy-management.json +0 -245
- package/test-data/zen-reference/graphs/affiliate-commission-calculator.json +0 -228
- package/test-data/zen-reference/graphs/airline-loyalty-points-calculations.json +0 -285
- package/test-data/zen-reference/graphs/airline-upgrade-eligibility.json +0 -466
- package/test-data/zen-reference/graphs/aml.json +0 -537
- package/test-data/zen-reference/graphs/application-risk-assessment.json +0 -474
- package/test-data/zen-reference/graphs/auto-insurance-premium-calculator.json +0 -412
- package/test-data/zen-reference/graphs/booking-personalization-system.json +0 -553
- package/test-data/zen-reference/graphs/care-team-assignment-system.json +0 -585
- package/test-data/zen-reference/graphs/cellular-data-rollover-system.json +0 -281
- package/test-data/zen-reference/graphs/claim-validation-system.json +0 -307
- package/test-data/zen-reference/graphs/clinical-lab-result-interpreter.json +0 -433
- package/test-data/zen-reference/graphs/clinical-pathway-selection.json +0 -454
- package/test-data/zen-reference/graphs/clinical-treatment-protocol.json +0 -474
- package/test-data/zen-reference/graphs/company-analysis.json +0 -390
- package/test-data/zen-reference/graphs/credit-limit-adjustment.json +0 -479
- package/test-data/zen-reference/graphs/customer-eligibility-engine.json +0 -551
- package/test-data/zen-reference/graphs/customer-lifetime-value.json +0 -200
- package/test-data/zen-reference/graphs/customer-onboarding-kyc-verification.json +0 -611
- package/test-data/zen-reference/graphs/customer-service-escalation.json +0 -191
- package/test-data/zen-reference/graphs/decision-table-discounts.json +0 -168
- package/test-data/zen-reference/graphs/decision-table-shipping.json +0 -398
- package/test-data/zen-reference/graphs/delivery-route-optimizer.json +0 -271
- package/test-data/zen-reference/graphs/device-compatibility-checker.json +0 -303
- package/test-data/zen-reference/graphs/disaster-relief-fund-allocation.json +0 -296
- package/test-data/zen-reference/graphs/dynamic-fx-rate-pricing-system.json +0 -237
- package/test-data/zen-reference/graphs/dynamic-marketplace-comission-calculator.json +0 -242
- package/test-data/zen-reference/graphs/dynamic-shipping-cost-calculator.json +0 -378
- package/test-data/zen-reference/graphs/dynamic-tarrif-engine.json +0 -289
- package/test-data/zen-reference/graphs/dynamic-ticket-pricing.json +0 -325
- package/test-data/zen-reference/graphs/empty-column-with-space.json +0 -100
- package/test-data/zen-reference/graphs/empty-column-without-space.json +0 -100
- package/test-data/zen-reference/graphs/environment-compliance-assessment.json +0 -386
- package/test-data/zen-reference/graphs/expression-default.json +0 -93
- package/test-data/zen-reference/graphs/expression-fields.json +0 -94
- package/test-data/zen-reference/graphs/expression-loop.json +0 -94
- package/test-data/zen-reference/graphs/expression-passthrough.json +0 -108
- package/test-data/zen-reference/graphs/expression-table-map.json +0 -313
- package/test-data/zen-reference/graphs/flash-sale-eligibility.json +0 -366
- package/test-data/zen-reference/graphs/flight-dispatch-decision-system.json +0 -455
- package/test-data/zen-reference/graphs/flight-rebooking-fee-calculator.json +0 -406
- package/test-data/zen-reference/graphs/government-assistance.json +0 -299
- package/test-data/zen-reference/graphs/grant-funding-distribution.json +0 -307
- package/test-data/zen-reference/graphs/hazardous-materials-management-system.json +0 -414
- package/test-data/zen-reference/graphs/immigration-eligibility-evaluator.json +0 -765
- package/test-data/zen-reference/graphs/import-duties-calculator.json +0 -318
- package/test-data/zen-reference/graphs/insurance-agent-commission.json +0 -228
- package/test-data/zen-reference/graphs/insurance-breakdown.json +0 -421
- package/test-data/zen-reference/graphs/insurance-coverage-calculator.json +0 -362
- package/test-data/zen-reference/graphs/insurance-prior-authorization.json +0 -467
- package/test-data/zen-reference/graphs/insurance-underwriting-risk.json +0 -321
- package/test-data/zen-reference/graphs/international-roaming-policy-manager.json +0 -199
- package/test-data/zen-reference/graphs/last-mile-delivery-assignment.json +0 -373
- package/test-data/zen-reference/graphs/legacy-plan-management.json +0 -434
- package/test-data/zen-reference/graphs/loan-approval.json +0 -469
- package/test-data/zen-reference/graphs/marketplace-listing-verification-system.json +0 -334
- package/test-data/zen-reference/graphs/medication-dosage-calculator.json +0 -318
- package/test-data/zen-reference/graphs/merch-bags.json +0 -171
- package/test-data/zen-reference/graphs/multi-switch.json +0 -498
- package/test-data/zen-reference/graphs/municipal-permit-evaluation-system.json +0 -364
- package/test-data/zen-reference/graphs/mvno-partner-enablement.json +0 -313
- package/test-data/zen-reference/graphs/nested-request.json +0 -125
- package/test-data/zen-reference/graphs/online-checkin-eligibility.json +0 -285
- package/test-data/zen-reference/graphs/order-consolidation-system.json +0 -493
- package/test-data/zen-reference/graphs/partner-revenue-sharing.json +0 -244
- package/test-data/zen-reference/graphs/payment-routing-and-fee-calculator.json +0 -475
- package/test-data/zen-reference/graphs/policy-discount-calculator.json +0 -307
- package/test-data/zen-reference/graphs/policy-eligibility-analyzer.json +0 -299
- package/test-data/zen-reference/graphs/product-listing-scoring.json +0 -358
- package/test-data/zen-reference/graphs/realtime-fraud-detection.json +0 -235
- package/test-data/zen-reference/graphs/regional-compliance-manager.json +0 -278
- package/test-data/zen-reference/graphs/returns-and-refund-policy.json +0 -366
- package/test-data/zen-reference/graphs/returns-processing-system.json +0 -448
- package/test-data/zen-reference/graphs/school-district-resource-allocation.json +0 -282
- package/test-data/zen-reference/graphs/seat-map-optimization.json +0 -325
- package/test-data/zen-reference/graphs/seller-approval-workflow.json +0 -383
- package/test-data/zen-reference/graphs/seller-fee-calculator.json +0 -307
- package/test-data/zen-reference/graphs/service-level-agreement-enforcement.json +0 -575
- package/test-data/zen-reference/graphs/set-fee.json +0 -243
- package/test-data/zen-reference/graphs/shipping-carrier-selector.json +0 -379
- package/test-data/zen-reference/graphs/smart-financial-product-matcher.json +0 -249
- package/test-data/zen-reference/graphs/supply-chain-risk.json +0 -316
- package/test-data/zen-reference/graphs/table-loop.json +0 -93
- package/test-data/zen-reference/graphs/tax-exemption.json +0 -295
- package/test-data/zen-reference/graphs/traffic-violation-penalty-calculator.json +0 -436
- package/test-data/zen-reference/graphs/transaction-compliance-classifier.json +0 -525
- package/test-data/zen-reference/graphs/vehicle-claims-resolution.json +0 -310
- package/test-data/zen-reference/graphs/warehouse-cross-docking.json +0 -313
- package/test-data/zen-reference/graphs/warehouse-storage-location.json +0 -345
- package/test-data/zen-reference/http-function.json +0 -34
- package/test-data/zen-reference/infinite-function.json +0 -46
- package/test-data/zen-reference/js/imports.js +0 -25
- package/test-data/zen-reference/passthrough.json +0 -31
- package/test-data/zen-reference/recursive-table1.json +0 -49
- package/test-data/zen-reference/recursive-table2.json +0 -49
- package/test-data/zen-reference/sleep-function.json +0 -34
- package/test-data/zen-reference/switch-node.json +0 -167
- package/test-data/zen-reference/switch-performance-2.json +0 -1307
- package/test-data/zen-reference/switch-performance.json +0 -691
- package/test-data/zen-reference/table.json +0 -76
- package/tests/helpers/index.ts +0 -73
- package/tests/helpers/mock-context.ts +0 -231
- package/tests/helpers/round-trip.ts +0 -398
- package/tests/helpers/test-harness-comparison.ts +0 -325
- package/tests/helpers/test-harness-wasm.ts +0 -710
- package/tests/helpers/test-harness.ts +0 -28
- package/tests/helpers/wasm-test.ts +0 -659
- package/tests/integration/compilation-errors.test.ts +0 -864
- package/tests/integration/decision-tables.test.ts +0 -531
- package/tests/integration/edge-cases.test.ts +0 -787
- package/tests/integration/expressions.test.ts +0 -513
- package/tests/integration/function-node-integration.test.ts +0 -182
- package/tests/integration/sub-decisions.test.ts +0 -108
- package/tests/integration/switch-nodes.test.ts +0 -399
- package/tests/integration/unary-or-matching.test.ts +0 -53
- package/tests/integration/wasm-data-types.test.ts +0 -398
- package/tests/integration/wasm-errors.test.ts +0 -199
- package/tests/integration/wasm-execution.test.ts +0 -348
- package/tests/integration/wasm-memory.test.ts +0 -228
- package/tests/scripts/analyze-coverage.ts +0 -166
- package/tests/scripts/categorize-tests.ts +0 -396
- package/tests/scripts/coverage-analysis.ts +0 -836
- package/tests/unit/compiler/cache.test.ts +0 -238
- package/tests/unit/compiler/errors.test.ts +0 -316
- package/tests/unit/compiler/graph-scalability.test.ts +0 -510
- package/tests/unit/compiler/graph.test.ts +0 -878
- package/tests/unit/compiler/input-validation.test.ts +0 -447
- package/tests/unit/compiler/logical-and-parser.test.ts +0 -143
- package/tests/unit/compiler/logical-not-parser.test.ts +0 -107
- package/tests/unit/compiler/logical-or-parser.test.ts +0 -236
- package/tests/unit/compiler/marshal-gen/marshal-gen.test.ts +0 -97
- package/tests/unit/compiler/nodes/decision-table.test.ts +0 -103
- package/tests/unit/compiler/nodes/decision.test.ts +0 -182
- package/tests/unit/compiler/nodes/function-compile.test.ts +0 -204
- package/tests/unit/compiler/nodes/function.test.ts +0 -176
- package/tests/unit/compiler/nodes/input.test.ts +0 -30
- package/tests/unit/compiler/nodes/switch.test.ts +0 -127
- package/tests/unit/compiler/optimizer-cache.test.ts +0 -327
- package/tests/unit/compiler/optimizer-implementation.test.ts +0 -625
- package/tests/unit/compiler/parser.test.ts +0 -508
- package/tests/unit/compiler/runtime-error-cleanup.test.ts +0 -426
- package/tests/unit/compiler/runtime-validation.test.ts +0 -303
- package/tests/unit/compiler/runtime.test.ts +0 -221
- package/tests/unit/compiler/schema/schema.test.ts +0 -248
- package/tests/unit/compiler/unary-ast-transforms.test.ts +0 -245
- package/tsconfig.json +0 -27
- package/tsup.config.ts +0 -11
- package/vitest.config.ts +0 -12
|
@@ -1,948 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optimization Pipeline for JDM to WebAssembly Compilation
|
|
3
|
-
*
|
|
4
|
-
* This module defines the interface for optimization passes and the runner
|
|
5
|
-
* that applies them sequentially to transform the compiled AST into an
|
|
6
|
-
* optimized form before code generation.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { type JDMDecision, type JDMNode, NodeType } from './parser';
|
|
10
|
-
import {
|
|
11
|
-
type ExpressionAST,
|
|
12
|
-
type NumberLiteral,
|
|
13
|
-
type StringLiteral,
|
|
14
|
-
type BooleanLiteral,
|
|
15
|
-
isNumberLiteral,
|
|
16
|
-
isStringLiteral,
|
|
17
|
-
isBooleanLiteral,
|
|
18
|
-
isNullLiteral,
|
|
19
|
-
isBinaryOp,
|
|
20
|
-
isUnaryOp,
|
|
21
|
-
isTernaryOp,
|
|
22
|
-
} from './ast-types';
|
|
23
|
-
import { parseStandardExpression, parseUnaryExpression } from './unary-parser';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Cache for parsed expression ASTs.
|
|
27
|
-
* Maps expression strings to their parsed AST nodes to avoid redundant parsing.
|
|
28
|
-
*/
|
|
29
|
-
type ExpressionCache = Map<string, ExpressionAST>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Compiled AST - the intermediate representation after parsing.
|
|
33
|
-
*
|
|
34
|
-
* JDMDecision structure is simple enough for in-place transformation, avoiding the
|
|
35
|
-
* complexity and memory overhead of building a separate intermediate representation.
|
|
36
|
-
*/
|
|
37
|
-
export type CompiledAST = JDMDecision;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Interface for an optimization pass.
|
|
41
|
-
*
|
|
42
|
-
* Each optimization pass receives the compiled AST and returns a
|
|
43
|
-
* potentially modified/optimized AST.
|
|
44
|
-
*/
|
|
45
|
-
export interface OptimizationPass {
|
|
46
|
-
/** Human-readable name of the optimization pass */
|
|
47
|
-
name: string;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Run the optimization pass on the given AST.
|
|
51
|
-
*
|
|
52
|
-
* @param ast - The compiled AST to optimize
|
|
53
|
-
* @returns The optimized AST
|
|
54
|
-
*/
|
|
55
|
-
run(ast: CompiledAST): CompiledAST;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Ordered list of optimization passes.
|
|
60
|
-
*
|
|
61
|
-
* Passes have dependencies - one pass may create opportunities for the next. For example,
|
|
62
|
-
* constant folding might convert "if (2 > 3)" to "if (false)", then dead code elimination
|
|
63
|
-
* removes the unreachable branch. Running DCE before folding would miss this optimization.
|
|
64
|
-
*/
|
|
65
|
-
export const optimizer: OptimizationPass[] = [
|
|
66
|
-
{ name: 'Constant Folding', run: constantFolding },
|
|
67
|
-
{ name: 'Dead Code Elimination', run: deadCodeElimination },
|
|
68
|
-
{ name: 'Expression Inlining', run: expressionInlining },
|
|
69
|
-
{ name: 'Table Indexing', run: tableIndexing },
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Run all optimization passes sequentially on the given AST.
|
|
74
|
-
*
|
|
75
|
-
* @param ast - The compiled AST to optimize
|
|
76
|
-
* @returns The fully optimized AST
|
|
77
|
-
*/
|
|
78
|
-
export function runOptimizations(ast: CompiledAST): CompiledAST {
|
|
79
|
-
let optimized = ast;
|
|
80
|
-
|
|
81
|
-
for (const pass of optimizer) {
|
|
82
|
-
optimized = pass.run(optimized);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return optimized;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ============================================================================
|
|
89
|
-
// Expression Cache Utilities
|
|
90
|
-
// ============================================================================
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Get the expression cache for a node, creating it if it doesn't exist.
|
|
94
|
-
*/
|
|
95
|
-
function getExpressionCache(node: JDMNode): ExpressionCache {
|
|
96
|
-
if (!node.parsedContent) {
|
|
97
|
-
node.parsedContent = {};
|
|
98
|
-
}
|
|
99
|
-
if (!node.parsedContent._cachedExprs) {
|
|
100
|
-
node.parsedContent._cachedExprs = new Map<string, ExpressionAST>();
|
|
101
|
-
}
|
|
102
|
-
return node.parsedContent._cachedExprs as ExpressionCache;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Parse an expression with caching.
|
|
107
|
-
* Checks the cache first, and only parses if not found.
|
|
108
|
-
*/
|
|
109
|
-
function cachedParseUnary(node: JDMNode, expr: string): ExpressionAST {
|
|
110
|
-
const cache = getExpressionCache(node);
|
|
111
|
-
const cached = cache.get(expr);
|
|
112
|
-
if (cached) {
|
|
113
|
-
return cached;
|
|
114
|
-
}
|
|
115
|
-
const parsed = parseUnaryExpression(expr);
|
|
116
|
-
cache.set(expr, parsed);
|
|
117
|
-
return parsed;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Parse a standard expression with caching.
|
|
122
|
-
* Checks the cache first, and only parses if not found.
|
|
123
|
-
*/
|
|
124
|
-
function cachedParseStandard(node: JDMNode, expr: string): ExpressionAST {
|
|
125
|
-
const cache = getExpressionCache(node);
|
|
126
|
-
const cached = cache.get(expr);
|
|
127
|
-
if (cached) {
|
|
128
|
-
return cached;
|
|
129
|
-
}
|
|
130
|
-
const parsed = parseStandardExpression(expr);
|
|
131
|
-
cache.set(expr, parsed);
|
|
132
|
-
return parsed;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Clear the expression cache for a node.
|
|
137
|
-
* Should be called when the node's expressions are modified.
|
|
138
|
-
*/
|
|
139
|
-
function clearExpressionCache(node: JDMNode): void {
|
|
140
|
-
if (node.parsedContent && node.parsedContent._cachedExprs) {
|
|
141
|
-
node.parsedContent._cachedExprs.clear();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Export individual optimization passes for testing
|
|
146
|
-
export { constantFolding, deadCodeElimination, expressionInlining, tableIndexing };
|
|
147
|
-
|
|
148
|
-
// Export cache utilities for testing
|
|
149
|
-
export { getExpressionCache, clearExpressionCache };
|
|
150
|
-
|
|
151
|
-
// ============================================================================
|
|
152
|
-
// Constant Folding
|
|
153
|
-
// ============================================================================
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Fold constant expressions at compile time.
|
|
157
|
-
*
|
|
158
|
-
* This pass traverses expression ASTs and evaluates them at compile time
|
|
159
|
-
* when possible. Examples:
|
|
160
|
-
* - Replace (2 + 3) with 5
|
|
161
|
-
* - Replace true and false with false
|
|
162
|
-
* - Replace "hello" + " world" with "hello world"
|
|
163
|
-
*
|
|
164
|
-
* @param ast - The compiled AST to optimize
|
|
165
|
-
* @returns The AST with folded constants
|
|
166
|
-
*/
|
|
167
|
-
function constantFolding(ast: CompiledAST): CompiledAST {
|
|
168
|
-
// Create a deep copy to avoid mutating the original
|
|
169
|
-
const optimized = JSON.parse(JSON.stringify(ast)) as JDMDecision;
|
|
170
|
-
|
|
171
|
-
// Apply constant folding to each node's expression content
|
|
172
|
-
optimized.nodes = optimized.nodes.map((node: JDMNode) => foldNodeExpressions(node));
|
|
173
|
-
|
|
174
|
-
return optimized;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Apply constant folding to expressions within a node.
|
|
179
|
-
*/
|
|
180
|
-
function foldNodeExpressions(node: JDMNode): JDMNode {
|
|
181
|
-
if (!node.content) {
|
|
182
|
-
return node;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const content = node.content;
|
|
186
|
-
|
|
187
|
-
// Handle decision table nodes
|
|
188
|
-
if (node.type === NodeType.DECISION_TABLE) {
|
|
189
|
-
return foldDecisionTableExpressions(node, content);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Handle switch nodes
|
|
193
|
-
if (node.type === NodeType.SWITCH) {
|
|
194
|
-
return foldSwitchExpressions(node, content);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Handle expression nodes
|
|
198
|
-
if (node.type === NodeType.EXPRESSION) {
|
|
199
|
-
return foldExpressionNode(node, content);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return node;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Fold expressions in a decision table node.
|
|
207
|
-
*/
|
|
208
|
-
function foldDecisionTableExpressions(node: JDMNode, content: Record<string, unknown>): JDMNode {
|
|
209
|
-
const { rules, inputs } = content as { rules: any[]; inputs: any[] };
|
|
210
|
-
let modified = false;
|
|
211
|
-
|
|
212
|
-
// Update each rule's input conditions with folded expressions
|
|
213
|
-
const foldedRules = rules.map((rule: Record<string, unknown>) => {
|
|
214
|
-
const foldedRule = { ...rule };
|
|
215
|
-
|
|
216
|
-
for (const input of inputs) {
|
|
217
|
-
const expr = rule[input.id] as string | undefined;
|
|
218
|
-
if (!expr || expr === '' || expr === '-') {
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Parse the expression in unary mode (using cache)
|
|
223
|
-
try {
|
|
224
|
-
const ast = cachedParseUnary(node, expr);
|
|
225
|
-
const folded = foldExpression(ast);
|
|
226
|
-
|
|
227
|
-
// If folding succeeded, update the expression string
|
|
228
|
-
if (folded !== null && isLiteral(folded)) {
|
|
229
|
-
const newExpr = literalToExprString(folded);
|
|
230
|
-
if (newExpr !== expr) {
|
|
231
|
-
foldedRule[input.id] = newExpr;
|
|
232
|
-
modified = true;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
// If expression can't be parsed or folded, keep original
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return foldedRule;
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Clear cache if expressions were modified
|
|
244
|
-
if (modified) {
|
|
245
|
-
clearExpressionCache(node);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
...node,
|
|
250
|
-
content: {
|
|
251
|
-
...content,
|
|
252
|
-
rules: foldedRules,
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Fold expressions in a switch node.
|
|
259
|
-
*/
|
|
260
|
-
function foldSwitchExpressions(node: JDMNode, content: any): JDMNode {
|
|
261
|
-
const { conditions } = content;
|
|
262
|
-
let modified = false;
|
|
263
|
-
|
|
264
|
-
const foldedConditions = conditions.map((condition: any) => {
|
|
265
|
-
const expr = condition.expression;
|
|
266
|
-
if (!expr || expr === '') {
|
|
267
|
-
return condition;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
const ast = cachedParseStandard(node, expr);
|
|
272
|
-
const folded = foldExpression(ast);
|
|
273
|
-
|
|
274
|
-
if (folded !== null && isLiteral(folded)) {
|
|
275
|
-
const newExpr = literalToExprString(folded);
|
|
276
|
-
if (newExpr !== expr) {
|
|
277
|
-
modified = true;
|
|
278
|
-
return {
|
|
279
|
-
...condition,
|
|
280
|
-
expression: newExpr,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
} catch {
|
|
285
|
-
// If expression can't be parsed or folded, keep original
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return condition;
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Clear cache if expressions were modified
|
|
292
|
-
if (modified) {
|
|
293
|
-
clearExpressionCache(node);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return {
|
|
297
|
-
...node,
|
|
298
|
-
content: {
|
|
299
|
-
...content,
|
|
300
|
-
conditions: foldedConditions,
|
|
301
|
-
},
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Fold expressions in an expression node.
|
|
307
|
-
*/
|
|
308
|
-
function foldExpressionNode(node: JDMNode, content: any): JDMNode {
|
|
309
|
-
const expr = content.expression;
|
|
310
|
-
if (!expr) {
|
|
311
|
-
return node;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
try {
|
|
315
|
-
const ast = cachedParseStandard(node, expr);
|
|
316
|
-
const folded = foldExpression(ast);
|
|
317
|
-
|
|
318
|
-
if (folded !== null && isLiteral(folded)) {
|
|
319
|
-
const newExpr = literalToExprString(folded);
|
|
320
|
-
if (newExpr !== expr) {
|
|
321
|
-
// Clear cache since we're modifying the expression
|
|
322
|
-
clearExpressionCache(node);
|
|
323
|
-
return {
|
|
324
|
-
...node,
|
|
325
|
-
content: {
|
|
326
|
-
...content,
|
|
327
|
-
expression: newExpr,
|
|
328
|
-
},
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
} catch {
|
|
333
|
-
// If expression can't be parsed or folded, keep original
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return node;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Recursively fold an expression AST.
|
|
341
|
-
* Returns the folded AST node, or null if folding is not possible.
|
|
342
|
-
*/
|
|
343
|
-
function foldExpression(expr: ExpressionAST): ExpressionAST | null {
|
|
344
|
-
if (isLiteralExpr(expr)) {
|
|
345
|
-
return expr;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (isBinaryOp(expr)) {
|
|
349
|
-
const leftFolded = foldExpression(expr.left);
|
|
350
|
-
const rightFolded = foldExpression(expr.right);
|
|
351
|
-
|
|
352
|
-
// If both sides are literals, try to evaluate at compilation time
|
|
353
|
-
if (leftFolded && rightFolded && isLiteral(leftFolded) && isLiteral(rightFolded)) {
|
|
354
|
-
const result = evaluateConstantBinaryOp(expr.op, leftFolded, rightFolded);
|
|
355
|
-
if (result !== null) {
|
|
356
|
-
return result;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return {
|
|
361
|
-
...expr,
|
|
362
|
-
left: leftFolded || expr.left,
|
|
363
|
-
right: rightFolded || expr.right,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (isUnaryOp(expr)) {
|
|
368
|
-
const operandFolded = foldExpression(expr.operand);
|
|
369
|
-
|
|
370
|
-
if (operandFolded && isLiteral(operandFolded)) {
|
|
371
|
-
const result = evaluateConstantUnaryOp(expr.op, operandFolded);
|
|
372
|
-
if (result !== null) {
|
|
373
|
-
return result;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return {
|
|
378
|
-
...expr,
|
|
379
|
-
operand: operandFolded || expr.operand,
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (isTernaryOp(expr)) {
|
|
384
|
-
const conditionFolded = foldExpression(expr.condition);
|
|
385
|
-
const consequentFolded = foldExpression(expr.consequent);
|
|
386
|
-
const alternateFolded = foldExpression(expr.alternate);
|
|
387
|
-
|
|
388
|
-
if (conditionFolded && isLiteral(conditionFolded) && isBooleanLiteral(conditionFolded)) {
|
|
389
|
-
return conditionFolded.value
|
|
390
|
-
? consequentFolded || expr.consequent
|
|
391
|
-
: alternateFolded || expr.alternate;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return {
|
|
395
|
-
...expr,
|
|
396
|
-
condition: conditionFolded || expr.condition,
|
|
397
|
-
consequent: consequentFolded || expr.consequent,
|
|
398
|
-
alternate: alternateFolded || expr.alternate,
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Can't fold other expression types (identifiers, function calls, etc.)
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Check if an expression is a literal value.
|
|
408
|
-
*/
|
|
409
|
-
function isLiteralExpr(expr: ExpressionAST): boolean {
|
|
410
|
-
return (
|
|
411
|
-
isNumberLiteral(expr) || isStringLiteral(expr) || isBooleanLiteral(expr) || isNullLiteral(expr)
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Check if a node is a literal.
|
|
417
|
-
*/
|
|
418
|
-
function isLiteral(
|
|
419
|
-
node: ExpressionAST | null,
|
|
420
|
-
): node is NumberLiteral | StringLiteral | BooleanLiteral {
|
|
421
|
-
return node !== null && isLiteralExpr(node);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Evaluate a binary operation with constant operands.
|
|
426
|
-
*/
|
|
427
|
-
function evaluateConstantBinaryOp(
|
|
428
|
-
op: string,
|
|
429
|
-
left: NumberLiteral | StringLiteral | BooleanLiteral,
|
|
430
|
-
right: NumberLiteral | StringLiteral | BooleanLiteral,
|
|
431
|
-
): ExpressionAST | null {
|
|
432
|
-
// Numeric operations
|
|
433
|
-
if (isNumberLiteral(left) && isNumberLiteral(right)) {
|
|
434
|
-
switch (op) {
|
|
435
|
-
case '+':
|
|
436
|
-
return { type: 'NumberLiteral', value: left.value + right.value };
|
|
437
|
-
case '-':
|
|
438
|
-
return { type: 'NumberLiteral', value: left.value - right.value };
|
|
439
|
-
case '*':
|
|
440
|
-
return { type: 'NumberLiteral', value: left.value * right.value };
|
|
441
|
-
case '/':
|
|
442
|
-
return { type: 'NumberLiteral', value: left.value / right.value };
|
|
443
|
-
case '%':
|
|
444
|
-
return { type: 'NumberLiteral', value: left.value % right.value };
|
|
445
|
-
case '^':
|
|
446
|
-
return { type: 'NumberLiteral', value: Math.pow(left.value, right.value) };
|
|
447
|
-
case '<':
|
|
448
|
-
return { type: 'BooleanLiteral', value: left.value < right.value };
|
|
449
|
-
case '>':
|
|
450
|
-
return { type: 'BooleanLiteral', value: left.value > right.value };
|
|
451
|
-
case '<=':
|
|
452
|
-
return { type: 'BooleanLiteral', value: left.value <= right.value };
|
|
453
|
-
case '>=':
|
|
454
|
-
return { type: 'BooleanLiteral', value: left.value >= right.value };
|
|
455
|
-
case '==':
|
|
456
|
-
return { type: 'BooleanLiteral', value: left.value === right.value };
|
|
457
|
-
case '!=':
|
|
458
|
-
return { type: 'BooleanLiteral', value: left.value !== right.value };
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// String operations
|
|
463
|
-
if (isStringLiteral(left) && isStringLiteral(right)) {
|
|
464
|
-
switch (op) {
|
|
465
|
-
case '+':
|
|
466
|
-
return { type: 'StringLiteral', value: left.value + right.value };
|
|
467
|
-
case '==':
|
|
468
|
-
return { type: 'BooleanLiteral', value: left.value === right.value };
|
|
469
|
-
case '!=':
|
|
470
|
-
return { type: 'BooleanLiteral', value: left.value !== right.value };
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Boolean operations
|
|
475
|
-
if (isBooleanLiteral(left) && isBooleanLiteral(right)) {
|
|
476
|
-
switch (op) {
|
|
477
|
-
case 'and':
|
|
478
|
-
return { type: 'BooleanLiteral', value: left.value && right.value };
|
|
479
|
-
case 'or':
|
|
480
|
-
return { type: 'BooleanLiteral', value: left.value || right.value };
|
|
481
|
-
case '==':
|
|
482
|
-
return { type: 'BooleanLiteral', value: left.value === right.value };
|
|
483
|
-
case '!=':
|
|
484
|
-
return { type: 'BooleanLiteral', value: left.value !== right.value };
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return null;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Evaluate a unary operation with a constant operand.
|
|
493
|
-
*/
|
|
494
|
-
function evaluateConstantUnaryOp(
|
|
495
|
-
op: string,
|
|
496
|
-
operand: NumberLiteral | StringLiteral | BooleanLiteral,
|
|
497
|
-
): ExpressionAST | null {
|
|
498
|
-
if (op === 'not' && isBooleanLiteral(operand)) {
|
|
499
|
-
return { type: 'BooleanLiteral', value: !operand.value };
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (op === '-' && isNumberLiteral(operand)) {
|
|
503
|
-
return { type: 'NumberLiteral', value: -operand.value };
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return null;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Convert a literal AST node to its string representation.
|
|
511
|
-
*/
|
|
512
|
-
function literalToExprString(node: NumberLiteral | StringLiteral | BooleanLiteral): string {
|
|
513
|
-
switch (node.type) {
|
|
514
|
-
case 'NumberLiteral':
|
|
515
|
-
return String(node.value);
|
|
516
|
-
case 'StringLiteral':
|
|
517
|
-
return `"${node.value}"`;
|
|
518
|
-
case 'BooleanLiteral':
|
|
519
|
-
return String(node.value);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// ============================================================================
|
|
524
|
-
// Dead Code Elimination
|
|
525
|
-
// ============================================================================
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Remove unreachable code.
|
|
529
|
-
*
|
|
530
|
-
* This pass identifies and removes branches that can never be reached.
|
|
531
|
-
* Examples:
|
|
532
|
-
* - Remove if branches with constant false conditions
|
|
533
|
-
* - Remove decision table rows with always-false conditions
|
|
534
|
-
* - Remove decision table rows that can never match after previous rows
|
|
535
|
-
*
|
|
536
|
-
* @param ast - The compiled AST to optimize
|
|
537
|
-
* @returns The AST with dead code removed
|
|
538
|
-
*/
|
|
539
|
-
function deadCodeElimination(ast: CompiledAST): CompiledAST {
|
|
540
|
-
const optimized = JSON.parse(JSON.stringify(ast)) as JDMDecision;
|
|
541
|
-
|
|
542
|
-
optimized.nodes = optimized.nodes.map((node: JDMNode) => eliminateDeadCodeInNode(node));
|
|
543
|
-
|
|
544
|
-
return optimized;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Apply dead code elimination to a node.
|
|
549
|
-
*/
|
|
550
|
-
function eliminateDeadCodeInNode(node: JDMNode): JDMNode {
|
|
551
|
-
if (!node.content) {
|
|
552
|
-
return node;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
const content = node.content;
|
|
556
|
-
|
|
557
|
-
if (node.type === NodeType.DECISION_TABLE) {
|
|
558
|
-
return eliminateDeadCodeInDecisionTable(node, content);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (node.type === NodeType.SWITCH) {
|
|
562
|
-
return eliminateDeadCodeInSwitch(node, content);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return node;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Check if a unary expression is always false.
|
|
570
|
-
* In unary mode, "false" is equivalent to "$ == false".
|
|
571
|
-
*/
|
|
572
|
-
function isUnaryAlwaysFalse(expr: ExpressionAST): boolean {
|
|
573
|
-
if (isBinaryOp(expr) && expr.op === '==') {
|
|
574
|
-
const leftIsDollar = expr.left.type === 'DollarRef';
|
|
575
|
-
const rightIsFalse = isBooleanLiteral(expr.right) && !expr.right.value;
|
|
576
|
-
return leftIsDollar && rightIsFalse;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (isBinaryOp(expr) && expr.op === '!=') {
|
|
580
|
-
const leftIsDollar = expr.left.type === 'DollarRef';
|
|
581
|
-
const rightIsTrue = isBooleanLiteral(expr.right) && expr.right.value;
|
|
582
|
-
return leftIsDollar && rightIsTrue;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Eliminate dead code in decision table nodes.
|
|
590
|
-
*/
|
|
591
|
-
function eliminateDeadCodeInSwitch(node: JDMNode, content: any): JDMNode {
|
|
592
|
-
const { conditions } = content;
|
|
593
|
-
|
|
594
|
-
// Filter out conditions that are always false
|
|
595
|
-
const liveConditions = conditions.filter((condition: any) => {
|
|
596
|
-
const expr = condition.expression;
|
|
597
|
-
if (!expr || expr === '') {
|
|
598
|
-
return true;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
try {
|
|
602
|
-
const parsed = cachedParseStandard(node, expr);
|
|
603
|
-
|
|
604
|
-
if (isBooleanLiteral(parsed) && !parsed.value) {
|
|
605
|
-
return false;
|
|
606
|
-
}
|
|
607
|
-
} catch {}
|
|
608
|
-
|
|
609
|
-
return true;
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
return {
|
|
613
|
-
...node,
|
|
614
|
-
content: {
|
|
615
|
-
...content,
|
|
616
|
-
conditions: liveConditions,
|
|
617
|
-
},
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Eliminate dead code in decision table nodes.
|
|
623
|
-
*/
|
|
624
|
-
function eliminateDeadCodeInDecisionTable(node: JDMNode, content: any): JDMNode {
|
|
625
|
-
const { rules, inputs } = content;
|
|
626
|
-
|
|
627
|
-
// Filter out rules that have a non-trivial always-false condition
|
|
628
|
-
const liveRules = rules.filter((rule: any) => {
|
|
629
|
-
for (const input of inputs) {
|
|
630
|
-
const expr = rule[input.id];
|
|
631
|
-
if (!expr || expr === '' || expr === '-') {
|
|
632
|
-
continue;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
try {
|
|
636
|
-
const parsed = cachedParseUnary(node, expr);
|
|
637
|
-
|
|
638
|
-
// Check if this is a boolean literal (not common in unary mode, but possible)
|
|
639
|
-
if (isBooleanLiteral(parsed) && !parsed.value) {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// Check if this is a unary expression that's always false
|
|
644
|
-
// e.g., "false" in a decision table cell is interpreted as "$ == false"
|
|
645
|
-
if (isUnaryAlwaysFalse(parsed)) {
|
|
646
|
-
return false;
|
|
647
|
-
}
|
|
648
|
-
} catch {
|
|
649
|
-
// If we can't parse it, assume the rule is live
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
return true;
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
return {
|
|
657
|
-
...node,
|
|
658
|
-
content: {
|
|
659
|
-
...content,
|
|
660
|
-
rules: liveRules,
|
|
661
|
-
},
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// ============================================================================
|
|
666
|
-
// Expression Inlining
|
|
667
|
-
// ============================================================================
|
|
668
|
-
|
|
669
|
-
/**
|
|
670
|
-
* Inline small expressions.
|
|
671
|
-
*
|
|
672
|
-
* This pass replaces function calls with evaluated results for pure functions.
|
|
673
|
-
* Examples:
|
|
674
|
-
* - Inline small arithmetic operations in decision table cells
|
|
675
|
-
* - Pre-evaluate expressions that don't depend on runtime values
|
|
676
|
-
*
|
|
677
|
-
* @param ast - The compiled AST to optimize
|
|
678
|
-
* @returns The AST with inlined expressions
|
|
679
|
-
*/
|
|
680
|
-
function expressionInlining(ast: CompiledAST): CompiledAST {
|
|
681
|
-
// Create a deep copy to avoid mutating the original
|
|
682
|
-
const optimized = JSON.parse(JSON.stringify(ast)) as JDMDecision;
|
|
683
|
-
|
|
684
|
-
// Only functions without side effects can be safely evaluated at compile time. Arithmetic
|
|
685
|
-
// (sum, max, min, abs, floor, ceil, round) and string functions (upper, lower, len) are
|
|
686
|
-
// pure and deterministic, so their results with constant inputs are predictable.
|
|
687
|
-
optimized.nodes = optimized.nodes.map((node: JDMNode) => inlineExpressionsInNode(node));
|
|
688
|
-
|
|
689
|
-
return optimized;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
* Apply expression inlining to a node.
|
|
694
|
-
*/
|
|
695
|
-
function inlineExpressionsInNode(node: JDMNode): JDMNode {
|
|
696
|
-
if (!node.content) {
|
|
697
|
-
return node;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
const content = node.content;
|
|
701
|
-
|
|
702
|
-
// Handle decision table nodes
|
|
703
|
-
if (node.type === NodeType.DECISION_TABLE && content && content.rules && content.inputs) {
|
|
704
|
-
return inlineDecisionTableExpressions(node, content);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
return node;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Inline expressions in decision table nodes.
|
|
712
|
-
*/
|
|
713
|
-
function inlineDecisionTableExpressions(node: JDMNode, content: any): JDMNode {
|
|
714
|
-
const { rules, inputs } = content;
|
|
715
|
-
|
|
716
|
-
const inlinedRules = rules.map((rule: any) => {
|
|
717
|
-
const inlinedRule = { ...rule };
|
|
718
|
-
|
|
719
|
-
for (const input of inputs) {
|
|
720
|
-
const expr = rule[input.id];
|
|
721
|
-
if (!expr || expr === '' || expr === '-') {
|
|
722
|
-
continue;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const inlined = tryInlineExpression(expr);
|
|
726
|
-
if (inlined !== expr) {
|
|
727
|
-
inlinedRule[input.id] = inlined;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
return inlinedRule;
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
return {
|
|
735
|
-
...node,
|
|
736
|
-
content: {
|
|
737
|
-
...content,
|
|
738
|
-
rules: inlinedRules,
|
|
739
|
-
},
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
* Try to inline an expression by evaluating simple pure function calls.
|
|
745
|
-
* Returns the inlined expression string, or the original if inlining is not possible.
|
|
746
|
-
*/
|
|
747
|
-
function tryInlineExpression(expr: string): string {
|
|
748
|
-
const trimmed = expr.trim();
|
|
749
|
-
|
|
750
|
-
// Inline simple arithmetic operations wrapped in function notation
|
|
751
|
-
// Examples: sum(2, 3) → 5, min(10, 5) → 5, abs(-5) → 5, upper("hello") → "HELLO"
|
|
752
|
-
|
|
753
|
-
// Match sum(a, b, ...) pattern where all arguments are numeric literals
|
|
754
|
-
const sumMatch = trimmed.match(/^sum\((.+)\)$/i);
|
|
755
|
-
if (sumMatch) {
|
|
756
|
-
const args = sumMatch[1].split(',').map((s: string) => s.trim());
|
|
757
|
-
if (args.every((arg: string) => /^-?\d+\.?\d*$/.test(arg))) {
|
|
758
|
-
const sum = args.reduce((acc: number, arg: string) => acc + parseFloat(arg), 0);
|
|
759
|
-
return String(sum);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Match max(a, b) pattern where both arguments are numeric literals
|
|
764
|
-
const maxMatch = trimmed.match(/^max\((.+),(.+)\)$/i);
|
|
765
|
-
if (maxMatch) {
|
|
766
|
-
const [_, arg1, arg2] = maxMatch;
|
|
767
|
-
const num1 = parseFloat(arg1.trim());
|
|
768
|
-
const num2 = parseFloat(arg2.trim());
|
|
769
|
-
if (!isNaN(num1) && !isNaN(num2)) {
|
|
770
|
-
return String(Math.max(num1, num2));
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Match min(a, b) pattern where both arguments are numeric literals
|
|
775
|
-
const minMatch = trimmed.match(/^min\((.+),(.+)\)$/i);
|
|
776
|
-
if (minMatch) {
|
|
777
|
-
const [_, arg1, arg2] = minMatch;
|
|
778
|
-
const num1 = parseFloat(arg1.trim());
|
|
779
|
-
const num2 = parseFloat(arg2.trim());
|
|
780
|
-
if (!isNaN(num1) && !isNaN(num2)) {
|
|
781
|
-
return String(Math.min(num1, num2));
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Match abs(a) pattern where argument is a numeric literal
|
|
786
|
-
const absMatch = trimmed.match(/^abs\((.+)\)$/i);
|
|
787
|
-
if (absMatch) {
|
|
788
|
-
const val = parseFloat(absMatch[1].trim());
|
|
789
|
-
if (!isNaN(val)) {
|
|
790
|
-
return String(Math.abs(val));
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// Match floor(a) pattern where argument is a numeric literal
|
|
795
|
-
const floorMatch = trimmed.match(/^floor\((.+)\)$/i);
|
|
796
|
-
if (floorMatch) {
|
|
797
|
-
const val = parseFloat(floorMatch[1].trim());
|
|
798
|
-
if (!isNaN(val)) {
|
|
799
|
-
return String(Math.floor(val));
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Match ceil(a) pattern where argument is a numeric literal
|
|
804
|
-
const ceilMatch = trimmed.match(/^ceil\((.+)\)$/i);
|
|
805
|
-
if (ceilMatch) {
|
|
806
|
-
const val = parseFloat(ceilMatch[1].trim());
|
|
807
|
-
if (!isNaN(val)) {
|
|
808
|
-
return String(Math.ceil(val));
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// Match round(a) pattern where argument is a numeric literal
|
|
813
|
-
const roundMatch = trimmed.match(/^round\((.+)\)$/i);
|
|
814
|
-
if (roundMatch) {
|
|
815
|
-
const val = parseFloat(roundMatch[1].trim());
|
|
816
|
-
if (!isNaN(val)) {
|
|
817
|
-
return String(Math.round(val));
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// Match upper("string") pattern for constant string literals
|
|
822
|
-
const upperMatch = trimmed.match(/^upper\("(.+)"\)$/i);
|
|
823
|
-
if (upperMatch) {
|
|
824
|
-
return `"${upperMatch[1].toUpperCase()}"`;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// Match lower("string") pattern for constant string literals
|
|
828
|
-
const lowerMatch = trimmed.match(/^lower\("(.+)"\)$/i);
|
|
829
|
-
if (lowerMatch) {
|
|
830
|
-
return `"${lowerMatch[1].toLowerCase()}"`;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
// Match len("string") pattern for constant string literals
|
|
834
|
-
const lenMatch = trimmed.match(/^len\("(.*)"\)$/i);
|
|
835
|
-
if (lenMatch) {
|
|
836
|
-
return String(lenMatch[1].length);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// Match len(123) pattern for numeric literals (counting digits excluding sign)
|
|
840
|
-
const lenNumMatch = trimmed.match(/^len\(-?\d+\.?\d*\)$/i);
|
|
841
|
-
if (lenNumMatch) {
|
|
842
|
-
const numStr = trimmed.match(/^len\((.+)\)$/i)![1].trim();
|
|
843
|
-
// Count digits ignoring sign and decimal point
|
|
844
|
-
const digitCount = numStr.replace(/^-/, '').replace('.', '').length;
|
|
845
|
-
return String(digitCount);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
return expr;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// ============================================================================
|
|
852
|
-
// Table Indexing
|
|
853
|
-
// ============================================================================
|
|
854
|
-
|
|
855
|
-
/**
|
|
856
|
-
* Build index structures for efficient table lookup.
|
|
857
|
-
*
|
|
858
|
-
* This pass transforms linear search structures into more efficient alternatives.
|
|
859
|
-
* Examples:
|
|
860
|
-
* - Convert decision table rows with equality checks to index-based lookup
|
|
861
|
-
* - Mark tables that are good candidates for switch statement generation
|
|
862
|
-
*
|
|
863
|
-
* Table indexing converts O(n) linear search to O(1) hash lookup when all rules check
|
|
864
|
-
* equality on a single column with unique values - this is common in lookup tables
|
|
865
|
-
* (e.g., pricing tiers by customer type) and can improve evaluation time by 10-100x
|
|
866
|
-
* for large tables.
|
|
867
|
-
*
|
|
868
|
-
* @param ast - The compiled AST to optimize
|
|
869
|
-
* @returns The AST with optimized table lookups
|
|
870
|
-
*/
|
|
871
|
-
function tableIndexing(ast: CompiledAST): CompiledAST {
|
|
872
|
-
const optimized = JSON.parse(JSON.stringify(ast)) as JDMDecision;
|
|
873
|
-
|
|
874
|
-
optimized.nodes = optimized.nodes.map((node: JDMNode) => indexTableInNode(node));
|
|
875
|
-
|
|
876
|
-
return optimized;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* Apply table indexing to a node.
|
|
881
|
-
*/
|
|
882
|
-
function indexTableInNode(node: JDMNode): JDMNode {
|
|
883
|
-
if (!node.content) {
|
|
884
|
-
return node;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
const content = node.content;
|
|
888
|
-
|
|
889
|
-
if (node.type === NodeType.DECISION_TABLE) {
|
|
890
|
-
return indexDecisionTable(node, content);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
return node;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* Index decision table for efficient lookup.
|
|
898
|
-
*
|
|
899
|
-
* This pass analyzes the decision table to identify patterns that can be
|
|
900
|
-
* optimized, such as equality checks on a single column that could be
|
|
901
|
-
* converted to a switch statement or hash map.
|
|
902
|
-
*/
|
|
903
|
-
function indexDecisionTable(node: JDMNode, content: any): JDMNode {
|
|
904
|
-
const { rules, inputs } = content;
|
|
905
|
-
|
|
906
|
-
// Analyze each input column to find indexing opportunities
|
|
907
|
-
const indexedInputs = inputs.map((input: any) => {
|
|
908
|
-
const inputValues = rules.map((rule: any) => {
|
|
909
|
-
const expr = rule[input.id];
|
|
910
|
-
if (!expr || expr === '' || expr === '-') {
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
return expr.trim();
|
|
914
|
-
});
|
|
915
|
-
|
|
916
|
-
// Check if this column is a good candidate for indexing:
|
|
917
|
-
// - All values are string or number literals
|
|
918
|
-
// - No intervals or complex expressions
|
|
919
|
-
// - No duplicate values
|
|
920
|
-
const allLiterals = inputValues.every((val: string | null) => {
|
|
921
|
-
if (!val) {
|
|
922
|
-
return true;
|
|
923
|
-
}
|
|
924
|
-
return /^(["'])(?:(?=(\\?))\2.)*?\1$|^-?\d+\.?\d*$/.test(val);
|
|
925
|
-
});
|
|
926
|
-
|
|
927
|
-
const uniqueValues = new Set(inputValues.filter((v: string | null) => v !== null));
|
|
928
|
-
const hasDuplicates =
|
|
929
|
-
uniqueValues.size < inputValues.filter((v: string | null) => v !== null).length;
|
|
930
|
-
|
|
931
|
-
return {
|
|
932
|
-
...input,
|
|
933
|
-
// Hash-based lookup becomes beneficial with ~10+ unique values, but overhead matters
|
|
934
|
-
// for small tables. 50 is a practical limit where indexing provides clear wins without
|
|
935
|
-
// excessive memory for the lookup structure.
|
|
936
|
-
_indexable: allLiterals && !hasDuplicates && uniqueValues.size > 0 && uniqueValues.size < 50,
|
|
937
|
-
_uniqueValues: Array.from(uniqueValues),
|
|
938
|
-
};
|
|
939
|
-
});
|
|
940
|
-
|
|
941
|
-
return {
|
|
942
|
-
...node,
|
|
943
|
-
content: {
|
|
944
|
-
...content,
|
|
945
|
-
inputs: indexedInputs,
|
|
946
|
-
},
|
|
947
|
-
};
|
|
948
|
-
}
|