pineforge-codegen 0.9.0__tar.gz → 0.10.0__tar.gz

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 (402) hide show
  1. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/PKG-INFO +1 -1
  2. pineforge_codegen-0.10.0/VERSION +1 -0
  3. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/base.py +220 -22
  4. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/emit_top.py +5 -0
  5. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/security.py +77 -25
  6. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/types.py +53 -0
  7. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/visit_expr.py +87 -1
  8. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/visit_stmt.py +11 -2
  9. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_ta_derived_length.py +169 -0
  10. pineforge_codegen-0.10.0/tests/test_na_reassign_retype.py +183 -0
  11. pineforge_codegen-0.10.0/tests/test_na_relational_lowering.py +219 -0
  12. pineforge_codegen-0.10.0/tests/test_security_tf_input_derived.py +175 -0
  13. pineforge_codegen-0.9.0/VERSION +0 -1
  14. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.github/dependabot.yml +0 -0
  15. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.github/workflows/gate.yml +0 -0
  16. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.github/workflows/publish-pyodide.yml +0 -0
  17. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.github/workflows/release.yml +0 -0
  18. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.github/workflows/test.yml +0 -0
  19. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/.gitignore +0 -0
  20. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/CLAUDE.md +0 -0
  21. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/LEGAL.md +0 -0
  22. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/LICENSE +0 -0
  23. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/PYODIDE_TARGET +0 -0
  24. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/README.md +0 -0
  25. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/docs/codegen-coverage-gaps.md +0 -0
  26. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/gate/compare.mjs +0 -0
  27. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/gate/glue.py +0 -0
  28. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/gate/oracle.py +0 -0
  29. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/gate/run-gate.mjs +0 -0
  30. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/gate/selftest.mjs +0 -0
  31. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/npm/README.md +0 -0
  32. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/npm/index.mjs +0 -0
  33. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/npm/package.json +0 -0
  34. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/package-lock.json +0 -0
  35. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/package.json +0 -0
  36. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/__init__.py +0 -0
  37. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/__init__.py +0 -0
  38. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/base.py +0 -0
  39. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/call_handlers.py +0 -0
  40. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/contracts.py +0 -0
  41. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/diagnostics.py +0 -0
  42. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/tables.py +0 -0
  43. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/analyzer/types.py +0 -0
  44. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/ast_nodes.py +0 -0
  45. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/__init__.py +0 -0
  46. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/drawing.py +0 -0
  47. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/helpers.py +0 -0
  48. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/helpers_syminfo.py +0 -0
  49. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/input.py +0 -0
  50. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/ta.py +0 -0
  51. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/tables.py +0 -0
  52. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/codegen/visit_call.py +0 -0
  53. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/errors.py +0 -0
  54. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/lexer.py +0 -0
  55. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/parser.py +0 -0
  56. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/pragmas.py +0 -0
  57. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/signatures.py +0 -0
  58. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/support_checker.py +0 -0
  59. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/symbols.py +0 -0
  60. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/tokens.py +0 -0
  61. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pineforge_codegen/tv_input_choices.py +0 -0
  62. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/pyproject.toml +0 -0
  63. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/scripts/build-npm-package.mjs +0 -0
  64. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/scripts/dump-tables.py +0 -0
  65. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/scripts/worker-template.mjs +0 -0
  66. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/__init__.py +0 -0
  67. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/_compile.py +0 -0
  68. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/const_ns_plot_style_free.pine +0 -0
  69. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/const_ns_shape_free.pine +0 -0
  70. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/divergent_last_bar_index.pine +0 -0
  71. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/export_func.pine +0 -0
  72. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/footprint_new.pine +0 -0
  73. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/hard_reject_dividends.pine +0 -0
  74. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/matrix_unknown_elem.pine +0 -0
  75. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/multi_error_one_line.pine +0 -0
  76. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/sec_tf_invalid.pine +0 -0
  77. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_color.pine +0 -0
  78. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_math.pine +0 -0
  79. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_str.pine +0 -0
  80. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_strategy_fn.pine +0 -0
  81. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_syminfo.pine +0 -0
  82. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/err/unknown_ta.pine +0 -0
  83. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/AAPL__session-ismarket-nyse-rth-01.pine +0 -0
  84. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/AAPL__time-tradingday-daily-reset-counter-01.pine +0 -0
  85. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/QQQ__session-ispremarket-nasdaq-01.pine +0 -0
  86. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/SPY__session-firstbar-vwap-anchor-01.pine +0 -0
  87. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/SPY__session-lastbar-flatten-01.pine +0 -0
  88. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/chart_bg_color.pine +0 -0
  89. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/crypto-htf__mtf-htf-monthly-ema-cross-01.pine +0 -0
  90. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/crypto-leverage__leverage-margin-call-perp-5x-01.pine +0 -0
  91. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/forex__symbol-fx-5dp-eurusd-01.pine +0 -0
  92. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/futures__symbol-futures-pointvalue-es-01.pine +0 -0
  93. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/tutorial__macd__strategy.pine +0 -0
  94. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/tutorial__mtf__strategy_htf.pine +0 -0
  95. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/tutorial__mtf__strategy_ltf.pine +0 -0
  96. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/us-equity__symbol-equity-rth-gaps-aapl-01.pine +0 -0
  97. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/us-equity__us-equity-exchange-tz-intraday-cap-01.pine +0 -0
  98. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-parity-choch-bos-isolator-01.pine +0 -0
  99. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-parity-edge-margin-50-pct-01.pine +0 -0
  100. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-parity-percent-of-equity-sizing-01.pine +0 -0
  101. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-parity-small-equity-fraction-01.pine +0 -0
  102. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-parity-stop-limit-timing-01.pine +0 -0
  103. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__analyzer-self-test-multi-mode-01.pine +0 -0
  104. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__anomaly-equity-mirror-strategy-equity-01.pine +0 -0
  105. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__barstate-isconfirmed-magnifier-off-01b.pine +0 -0
  106. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__barstate-isconfirmed-magnifier-on-01a.pine +0 -0
  107. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-atr-trail-series-int-points-01.pine +0 -0
  108. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-atr-trailing-stop-state-01.pine +0 -0
  109. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-entry-exit-same-pass-attach-01.pine +0 -0
  110. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-exit-stop-limit-trail-same-bar-01.pine +0 -0
  111. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-exit-three-way-set-once-entry-01.pine +0 -0
  112. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-exit-tp-sl-fixed-01.pine +0 -0
  113. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-narrow-stop-limit-with-trail8-01.pine +0 -0
  114. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-partial-exit-qty-percent-01.pine +0 -0
  115. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-same-id-exit-replace-01.pine +0 -0
  116. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-tp-sl-oca-reduce-isolate-01.pine +0 -0
  117. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-trail-points-no-offset-explicit-01.pine +0 -0
  118. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-trail-points-with-offset-only-01.pine +0 -0
  119. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__bracket-trailing-activation-offset-path-01.pine +0 -0
  120. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__cap-max-intraday-filled-orders-isolate-01.pine +0 -0
  121. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__cap-risk-gates-allow-max-intraday-01.pine +0 -0
  122. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-4emarsi-integration-01.pine +0 -0
  123. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-4emarsi-quad-ema-stack-01.pine +0 -0
  124. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-4emarsi-rsi-pullback-latch-01.pine +0 -0
  125. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-4emarsi-session-window-nbar-exit-01.pine +0 -0
  126. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-boscurv-integration-01.pine +0 -0
  127. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-boscurv-linreg-slope-channel-01.pine +0 -0
  128. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-boscurv-pivot-bos-trigger-01.pine +0 -0
  129. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-bracket-cap-range-pending-stop-01.pine +0 -0
  130. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-adx-regime-classify-01.pine +0 -0
  131. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-bb-kc-squeeze-release-01.pine +0 -0
  132. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-cooldown-daily-cap-01.pine +0 -0
  133. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-equity-feedback-sizing-01.pine +0 -0
  134. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-integration-01.pine +0 -0
  135. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-pivot-liquidity-sweep-01.pine +0 -0
  136. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-pressure-gauge-01.pine +0 -0
  137. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-rsi-macd-momentum-01.pine +0 -0
  138. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-ies-three-ema-bias-score-01.pine +0 -0
  139. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kanuck-calc-on-every-tick-01.pine +0 -0
  140. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kanuck-integration-01.pine +0 -0
  141. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kanuck-kama-state-recurrence-01.pine +0 -0
  142. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kanuck-max-bars-back-500-01.pine +0 -0
  143. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kkb-ema-atr-breakout-band-01.pine +0 -0
  144. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kkb-integration-01.pine +0 -0
  145. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kkb-kalman-filter-1d-01.pine +0 -0
  146. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-kkb-margin-100-pct-01.pine +0 -0
  147. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-liqsweep-integration-01.pine +0 -0
  148. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-liqsweep-pivot-hh-ll-01.pine +0 -0
  149. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-liqsweep-wait-one-continuation-01.pine +0 -0
  150. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-liqsweep-wick-pierce-close-back-01.pine +0 -0
  151. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-marketshift-integration-01.pine +0 -0
  152. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-marketshift-pivot-state-machine-01.pine +0 -0
  153. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-marketshift-rolling-highest-lowest-01.pine +0 -0
  154. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-marketshift-state-edge-detector-01.pine +0 -0
  155. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-scalping-fast-ma-cross-trigger-01.pine +0 -0
  156. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-scalping-integration-01.pine +0 -0
  157. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-scalping-tight-tp-sl-points-01.pine +0 -0
  158. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-trendmaster-integration-01.pine +0 -0
  159. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-trendmaster-line-new-projection-01.pine +0 -0
  160. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-trendmaster-pivot-anchored-bracket-01.pine +0 -0
  161. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-trendmaster-three-tier-ema-state-01.pine +0 -0
  162. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-trendmaster-trend-momentum-structure-gate-01.pine +0 -0
  163. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-cumulative-volume-delta-01.pine +0 -0
  164. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-fvg-active-zones-01.pine +0 -0
  165. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-integration-01.pine +0 -0
  166. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-manual-adx-regime-01.pine +0 -0
  167. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-pivot-strength-5-01.pine +0 -0
  168. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-rsi-smooth-divergence-01.pine +0 -0
  169. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-session-tz-newyork-01.pine +0 -0
  170. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-vcp-vol-zscore-anomaly-01.pine +0 -0
  171. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-wunderscalper-alert-templates-01.pine +0 -0
  172. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-wunderscalper-explicit-reverse-01.pine +0 -0
  173. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__composite-wunderscalper-integration-01.pine +0 -0
  174. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__input-source-runtime-override-high-01.pine +0 -0
  175. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__input-source-subscript-hl2-01.pine +0 -0
  176. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ltf-bool-array-bull-majority-01.pine +0 -0
  177. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ltf-numeric-float-ratio15-01.pine +0 -0
  178. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__magnifier-tick-dist-endpoints-01.pine +0 -0
  179. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__magnifier-tick-dist-endpoints-rsi-cross-08a.pine +0 -0
  180. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__magnifier-tick-dist-volume-weighted-on-01.pine +0 -0
  181. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-bool-mask-explicit-utc-tz-01.pine +0 -0
  182. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-bool-mask-no-transpose-01.pine +0 -0
  183. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-bool-mask-transpose-roundtrip-01.pine +0 -0
  184. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-bool-regime-mask-24x7-01.pine +0 -0
  185. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-covariance-eigen-pca-01.pine +0 -0
  186. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__matrix-eigen-rank-deficient-cov-01.pine +0 -0
  187. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__max-bars-back-function-call-deep-history-01.pine +0 -0
  188. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-daily-array-median-percentrank-01.pine +0 -0
  189. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-daily-ema26-warmup-01.pine +0 -0
  190. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-daily-prev-high-break-01.pine +0 -0
  191. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-dual-tf-60-240-rising-01.pine +0 -0
  192. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-close-change-baseline-01.pine +0 -0
  193. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-close-roll-01.pine +0 -0
  194. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-gaps-on-roll-01.pine +0 -0
  195. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-rsi14-inside-security-01.pine +0 -0
  196. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-sma20-inside-security-01.pine +0 -0
  197. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-60-volume-spike-01.pine +0 -0
  198. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-confluence-manual-trail-01.pine +0 -0
  199. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-confluence-static-bracket-01.pine +0 -0
  200. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-htf-weekly-sma-cross-01.pine +0 -0
  201. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-roll-state-60-240-d-minimal-01.pine +0 -0
  202. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-triple-tf-close-confluence-01.pine +0 -0
  203. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__mtf-triple-tf-macd-hist-confluence-01.pine +0 -0
  204. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__na-deep-history-int-na-01.pine +0 -0
  205. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__na-nz-fixnan-history-chain-01.pine +0 -0
  206. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__oca-exit-bracket-internal-cancel-01.pine +0 -0
  207. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__oca-multi-bracket-isolation-01.pine +0 -0
  208. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__oca-raw-strategy-order-reduce-01.pine +0 -0
  209. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-close-all-cancel-all-01.pine +0 -0
  210. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-close-immediate-vs-next-bar-01.pine +0 -0
  211. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-cross-entry-cancel-same-pass-01.pine +0 -0
  212. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-cross-entry-close-same-pass-01.pine +0 -0
  213. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-cross-exit-close-same-pass-01.pine +0 -0
  214. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-deferred-flip-guaranteed-gap-stops-01.pine +0 -0
  215. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-deferred-flip-pooc-cross-bar-01.pine +0 -0
  216. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-four-bar-stop-no-close-01.pine +0 -0
  217. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-side-same-id-stop-no-cancel-01.pine +0 -0
  218. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-both-touch-priority-01.pine +0 -0
  219. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-cancel-rotation-01.pine +0 -0
  220. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-far-only-01.pine +0 -0
  221. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-near-only-01.pine +0 -0
  222. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-open-high-first-path-01.pine +0 -0
  223. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-open-low-first-path-01.pine +0 -0
  224. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-open-tie-01.pine +0 -0
  225. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-source-order-long-first-01.pine +0 -0
  226. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-dual-stop-source-order-short-first-01.pine +0 -0
  227. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-entry-implicit-reversal-exit-01.pine +0 -0
  228. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-flip-stop-no-paired-close-01.pine +0 -0
  229. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-market-close-fill-basis-01.pine +0 -0
  230. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-one-side-four-bar-far-opposite-01.pine +0 -0
  231. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-opposite-entry-close-same-pass-01.pine +0 -0
  232. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-percent-equity-cash-commission-01.pine +0 -0
  233. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-process-on-close-false-01.pine +0 -0
  234. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-process-on-close-true-01.pine +0 -0
  235. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-range-expansion-pending-stop-01.pine +0 -0
  236. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-entry-close-same-bar-01.pine +0 -0
  237. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-market-entry-repeat-01.pine +0 -0
  238. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-after-flat-01.pine +0 -0
  239. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-cross-before-modify-01.pine +0 -0
  240. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-minute-zero-01.pine +0 -0
  241. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-modification-01.pine +0 -0
  242. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-raise-only-01.pine +0 -0
  243. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-same-id-stop-window-four-bars-01.pine +0 -0
  244. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-stale-stop-after-close-no-cancel-01.pine +0 -0
  245. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-stop-cancel-no-regime-close-01.pine +0 -0
  246. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-stop-entry-cancel-opposite-01.pine +0 -0
  247. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-stop-entry-reversal-grouping-01.pine +0 -0
  248. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__order-stop-entry-touch-boundary-01.pine +0 -0
  249. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__pyramid-cash-fractional-commission-01.pine +0 -0
  250. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__pyramid-close-id-grouping-01.pine +0 -0
  251. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__pyramid-deferred-flip-close-all-01.pine +0 -0
  252. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__pyramid-flip-stop-pyramiding-2-01.pine +0 -0
  253. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__recompute-alma-sar-corr-magnifier-01.pine +0 -0
  254. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__recompute-mtf-rsi-macd-bb-01.pine +0 -0
  255. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__risk-max-contracts-held-gate-pyramid-01.pine +0 -0
  256. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__session-hour-minute-pulse-filter-01.pine +0 -0
  257. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__session-ny-spring-forward-dst-01.pine +0 -0
  258. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__stats-eventrades-zero-pnl-count-01.pine +0 -0
  259. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-accdist-ema-cross-01.pine +0 -0
  260. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-bb-kc-squeeze-breakout-01.pine +0 -0
  261. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-bb-rsi-mean-reversion-01.pine +0 -0
  262. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-cci-threshold-cross-01.pine +0 -0
  263. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-chandelier-exit-direction-01.pine +0 -0
  264. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-closedtrades-risk-introspection-01.pine +0 -0
  265. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-cmo-9-zero-cross-01.pine +0 -0
  266. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-cog-10-signal-cross-01.pine +0 -0
  267. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-dmi-adx-di-cross-01.pine +0 -0
  268. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-donchian-channel-breakout-01.pine +0 -0
  269. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-dual-ma-switch-dispatch-01.pine +0 -0
  270. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-dual-thrust-open-anchored-range-01.pine +0 -0
  271. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-elder-ray-bull-bear-power-01.pine +0 -0
  272. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-ema-ribbon-stack-transition-01.pine +0 -0
  273. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-engulfing-candle-pattern-01.pine +0 -0
  274. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-highestbars-lowestbars-breakout-01.pine +0 -0
  275. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-hma-55-close-cross-01.pine +0 -0
  276. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-hma-fast-slow-cross-01.pine +0 -0
  277. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-inside-bar-engulfing-01.pine +0 -0
  278. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-kama-style-efficiency-ratio-01.pine +0 -0
  279. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-keltner-channel-break-01.pine +0 -0
  280. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-linreg-stdev-channel-revert-01.pine +0 -0
  281. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-macd-12-26-9-line-signal-cross-01.pine +0 -0
  282. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-macd-histogram-reversal-01.pine +0 -0
  283. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-macd-line-gt-signal-continuous-state-01.pine +0 -0
  284. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-map-regime-threshold-lookup-01.pine +0 -0
  285. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-median-vs-ema-cross-01.pine +0 -0
  286. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-mfi-14-bands-20-80-01.pine +0 -0
  287. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-momentum-roc-zero-cross-01.pine +0 -0
  288. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-multi-indicator-score-composite-01.pine +0 -0
  289. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-nvi-pvi-cross-01.pine +0 -0
  290. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-obv-ema-cross-01.pine +0 -0
  291. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-percentrank-mean-reversion-01.pine +0 -0
  292. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-pivot-array-unshift-pop-01.pine +0 -0
  293. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-pivot-atr-stop-target-01.pine +0 -0
  294. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-pivot-confirmed-break-01.pine +0 -0
  295. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-pivot-point-levels-break-01.pine +0 -0
  296. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-pvt-ema-cross-01.pine +0 -0
  297. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-range-filter-var-band-01.pine +0 -0
  298. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rci-14-zero-cross-01.pine +0 -0
  299. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi-bb-self-bands-01.pine +0 -0
  300. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi-ema-signal-cross-01.pine +0 -0
  301. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi-macd-and-continuous-state-01.pine +0 -0
  302. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi14-bands-30-70-01.pine +0 -0
  303. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi14-cross-50-01.pine +0 -0
  304. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi14-gt-50-continuous-state-01.pine +0 -0
  305. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-rsi14-gt60-lt45-no-matrix-01.pine +0 -0
  306. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-sar-flip-entry-01.pine +0 -0
  307. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-sma-152-close-cross-01.pine +0 -0
  308. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-sma-dual-cross-01.pine +0 -0
  309. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-stdev-sma-expansion-break-01.pine +0 -0
  310. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-stoch-slow-k-d-cross-01.pine +0 -0
  311. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-stochastic-rsi-cross-01.pine +0 -0
  312. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-str-match-regex-filter-01.pine +0 -0
  313. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-supertrend-adx-filter-01.pine +0 -0
  314. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-supertrend-direction-flip-01.pine +0 -0
  315. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-triple-sma-stack-latch-01.pine +0 -0
  316. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-tsi-25-13-signal-cross-01.pine +0 -0
  317. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-volume-spike-atr-breakout-01.pine +0 -0
  318. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-vwma-vs-sma-divergence-01.pine +0 -0
  319. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__ta-wpr-14-bands-01.pine +0 -0
  320. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__timeframe-main-period-self-adaptive-01.pine +0 -0
  321. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-calls-sibling-cumulative-01.pine +0 -0
  322. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-default-param-kwargs-01.pine +0 -0
  323. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-drives-strategy-entry-01.pine +0 -0
  324. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-extra-primitive-args-01.pine +0 -0
  325. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-feeds-strategy-exit-prices-01.pine +0 -0
  326. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-in-for-loop-01.pine +0 -0
  327. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-in-if-else-branch-01.pine +0 -0
  328. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-in-switch-arms-01.pine +0 -0
  329. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-in-while-loop-01.pine +0 -0
  330. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-mutating-self-ref-01.pine +0 -0
  331. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-on-array-element-01.pine +0 -0
  332. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-reads-strategy-state-01.pine +0 -0
  333. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-receives-ta-series-param-01.pine +0 -0
  334. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-scalar-return-01.pine +0 -0
  335. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-tuple-return-destructure-01.pine +0 -0
  336. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-udt-return-from-func-01.pine +0 -0
  337. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-uses-history-globals-01.pine +0 -0
  338. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-uses-math-funcs-01.pine +0 -0
  339. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-uses-na-nz-fixnan-01.pine +0 -0
  340. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-var-instance-streak-01.pine +0 -0
  341. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-method-windowed-method-chain-01.pine +0 -0
  342. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__udt-regime-stack-stress-01.pine +0 -0
  343. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__vwap-bands-breakout-1sigma-01.pine +0 -0
  344. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/gate-corpus/ok/validation__vwap-bands-mean-reversion-2sigma-01.pine +0 -0
  345. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/golden/matrix_eigen_pca.cpp +0 -0
  346. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_analyzer.py +0 -0
  347. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_analyzer_matrix_inference.py +0 -0
  348. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_analyzer_ta_return_types.py +0 -0
  349. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_calc_on_order_fills_codegen.py +0 -0
  350. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_audit_fixes.py +0 -0
  351. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_determinism.py +0 -0
  352. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_drawing_data.py +0 -0
  353. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_drawing_handles.py +0 -0
  354. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_fallthrough_guards.py +0 -0
  355. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_golden.py +0 -0
  356. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_input_getters.py +0 -0
  357. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_matrix_typed.py +0 -0
  358. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_new.py +0 -0
  359. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_string_escape.py +0 -0
  360. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_codegen_validation_fixes.py +0 -0
  361. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_compile_corpus.py +0 -0
  362. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_compile_smoke.py +0 -0
  363. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_errors.py +0 -0
  364. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_extract_inputs.py +0 -0
  365. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_glue_smoke.py +0 -0
  366. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_import_all.py +0 -0
  367. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_input_time_int64.py +0 -0
  368. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_int64_reassign_promotion.py +0 -0
  369. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_int64_time_storage.py +0 -0
  370. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_lexer.py +0 -0
  371. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_official_surface.py +0 -0
  372. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_parser.py +0 -0
  373. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_security_math_reducer.py +0 -0
  374. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_security_ohlc_hist_is_complete.py +0 -0
  375. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_security_tf_literal.py +0 -0
  376. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_signatures.py +0 -0
  377. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker.py +0 -0
  378. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_chart_visible_bar_time.py +0 -0
  379. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_color_cast.py +0 -0
  380. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_const_namespaces.py +0 -0
  381. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_dividends_earnings.py +0 -0
  382. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_footprint.py +0 -0
  383. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_input_color.py +0 -0
  384. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_input_source.py +0 -0
  385. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_matrix.py +0 -0
  386. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_security_adjustment.py +0 -0
  387. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_syminfo_gap_fields.py +0 -0
  388. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_timeframe_from_seconds.py +0 -0
  389. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_varip.py +0 -0
  390. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_support_checker_volume_row.py +0 -0
  391. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_symbols.py +0 -0
  392. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_ta_official_surface.py +0 -0
  393. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_transpile_division.py +0 -0
  394. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_transpile_enum_order.py +0 -0
  395. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_transpile_pf_trace.py +0 -0
  396. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_transpile_tr_handle_na.py +0 -0
  397. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_transpiler_matrix_kwargs.py +0 -0
  398. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_typespec_matrix.py +0 -0
  399. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_udt_drawing_field_cleanup.py +0 -0
  400. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_udt_lvalue_alias.py +0 -0
  401. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_unsupported_reporting.py +0 -0
  402. {pineforge_codegen-0.9.0 → pineforge_codegen-0.10.0}/tests/test_vwap_tuple_unpack.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pineforge-codegen
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: PineScript v6 to C++ transpiler that targets the pineforge-engine runtime.
5
5
  Project-URL: Homepage, https://github.com/pineforge-4pass/pineforge-codegen-oss
