openpinescript 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +346 -2
  3. package/dist/cli.mjs +108686 -0
  4. package/dist/index.cjs +108670 -0
  5. package/dist/index.mjs +108624 -0
  6. package/dist/types/index.d.ts +31 -0
  7. package/dist/types/parser/v1/generated/PineV1Lexer.d.ts +66 -0
  8. package/dist/types/parser/v1/generated/PineV1Parser.d.ts +555 -0
  9. package/dist/types/parser/v1/generated/PineV1ParserVisitor.d.ts +318 -0
  10. package/dist/types/parser/v1/index.d.ts +39 -0
  11. package/dist/types/parser/v2/generated/PineV2Lexer.d.ts +67 -0
  12. package/dist/types/parser/v2/generated/PineV2Parser.d.ts +568 -0
  13. package/dist/types/parser/v2/generated/PineV2ParserVisitor.d.ts +325 -0
  14. package/dist/types/parser/v2/index.d.ts +2 -0
  15. package/dist/types/parser/v3/generated/PineV3Lexer.d.ts +67 -0
  16. package/dist/types/parser/v3/generated/PineV3Parser.d.ts +568 -0
  17. package/dist/types/parser/v3/generated/PineV3ParserVisitor.d.ts +325 -0
  18. package/dist/types/parser/v3/index.d.ts +2 -0
  19. package/dist/types/parser/v4/generated/PineV4Lexer.d.ts +86 -0
  20. package/dist/types/parser/v4/generated/PineV4Parser.d.ts +673 -0
  21. package/dist/types/parser/v4/generated/PineV4ParserVisitor.d.ts +367 -0
  22. package/dist/types/parser/v4/index.d.ts +2 -0
  23. package/dist/types/parser/v5/generated/PineV5Lexer.d.ts +95 -0
  24. package/dist/types/parser/v5/generated/PineV5Parser.d.ts +964 -0
  25. package/dist/types/parser/v5/generated/PineV5ParserVisitor.d.ts +521 -0
  26. package/dist/types/parser/v5/index.d.ts +2 -0
  27. package/dist/types/parser/v6/generated/PineV6Lexer.d.ts +98 -0
  28. package/dist/types/parser/v6/generated/PineV6Parser.d.ts +984 -0
  29. package/dist/types/parser/v6/generated/PineV6ParserVisitor.d.ts +528 -0
  30. package/dist/types/parser/v6/index.d.ts +2 -0
  31. package/dist/types/runtime/v1/Series.d.ts +61 -0
  32. package/dist/types/runtime/v1/broker/Broker.d.ts +325 -0
  33. package/dist/types/runtime/v1/broker/index.d.ts +158 -0
  34. package/dist/types/runtime/v1/broker/val.d.ts +28 -0
  35. package/dist/types/runtime/v1/constDefault.d.ts +11 -0
  36. package/dist/types/runtime/v1/context.d.ts +1705 -0
  37. package/dist/types/runtime/v1/footprint.d.ts +32 -0
  38. package/dist/types/runtime/v1/index.d.ts +7 -0
  39. package/dist/types/runtime/v1/language-spec.d.ts +12 -0
  40. package/dist/types/runtime/v1/openTrades.d.ts +12 -0
  41. package/dist/types/runtime/v1/palette.d.ts +18 -0
  42. package/dist/types/runtime/v1/session.d.ts +482 -0
  43. package/dist/types/runtime/v1/sessions.d.ts +31 -0
  44. package/dist/types/runtime/v1/stateClone.d.ts +19 -0
  45. package/dist/types/runtime/v1/stdlib/alerts.d.ts +31 -0
  46. package/dist/types/runtime/v1/stdlib/arrays.d.ts +133 -0
  47. package/dist/types/runtime/v1/stdlib/barmerge.d.ts +27 -0
  48. package/dist/types/runtime/v1/stdlib/barstate.d.ts +79 -0
  49. package/dist/types/runtime/v1/stdlib/cast.d.ts +81 -0
  50. package/dist/types/runtime/v1/stdlib/chart.d.ts +283 -0
  51. package/dist/types/runtime/v1/stdlib/color.d.ts +51 -0
  52. package/dist/types/runtime/v1/stdlib/core.d.ts +162 -0
  53. package/dist/types/runtime/v1/stdlib/dataset.d.ts +36 -0
  54. package/dist/types/runtime/v1/stdlib/display.d.ts +50 -0
  55. package/dist/types/runtime/v1/stdlib/drawings.d.ts +407 -0
  56. package/dist/types/runtime/v1/stdlib/errors.d.ts +21 -0
  57. package/dist/types/runtime/v1/stdlib/font.d.ts +20 -0
  58. package/dist/types/runtime/v1/stdlib/footprint.d.ts +76 -0
  59. package/dist/types/runtime/v1/stdlib/format.d.ts +57 -0
  60. package/dist/types/runtime/v1/stdlib/index.d.ts +71 -0
  61. package/dist/types/runtime/v1/stdlib/input.d.ts +14 -0
  62. package/dist/types/runtime/v1/stdlib/inputs.d.ts +60 -0
  63. package/dist/types/runtime/v1/stdlib/late_members.d.ts +97 -0
  64. package/dist/types/runtime/v1/stdlib/logging.d.ts +31 -0
  65. package/dist/types/runtime/v1/stdlib/maps.d.ts +55 -0
  66. package/dist/types/runtime/v1/stdlib/math.d.ts +71 -0
  67. package/dist/types/runtime/v1/stdlib/matrix.d.ts +201 -0
  68. package/dist/types/runtime/v1/stdlib/metadata.d.ts +14 -0
  69. package/dist/types/runtime/v1/stdlib/mtf.d.ts +15 -0
  70. package/dist/types/runtime/v1/stdlib/renames.d.ts +117 -0
  71. package/dist/types/runtime/v1/stdlib/renames5.d.ts +113 -0
  72. package/dist/types/runtime/v1/stdlib/renames6.d.ts +81 -0
  73. package/dist/types/runtime/v1/stdlib/request.d.ts +207 -0
  74. package/dist/types/runtime/v1/stdlib/scale.d.ts +19 -0
  75. package/dist/types/runtime/v1/stdlib/session.d.ts +113 -0
  76. package/dist/types/runtime/v1/stdlib/sort_field.d.ts +12 -0
  77. package/dist/types/runtime/v1/stdlib/sources.d.ts +24 -0
  78. package/dist/types/runtime/v1/stdlib/str.d.ts +117 -0
  79. package/dist/types/runtime/v1/stdlib/strategy.d.ts +314 -0
  80. package/dist/types/runtime/v1/stdlib/syminfo.d.ts +209 -0
  81. package/dist/types/runtime/v1/stdlib/ta.d.ts +409 -0
  82. package/dist/types/runtime/v1/stdlib/time.d.ts +158 -0
  83. package/dist/types/runtime/v1/stdlib/trades.d.ts +108 -0
  84. package/dist/types/runtime/v1/stdlib/ui.d.ts +196 -0
  85. package/dist/types/runtime/v1/stdlib/v6_members.d.ts +68 -0
  86. package/dist/types/runtime/v1/timeframes.d.ts +139 -0
  87. package/dist/types/runtime/v1/zones.d.ts +30 -0
  88. package/dist/types/transpiler/common/index.d.ts +51 -0
  89. package/dist/types/transpiler/common/libraries.d.ts +29 -0
  90. package/dist/types/transpiler/common/parseErrors.d.ts +38 -0
  91. package/dist/types/transpiler/common/passes/ScopeAnalysis.d.ts +158 -0
  92. package/dist/types/transpiler/common/passes/TypeRules.d.ts +26 -0
  93. package/dist/types/transpiler/common/profiles/index.d.ts +96 -0
  94. package/dist/types/transpiler/common/version.d.ts +40 -0
  95. package/dist/types/transpiler/v1/ToJsVisitor.d.ts +720 -0
  96. package/dist/types/transpiler/v2/ToJsVisitor.d.ts +72 -0
  97. package/dist/types/transpiler/v3/ToJsVisitor.d.ts +87 -0
  98. package/dist/types/transpiler/v4/ToJsVisitor.d.ts +263 -0
  99. package/dist/types/transpiler/v5/ToJsVisitor.d.ts +1101 -0
  100. package/dist/types/transpiler/v6/ToJsVisitor.d.ts +237 -0
  101. package/dist/types/transpiler/v6/rules.d.ts +94 -0
  102. package/dist/types/utils/v2/common.d.ts +4 -0
  103. package/package.json +92 -4
