openpinescript 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +332 -2
- package/dist/cli.mjs +108691 -0
- package/dist/index.cjs +108670 -0
- package/dist/index.mjs +108624 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/parser/v1/generated/PineV1Lexer.d.ts +66 -0
- package/dist/types/parser/v1/generated/PineV1Parser.d.ts +555 -0
- package/dist/types/parser/v1/generated/PineV1ParserVisitor.d.ts +318 -0
- package/dist/types/parser/v1/index.d.ts +39 -0
- package/dist/types/parser/v2/generated/PineV2Lexer.d.ts +67 -0
- package/dist/types/parser/v2/generated/PineV2Parser.d.ts +568 -0
- package/dist/types/parser/v2/generated/PineV2ParserVisitor.d.ts +325 -0
- package/dist/types/parser/v2/index.d.ts +2 -0
- package/dist/types/parser/v3/generated/PineV3Lexer.d.ts +67 -0
- package/dist/types/parser/v3/generated/PineV3Parser.d.ts +568 -0
- package/dist/types/parser/v3/generated/PineV3ParserVisitor.d.ts +325 -0
- package/dist/types/parser/v3/index.d.ts +2 -0
- package/dist/types/parser/v4/generated/PineV4Lexer.d.ts +86 -0
- package/dist/types/parser/v4/generated/PineV4Parser.d.ts +673 -0
- package/dist/types/parser/v4/generated/PineV4ParserVisitor.d.ts +367 -0
- package/dist/types/parser/v4/index.d.ts +2 -0
- package/dist/types/parser/v5/generated/PineV5Lexer.d.ts +95 -0
- package/dist/types/parser/v5/generated/PineV5Parser.d.ts +964 -0
- package/dist/types/parser/v5/generated/PineV5ParserVisitor.d.ts +521 -0
- package/dist/types/parser/v5/index.d.ts +2 -0
- package/dist/types/parser/v6/generated/PineV6Lexer.d.ts +98 -0
- package/dist/types/parser/v6/generated/PineV6Parser.d.ts +984 -0
- package/dist/types/parser/v6/generated/PineV6ParserVisitor.d.ts +528 -0
- package/dist/types/parser/v6/index.d.ts +2 -0
- package/dist/types/runtime/v1/Series.d.ts +61 -0
- package/dist/types/runtime/v1/broker/Broker.d.ts +325 -0
- package/dist/types/runtime/v1/broker/index.d.ts +158 -0
- package/dist/types/runtime/v1/broker/val.d.ts +28 -0
- package/dist/types/runtime/v1/constDefault.d.ts +11 -0
- package/dist/types/runtime/v1/context.d.ts +1705 -0
- package/dist/types/runtime/v1/footprint.d.ts +32 -0
- package/dist/types/runtime/v1/index.d.ts +7 -0
- package/dist/types/runtime/v1/language-spec.d.ts +12 -0
- package/dist/types/runtime/v1/openTrades.d.ts +12 -0
- package/dist/types/runtime/v1/palette.d.ts +18 -0
- package/dist/types/runtime/v1/session.d.ts +482 -0
- package/dist/types/runtime/v1/sessions.d.ts +31 -0
- package/dist/types/runtime/v1/stateClone.d.ts +19 -0
- package/dist/types/runtime/v1/stdlib/alerts.d.ts +31 -0
- package/dist/types/runtime/v1/stdlib/arrays.d.ts +133 -0
- package/dist/types/runtime/v1/stdlib/barmerge.d.ts +27 -0
- package/dist/types/runtime/v1/stdlib/barstate.d.ts +79 -0
- package/dist/types/runtime/v1/stdlib/cast.d.ts +81 -0
- package/dist/types/runtime/v1/stdlib/chart.d.ts +283 -0
- package/dist/types/runtime/v1/stdlib/color.d.ts +51 -0
- package/dist/types/runtime/v1/stdlib/core.d.ts +162 -0
- package/dist/types/runtime/v1/stdlib/dataset.d.ts +36 -0
- package/dist/types/runtime/v1/stdlib/display.d.ts +50 -0
- package/dist/types/runtime/v1/stdlib/drawings.d.ts +407 -0
- package/dist/types/runtime/v1/stdlib/errors.d.ts +21 -0
- package/dist/types/runtime/v1/stdlib/font.d.ts +20 -0
- package/dist/types/runtime/v1/stdlib/footprint.d.ts +76 -0
- package/dist/types/runtime/v1/stdlib/format.d.ts +57 -0
- package/dist/types/runtime/v1/stdlib/index.d.ts +71 -0
- package/dist/types/runtime/v1/stdlib/input.d.ts +14 -0
- package/dist/types/runtime/v1/stdlib/inputs.d.ts +60 -0
- package/dist/types/runtime/v1/stdlib/late_members.d.ts +97 -0
- package/dist/types/runtime/v1/stdlib/logging.d.ts +31 -0
- package/dist/types/runtime/v1/stdlib/maps.d.ts +55 -0
- package/dist/types/runtime/v1/stdlib/math.d.ts +71 -0
- package/dist/types/runtime/v1/stdlib/matrix.d.ts +201 -0
- package/dist/types/runtime/v1/stdlib/metadata.d.ts +14 -0
- package/dist/types/runtime/v1/stdlib/mtf.d.ts +15 -0
- package/dist/types/runtime/v1/stdlib/renames.d.ts +117 -0
- package/dist/types/runtime/v1/stdlib/renames5.d.ts +113 -0
- package/dist/types/runtime/v1/stdlib/renames6.d.ts +81 -0
- package/dist/types/runtime/v1/stdlib/request.d.ts +207 -0
- package/dist/types/runtime/v1/stdlib/scale.d.ts +19 -0
- package/dist/types/runtime/v1/stdlib/session.d.ts +113 -0
- package/dist/types/runtime/v1/stdlib/sort_field.d.ts +12 -0
- package/dist/types/runtime/v1/stdlib/sources.d.ts +24 -0
- package/dist/types/runtime/v1/stdlib/str.d.ts +117 -0
- package/dist/types/runtime/v1/stdlib/strategy.d.ts +314 -0
- package/dist/types/runtime/v1/stdlib/syminfo.d.ts +209 -0
- package/dist/types/runtime/v1/stdlib/ta.d.ts +409 -0
- package/dist/types/runtime/v1/stdlib/time.d.ts +158 -0
- package/dist/types/runtime/v1/stdlib/trades.d.ts +108 -0
- package/dist/types/runtime/v1/stdlib/ui.d.ts +196 -0
- package/dist/types/runtime/v1/stdlib/v6_members.d.ts +68 -0
- package/dist/types/runtime/v1/timeframes.d.ts +139 -0
- package/dist/types/runtime/v1/zones.d.ts +30 -0
- package/dist/types/transpiler/common/index.d.ts +51 -0
- package/dist/types/transpiler/common/libraries.d.ts +29 -0
- package/dist/types/transpiler/common/parseErrors.d.ts +38 -0
- package/dist/types/transpiler/common/passes/ScopeAnalysis.d.ts +158 -0
- package/dist/types/transpiler/common/passes/TypeRules.d.ts +26 -0
- package/dist/types/transpiler/common/profiles/index.d.ts +96 -0
- package/dist/types/transpiler/common/version.d.ts +40 -0
- package/dist/types/transpiler/v1/ToJsVisitor.d.ts +720 -0
- package/dist/types/transpiler/v2/ToJsVisitor.d.ts +72 -0
- package/dist/types/transpiler/v3/ToJsVisitor.d.ts +87 -0
- package/dist/types/transpiler/v4/ToJsVisitor.d.ts +263 -0
- package/dist/types/transpiler/v5/ToJsVisitor.d.ts +1101 -0
- package/dist/types/transpiler/v6/ToJsVisitor.d.ts +237 -0
- package/dist/types/transpiler/v6/rules.d.ts +94 -0
- package/dist/types/utils/v2/common.d.ts +4 -0
- 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 { AbstractParseTreeVisitor } 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 AbstractParseTreeVisitor<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 {};
|