6
6
  Project-URL: Issues, https://github.com/pineforge-4pass/pineforge-codegen-oss/issues
@@ -0,0 +1 @@
1
+ 0.10.0
@@ -1262,6 +1262,11 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1262
1262
  # First, find all variables that are reassigned anywhere in the AST.
1263
1263
  # These cannot be inlined as constants since their value changes at runtime.
1264
1264
  reassigned = self._find_reassigned_vars()
1265
+ # Register never-reassigned ``var``/``varip`` scalars with a stable
1266
+ # init FIRST — a later derived var (or a UDF body) may reference such a
1267
+ # scalar as a stable length component, so it must be classified stable
1268
+ # before those exprs are evaluated below.
1269
+ self._collect_stable_var_scalars(reassigned)
1265
1270
  for stmt in self.ctx.ast.body:
1266
1271
  if isinstance(stmt, VarDecl) and stmt.name not in reassigned:
1267
1272
  self._collect_known_var(stmt)
@@ -1274,6 +1279,48 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1274
1279
  # dependent reassignments are left untracked (rejected by the guard).
1275
1280
  self._collect_reassigned_stable_scalars(reassigned)
1276
1281
 
1282
+ def _collect_stable_var_scalars(self, reassigned: set[str]) -> None:
1283
+ """Track top-level ``var``/``varip`` scalars declared exactly once (never
1284
+ reassigned) from a stable init expression.
1285
+
1286
+ A ``var`` scalar's one-shot initializer runs once and the value never
1287
+ changes across bars, so a never-reassigned ``var`` over a stable init
1288
+ (``var int _tfSec = timeframe.in_seconds()``) is a bar-invariant scalar —
1289
+ safe to embed in a TA ctor runtime-reset expression. Recording it in
1290
+ ``_derived_input_expr`` + ``_stable_runtime_vars`` lets the reset path
1291
+ expand the name and lets ``_expr_is_stable`` classify it (the
1292
+ ``_stable_runtime_vars`` check precedes the ``_var_names`` rejection).
1293
+
1294
+ A ``var`` that is reassigned anywhere (``:=`` in ``reassigned``), is a
1295
+ series var, or is initialized from a non-stable value stays untracked —
1296
+ so a TA length fed by genuinely-mutable persistent state is still
1297
+ rejected by the constructor guard. Names are NOT folded into
1298
+ ``_known_vars`` (no use-site inlining): only the length-analysis path is
1299
+ affected, and the ``var`` member still emits and initializes normally.
1300
+ """
1301
+ for stmt in (self.ctx.ast.body or []):
1302
+ if not isinstance(stmt, VarDecl):
1303
+ continue
1304
+ if not (stmt.is_var or stmt.is_varip):
1305
+ continue
1306
+ if stmt.name in reassigned:
1307
+ continue
1308
+ if stmt.name in self.ctx.series_vars:
1309
+ continue
1310
+ if stmt.value is None or not self._expr_is_stable(stmt.value):
1311
+ continue
1312
+ expr_str = self._arith_expr_to_str(stmt.value)
1313
+ if expr_str is None:
1314
+ continue
1315
+ self._derived_input_expr[stmt.name] = expr_str
1316
+ self._stable_runtime_vars.add(stmt.name)
1317
+ # Mark input-backed iff the init references an input, so the reset
1318
+ # emits override-aware get_input_*() reads for it.
1319
+ import re as _re
1320
+ toks = set(_re.findall(r"[A-Za-z_][A-Za-z_0-9]*", expr_str))
1321
+ if any(t in self._input_backed_vars for t in toks):
1322
+ self._input_backed_vars.add(stmt.name)
1323
+
1277
1324
  def _collect_reassigned_stable_scalars(self, reassigned: set[str]) -> None:
1278
1325
  """Track class-scope scalars that are reassigned but only along stable
1279
1326
  if/elif paths (see ``test_stable_reassigned_class_scope_length``).
@@ -1438,7 +1485,117 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1438
1485
  "ismonthly", "isdwm", "isseconds", "in_seconds", "isticks",
1439
1486
  })
1440
1487
 
1441
- def _expr_is_stable(self, node) -> bool:
1488
+ # Depth ceiling for inlining nested single-expression user functions while
1489
+ # classifying a TA length's stability. Pine forbids recursion, so any real
1490
+ # chain is shallow; the cap is a backstop against pathological input and is
1491
+ # enforced together with a name-stack cycle guard.
1492
+ _UDF_INLINE_MAX_DEPTH = 16
1493
+
1494
+ def _get_udf_def(self, name: str):
1495
+ """Return the top-level single-name ``FuncDef`` for ``name`` (or None).
1496
+
1497
+ Built lazily and cached. UDT ``MethodDef``s are intentionally excluded —
1498
+ only a free function can appear as a bare-name TA length call.
1499
+ """
1500
+ cache = getattr(self, "_udf_def_cache", None)
1501
+ if cache is None:
1502
+ cache = {}
1503
+ for stmt in (self.ctx.ast.body or []):
1504
+ if isinstance(stmt, FuncDef):
1505
+ # Last definition wins; a name map is all the length path needs.
1506
+ cache[stmt.name] = stmt
1507
+ self._udf_def_cache = cache
1508
+ return cache.get(name)
1509
+
1510
+ def _inline_single_expr_udf(self, node, _udf_stack: frozenset = frozenset(),
1511
+ _depth: int = 0):
1512
+ """If ``node`` is a call to a user-defined SINGLE-EXPRESSION function,
1513
+ return its body expression with each parameter substituted by the
1514
+ corresponding call-argument node. Returns None when the call is not such
1515
+ a function, the arity/kwargs do not match, the body is not a single
1516
+ expression, the body contains a shape we do not clone, or the call would
1517
+ recurse (cycle / depth-limit).
1518
+
1519
+ Purely structural: it does NOT judge stability (the caller does, via
1520
+ ``_expr_is_stable`` on the returned node). The conservative None keeps
1521
+ the TA-ctor guard intact.
1522
+ """
1523
+ if not isinstance(node, FuncCall):
1524
+ return None
1525
+ func_name, namespace = self._resolve_callee(node.callee)
1526
+ if namespace is not None or func_name is None:
1527
+ return None
1528
+ if func_name in _udf_stack or _depth >= self._UDF_INLINE_MAX_DEPTH:
1529
+ return None
1530
+ fdef = self._get_udf_def(func_name)
1531
+ if fdef is None:
1532
+ return None
1533
+ # A body that is exactly ONE expression statement is inlinable, whether
1534
+ # written inline after ``=>`` (``is_single_expr=True``) or as a one-line
1535
+ # indented block (``f(x) =>`` then a single indented expr, which the
1536
+ # parser records as ``is_single_expr=False`` with a one-ExprStmt body —
1537
+ # the gonzowiththewind-sisyphus ``f_bars`` shape). A multi-statement
1538
+ # body (len != 1, or a non-ExprStmt) is conservatively refused.
1539
+ body = fdef.body
1540
+ if not body or len(body) != 1 or not isinstance(body[0], ExprStmt):
1541
+ return None
1542
+ # Require a plain positional call: one arg per param, no kwargs, no
1543
+ # default-parameter fill-in — anything else is conservatively refused.
1544
+ if node.kwargs or len(node.args) != len(fdef.params):
1545
+ return None
1546
+ subst = dict(zip(fdef.params, node.args))
1547
+ return self._subst_params(body[0].expr, subst)
1548
+
1549
+ def _subst_params(self, node, subst: dict):
1550
+ """Return a copy of ``node`` with every ``Identifier`` whose name is a
1551
+ key in ``subst`` replaced by the mapped argument node. Returns None for
1552
+ any node outside the small arithmetic/call grammar we fold (an
1553
+ unrecognised construct — e.g. a history ``Subscript`` — conservatively
1554
+ aborts the inline). ``dataclasses.replace`` preserves ``loc``/
1555
+ ``annotations`` so diagnostics still point at real source spans."""
1556
+ import dataclasses as _dc
1557
+ if isinstance(node, (NumberLiteral, StringLiteral, BoolLiteral, NaLiteral)):
1558
+ return node
1559
+ if isinstance(node, Identifier):
1560
+ return subst.get(node.name, node)
1561
+ if isinstance(node, MemberAccess):
1562
+ obj = self._subst_params(node.object, subst)
1563
+ if obj is None:
1564
+ return None
1565
+ return _dc.replace(node, object=obj)
1566
+ if isinstance(node, Ternary):
1567
+ c = self._subst_params(node.condition, subst)
1568
+ t = self._subst_params(node.true_val, subst)
1569
+ f = self._subst_params(node.false_val, subst)
1570
+ if c is None or t is None or f is None:
1571
+ return None
1572
+ return _dc.replace(node, condition=c, true_val=t, false_val=f)
1573
+ if isinstance(node, BinOp):
1574
+ l = self._subst_params(node.left, subst)
1575
+ r = self._subst_params(node.right, subst)
1576
+ if l is None or r is None:
1577
+ return None
1578
+ return _dc.replace(node, left=l, right=r)
1579
+ if isinstance(node, UnaryOp):
1580
+ o = self._subst_params(node.operand, subst)
1581
+ if o is None:
1582
+ return None
1583
+ return _dc.replace(node, operand=o)
1584
+ if isinstance(node, FuncCall):
1585
+ if node.kwargs:
1586
+ return None
1587
+ new_args = []
1588
+ for a in node.args:
1589
+ sa = self._subst_params(a, subst)
1590
+ if sa is None:
1591
+ return None
1592
+ new_args.append(sa)
1593
+ return _dc.replace(node, args=new_args)
1594
+ # Subscript (history read) and anything else: not a stable-length shape.
1595
+ return None
1596
+
1597
+ def _expr_is_stable(self, node, _udf_stack: frozenset = frozenset(),
1598
+ _depth: int = 0) -> bool:
1442
1599
  """True iff ``node``'s value is a bar-invariant scalar.
