Interpreter: lazy and/or — skip right-operand evaluation when the left decides (stacked on #51)#53
Draft
FellowTraveler wants to merge 3 commits into
Draft
Conversation
Arithmetic, comparison, and unary operators previously raised 'Expected number' type errors when an operand was na; conditions treated na as an error too, and NaN was truthy (n != 0.0 is true for NaN in IEEE 754). Pine v6 semantics: - to_number()/to_bool() return Option (None = na) so callers can propagate na; as_number()/as_bool() keep their signatures for builtins (na -> NaN / false). - Arithmetic and ordering comparisons with na yield na. - and/or use three-valued logic: false absorbs na, true absorbs na; otherwise na. - if/while/ternary conditions use truthy_for_condition(): na takes the false/else branch. - NaN is no longer truthy in numeric->bool coercion. - The na keyword doubles as a function: na(x) tests whether x is na. New integration script basics/na_propagation.pine pins all of the above (arithmetic, comparison, three-valued and/or, na(), na-condition if-expression).
Follow-up to the na-propagation pass: == and != were missed because they
route through values_equal (structural equality) rather than to_number(),
so a comparison with na leaked a structural bool ('1 != na' -> true,
'na == na' -> true). Pine semantics: any comparison with an na operand
yields na (falsy in conditions); testing for na requires the na()
function. Reference: TradingView Pine Script v6 User Manual, 'na value'
(https://www.tradingview.com/pine-script-docs/language/type-system/#na-value).
The practical failure mode is first-bar seeding: series like
'dayofweek != dayofweek[1]' must be na (falsy) on the first bar, where
[1] is na — with structural equality they evaluate true and fire
new-day/new-session logic one bar early.
values_equal itself is unchanged: switch-pattern matching still uses
structural equality, matching Pine's switch behavior.
New integration test crates/pine-interpreter/tests/na_comparison.rs pins
eq/neq with na operands (na), na == na (na), non-na comparisons (bool),
and the falsy-in-if regression.
…eft decides Pine's and/or short-circuit: a false left operand decides 'and', a true left operand decides 'or', and in those cases the right operand is not evaluated at all — side effects inside it (function calls, stateful built-ins such as ta.crossover) must not run. Validated against TradingView strategy-tester ground truth: a strategy calling ta.crossover inside an 'or' right-operand only advances that call's internal cross state on bars where the operand is actually evaluated. Results are unchanged in every case — the existing three-valued arms still apply when the left operand does not decide (false absorbs na in 'and', true absorbs na in 'or', and an na left operand still requires the right operand's value). New integration test crates/pine-interpreter/tests/lazy_eval.rs probes evaluation with an undefined variable in the right operand (evaluated → UndefinedVariable error; skipped → success) and pins all three-valued result combinations.
ferranbt
requested changes
Jul 9, 2026
| @@ -0,0 +1,62 @@ | |||
| // This Source Code Form is subject to the terms of the Mozilla Public | |||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Stacked on #51 — do not merge before it. This change calls
to_bool(), introduced by #51's na-propagation commit, so the branch necessarily contains #51's two commits and they appear in this diff. Only the last commit (lazy and/or) is new here — +13 lines in the interpreter plus a test file. Opened as a draft; once #51 merges I'll rebase ontomain(the diff collapses to just that commit) and mark it ready. Independent of #49, #50, and #52.Summary
Completes the TV-semantics series (#49/#50/#51/#52), same origin: found by diffing pinecone's output against TradingView's strategy tester on real strategies.
feat(interpreter): lazyand/or— skip right-operand evaluation when the left decidesPine's
and/orshort-circuit: afalseleft operand decidesand, atrueleft operand decidesor, and in those cases the right operand is not evaluated at all — side effects inside it (function calls, stateful built-ins such asta.crossover) must not run.This was validated against TradingView strategy-tester ground truth: a strategy calling
ta.crossoverinside anorright-operand only advances that call's internal cross state on bars where the operand is actually evaluated. We arbitrated this empirically with a 15-year BTCUSD trade-list export (~1,900 trades) on a strategy whose entry logic has exactly this shape — eager evaluation diverges from TV within the first months; lazy evaluation reproduces TV's trades at the data-noise ceiling.Results are unchanged in every case — #51's three-valued arms still apply when the left operand does not decide (
falseabsorbsnainand,trueabsorbsnainor, and annaleft operand still requires the right operand's value).Tests: new
crates/pine-interpreter/tests/lazy_eval.rsprobes evaluation with an undefined variable in the right operand (evaluated →UndefinedVariableerror; skipped → success) and pins all three-valued result combinations.Note: skipping evaluation means builtin call sites no longer execute on every bar, which is what makes execution-order state keying unsound for hosts — #52 (independent of this PR) provides the stable call-site identity that hosts can key on instead.