@@ -0,0 +1,720 @@
1
+ /**
2
+ * Pine Script v1 → JavaScript emitter. THE BASE OF THE VISITOR HIERARCHY.
3
+ *
4
+ * Target: OpenPineScript Runtime (Series Object Architecture).
5
+ *
6
+ * ── RULES FOR EDITING THIS FILE ─────────────────────────────────────────────
7
+ *
8
+ * This class implements Pine Script v1 and nothing else. It must not contain a
9
+ * rule, a flag, or an empty hook that exists only to serve a later version.
10
+ *
11
+ * A later version changes behaviour by SUBCLASSING and overriding the relevant
12
+ * visit* method, usually delegating to `super`:
13
+ *
14
+ * override visitVar_def(ctx: Var_defContext): string {
15
+ * this.enforceNoSelfReference(...); // the v3 rule
16
+ * return super.visitVar_def(ctx); // the inherited emit logic
17
+ * }
18
+ *
19
+ * That is why the helpers here are `protected` rather than `private`: they are
20
+ * the documented extension surface. It is also why there is no `restricts(id)`
21
+ * lookup any more — a rule that applies to v1 fires unconditionally here, and a
22
+ * rule that does not apply to v1 simply is not written here.
23
+ *
24
+ * See transpiler/v3/ToJsVisitor.ts for a worked example, and
25
+ * docs/common/ARCHITECTURE.md ("Design rules") for the rationale.
26
+ */
27
+ import { ParseTreeVisitor } from "antlr4ng";
28
+ import type { TerminalNode, ParserRuleContext, ParseTree } from "antlr4ng";
29
+ import type { PineVersion } from "../common/version.js";
30
+ import { Pine_scriptContext, StmtContext, Global_stmtContext, Global_stmt_contentContext, Fun_def_stmtContext, Fun_def_singlelineContext, Fun_def_multilineContext, Fun_headContext, Fun_body_singlelineContext, Local_stmt_singlelineContext, Local_stmt_contentContext, Loop_breakContext, Loop_continueContext, Fun_body_multilineContext, Local_stmts_multilineContext, Local_stmts_listContext, Local_stmt_multilineContext, Var_defContext, Var_defsContext, Ids_arrayContext, Arith_exprsContext, Arith_exprContext, If_exprContext, For_exprContext, Stmts_blockContext, Ternary_exprContext, Or_exprContext, And_exprContext, Eq_exprContext, Cmp_exprContext, Add_exprContext, Mult_exprContext, Unary_exprContext, Sqbr_exprContext, AtomContext, Fun_callContext, Fun_actual_argsContext, Pos_argsContext, Kw_argsContext, Kw_argContext, LiteralContext, Num_literalContext, Other_literalContext, IdContext } from "../../parser/v1/generated/PineV1Parser.js";
31
+ import type { StdlibEntry } from "../../runtime/v1/stdlib/metadata.js";
32
+ import { ScopeInfo } from "../common/passes/ScopeAnalysis.js";
33
+ /**
34
+ * Anything carrying a source position. Structural rather than
35
+ * `ParserRuleContext`, because the generated context classes are generic in
36
+ * their visit result and do not assign to the base type.
37
+ */
38
+ interface SourceLocated {
39
+ start?: {
40
+ line: number;
41
+ column: number;
42
+ } | null;
43
+ }
44
+ /**
45
+ * Is `node` a parse-tree node for the named grammar rule?
46
+ *
47
+ * MUST be used instead of `instanceof`. TypeScript is structural, so a v3
48
+ * `IdContext` satisfies the v1 `IdContext` TYPE and every override in the
49
+ * hierarchy type-checks — but `instanceof` is nominal at RUNTIME, and each
50
+ * version generates its own distinct context classes. `node instanceof
51
+ * IdContext` in this file is therefore FALSE for every v2 or v3 tree, which
52
+ * silently disables any guard built on it rather than failing loudly.
53
+ *
54
+ * Rule indices are no good either: an importing grammar lists its new and
55
+ * overridden rules first, so the same rule has a different index per version.
56
+ * The class NAME is the one identifier ANTLR keeps stable across the hierarchy.
57
+ */
58
+ /**
59
+ * Names the RUNTIME binds into the sandbox directly, outside the registry.
60
+ *
61
+ * initializeSandbox() installs the OHLCV series and both spellings of the bar
62
+ * counter as Series objects. They have no registry entry because they are DATA
63
+ * rather than functions, so any check asking "does this name exist?" has to
64
+ * know about them separately or it rejects `close`.
65
+ *
66
+ * `n` and `bar_index` are both here on purpose. Exactly one of them is
67
+ * spellable at any version, and the one that is not is a poison pill installed
68
+ * by the profile — which throws its own, better message on read. Rejecting it
69
+ * here instead would replace "renamed in v4" with "undeclared identifier".
70
+ */
71
+ export declare const RUNTIME_GLOBALS: ReadonlySet<string>;
72
+ /**
73
+ * Is `node` a parse-tree node of the named rule?
74
+ *
75
+ * ⚠ This dispatches on the CLASS NAME, which makes those names part of the
76
+ * build contract. Each version's generated parser declares its own
77
+ * `Kw_argContext`, `Fun_callContext` and so on, so any bundler that merges the
78
+ * five of them has to rename four — and every check here then silently returns
79
+ * false for v2-v5. scripts/common/build-dist.mjs passes `keepNames` for
80
+ * exactly this reason, and conformance/common/bundle.test.ts fails if the
81
+ * published bundle ever loses it again.
82
+ */
83
+ export declare function isRule(node: unknown, ruleContextName: string): boolean;
84
+ /**
85
+ * Fold a WRAPPED string literal back onto one line.
86
+ *
87
+ * The lexer admits a newline inside a string when the next line is indented,
88
+ * because TradingView's line-continuation pass runs before its lexer does and
89
+ * the wrap never reaches the token. This is the other half of that rule: the
90
+ * manual states the resulting sequence "adds only one space to the start of the
91
+ * wrapped lines, and it does not automatically add line terminators", so the
92
+ * newline and the indentation together become exactly one space.
93
+ *
94
+ * Without this the literal would reach the emitter with a real newline in it and
95
+ * produce JavaScript that does not parse.
96
+ */
97
+ export declare function unwrapString(text: string): string;
98
+ export declare class V1ToJsVisitor extends ParseTreeVisitor<string> {
99
+ /**
100
+ * Depth of user-function bodies currently being emitted.
101
+ *
102
+ * Pine scopes a function body separately from script scope, and several
103
+ * guards need to know which they are in — a name bound only inside some
104
+ * function says nothing about script scope. Tracked here rather than in a
105
+ * subclass so every version shares one counter.
106
+ */
107
+ protected fnDepth: number;
108
+ /**
109
+ * The user function whose body is being emitted, if any.
110
+ *
111
+ * ── The bug this exists for ─────────────────────────────────────────────────
112
+ *
113
+ * A local's SERIES is registered on the Context under its emitted JavaScript
114
+ * name, and that name is `opsv2_<pine name>` wherever the local is written.
115
+ * The JavaScript `let` is per-call and correctly scoped; the SERIES KEY was
116
+ * global. So two functions that both use the name `result`
117
+ *
118
+ * hex(int value) =>
119
+ * string result = ""
120
+ * …
121
+ * hexMap(array<int> numbers) =>
122
+ * result = map.new<int, string>()
123
+ * result.put(number, hex(number))
124
+ *
125
+ * shared ONE series. `hex()` set it to a string, `hexMap()` read a map back
126
+ * out of it, and the script died with "'str' has no method 'put'" — a
127
+ * diagnostic about a name the script never wrote. TradingView's own
128
+ * `v5-language-maps-09` and `v5-language-maps-18` are both this.
129
+ *
130
+ * The same collision applies between a function local and a script-scope
131
+ * variable of the same name, which is ordinary shadowing and must not alias.
132
+ *
133
+ * ── The other half ──────────────────────────────────────────────────────────
134
+ *
135
+ * Pine also gives every CALL SITE its own instance of a function's locals.
136
+ * That is a run-time question — the same declaration is one variable per
137
+ * frame — so it is answered by `Context.call`, which opens a series namespace
138
+ * per user-function frame. This half is the compile-time one: two DIFFERENT
139
+ * functions that happen to use the same word are two variables, whether or
140
+ * not either is ever called twice.
141
+ */
142
+ protected fnScope: string[];
143
+ /**
144
+ * The key a variable's Series is registered under.
145
+ *
146
+ * The emitted JavaScript identifier at script scope, and that identifier
147
+ * qualified by the enclosing function's emitted name inside a function body.
148
+ * Every site that writes a series key goes through here so that a declaration,
149
+ * a `:=`, and a history read can never disagree about which series they mean.
150
+ */
151
+ protected seriesKey(jsName: string): string;
152
+ /** Depth of for-loop bodies currently being emitted. */
153
+ protected loopDepth: number;
154
+ /**
155
+ * While positive, a block does NOT turn its last statement into a `return`.
156
+ * Set only for a statement-position `if` inside a loop — see visitIf_expr.
157
+ */
158
+ protected suppressBlockReturn: number;
159
+ /**
160
+ * The version this emitter implements. Subclasses override it, and every
161
+ * diagnostic is stamped with it, so an inherited guard reports the version
162
+ * that actually rejected the code rather than the one that wrote the check.
163
+ */
164
+ protected readonly version: PineVersion;
165
+ /**
166
+ * The names this VERSION may spell, and what each one means.
167
+ *
168
+ * An extension point, not a constant, because the stdlib is not the same set
169
+ * at every version: v4 renamed a pile of globals into namespaces, so `red`
170
+ * and `color.red` are each valid at exactly one version and invalid at the
171
+ * other. A single shared table cannot say that — it would accept both
172
+ * dialects everywhere and quietly compile scripts TradingView rejects.
173
+ *
174
+ * v1–v3 get the generated registry unchanged. v4 overrides this with its own
175
+ * view. Nothing about v4 appears in this file, which is the point: adding a
176
+ * version must never mean editing an earlier one
177
+ * (docs/common/ARCHITECTURE.md ("Design rules")).
178
+ */
179
+ protected get registry(): Record<string, StdlibEntry>;
180
+ protected readonly PREFIX = "opsv2_";
181
+ protected anonCounter: number;
182
+ protected scriptKind: 'study' | 'indicator' | 'strategy' | 'library' | undefined;
183
+ /** Names of user-defined functions; populated at the entry point. */
184
+ protected userFunctions: ReadonlySet<string>;
185
+ /**
186
+ * Script-scope variables READ above the line that declares them.
187
+ *
188
+ * v1 and v2 permit forward references, and published code relies on it to
189
+ * seed a counter from its own previous bar:
190
+ *
191
+ * Seq_Gn_t = seq_diff > 0 ? nz(Seq_Gn[1]) + 1 : 0 // line 705
192
+ * Seq_Gn = Seq_Gn_t > 9 ? 1 : Seq_Gn_t // line 707
193
+ *
194
+ * Emitting `let opsv2_Seq_Gn` at its declaration puts line 705 in the
195
+ * temporal dead zone. These names get a hoisted `let` prologue instead, so
196
+ * the binding exists (as undefined) before any read — at which point
197
+ * ctx.get() falls through to the by-name series lookup, which is the previous
198
+ * bar's value. That is exactly Pine's semantics.
199
+ *
200
+ * v3 rejects forward references outright, so this set is empty there.
201
+ */
202
+ protected hoisted: ReadonlySet<string>;
203
+ /**
204
+ * Whole-script scope facts, filled by visitPine_script.
205
+ *
206
+ * Lives at v1 rather than v2 because `Undeclared identifier` must behave the
207
+ * same at both — see conformance/v1_equals_v2.test.ts. v2 additionally uses
208
+ * it for ':=' , which is why the analysis pass was originally introduced
209
+ * there.
210
+ */
211
+ protected scopes: ScopeInfo;
212
+ /** Prefix every diagnostic with the version that rejected the code. */
213
+ protected err(ctx: SourceLocated, message: string): Error;
214
+ protected defaultResult(): string;
215
+ protected aggregateResult(aggregate: string, nextResult: string): string;
216
+ visitTerminal(node: TerminalNode): string;
217
+ /** * Helper: Generates a Deterministic ID based on Source Location.
218
+ * Example: "@L10:C5" (Line 10, Column 5)
219
+ */
220
+ protected getLocId(ctx: SourceLocated): string;
221
+ /** '==' / '!=' against 'na' silently misbehaves — require the na(x) function. */
222
+ protected enforceNaComparison(ctx: Eq_exprContext): void;
223
+ /** User-defined functions cannot recurse — reject direct self-reference. */
224
+ protected enforceNoRecursion(name: string, body: ParseTree, ctx: SourceLocated): void;
225
+ /**
226
+ * A study() script is in indicator mode — the broker emulator is disabled, so
227
+ * any strategy.* order/state call is a fatal error. Allowed under strategy()
228
+ * or when no directive is declared (permissive default).
229
+ */
230
+ protected enforceStrategyContext(name: string, ctx: SourceLocated): void;
231
+ /**
232
+ * The arity of the tuple this call returns BECAUSE ITS ARGUMENT IS ONE, or
233
+ * null when nothing about the call says it returns a tuple.
234
+ *
235
+ * Always null here: tuple expressions are a v4 addition ("the `expression`
236
+ * parameter can be series or tuple", v4 release notes, May 2020), and before
237
+ * that a bracketed list is not admitted as an argument at all.
238
+ */
239
+ protected tupleArityFromArgument(_call: Fun_callContext): number | null;
240
+ /**
241
+ * Whose return type a `[a, b] = <call>` is asking about.
242
+ *
243
+ * The call itself here, because before v5 a call is the whole expression.
244
+ * v5 overrides it: in `arr.maintainQueue(x).calcBB(y)` the value being
245
+ * unpacked is `calcBB`'s, not `maintainQueue`'s, and reading the base call
246
+ * asked the wrong function whether it returns a tuple.
247
+ */
248
+ protected destructuringCallee(call: Fun_callContext): string;
249
+ /** User-defined functions cannot return tuples. */
250
+ protected enforceNoTupleReturn(ctx: SourceLocated, detail?: string): void;
251
+ /**
252
+ * Does this subtree reference `name` as a bare identifier?
253
+ *
254
+ * `skipCallees` ignores the NAME position of a call while still scanning its
255
+ * arguments — `sma(x)` is not a reference to the variable `sma`, but
256
+ * `nz(s[1])` is a reference to `s`.
257
+ *
258
+ * `skipCalls` ignores whole call subtrees, arguments included.
259
+ */
260
+ protected referencesIdentifier(node: ParseTree | null, name: string, opts?: {
261
+ skipCalls?: boolean;
262
+ skipCallees?: boolean;
263
+ }): boolean;
264
+ /** Recursively scan a subtree for a call to the named function. */
265
+ protected callsFunction(node: ParseTree, name: string): boolean;
266
+ visitPine_script(ctx: Pine_scriptContext): string;
267
+ /**
268
+ * Script-scope names whose first READ is above their declaration.
269
+ *
270
+ * Only script scope: a function body runs at call time, so a name declared
271
+ * inside one is not lexically ordered against the script around it.
272
+ */
273
+ protected collectForwardReferenced(root: ParseTree): Set<string>;
274
+ /** Every user-defined function name in the script. */
275
+ protected collectFunctionNames(node: ParseTree): Set<string>;
276
+ /**
277
+ * The emitted name of a user-defined function.
278
+ *
279
+ * Pine keeps FUNCTIONS and VARIABLES in separate namespaces; JavaScript does
280
+ * not. So this is legal Pine:
281
+ *
282
+ * mama(src, fast, slow) => ...
283
+ * mama = mama(src, fast, slow) // variable named after the function
284
+ *
285
+ * Emitted with one namespace, the `let opsv2_mama` shadows the function it is
286
+ * initialised from, and the call hits the temporal dead zone. Built-in callees
287
+ * were already routed around this through ctx.builtin(); user-defined ones
288
+ * need their own namespace, which is what the '$' does — it is legal in a
289
+ * JavaScript identifier and cannot occur in a Pine one, so a collision with a
290
+ * script variable is impossible rather than merely unlikely.
291
+ */
292
+ protected funcName(pineName: string): string;
293
+ /**
294
+ * Find the first study()/strategy() directive call in the script, if any.
295
+ *
296
+ * The return type admits 'indicator' so a later version can override this
297
+ * without widening it; v1 knows only the two spellings it tests for.
298
+ */
299
+ protected detectDirective(node: ParseTree): 'study' | 'indicator' | 'strategy' | 'library' | undefined;
300
+ visitStmt(ctx: StmtContext): string;
301
+ visitGlobal_stmt(ctx: Global_stmtContext): string;
302
+ visitGlobal_stmt_content(ctx: Global_stmt_contentContext): string;
303
+ protected visitContent(ctx: Global_stmt_contentContext | Local_stmt_contentContext): string;
304
+ visitFun_def_stmt(ctx: Fun_def_stmtContext): string;
305
+ /**
306
+ * The JavaScript name a function DEFINITION is emitted under.
307
+ *
308
+ * The same as `funcName` here, and a seam because v5's method overloads are
309
+ * not: a name declared for several receiver types needs one JavaScript
310
+ * function per type, and definition and call site must agree on how that name
311
+ * is spelled.
312
+ */
313
+ protected definitionFunctionName(pineName: string): string;
314
+ /**
315
+ * The emitted name a CALL to a user function targets.
316
+ *
317
+ * The mirror of `definitionFunctionName`, and a seam for the same reason: v5
318
+ * lets one name carry several definitions, so the call site has to be able to
319
+ * choose between them. Identical here, where a name has exactly one body.
320
+ */
321
+ protected callFunctionName(pineName: string, _ctx: Fun_callContext): string;
322
+ visitFun_def_singleline(ctx: Fun_def_singlelineContext): string;
323
+ visitFun_def_multiline(ctx: Fun_def_multilineContext): string;
324
+ /**
325
+ * The PINE names of a function's parameters, in order.
326
+ *
327
+ * Emitted onto the function object so `Context.call` can resolve a keyword
328
+ * argument against it. The built-in registry carries this for every built-in;
329
+ * a user-defined function has nowhere else to put it, and without it
330
+ * `f(b = 5)` reaches the function as a positional bundle rather than as `b`.
331
+ *
332
+ * A seam because v5's parameters can carry defaults and are a rule of their
333
+ * own; here a parameter is just an id.
334
+ */
335
+ protected paramNames(ctx: Fun_headContext): string[];
336
+ /**
337
+ * `fn.__params = [...]`, appended to a function definition.
338
+ *
339
+ * A property rather than a lookup table because the two have to survive
340
+ * bundling together: the emitter's names are preserved by `keepNames`, and a
341
+ * property name is not mangled at all.
342
+ */
343
+ protected paramNamesAssignment(jsName: string, head: Fun_headContext): string;
344
+ visitFun_head(ctx: Fun_headContext): string;
345
+ visitFun_body_singleline(ctx: Fun_body_singlelineContext): string;
346
+ /**
347
+ * A single-line body — `f(x) => a = 1, b = 2, a + b` — as the INSIDE of a
348
+ * JavaScript block that hands back the body's value.
349
+ *
350
+ * SOURCING. From the manual's user-defined functions page: "The body of the
351
+ * function must separate all individual statements in its body with commas",
352
+ * and what a call returns is "the final expression, variable, or tuple in the
353
+ * function's body".
354
+ *
355
+ * So a comma list is a list of STATEMENTS. Joining it with `,` into a
356
+ * JavaScript comma expression works only while every one of them happens to
357
+ * BE an expression, and Pine's do not have to be. TradingView's own
358
+ *
359
+ * printTable(txt) => var table t = table.new(...), table.cell(t, 0, 0, txt)
360
+ *
361
+ * emitted `return let opsv2_t = ...`, which is not JavaScript — so the script
362
+ * passed compilation and then threw a SyntaxError the moment it was run.
363
+ */
364
+ protected singleLineBody(ctx: Local_stmt_singlelineContext): string;
365
+ /**
366
+ * Retained for generic visits only — `singleLineBody` is what the two call
367
+ * sites for this rule (a single-line function body, and a v5 switch arm) use,
368
+ * because they need the trailing statement's VALUE and this does not produce
369
+ * it.
370
+ */
371
+ visitLocal_stmt_singleline(ctx: Local_stmt_singlelineContext): string;
372
+ visitLocal_stmt_content(ctx: Local_stmt_contentContext): string;
373
+ visitLoop_break(_ctx: Loop_breakContext): string;
374
+ visitLoop_continue(_ctx: Loop_continueContext): string;
375
+ visitFun_body_multiline(ctx: Fun_body_multilineContext): string;
376
+ visitLocal_stmts_multiline(ctx: Local_stmts_multilineContext): string;
377
+ /**
378
+ * The variable a trailing BINDING statement evaluates to, or null if the
379
+ * statement is not a binding.
380
+ *
381
+ * A Pine function returns the value of its last statement, whatever form that
382
+ * statement takes. For an expression the emitted JS already IS the value; for
383
+ * a binding (`b = x * 2`) the value is the bound variable, and the emitter has
384
+ * to name it explicitly.
385
+ *
386
+ * Subclasses extend this when they add a binding form — v2 adds ':='.
387
+ */
388
+ protected trailingValueName(content: Local_stmt_contentContext): string | null;
389
+ /**
390
+ * `js`, the emitted form of a block's LAST statement, turned into whatever
391
+ * hands that statement's value back to the caller.
392
+ *
393
+ * Shared with `singleLineBody`, because the rule is the same one either way:
394
+ * a body evaluates to its final statement.
395
+ */
396
+ protected returnLastStatement(content: Local_stmt_contentContext, js: string): string;
397
+ visitLocal_stmts_list(ctx: Local_stmts_listContext): string;
398
+ /** @see visitLocal_stmts_list — the flattening there is what this rule needs. */
399
+ visitLocal_stmt_multiline(ctx: Local_stmt_multilineContext): string;
400
+ visitVar_def(ctx: Var_defContext): string;
401
+ visitVar_defs(ctx: Var_defsContext): string;
402
+ /**
403
+ * Where a `[a, b] = …` may take its values from, and how many there are.
404
+ * Throws on a source this version does not admit. A seam: v5 widens it.
405
+ */
406
+ protected checkTupleSource(ctx: Var_defsContext, ids: string[]): void;
407
+ visitIds_array(ctx: Ids_arrayContext): string;
408
+ visitArith_exprs(ctx: Arith_exprsContext): string;
409
+ visitArith_expr(ctx: Arith_exprContext): string;
410
+ /**
411
+ * Is this `if` a STATEMENT, whose value is discarded, rather than an
412
+ * expression being bound to something?
413
+ *
414
+ * The grammar admits `if_expr` in exactly two places — `*_stmt_content` and
415
+ * `arith_expr` — so the parent rule answers it.
416
+ */
417
+ /**
418
+ * The `if` this statement IS, or null if the statement is something else.
419
+ *
420
+ * `global_stmt_content` names if_expr directly; `local_stmt_content` reaches
421
+ * it through the transparent `arith_expr` wrapper.
422
+ */
423
+ protected statementIf(content: any): any | null;
424
+ protected isStatementPosition(ctx: If_exprContext): boolean;
425
+ visitIf_expr(ctx: If_exprContext): string;
426
+ /**
427
+ * What an if-EXPRESSION evaluates to when no branch runs.
428
+ *
429
+ * `na` here, per the manual sentence quoted in `visitIf_expr`. A seam because
430
+ * v6 changes the answer for a bool-valued `if`: bools may no longer be `na`,
431
+ * so the unspecified branch yields `false` — which is the "KNOWN GAP" noted
432
+ * above, closed for the version where TradingView made it the rule.
433
+ */
434
+ protected ifFallthrough(_ctx: If_exprContext): string;
435
+ /**
436
+ * The `else` branch, already emitted, or null when the `if` has none.
437
+ *
438
+ * A seam, not a behaviour: v1 has exactly one shape of else — a block — and
439
+ * this returns it. v4 adds `else if`, and overrides here rather than
440
+ * re-implementing `visitIf_expr`, because everything the chain needs is
441
+ * decided by the code ABOVE this call:
442
+ *
443
+ * - the IIFE wrapper. One `if/else if/else` is ONE expression, so it gets
444
+ * one wrapper. A nested `if` visited normally would build its own, and in
445
+ * expression position its value would be computed and then dropped.
446
+ * - `suppressBlockReturn`. Every block in the chain is at the same depth
447
+ * and must make the same return-or-not decision.
448
+ *
449
+ * Both are already set when this runs, so an override inherits them for free.
450
+ */
451
+ protected emitElseBranch(ctx: If_exprContext): string | null;
452
+ visitFor_expr(ctx: For_exprContext): string;
453
+ /**
454
+ * Rewrites a loop body's OWN trailing `return X` into `<res> = X`.
455
+ *
456
+ * Extracted from `visitFor_expr` so `for … in` (v5) can reuse it verbatim.
457
+ * Every loop in the language is an EXPRESSION whose value is the last
458
+ * `return_expression` its body evaluated, and a block emits that value as a
459
+ * `return` — which inside a real JavaScript `for` would leave the enclosing
460
+ * function instead.
461
+ *
462
+ * ── Why nesting alone is not enough ─────────────────────────────────────
463
+ *
464
+ * The body can contain whole FUNCTIONS — an if-expression emits
465
+ * `(() => { … })()`, and its returns belong to that function, not to the
466
+ * loop. Ranking by brace depth and taking the shallowest picked one of them
467
+ * whenever the loop had no trailing return of its own, which is exactly the
468
+ * case for a statement-`if` body:
469
+ *
470
+ * v = if high > low
471
+ * 1
472
+ * else
473
+ * 2
474
+ *
475
+ * came out as `if (…) { return 1; } else { _res0 = 2; }`, so the else branch
476
+ * assigned the LOOP's result variable and the expression evaluated to
477
+ * undefined. Silent: no error, just an `na` on every bar that took the else.
478
+ *
479
+ * So function bodies are tracked separately from plain blocks, and only a
480
+ * return at function depth 0 can be the loop's.
481
+ */
482
+ protected captureLoopResult(rawBody: string, res: string): string;
483
+ /**
484
+ * A `break`/`continue` that had to leave a function boundary (a v5 `switch`
485
+ * is emitted as one) arrives as a thrown loop signal; the loop whose body
486
+ * this is is the one it is for when no loop opened in between. Every loop
487
+ * form wraps its body with this, `while` included.
488
+ */
489
+ protected catchLoopSignals(body: string): string;
490
+ visitStmts_block(ctx: Stmts_blockContext): string;
491
+ visitTernary_expr(ctx: Ternary_exprContext): string;
492
+ visitOr_expr(ctx: Or_exprContext): string;
493
+ visitAnd_expr(ctx: And_exprContext): string;
494
+ /**
495
+ * The operator BETWEEN operand `i - 1` and operand `i`, in a flat chain.
496
+ *
497
+ * ── Why not ctx.MUL(i - 1) ────────────────────────────────────────────────
498
+ *
499
+ * Because that indexes the list of MUL tokens, not the position in the
500
+ * expression. In `a * b / c` there is one DIV and it is `DIV(0)`, so asking
501
+ * `MUL(1) ? "*" : DIV(1) ? "/" : "%"` for the second operator finds neither
502
+ * and falls through to MODULO:
503
+ *
504
+ * a * b / c → (a * b) % c
505
+ *
506
+ * Silent, and wrong for every mixed `*`/`/` chain — which is most of them.
507
+ * `a > b < c` and `a != b == c` had the same defect for the same reason.
508
+ *
509
+ * ANTLR alternates children operand, operator, operand, so operand `i` is
510
+ * preceded by the operator at `i * 2 - 1`. `visitAdd_expr` below already read
511
+ * it that way; these did not.
512
+ */
513
+ protected operatorBefore(ctx: ParserRuleContext, index: number): string;
514
+ /**
515
+ * `a == b` / `a != b`.
516
+ *
517
+ * Routed through a helper rather than emitted as a bare operator, because
518
+ * every variable is a Series OBJECT and JavaScript compares two objects by
519
+ * REFERENCE:
520
+ *
521
+ * x = 1.0
522
+ * y = 1.0
523
+ * equal = x == y // false, at every version
524
+ *
525
+ * `<`, `>`, `<=` and `>=` coerce through `valueOf` unconditionally, and a
526
+ * comparison against a literal has only one object in it, so this was the one
527
+ * shape that reached the reference test — and it was silently, permanently
528
+ * false. See `safe_eq` for why the helper compares loosely after unwrapping
529
+ * rather than strictly.
530
+ */
531
+ visitEq_expr(ctx: Eq_exprContext): string;
532
+ visitCmp_expr(ctx: Cmp_exprContext): string;
533
+ visitAdd_expr(ctx: Add_exprContext): string;
534
+ visitMult_expr(ctx: Mult_exprContext): string;
535
+ /**
536
+ * "v5: Division of two const int values truncates (5/2 = 2). Any input,
537
+ * simple, or series int keeps the remainder (2.5)." — the v6 migration
538
+ * guide, describing v5, and TradingView's own log agrees: `5 / 2` logs 2,
539
+ * while `input.int(5) / 2` and a series 5 / 2 log 2.5
540
+ * (conformance/common/v5_gaps_golden.test.ts).
541
+ *
542
+ * v1-v4 do the same (probes v1..v4-int-div: `1 / 3` is 0, `-7 / 2` is -3,
543
+ * `7 / 2.0` is 3.5, `bar_index / 7` keeps its fraction).
544
+ *
545
+ * Decided per operator, left to right: the chain so far must be a constant
546
+ * int, and so must the divisor. A seam rather than a flag, because v6
547
+ * reverses the rule and V6ToJsVisitor overrides `truncatesConstIntDivision`.
548
+ */
549
+ protected emitMultOp(left: string, op: string, right: string, ctx: Mult_exprContext, index: number): string;
550
+ /** v1 through v5 truncate; see emitMultOp. */
551
+ protected truncatesConstIntDivision(): boolean;
552
+ visitUnary_expr(ctx: Unary_exprContext): string;
553
+ /**
554
+ * Statements to emit immediately before the script-level statement being
555
+ * visited, or null outside one. Filled by `hoistHistoryBase`.
556
+ */
557
+ protected globalPrelude: string[] | null;
558
+ /**
559
+ * v2/v3 (any base) and v4 (call-free base) compute the base of `(expr)[n]`
560
+ * on EVERY bar when it sits in an `if`/`for` body, so TradingView reads true
561
+ * per-bar history there (v5/v6 carry the last value instead). Checked with
562
+ * Pine Logs: dev-docs/tv-parity/_probes/version-scope/ (v2-runtime,
563
+ * v3-runtime, v4-runtime, v6-expr-hist, v3-call-in-if).
564
+ *
565
+ * Returns the name of a series written every bar, emitted before the
566
+ * outermost script-level statement, or null when the hoist is not legal:
567
+ * not in a body, inside a function body, or a base that reads anything
568
+ * declared in a block, a loop counter, or a global not yet declared. Those
569
+ * cases are UNVERIFIED on TradingView and keep the carried series.
570
+ */
571
+ protected hoistHistoryBase(ctx: Sqbr_exprContext, base: string, id: string): string | null;
572
+ /**
573
+ * `hlc3[1]`, `strategy.equity[1]`: a built-in series keeps its history on
574
+ * every bar, wherever the `[n]` sits. Emitted in place, the read is a getter
575
+ * call that only runs when its `if`/`for`/function body does, so the series
576
+ * had holes (`na`) on the bars the body skipped. Writing it before the
577
+ * script-level statement records it on every bar (probes:
578
+ * dev-docs/tv-parity/_probes/hlc3, sizing).
579
+ *
580
+ * Returns null when the base is not a bare built-in getter, there is no
581
+ * statement to hoist to, or it sits in a `security` argument (evaluated in
582
+ * the other context).
583
+ */
584
+ /**
585
+ * User functions called (directly or through another user function) from
586
+ * inside a `security` call's arguments. Filled by visitPine_script.
587
+ */
588
+ protected securityFunctions: ReadonlySet<string>;
589
+ protected collectSecurityFunctions(root: ParseTree): Set<string>;
590
+ protected hoistBuiltinHistory(ctx: Sqbr_exprContext, base: string): {
591
+ tmp: string;
592
+ key: string;
593
+ } | null;
594
+ visitSqbr_expr(ctx: Sqbr_exprContext): string;
595
+ visitAtom(ctx: AtomContext): string;
596
+ visitFun_call(ctx: Fun_callContext): string;
597
+ /**
598
+ * Emit a security() call with the `expression` argument deferred as a thunk
599
+ * `() => (expr)`, so the runtime can evaluate it in the HTF sub-context.
600
+ * Every other argument (symbol, resolution, gaps, lookahead) is eager.
601
+ *
602
+ * KEYWORD ARGUMENTS ARE INCLUDED. This method used to read `pos_args()` only,
603
+ * which silently discarded them:
604
+ *
605
+ * security(tickerid, "D", close, lookahead=barmerge.lookahead_off)
606
+ *
607
+ * emitted no `lookahead` at all, so `resolveLookahead` in
608
+ * runtime/v1/stdlib/mtf.ts never saw an explicit value and always applied the
609
+ * version default. That made the documented per-call escape hatch unreachable,
610
+ * and a v2 script asking for `lookahead_off` silently got `on` — the exact
611
+ * behaviour the v2→v3 default flip exists to distinguish.
612
+ *
613
+ * `ctx.call` maps the keyword object onto positional slots by name, so the
614
+ * object is appended unchanged and `expression=` is deferred wherever it lands.
615
+ */
616
+ protected emitSecurity(ctx: Fun_callContext): string;
617
+ /** Emit a study()/strategy() directive as a metadata declaration. */
618
+ protected emitDirective(kind: string, ctx: Fun_callContext): string;
619
+ visitFun_actual_args(ctx: Fun_actual_argsContext): string;
620
+ /**
621
+ * The positional arguments of a call, in order.
622
+ *
623
+ * A seam, not a behaviour. v1's `pos_args` is a list of `arith_expr`, and v4
624
+ * widens it so a BRACKETED LIST can be one — `security(sym, tf,
625
+ * [close, open])` and `input.string("FQ", "Period", ["FQ", "FH"])`. The
626
+ * generated context therefore exposes `pos_arg()` at v4 and `arith_expr()`
627
+ * before it, and every place that walks the argument list has to ask the
628
+ * same question.
629
+ */
630
+ protected positionalArgs(ctx: Pos_argsContext): any[];
631
+ visitPos_args(ctx: Pos_argsContext): string;
632
+ visitKw_args(ctx: Kw_argsContext): string;
633
+ visitKw_arg(ctx: Kw_argContext): string;
634
+ visitLiteral(ctx: LiteralContext): string;
635
+ visitNum_literal(ctx: Num_literalContext): string;
636
+ visitOther_literal(ctx: Other_literalContext): string;
637
+ /**
638
+ * The namespace roots this version knows — `strategy`, `barstate`, `color`, …
639
+ * Derived from the version's own registry, so v4's extra roots appear only
640
+ * there. Cached: it is a scan of ~250 keys and `visitId` runs constantly.
641
+ */
642
+ private namespaceRootCache;
643
+ protected get namespaceRoots(): ReadonlySet<string>;
644
+ /**
645
+ * Is this identifier the NAME of a keyword argument rather than a read?
646
+ *
647
+ * `plot(close, title = "x")` reaches visitId twice: once for `close`, which
648
+ * is a variable read, and once for `title`, which is the callee's parameter
649
+ * name. Any check hooked on visitId sees both and must not treat the second
650
+ * as a variable — no script declares `title`.
651
+ */
652
+ protected isKeywordArgumentName(ctx: IdContext): boolean;
653
+ /**
654
+ * Reject a read of a name that nothing declares — the extension point.
655
+ *
656
+ * Lives at v1 so that v1 and v2 — which TradingView states are the same
657
+ * language, and which conformance/v1_equals_v2.test.ts asserts over the whole
658
+ * corpus — reject the same scripts. `bound` rather than `declared` is the
659
+ * right question: v1 and v2 both permit forward references.
660
+ *
661
+ * ── Why this exists ─────────────────────────────────────────────────────
662
+ *
663
+ * visitId's fallback emits `opsv2_<name>` for anything it does not
664
+ * recognise. Through `with(sandbox)` an unrecognised name is not a Pine
665
+ * diagnostic but a JavaScript ReferenceError on the first bar:
666
+ *
667
+ * opsv2_last_bar_index is not defined
668
+ *
669
+ * which reads as an engine crash. TradingView answers `Undeclared identifier`
670
+ * at compile time. Dotted names already had a guard here ("'chart' is not a
671
+ * namespace"); bare ones had none, so every unimplemented built-in surfaced
672
+ * as a crash. Running TradingView's own documentation corpus turned up 45
673
+ * scripts failing that way.
674
+ */
675
+ /**
676
+ * Is a binding of `name` in scope at the point it is READ?
677
+ *
678
+ * "The global scope is all of the script that is not inside a function, if
679
+ * statement, or other conditional structure ... Only code that is in the
680
+ * same local scope can access a local variable."
681
+ *
682
+ * Answered by token span rather than by nesting depth, because the question
683
+ * is about two POSITIONS: a name bound inside one `if` is invisible inside
684
+ * the `if` beside it, and those are at the same depth. See `scopesOf`.
685
+ *
686
+ * A name the analysis pass records no scope for is treated as visible. That
687
+ * is the safe direction — a binding form the pass does not model yet makes
688
+ * this check silent rather than making it reject working code.
689
+ */
690
+ protected visibleAt(name: string, ctx: IdContext): boolean;
691
+ protected checkUndeclaredRead(name: string, ctx: IdContext): void;
692
+ /**
693
+ * A member of a KNOWN namespace that this version does not have.
694
+ *
695
+ * The mirror of `checkUndeclaredRead`, and it existed for neither reason nor
696
+ * design: an unknown BARE name was rejected, while an unknown MEMBER of a
697
+ * real namespace fell through to `opsv2_syminfo.opsv2_nonesuch` and read as
698
+ * `undefined` on every bar. Nothing said so. `color.from_gradient` at v2 — a
699
+ * v4 addition — compiled, and every colour computed from it was `na` forever.
700
+ *
701
+ * Split by whether the name exists at ALL, because the two are different
702
+ * problems: `syminfo.tickerid` at v2 is a MIGRATION question with a version
703
+ * for an answer, and `syminfo.nonesuch` is a typo.
704
+ *
705
+ * Overridable because v5 has dotted names that are not namespace members —
706
+ * fields, enum members, library calls — and resolves those first.
707
+ */
708
+ protected checkNamespaceMember(root: string, text: string, ctx: IdContext): void;
709
+ /**
710
+ * The registry key of the VARIABLE, for a name this version also gives to a
711
+ * function. `undefined` when the name means only one thing.
712
+ *
713
+ * A read resolves to the variable and a call to the function, which is what
714
+ * Pine's separate namespaces do. Only `tickerid` needs it, and only at v1-v3
715
+ * — see the comment at the use site.
716
+ */
717
+ protected variableSpelling(name: string): string | undefined;
718
+ visitId(ctx: IdContext): string;
719
+ }
720
+ export {};