1443
1600
 
1444
1601
  A stable expression depends only on: literals, ``input.*`` values,
@@ -1496,13 +1653,14 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1496
1653
  # History read (``close[1]``) or indexed access — per-bar.
1497
1654
  return False
1498
1655
  if isinstance(node, Ternary):
1499
- return (self._expr_is_stable(node.condition)
1500
- and self._expr_is_stable(node.true_val)
1501
- and self._expr_is_stable(node.false_val))
1656
+ return (self._expr_is_stable(node.condition, _udf_stack, _depth)
1657
+ and self._expr_is_stable(node.true_val, _udf_stack, _depth)
1658
+ and self._expr_is_stable(node.false_val, _udf_stack, _depth))
1502
1659
  if isinstance(node, BinOp):
1503
- return self._expr_is_stable(node.left) and self._expr_is_stable(node.right)
1660
+ return (self._expr_is_stable(node.left, _udf_stack, _depth)
1661
+ and self._expr_is_stable(node.right, _udf_stack, _depth))
1504
1662
  if isinstance(node, UnaryOp):
1505
- return self._expr_is_stable(node.operand)
1663
+ return self._expr_is_stable(node.operand, _udf_stack, _depth)
1506
1664
  if isinstance(node, FuncCall):
1507
1665
  func_name, namespace = self._resolve_callee(node.callee)
1508
1666
  if namespace == "ta":
@@ -1510,20 +1668,40 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1510
1668
  if namespace == "math":
1511
1669
  if func_name not in self._MATH_STABLE_MEMBERS:
1512
1670
  return False
1513
- return all(self._expr_is_stable(a) for a in node.args)
1671
+ return all(self._expr_is_stable(a, _udf_stack, _depth)
1672
+ for a in node.args)
1514
1673
  if namespace == "timeframe":
1515
1674
  # ``timeframe.in_seconds()`` (and any other function-form
1516
1675
  # timeframe member) is a stable per-run scalar — it reflects
1517
1676
  # the script's resolution, not a per-bar value.
1518
1677
  if func_name not in self._TF_STABLE_MEMBERS:
1519
1678
  return False
1520
- return all(self._expr_is_stable(a) for a in node.args)
1679
+ return all(self._expr_is_stable(a, _udf_stack, _depth)
1680
+ for a in node.args)
1521
1681
  if namespace == "input":
1522
1682
  return True
1523
1683
  if namespace is None and func_name in ("int", "float", "bool", "string"):
1524
- return all(self._expr_is_stable(a) for a in node.args)
1525
- # Any other call (user functions, str.*, array.*, ...) — series
1526
- # by default; the conservative answer keeps the guard honest.
1684
+ return all(self._expr_is_stable(a, _udf_stack, _depth)
1685
+ for a in node.args)
1686
+ # A user-defined single-expression function is stable iff every
1687
+ # argument is stable AND its body (with the params bound to those
1688
+ # args) is stable — i.e. the body references only stable scalars
1689
+ # (inputs / consts / timeframe.* / math.* / never-reassigned var
1690
+ # scalars) and no series / strategy state / ta.* results. Inlining
1691
+ # the body (params substituted by the arg nodes) lets the ordinary
1692
+ # classifier decide; the name-stack + depth guard refuses recursion
1693
+ # so a cyclic / malformed UDF is rejected, not looped forever.
1694
+ if namespace is None and func_name is not None:
1695
+ inlined = self._inline_single_expr_udf(node, _udf_stack, _depth)
1696
+ if inlined is not None:
1697
+ if not all(self._expr_is_stable(a, _udf_stack, _depth)
1698
+ for a in node.args):
1699
+ return False
1700
+ return self._expr_is_stable(
1701
+ inlined, _udf_stack | {func_name}, _depth + 1)
1702
+ # Any other call (multi-statement user functions, str.*, array.*,
1703
+ # ...) — series by default; the conservative answer keeps the guard
1704
+ # honest.
1527
1705
  return False
1528
1706
  return False
1529
1707
 
@@ -1536,22 +1714,28 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1536
1714
  # re-parsed and lowered through the expression visitor).
1537
1715
  _ATOMIC_ARITH_NODES = (NumberLiteral, Identifier, MemberAccess, FuncCall)
1538
1716
 
1539
- def _arith_operand_to_str(self, node) -> str | None:
1717
+ def _arith_operand_to_str(self, node, _udf_stack: frozenset = frozenset(),
1718
+ _depth: int = 0) -> str | None:
1540
1719
  """Serialize ``node`` for use as an operand: parenthesize it unless its
1541
1720
  serialized form is already self-delimiting, so grouping survives a
1542
1721
  round-trip through the parser."""
1543
- s = self._arith_expr_to_str(node)
1722
+ s = self._arith_expr_to_str(node, _udf_stack, _depth)
1544
1723
  if s is None:
1545
1724
  return None
1546
1725
  if isinstance(node, self._ATOMIC_ARITH_NODES):
1547
1726
  return s
1548
1727
  return f"({s})"
1549
1728
 
1550
- def _arith_expr_to_str(self, node) -> str | None:
1729
+ def _arith_expr_to_str(self, node, _udf_stack: frozenset = frozenset(),
1730
+ _depth: int = 0) -> str | None:
1551
1731
  """Render a numeric arithmetic-over-identifiers expression to a string
1552
1732
  that re-parses to the SAME tree (grouping preserved via
1553
1733
  ``_arith_operand_to_str``). Returns None for any node shape we don't
1554
1734
  fold (series subscripts, etc.) so the caller leaves the var untracked.
1735
+
1736
+ ``_udf_stack``/``_depth`` guard the single-expression-UDF inlining below
1737
+ against recursion cycles (Pine forbids recursion, but a malformed source
1738
+ must be refused, not looped forever).
1555
1739
  """
1556
1740
  if isinstance(node, NumberLiteral):
1557
1741
  v = node.value
@@ -1563,30 +1747,44 @@ class CodeGen(CallVisitor, ExprVisitor, StmtVisitor, TopLevelEmitter, SecurityEm
1563
1747
  if isinstance(node, MemberAccess) and isinstance(node.object, Identifier):
1564
1748
  return f"{node.object.name}.{node.member}"
1565
1749
  if isinstance(node, BinOp):
1566
- l = self._arith_operand_to_str(node.left)
1567
- r = self._arith_operand_to_str(node.right)
1750
+ l = self._arith_operand_to_str(node.left, _udf_stack, _depth)
1751
+ r = self._arith_operand_to_str(node.right, _udf_stack, _depth)
1568
1752
  if l is None or r is None:
1569
1753
  return None
1570
1754
  return f"{l} {node.op} {r}"
1571
1755
  if isinstance(node, UnaryOp):
1572
- o = self._arith_operand_to_str(node.operand)
1756
+ o = self._arith_operand_to_str(node.operand, _udf_stack, _depth)
1573
1757
  if o is None:
1574
1758
  return None
1575
1759
  return f"{node.op}{o}"
1576
1760
  if isinstance(node, Ternary):
1577
- c = self._arith_operand_to_str(node.condition)
1578
- t = self._arith_operand_to_str(node.true_val)
1579
- f = self._arith_operand_to_str(node.false_val)
1761
+ c = self._arith_operand_to_str(node.condition, _udf_stack, _depth)
1762
+ t = self._arith_operand_to_str(node.true_val, _udf_stack, _depth)
1763
+ f = self._arith_operand_to_str(node.false_val, _udf_stack, _depth)
1580
1764
  if c is None or t is None or f is None:
1581
1765
  return None
1582
1766
  return f"{c} ? {t} : {f}"
1583
1767
  if isinstance(node, FuncCall):
1584
- callee = self._arith_expr_to_str(node.callee)
1768
+ # A bare-name call to a single-expression user function has no C++
1769
+ # counterpart at class scope — inline its body (params substituted
1770
+ # by the arg expressions) so the rendered string is pure
1771
+ # math/timeframe/input arithmetic the ctor-reset path can expand.
1772
+ # Namespaced calls (math.*/timeframe.*/int(...)) fall through to the
1773
+ # ordinary ``callee(args)`` rendering below. The stack/depth guard
1774
+ # refuses a recursive UDF (returns None -> caller leaves it untracked
1775
+ # -> the ctor guard rejects it loudly) instead of recursing forever.
1776
+ fn, ns = self._resolve_callee(node.callee)
1777
+ if ns is None and fn is not None and self._get_udf_def(fn) is not None:
1778
+ inlined = self._inline_single_expr_udf(node, _udf_stack, _depth)
1779
+ if inlined is None:
1780
+ return None
1781
+ return self._arith_expr_to_str(inlined, _udf_stack | {fn}, _depth + 1)
1782
+ callee = self._arith_expr_to_str(node.callee, _udf_stack, _depth)
1585
1783
  if callee is None:
1586
1784
  return None
1587
1785
  parts = []
1588
1786
  for a in node.args:
1589
- s = self._arith_expr_to_str(a)
1787
+ s = self._arith_expr_to_str(a, _udf_stack, _depth)
1590
1788
  if s is None:
1591
1789
  return None
1592
1790
  parts.append(s)
@@ -1216,6 +1216,11 @@ class TopLevelEmitter:
1216
1216
  if (is_drawing or is_udt) and isinstance(init_ast, NaLiteral):
1217
1217
  continue
1218
1218
  init_cpp = self._visit_expr(init_ast)
1219
+ # A bare-``na`` initializer for an int/int64_t/bool ``var`` member
1220
+ # must be typed (``na<int>()``), mirroring the class-scope ctor
1221
+ # init (``_typed_na_init`` at _emit_constructor); a raw ``na<double>()``
1222
+ # NaN stored into an int member is UB and defeats is_na<T>().
1223
+ init_cpp = self._typed_na_init(init_cpp, name, ptype)
1219
1224
  init_lines.append(f" {target} = {init_cpp};")
1220
1225
  if not init_lines:
1221
1226
  return
@@ -140,42 +140,91 @@ class SecurityEmitter:
140
140
  : inputTf`` must be expanded to their source expression with direct
141
141
  ``get_input_*`` reads. Emitting the member name would register with its
142
142
  default-constructed value (usually an empty string).
143
+
144
+ Delegates to :meth:`_substitute_tf_input_reads`, which rewrites the
145
+ input-derived *leaves* of the expression tree — including leaves buried
146
+ inside a ternary CONDITION or any BinOp / UnaryOp / FuncCall — and then
147
+ renders the substituted tree through the normal expression visitor.
143
148
  """
144
149
  if node is None:
145
150
  return None
146
- resolving = resolving or set()
147
- if isinstance(node, StringLiteral):
148
- return self._visit_expr(node)
151
+ substituted = self._substitute_tf_input_reads(node, resolving or set())
152
+ return self._visit_expr(substituted)
153
+
154
+ def _substitute_tf_input_reads(self, node, resolving: set[str]):
155
+ """Return ``node`` with input-derived leaves rewritten to expressions
156
+ that are valid at security-registration time (before ``on_bar()``
157
+ assigns members from their inputs):
158
+
159
+ * an input-backed var -> its ``input.*()`` source call (``get_input_*``);
160
+ * a ``timeframe.period`` alias var -> ``timeframe.period`` (``script_tf_``);
161
+ * a known compile-time string var -> that string literal;
162
+ * a global alias -> its defining expression, expanded recursively.
163
+
164
+ The walk descends through Ternary / BinOp / UnaryOp / FuncCall /
165
+ Subscript, so an input-backed identifier nested inside e.g. a ternary
166
+ condition (``mode == "15" ? "240" : "60"``) resolves to its input read
167
+ instead of the uninitialised member. A subtree containing no
168
+ input-derived leaf is returned unchanged (same object) so unaffected
169
+ timeframe expressions render byte-identically to before the fix.
170
+ MemberAccess and literals are left verbatim: ``timeframe.period`` is
171
+ already lowered to ``script_tf_`` by the expression visitor.
172
+ """
173
+ if not isinstance(node, ASTNode):
174
+ return node
149
175
  if isinstance(node, Identifier):
150
176
  name = node.name
151
177
  if name in self._timeframe_period_vars:
152
- return "script_tf_"
178
+ return MemberAccess(object=Identifier(name="timeframe"), member="period")
153
179
  if name in self._input_backed_vars and name in self._input_var_to_call:
154
- return self._visit_expr(self._input_var_to_call[name])
180
+ return self._input_var_to_call[name]
155
181
  if (name in self._known_vars and name not in self._input_backed_vars
156
182
  and isinstance(self._known_vars[name], str)):
157
- return self._visit_expr(StringLiteral(value=self._known_vars[name]))
183
+ return StringLiteral(value=self._known_vars[name])
158
184
  global_expr_map = getattr(self.ctx, "global_expr_map", {}) or {}
159
185
  if name in global_expr_map and name not in resolving:
160
- resolving.add(name)
161
- out = self._security_tf_runtime_expr(global_expr_map[name], resolving)
162
- resolving.remove(name)
163
- return out
164
- return self._visit_expr(node)
165
- if (
166
- isinstance(node, MemberAccess)
167
- and isinstance(node.object, Identifier)
168
- and node.object.name == "timeframe"
169
- and node.member == "period"
170
- ):
171
- return "script_tf_"
186
+ return self._substitute_tf_input_reads(
187
+ global_expr_map[name], resolving | {name})
188
+ return node
172
189
  if isinstance(node, Ternary):
173
- cond = self._security_tf_runtime_expr(node.condition, resolving)
174
- tv = self._security_tf_runtime_expr(node.true_val, resolving)
175
- fv = self._security_tf_runtime_expr(node.false_val, resolving)
176
- if cond is not None and tv is not None and fv is not None:
177
- return f"(({cond}) ? ({tv}) : ({fv}))"
178
- return self._visit_expr(node)
190
+ cond = self._substitute_tf_input_reads(node.condition, resolving)
191
+ tv = self._substitute_tf_input_reads(node.true_val, resolving)
192
+ fv = self._substitute_tf_input_reads(node.false_val, resolving)
193
+ if cond is node.condition and tv is node.true_val and fv is node.false_val:
194
+ return node
195
+ return Ternary(condition=cond, true_val=tv, false_val=fv)
196
+ if isinstance(node, BinOp):
197
+ left = self._substitute_tf_input_reads(node.left, resolving)
198
+ right = self._substitute_tf_input_reads(node.right, resolving)
199
+ if left is node.left and right is node.right:
200
+ return node
201
+ return BinOp(left=left, op=node.op, right=right)
202
+ if isinstance(node, UnaryOp):
203
+ operand = self._substitute_tf_input_reads(node.operand, resolving)
204
+ if operand is node.operand:
205
+ return node
206
+ return UnaryOp(op=node.op, operand=operand)
207
+ if isinstance(node, FuncCall):
208
+ new_args = [self._substitute_tf_input_reads(a, resolving) for a in node.args]
209
+ new_kwargs = {
210
+ k: (self._substitute_tf_input_reads(v, resolving)
211
+ if isinstance(v, ASTNode) else v)
212
+ for k, v in node.kwargs.items()
213
+ }
214
+ unchanged = (
215
+ all(a is b for a, b in zip(new_args, node.args))
216
+ and all(new_kwargs[k] is node.kwargs[k] for k in node.kwargs)
217
+ )
218
+ if unchanged:
219
+ return node
220
+ return FuncCall(callee=node.callee, args=new_args, kwargs=new_kwargs)
221
+ if isinstance(node, Subscript):
222
+ obj = self._substitute_tf_input_reads(node.object, resolving)
223
+ idx = self._substitute_tf_input_reads(node.index, resolving)
224
+ if obj is node.object and idx is node.index:
225
+ return node
226
+ return Subscript(object=obj, index=idx)
227
+ return node
179
228
 
180
229
  def _resolve_param_tf_from_callsites(self, func_name: str, param_name: str):
181
230
  """For a ``request.security`` whose tf is function parameter ``param_name``
@@ -2226,7 +2275,10 @@ class SecurityEmitter:
2226
2275
  op = cpp_ops.get(expr_node.op, expr_node.op)
2227
2276
  if expr_node.op == "%":
2228
2277
  return f"std::fmod((double)({left}), (double)({right}))"
2229
- return f"({left} {op} {right})"
2278
+ # KI-71: honour Pine's falsy-on-na relational rule inside
2279
+ # request.security expressions too (this builder is a second
2280
+ # relational emission site independent of _visit_binop).
2281
+ return self._lower_relational(op, expr_node.left, expr_node.right, left, right)
2230
2282
 
2231
2283
  if isinstance(expr_node, UnaryOp):
2232
2284
  operand = self._build_security_expr(
@@ -552,6 +552,59 @@ class TypeInferer:
552
552
  return True
553
553
  return name in self._int64_reassign_targets()
554
554
 
555
+ def _na_reassign_cpp_type(self, name: str) -> str | None:
556
+ """Declared scalar C++ type of a ``:=`` reassignment target ``name``, so a
557
+ bare-``na`` RHS (``x := na``) can be spelled ``na<T>()`` matching the
558
+ member/local type instead of the default ``na<double>()``.
559
+
560
+ Assigning a double quiet-NaN into an ``int``/``int64_t``/``bool`` member is
561
+ undefined behaviour (NaN->int is unspecified; on ARM64 it saturates to 0,
562
+ which is not the ``na<T>()`` sentinel) and defeats ``is_na<T>()``. Mirrors
563
+ the member-declaration type logic (``base._emit_class_members`` /
564
+ ``_typed_na_init``): ``PINE_TYPE_TO_CPP`` plus the int->int64_t epoch
565
+ promotion. Returns ``None`` for collections / UDT / drawing handles and
566
+ for ``double`` (already the default lowering), so those paths are
567
+ unchanged.
568
+ """
569
+ # Collections / UDT / drawing handles never take a scalar ``na<T>()``:
570
+ # leave them to the drawing-na / default lowering in _visit_rhs_value.
571
+ if (name in self._array_vars
572
+ or name in self._map_vars
573
+ or name in getattr(self, "_matrix_specs", {})
574
+ or name in self._udt_var_types):
575
+ return None
576
+ cpp_type: str | None = None
577
+ # 1. ``var`` member (class-scope OR function-local: both are recorded in
578
+ # ctx.var_members). This is the authoritative declaration source.
579
+ for vname, ptype, _init in self.ctx.var_members:
580
+ if vname == name:
581
+ cpp_type = PINE_TYPE_TO_CPP.get(ptype, "double")
582
+ break
583
+ # 2. Function-local plain (non-``var``) scalar: its declared type was
584
+ # remembered at the VarDecl (``_type_for_decl``).
585
+ if cpp_type is None:
586
+ cpp_type = getattr(self, "_current_func_local_types", {}).get(name)
587
+ # 3. Function parameter.
588
+ if cpp_type is None:
589
+ cpp_type = getattr(self, "_current_func_param_types", {}).get(name)
590
+ # 4. Global-scope non-``var`` class member.
591
+ if cpp_type is None:
592
+ for gname, gptype in self.ctx.global_var_decls:
593
+ if gname == name:
594
+ cpp_type = PINE_TYPE_TO_CPP.get(gptype, "double")
595
+ break
596
+ if cpp_type is None:
597
+ return None
598
+ # int -> int64_t promotion for epoch-ms builtins, mirroring the member
599
+ # declaration so the na sentinel width matches the storage width.
600
+ if cpp_type == "int" and self._is_int64_builtin_init(name):
601
+ cpp_type = "int64_t"
602
+ # Only the retypeable scalar types are meaningful; ``double`` already
603
+ # lowers to ``na<double>()`` and everything else is left untouched.
604
+ if cpp_type in ("int", "int64_t", "bool", "std::string"):
605
+ return cpp_type
606
+ return None
607
+
555
608
  # ------------------------------------------------------------------
556
609
  # BUG C: user-defined-UDT lvalue aliasing
557
610
  # ------------------------------------------------------------------
@@ -166,6 +166,20 @@ _BUILTIN_NAMESPACE_NAMES: frozenset[str] = frozenset({
166
166
  })
167
167
 
168
168
 
169
+ # KI-71: Pine relational comparisons (``==`` ``!=`` ``<`` ``>`` ``<=`` ``>=``)
170
+ # with an ``na`` operand evaluate *falsy*. Naive C++ relationals do not honour
171
+ # this for the engine's na sentinels, so these ops route through na-aware
172
+ # lowering when an operand can be na (see ``_visit_binop`` / ``_operand_na_kind``).
173
+ _RELATIONAL_OPS: frozenset[str] = frozenset({"==", "!=", "<", ">", "<=", ">="})
174
+
175
+ # C++ scalar types carrying a detectable ``na`` sentinel via ``is_na``:
176
+ # ``double`` -> NaN (IEEE), ``int``/``int64_t`` -> ``numeric_limits<T>::min()``.
177
+ # Only these route through the na-aware relational lowering — ``is_na`` has no
178
+ # overload for ``bool``/``std::string``/vector/UDT-value operands, and Pine's
179
+ # na-bool is engine-indistinguishable from ``false`` (na<bool>() == false).
180
+ _NA_SCALAR_CPP: frozenset[str] = frozenset({"int", "int64_t", "double"})
181
+
182
+
169
183
  class ExprVisitor:
170
184
  """Expression-level visitor methods shared across the codegen.
171
185
 
@@ -760,6 +774,78 @@ class ExprVisitor:
760
774
  return f'std::string("{node.member}")'
761
775
  return f"{obj}.{node.member}"
762
776
 
777
+ def _operand_na_kind(self, node, cpp_type: str) -> str | None:
778
+ """Classify a relational operand by the ``na`` sentinel it can carry.
779
+
780
+ Returns:
781
+
782
+ * ``"int"`` — an ``int``/``int64_t`` expression that can hold the
783
+ ``INT_MIN`` sentinel. Because that sentinel is a *finite* very-negative
784
+ integer (not NaN), naive C++ diverges from Pine's falsy-on-na rule for
785
+ EVERY relational — ordered (``<`` ``>`` ``<=`` ``>=``) and equality
786
+ (``==`` ``!=``) alike.
787
+ * ``"float"`` — a ``double`` expression that can hold NaN. IEEE already
788
+ yields ``false`` for ``==`` ``<`` ``>`` ``<=`` ``>=`` against NaN
789
+ (matching Pine's falsy), so the ONLY diverging float cell is ``!=``
790
+ (IEEE ``NaN != x`` is true; Pine is falsy).
791
+ * ``None`` — provably not na (numeric/bool literal, inlined
792
+ compile-time constant) or a non-scalar type with no ``is_na`` overload;
793
+ the naive emission is already correct.
794
+ """
795
+ if cpp_type not in _NA_SCALAR_CPP:
796
+ return None
797
+ # Literals are never na.
798
+ if isinstance(node, (NumberLiteral, BoolLiteral)):
799
+ return None
800
+ # A bare ``na`` lowers to ``na<double>()`` — a real NaN, i.e. it IS na.
801
+ if self._is_na_expr(node):
802
+ return "float"
803
+ # Inlined compile-time constants (non-input known vars) never hold na.
804
+ if (isinstance(node, Identifier)
805
+ and node.name in self._known_vars
806
+ and node.name not in self._input_backed_vars):
807
+ return None
808
+ return "int" if cpp_type in ("int", "int64_t") else "float"
809
+
810
+ def _emit_na_relational(self, op: str, left: str, right: str) -> str:
811
+ """Emit an na-aware relational: ``false`` when either operand is ``na``.
812
+
813
+ Mirrors the ``nz()`` lambda idiom (``visit_call``): each operand is
814
+ hoisted to a temporary so a stateful operand expression is evaluated
815
+ exactly once (no double-step), then compared only when neither side is
816
+ na. ``is_na`` resolves via the emitted ``using namespace pineforge;``
817
+ (``double`` -> ``isnan``; integral -> ``== numeric_limits<T>::min()``).
818
+ """
819
+ return (f"([&]{{ auto _pna_l = ({left}); auto _pna_r = ({right}); "
820
+ f"return !is_na(_pna_l) && !is_na(_pna_r) && "
821
+ f"(_pna_l {op} _pna_r); }}())")
822
+
823
+ def _lower_relational(self, op: str, left_node, right_node,
824
+ left_cpp: str, right_cpp: str) -> str:
825
+ """Lower a Pine relational to C++, applying KI-71 na-aware wrapping.
826
+
827
+ Shared by ``_visit_binop`` and the ``request.security`` expression
828
+ builder so EVERY relational emission site honours Pine's falsy-on-na
829
+ rule. Wraps only the diverging cells: any na-capable *integer* operand
830
+ (the INT_MIN sentinel poisons all six operators) or a ``!=`` with an
831
+ na-capable *float* operand (the sole IEEE-diverging float cell). Pure
832
+ ``double`` ``==`` ``<`` ``>`` ``<=`` ``>=`` keep the naive emission —
833
+ IEEE is already falsy-on-NaN there, so wrapping would be a pure no-op.
834
+ Non-relational ``op`` (or non-scalar operands with no ``is_na``) fall
835
+ through to the naive ``(left op right)`` form unchanged.
836
+ """
837
+ if op in _RELATIONAL_OPS:
838
+ lt = self._infer_type(left_node)
839
+ rt = self._infer_type(right_node)
840
+ if lt in _NA_SCALAR_CPP and rt in _NA_SCALAR_CPP:
841
+ lk = self._operand_na_kind(left_node, lt)
842
+ rk = self._operand_na_kind(right_node, rt)
843
+ int_na = "int" in (lk, rk)
844
+ float_na = "float" in (lk, rk)
845
+ if int_na or (op == "!=" and float_na):
846
+ return self._emit_na_relational(op, left_cpp, right_cpp)
847
+ return f"({left_cpp} {op} {right_cpp})"
848
+
763
849
  def _visit_binop(self, node: BinOp) -> str:
764
850
  left = self._visit_expr(node.left)
765
851
  right = self._visit_expr(node.right)
@@ -786,7 +872,7 @@ class ExprVisitor:
786
872
  # Ref: https://www.tradingview.com/pine-script-docs/concepts/operators/
787
873
  if node.op == "/":
788
874
  return f"((double)({left}) / (double)({right}))"
789
- return f"({left} {op} {right})"
875
+ return self._lower_relational(op, node.left, node.right, left, right)
790
876
 
791
877
  def _visit_unaryop(self, node: UnaryOp) -> str:
792
878
  operand = self._visit_expr(node.operand)