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.
- package/LICENSE +674 -0
- package/README.md +346 -2
- package/dist/cli.mjs +108686 -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,1101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pine Script v5 → JavaScript emitter.
|
|
3
|
+
*
|
|
4
|
+
* v5 inherits every guard v3 added and every declaration form v4 added. The
|
|
5
|
+
* migration guide relaxes none of them, so nothing here undoes anything.
|
|
6
|
+
*
|
|
7
|
+
* What v5 adds falls into four groups:
|
|
8
|
+
*
|
|
9
|
+
* NAMES (runtime/v1/stdlib/renames5.ts + the V5_VIEW registry)
|
|
10
|
+
* The §4a namespace migration — `sma` → `ta.sma`, `abs` → `math.abs`,
|
|
11
|
+
* `security` → `request.security`, `tostring` → `str.tostring` — plus the
|
|
12
|
+
* §4b renames that keep their namespace (`study` → `indicator`) and the §4c
|
|
13
|
+
* removals (`iff`, `offset`, `transp=`). Those are lookups and rejections,
|
|
14
|
+
* driven from a data table, not emit rules.
|
|
15
|
+
*
|
|
16
|
+
* SYNTAX (this file)
|
|
17
|
+
* `while`, `switch`, and `for … in`. None is a v4 construct — an early
|
|
18
|
+
* draft of the delta spec said otherwise about the first two and was wrong
|
|
19
|
+
* — so v4 emits nothing for them and v5 emits all three here.
|
|
20
|
+
*
|
|
21
|
+
* RESERVED WORDS (this file)
|
|
22
|
+
* Twelve of the thirteen words v5 will not let a script NAME anything (the
|
|
23
|
+
* thirteenth, `in`, is a lexer keyword — see RESERVED below). Enforced on the
|
|
24
|
+
* identifier rather than in the lexer, because `text` is simultaneously a
|
|
25
|
+
* live namespace (`text.align_left`) and a forbidden variable name — a
|
|
26
|
+
* distinction a token cannot draw.
|
|
27
|
+
*
|
|
28
|
+
* KEYWORD ARGUMENTS (this file)
|
|
29
|
+
* §4b renamed parameters without renaming their functions:
|
|
30
|
+
* `study(resolution=)` became `indicator(timeframe=)`, `strategy.entry(long=)`
|
|
31
|
+
* became `direction=`. Where the RUNTIME still spells one the old way — it is
|
|
32
|
+
* one implementation across versions — the emitter translates. Where it has
|
|
33
|
+
* taken the new name (`strategy.entry`'s `direction`), there is nothing to
|
|
34
|
+
* translate, and v1–v4's `long=` is mapped the other way, in the runtime
|
|
35
|
+
* (`V4_RENAMED_PARAMS`). tests/v5/transpiler/keyword_aliases.test.ts fails
|
|
36
|
+
* if an alias targets a name the runtime does not have.
|
|
37
|
+
*
|
|
38
|
+
* ── What is NOT implemented at v5 ───────────────────────────────────────────
|
|
39
|
+
*
|
|
40
|
+
* User-defined types (`type`), methods (`method`), and libraries
|
|
41
|
+
* (`import`/`export`). Each needs a type system this engine does not have:
|
|
42
|
+
* `p.x` is currently always a namespace member, and telling it apart from a
|
|
43
|
+
* field access requires knowing that `p` came from `Point.new()`.
|
|
44
|
+
*
|
|
45
|
+
* `type` is absent from the GRAMMAR, so a script using it fails with "parsing
|
|
46
|
+
* failed" — the same verdict v1 gives `:=` and v4 gives `while`. The other three
|
|
47
|
+
* DO parse, because the inherited grammar can read what follows the keyword on
|
|
48
|
+
* its own, so they are rejected by NAME instead — see UNSUPPORTED below. Either
|
|
49
|
+
* way the script is refused rather than compiled into something it does not say.
|
|
50
|
+
*/
|
|
51
|
+
import type { PineVersion } from "../common/version.js";
|
|
52
|
+
import { V4ToJsVisitor } from "../v4/ToJsVisitor.js";
|
|
53
|
+
import type { StdlibEntry } from "../../runtime/v1/stdlib/metadata.js";
|
|
54
|
+
import type { ParseTree } from "antlr4ng";
|
|
55
|
+
import { parse as parseV5 } from "../../parser/v5/index.js";
|
|
56
|
+
import { While_exprContext, For_in_exprContext, Call_suffixContext, Switch_exprContext, Switch_caseContext, Fun_callContext, Var_defsContext, Var_assignContext, IdContext, StmtContext, Type_def_stmtContext, Enum_def_stmtContext, Method_def_stmtContext, Export_stmtContext, Import_stmtContext, Fun_headContext, Pine_scriptContext, Fun_def_singlelineContext, Fun_def_multilineContext } from "../../parser/v5/generated/PineV5Parser.js";
|
|
57
|
+
/** One declaration of an overloaded function name. */
|
|
58
|
+
/**
|
|
59
|
+
* A type as overloads compare it: no whitespace, and no library alias on any
|
|
60
|
+
* part — `lib.Point` and `Point`, `array<lib.Point>` and `array<Point>`, are
|
|
61
|
+
* the same type to a library's own declarations.
|
|
62
|
+
*/
|
|
63
|
+
export declare function normaliseType(t: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* `float` or `int` for an arithmetic expression whose type its text settles:
|
|
66
|
+
* a price series or a decimal literal makes it float, as does `/`; integer
|
|
67
|
+
* literals alone keep it int. Anything with a call or another name is
|
|
68
|
+
* unknown — a runtime value cannot tell `22.0` from `22`, so the guess has to
|
|
69
|
+
* be made here or not at all.
|
|
70
|
+
*/
|
|
71
|
+
export declare function numericTypeOf(text: string): "int" | "float" | null;
|
|
72
|
+
interface FnOverload {
|
|
73
|
+
/** Fewest arguments a call may pass — parameters without a default. */
|
|
74
|
+
min: number;
|
|
75
|
+
/** Most — every parameter. */
|
|
76
|
+
max: number;
|
|
77
|
+
/** Each parameter's DECLARED type, or null where none is written. */
|
|
78
|
+
types: Array<string | null>;
|
|
79
|
+
}
|
|
80
|
+
/** Anything carrying a source position (see the v1 base for why it is structural). */
|
|
81
|
+
interface SourceLocated {
|
|
82
|
+
start?: {
|
|
83
|
+
line: number;
|
|
84
|
+
column: number;
|
|
85
|
+
} | null;
|
|
86
|
+
}
|
|
87
|
+
/** Construction options — how a v5 script reaches code outside itself. */
|
|
88
|
+
export interface V5Options {
|
|
89
|
+
/**
|
|
90
|
+
* Library sources, keyed by the path an `import` names
|
|
91
|
+
* (`"someuser/somelib/1"`). Supplied by the host; see the field of the same
|
|
92
|
+
* name on the visitor for why they cannot be fetched.
|
|
93
|
+
*/
|
|
94
|
+
readonly libraries?: Readonly<Record<string, string>>;
|
|
95
|
+
/**
|
|
96
|
+
* Apply the checks TradingView's chart editor makes and its compile endpoint
|
|
97
|
+
* does not (an indicator with no output, `request.*()` in a conditional at
|
|
98
|
+
* v5, `color.new()` with no transparency, a colour for `input.string`).
|
|
99
|
+
* Off for bare transpiles, so fixtures need not be complete scripts.
|
|
100
|
+
*/
|
|
101
|
+
readonly editorRules?: boolean;
|
|
102
|
+
/** Disambiguates emitted source locations. Set when compiling a library. */
|
|
103
|
+
readonly locationTag?: string;
|
|
104
|
+
/** Disambiguates the names a script DECLARES. Set when compiling a library. */
|
|
105
|
+
readonly nameScope?: string;
|
|
106
|
+
/** The library paths being compiled above this one, outermost first. Set when compiling a library. */
|
|
107
|
+
readonly libraryChain?: readonly string[];
|
|
108
|
+
}
|
|
109
|
+
/** One field of a user-defined type. */
|
|
110
|
+
interface TypeField {
|
|
111
|
+
readonly name: string;
|
|
112
|
+
/** Its `= …` initialiser, or null — an undeclared default is `na`. */
|
|
113
|
+
readonly defaultExpr: any | null;
|
|
114
|
+
}
|
|
115
|
+
/** One member of an `enum` declaration. */
|
|
116
|
+
interface EnumField {
|
|
117
|
+
readonly name: string;
|
|
118
|
+
/**
|
|
119
|
+
* Its `= "title"` expression, or null.
|
|
120
|
+
*
|
|
121
|
+
* "If the programmer does not specify a field's title, its title is the
|
|
122
|
+
* 'string' representation of its name."
|
|
123
|
+
*/
|
|
124
|
+
readonly titleExpr: any | null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Keyword arguments v5 renamed, per CALLEE.
|
|
128
|
+
*
|
|
129
|
+
* §4b renamed these parameters without renaming their functions. The runtime
|
|
130
|
+
* still spells most of them the v1–v4 way — it is one implementation across versions,
|
|
131
|
+
* and `Context.call` matches a keyword argument by looking its written name up
|
|
132
|
+
* in the registry entry's parameter list — so an unmatched key is DROPPED
|
|
133
|
+
* SILENTLY. A v5 script writing `indicator(timeframe="D")` would therefore have
|
|
134
|
+
* had its timeframe ignored and fallen back to the chart's.
|
|
135
|
+
*
|
|
136
|
+
* Scoped per callee rather than applied globally because the new names are
|
|
137
|
+
* ordinary words: `source` is a parameter of half the `input.*` family, and
|
|
138
|
+
* rewriting it everywhere would corrupt those calls to fix `nz`.
|
|
139
|
+
*/
|
|
140
|
+
export declare const KEYWORD_ALIASES: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
141
|
+
export declare class V5ToJsVisitor extends V4ToJsVisitor {
|
|
142
|
+
protected readonly version: PineVersion;
|
|
143
|
+
/**
|
|
144
|
+
* v5's view of the stdlib: v4's, minus the spellings v5 renamed away, plus
|
|
145
|
+
* the namespaces it introduced and the aliases it created.
|
|
146
|
+
*
|
|
147
|
+
* Subtracting matters as much as adding — leaving `sma` in beside `ta.sma`
|
|
148
|
+
* would accept both dialects, and a script written half in each compiles here
|
|
149
|
+
* and fails on TradingView.
|
|
150
|
+
*/
|
|
151
|
+
protected get registry(): Record<string, StdlibEntry>;
|
|
152
|
+
/**
|
|
153
|
+
* The callee whose arguments are currently being emitted, innermost last.
|
|
154
|
+
*
|
|
155
|
+
* `visitKw_arg` needs to know which function a keyword belongs to before it
|
|
156
|
+
* can decide whether to translate its name, and the grammar gives it no way
|
|
157
|
+
* up to the call. A stack rather than a single field because arguments nest:
|
|
158
|
+
* `strategy.entry("L", direction=strategy.long, qty=nz(q, replacement=1))`.
|
|
159
|
+
*/
|
|
160
|
+
private calleeStack;
|
|
161
|
+
/** Declared type name → its fields, in declaration order. */
|
|
162
|
+
protected userTypes: Map<string, TypeField[]>;
|
|
163
|
+
/**
|
|
164
|
+
* `enum Name` declarations, name → its fields in declaration order.
|
|
165
|
+
*
|
|
166
|
+
* Collected in the same pre-pass as `userTypes`, and for the same reason: a
|
|
167
|
+
* member may be READ above the line that declares its enum, inside any
|
|
168
|
+
* function body, and a single-pass emitter cannot know that yet.
|
|
169
|
+
*/
|
|
170
|
+
protected userEnums: Map<string, EnumField[]>;
|
|
171
|
+
/**
|
|
172
|
+
* Names declared with `method`.
|
|
173
|
+
*
|
|
174
|
+
* Kept apart from `userFunctions` — which already contains them, since a
|
|
175
|
+
* method's body IS a function definition — because only a method may be
|
|
176
|
+
* called as `receiver.name(…)`. A plain function of the same name may not,
|
|
177
|
+
* and answering that question wrongly turns a typo into a silent field read.
|
|
178
|
+
*/
|
|
179
|
+
protected userMethods: Set<string>;
|
|
180
|
+
/**
|
|
181
|
+
* method name → the receiver type of each declaration of it, in file order.
|
|
182
|
+
*
|
|
183
|
+
* A single entry is the ordinary case and changes nothing. Two or more is an
|
|
184
|
+
* OVERLOAD SET: "User-defined methods can override and overload existing
|
|
185
|
+
* built-in and user-defined methods with the same identifier ... The
|
|
186
|
+
* underlying type of each variable determines which overload of `getType()`
|
|
187
|
+
* the compiler will use."
|
|
188
|
+
*/
|
|
189
|
+
protected methodOverloads: Map<string, string[]>;
|
|
190
|
+
/**
|
|
191
|
+
* method name → every declaration's receiver AND full signature, in file
|
|
192
|
+
* order. A receiver may carry several: "User-defined methods can override and
|
|
193
|
+
* overload existing built-in and user-defined methods", told apart the way
|
|
194
|
+
* any overload is — by required-parameter count, then by type.
|
|
195
|
+
*/
|
|
196
|
+
protected methodSignatures: Map<string, Array<{
|
|
197
|
+
receiver: string;
|
|
198
|
+
sig: FnOverload;
|
|
199
|
+
}>>;
|
|
200
|
+
/** type name → field name → the field's declared type, where one is written. */
|
|
201
|
+
protected userFieldTypes: Map<string, Map<string, string>>;
|
|
202
|
+
/**
|
|
203
|
+
* Declared types of the names in scope while a function or method body is
|
|
204
|
+
* emitted, innermost last: its parameters, then its typed or constructed
|
|
205
|
+
* locals. Script-scope names come from `scopes.staticTypes`.
|
|
206
|
+
*/
|
|
207
|
+
protected localTypes: Array<Map<string, string | null>>;
|
|
208
|
+
/** `import … as alias` → the library path it names. */
|
|
209
|
+
protected importAliases: Map<string, string>;
|
|
210
|
+
/** Library path → the names that library exports. */
|
|
211
|
+
protected libraryExports: Map<string, Set<string>>;
|
|
212
|
+
/**
|
|
213
|
+
* `<path>::<TypeName>` → that type's field names, in declaration order.
|
|
214
|
+
*
|
|
215
|
+
* Needed because `ml.Pair.new(1, 2)` has to map POSITIONAL arguments onto
|
|
216
|
+
* fields, and the field order lives in the library rather than here.
|
|
217
|
+
*/
|
|
218
|
+
protected libraryTypeFields: Map<string, string[]>;
|
|
219
|
+
/**
|
|
220
|
+
* An imported library's exported METHOD name → the alias that owns it.
|
|
221
|
+
*
|
|
222
|
+
* Importing a library brings its methods into scope on values of its types,
|
|
223
|
+
* so `pair.total()` has to resolve even though `total` is declared in another
|
|
224
|
+
* file. Without this the call fell through to `Context.builtinMethod`, which
|
|
225
|
+
* correctly reported that a plain object has no built-in methods — a true
|
|
226
|
+
* statement about the wrong question.
|
|
227
|
+
*/
|
|
228
|
+
protected libraryMethods: Map<string, string>;
|
|
229
|
+
/**
|
|
230
|
+
* Sources for `import`, keyed by the path a script writes.
|
|
231
|
+
*
|
|
232
|
+
* A library lives on TradingView, and this engine cannot fetch one — so the
|
|
233
|
+
* host supplies them, the same way it supplies higher-timeframe candles for
|
|
234
|
+
* `security()`. An import with no source is REFUSED rather than stubbed:
|
|
235
|
+
* a library that silently resolves to nothing produces a script full of `na`
|
|
236
|
+
* that still runs.
|
|
237
|
+
*/
|
|
238
|
+
protected readonly libraries: Readonly<Record<string, string>>;
|
|
239
|
+
/**
|
|
240
|
+
* Appended to every source location this visitor emits.
|
|
241
|
+
*
|
|
242
|
+
* Empty for a script, `@<alias>` for a library compiled into one. Call ids
|
|
243
|
+
* key per-call-site state — `ta.sma@L12:C4` is what keeps two `ta.sma` calls
|
|
244
|
+
* from sharing one rolling window — and a library's line 12 has nothing to do
|
|
245
|
+
* with the importing script's. Without the tag they collide silently, and two
|
|
246
|
+
* unrelated indicators quietly share a buffer.
|
|
247
|
+
*/
|
|
248
|
+
protected readonly locationTag: string;
|
|
249
|
+
/**
|
|
250
|
+
* Inserted into every name this script DECLARES. Empty for a script,
|
|
251
|
+
* `$lib$<alias>$` for a library compiled into one.
|
|
252
|
+
*
|
|
253
|
+
* ── Why the IIFE is not enough ────────────────────────────────────────────
|
|
254
|
+
*
|
|
255
|
+
* A library's body is wrapped in an immediately-invoked function, so its
|
|
256
|
+
* `let opsv2_len` is lexically its own. That is only half the isolation: a
|
|
257
|
+
* variable is also a SERIES, registered in `ctx.vars` under its emitted name,
|
|
258
|
+
* and `ctx.new_var` looks that registry up BY NAME. Two scripts each
|
|
259
|
+
* declaring `len` therefore shared one series — the second write overwrote
|
|
260
|
+
* the first, and the library's functions, closing over their own `let`, read
|
|
261
|
+
* the importing script's value.
|
|
262
|
+
*
|
|
263
|
+
* It was worth 220 instead of 26 in the first library this engine ran, and
|
|
264
|
+
* nothing about the emitted JavaScript looked wrong.
|
|
265
|
+
*
|
|
266
|
+
* Built-ins are NOT scoped: `close` must stay `opsv2_close`, which is what
|
|
267
|
+
* the sandbox binds. Only names the script itself binds are rewritten, so the
|
|
268
|
+
* declaration and every read of it move together.
|
|
269
|
+
*/
|
|
270
|
+
protected readonly nameScope: string;
|
|
271
|
+
/** The libraries being compiled above this visitor, so an import back into one is named as a cycle. */
|
|
272
|
+
protected readonly libraryChain: readonly string[];
|
|
273
|
+
constructor(options?: V5Options);
|
|
274
|
+
/**
|
|
275
|
+
* The emitted name of an identifier — scoped when this script declares it.
|
|
276
|
+
*
|
|
277
|
+
* The single place that decision is made, so the JS binding, the series
|
|
278
|
+
* registry key and every read of it can never disagree.
|
|
279
|
+
*/
|
|
280
|
+
protected localName(text: string): string;
|
|
281
|
+
protected funcName(pineName: string): string;
|
|
282
|
+
protected getLocId(ctx: SourceLocated): string;
|
|
283
|
+
/** Names this script marks `export`. Non-empty only in a library. */
|
|
284
|
+
protected exportedNames: Set<string>;
|
|
285
|
+
/** Does this script declare itself a library? Set at the entry point. */
|
|
286
|
+
protected get declaresLibrary(): boolean;
|
|
287
|
+
/** Is this visitor compiling a library INTO another script? */
|
|
288
|
+
protected get isImportedLibrary(): boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Types and methods are collected BEFORE anything is emitted.
|
|
291
|
+
*
|
|
292
|
+
* Both are whole-script facts a single pass cannot have: `p.x` has to know
|
|
293
|
+
* that `p` might be an object, and `r.area()` has to know that `area` is a
|
|
294
|
+
* method, and either can appear above its declaration inside a function body
|
|
295
|
+
* (which runs at call time, so it is not a forward reference).
|
|
296
|
+
*
|
|
297
|
+
* The same reasoning, and the same shape, as `collectFunctionNames` in the v1
|
|
298
|
+
* base.
|
|
299
|
+
*/
|
|
300
|
+
visitPine_script(ctx: Pine_scriptContext): string;
|
|
301
|
+
/**
|
|
302
|
+
* v5 `request.*()` is static unless the declaration says
|
|
303
|
+
* `dynamic_requests = true`. (v6 has its own, explicit-opt-out rule.)
|
|
304
|
+
*/
|
|
305
|
+
private editorRules;
|
|
306
|
+
private v5StaticRequests;
|
|
307
|
+
/**
|
|
308
|
+
* Whole-script rules TradingView's chart editor applies (its compile
|
|
309
|
+
* endpoint does not): an `indicator()` must display something.
|
|
310
|
+
*/
|
|
311
|
+
protected enforceEditorRules(root: any): void;
|
|
312
|
+
/** An `indicator()` must display something (log.* counts: a log-only script loads). */
|
|
313
|
+
protected enforceIndicatorOutput(root: any): void;
|
|
314
|
+
/** Call-site rules the chart editor applies at v5 and v6 alike. */
|
|
315
|
+
protected enforceEditorCallRules(name: string, ctx: any): void;
|
|
316
|
+
/** Walks the whole tree for `type` and `method` declarations. */
|
|
317
|
+
protected collectDeclarations(root: any): void;
|
|
318
|
+
/** A type declaration's fields, in declaration order. */
|
|
319
|
+
protected fieldsOf(ctx: Type_def_stmtContext): TypeField[];
|
|
320
|
+
/**
|
|
321
|
+
* Is `root` a variable or parameter bound in a scope that contains this
|
|
322
|
+
* call? A local or parameter counts inside its own span; a script-scope
|
|
323
|
+
* binding (recorded with a null span) counts everywhere, which is safe
|
|
324
|
+
* because the caller asks only for a name the namespace does not define,
|
|
325
|
+
* and the namespace path would fail on it anyway. A namespace is never
|
|
326
|
+
* shadowed by a name this script does not actually declare.
|
|
327
|
+
*/
|
|
328
|
+
protected shadowsNamespace(root: string, ctx: any): boolean;
|
|
329
|
+
/** Is `name` something this script binds — and therefore a possible receiver? */
|
|
330
|
+
protected isReceiverName(name: string): boolean;
|
|
331
|
+
/**
|
|
332
|
+
* Rejects a v1–v4 spelling with the name — or the construct — that replaced it.
|
|
333
|
+
*
|
|
334
|
+
* Hooked on identifiers and calls rather than driven off the registry,
|
|
335
|
+
* because a missing registry entry is not by itself an error: `visitId` emits
|
|
336
|
+
* any identifier it does not recognise, since most identifiers are the
|
|
337
|
+
* script's own variables. So `sma` at v5 would just emit `opsv2_sma` and read
|
|
338
|
+
* as `undefined` — an indicator that is silently `na` on a chart that still
|
|
339
|
+
* draws.
|
|
340
|
+
*/
|
|
341
|
+
protected enforceRenamedInV5(name: string, ctx: SourceLocated): void;
|
|
342
|
+
/** Rejects a declaration or reference that names one of v5's reserved words. */
|
|
343
|
+
protected enforceNotReserved(name: string, ctx: SourceLocated): void;
|
|
344
|
+
/**
|
|
345
|
+
* Rejects a v5 feature this engine does not implement, by name.
|
|
346
|
+
*
|
|
347
|
+
* Reported as "not implemented" rather than as a Pine error, because it is
|
|
348
|
+
* not one: TradingView accepts all four. Saying so is the difference between
|
|
349
|
+
* an author knowing to rewrite the script and an author hunting for a typo.
|
|
350
|
+
* The message reaches the script's author, so it names no repository path;
|
|
351
|
+
* the known gaps are in docs/common/VERSIONS.md §7.
|
|
352
|
+
*/
|
|
353
|
+
protected enforceSupported(name: string, ctx: SourceLocated): void;
|
|
354
|
+
visitId(ctx: IdContext): string;
|
|
355
|
+
/**
|
|
356
|
+
* `p.x`, `p.inner.x` → nested `ctx.field` reads, or null if `text` is not a
|
|
357
|
+
* field access at all.
|
|
358
|
+
*
|
|
359
|
+
* A namespace ALWAYS wins. `strategy.long` stays a namespace member even in a
|
|
360
|
+
* script that happens to bind a variable called `strategy`, because Pine
|
|
361
|
+
* would reject that script and reading it the other way would silently give
|
|
362
|
+
* `na` instead.
|
|
363
|
+
*/
|
|
364
|
+
protected emitFieldChain(text: string): string | null;
|
|
365
|
+
/**
|
|
366
|
+
* `p.x := expr` — a field WRITE, and `p.x += expr` with it.
|
|
367
|
+
*
|
|
368
|
+
* Not routed through the inherited `visitVar_assign`: that emits
|
|
369
|
+
* `<name> = ctx.new_var(…)`, and the name it would build here is a
|
|
370
|
+
* `ctx.field(…)` read, which is not assignable. The declared-before-assigned
|
|
371
|
+
* guard is skipped too, deliberately — a field is not a script-scope
|
|
372
|
+
* variable, and `p.x` is declared by `type Point`, not by an `=` statement.
|
|
373
|
+
*/
|
|
374
|
+
visitVar_assign(ctx: Var_assignContext): string;
|
|
375
|
+
/**
|
|
376
|
+
* The tuple form, `[a, b, c] = ta.bb(...)`.
|
|
377
|
+
*
|
|
378
|
+
* Overridden only to get the diagnostic right. The inherited implementation
|
|
379
|
+
* finds the callee and — when the name is absent from this version's registry
|
|
380
|
+
* but present in the runtime union — reports "added in a later version",
|
|
381
|
+
* which is exactly backwards for `[a, b, c] = bb(close, 20, 2)` at v5.
|
|
382
|
+
*/
|
|
383
|
+
visitVar_defs(ctx: Var_defsContext): string;
|
|
384
|
+
/** The name of the first function CALL in a subtree, if there is one. */
|
|
385
|
+
private firstCallee;
|
|
386
|
+
/**
|
|
387
|
+
* v5 renames `study()` to `indicator()`, keeps `strategy()`, and adds
|
|
388
|
+
* `library()` — a script that declares functions for others to import and
|
|
389
|
+
* plots nothing itself.
|
|
390
|
+
*/
|
|
391
|
+
protected detectDirective(node: ParseTree): 'indicator' | 'strategy' | 'library' | undefined;
|
|
392
|
+
/**
|
|
393
|
+
* An `indicator()` script is in indicator mode, so `strategy.*` is fatal —
|
|
394
|
+
* the same rule the base applies to `study()`, under v5's spelling of it.
|
|
395
|
+
*
|
|
396
|
+
* A LIBRARY is not refused. The manual's Libraries page restricts what an
|
|
397
|
+
* exported function may use (no non-const globals, no `input.*()`) and says
|
|
398
|
+
* nothing against `strategy.*`, and published libraries call it —
|
|
399
|
+
* `strategy.position_size`, `strategy.closedtrades`, `strategy.entry` — to
|
|
400
|
+
* be imported by strategies, whose broker they then read.
|
|
401
|
+
*/
|
|
402
|
+
protected enforceStrategyContext(name: string, ctx: SourceLocated): void;
|
|
403
|
+
/**
|
|
404
|
+
* A call to a function's own NAME is recursion only when it can reach this
|
|
405
|
+
* definition. The manual forbids recursion ("cannot contain calls to
|
|
406
|
+
* themselves within their bodies") but allows overloads, and an overload
|
|
407
|
+
* calling its sibling is the documented way to share code:
|
|
408
|
+
*
|
|
409
|
+
* export getHexString(series float r, series float g, series float b, series float t = 0.0) => …
|
|
410
|
+
* export getHexString(series color source) =>
|
|
411
|
+
* getHexString(color.r(source), color.g(source), color.b(source), color.t(source))
|
|
412
|
+
*
|
|
413
|
+
* So a call counts only when its argument count fits this definition and no
|
|
414
|
+
* sibling overload's — `f(int x) => f(x)` beside `f(float x, int y)` still
|
|
415
|
+
* calls itself, and would overflow the stack. Siblings of the same arity are
|
|
416
|
+
* told apart by type, which is not known here, so they are let through. A
|
|
417
|
+
* method `alert(string message, string key)` that calls the built-in
|
|
418
|
+
* `alert(message)` does not call itself either.
|
|
419
|
+
*/
|
|
420
|
+
protected enforceNoRecursion(name: string, body: ParseTree, ctx: SourceLocated): void;
|
|
421
|
+
visitFun_call(ctx: Fun_callContext): string;
|
|
422
|
+
/** The call itself, before any `.method()` chained onto its result. */
|
|
423
|
+
protected emitBaseCall(ctx: Fun_callContext): string;
|
|
424
|
+
/**
|
|
425
|
+
* The LAST link of a chain is what a destructuring unpacks.
|
|
426
|
+
*
|
|
427
|
+
* [sampleMean, highBand, lowBand] =
|
|
428
|
+
* sourceArray.maintainQueue(sourceInput, newSample).calcBB(multiplier, newSample)
|
|
429
|
+
*
|
|
430
|
+
* `maintainQueue` returns the array; `calcBB` returns "a tuple containing the
|
|
431
|
+
* basis, upper band, and lower band respectively". The base reads the call's
|
|
432
|
+
* own `id`, which here is the receiver plus the FIRST method — so the arity
|
|
433
|
+
* check asked `maintainQueue` and was told, correctly, that it returns no
|
|
434
|
+
* tuple.
|
|
435
|
+
*
|
|
436
|
+
* A trailing FIELD suffix is not a callee, so it is skipped: `f().x` unpacks
|
|
437
|
+
* nothing, and naming `x` would produce a message about a function that does
|
|
438
|
+
* not exist.
|
|
439
|
+
*/
|
|
440
|
+
/**
|
|
441
|
+
* v5 widens where a `[a, b] = …` may take its values from:
|
|
442
|
+
*
|
|
443
|
+
* - an `if` or `switch` whose branches end in tuples (manual: "If the
|
|
444
|
+
* final statement is a conditional structure … the function returns that
|
|
445
|
+
* structure's result", and the same holds for a declaration);
|
|
446
|
+
* - a METHOD call — `[lv, t] = this.eventHorizon(close)` — and a library
|
|
447
|
+
* function, whose definitions this script cannot see;
|
|
448
|
+
* - a function ending in a call or a `switch`, whose width is not written;
|
|
449
|
+
* - `ta.vwap(source, anchor, stdev_mult)`, which "will calculate the
|
|
450
|
+
* standard deviation bands … and return a [vwap, upper_band,
|
|
451
|
+
* lower_band] tuple".
|
|
452
|
+
*
|
|
453
|
+
* It still refuses what it can prove wrong: a built-in that returns one
|
|
454
|
+
* value, a width that disagrees with the definition's, and a function every
|
|
455
|
+
* one of whose exits is plainly a single value.
|
|
456
|
+
*/
|
|
457
|
+
protected checkTupleSource(ctx: any, ids: string[]): void;
|
|
458
|
+
/**
|
|
459
|
+
* v5's declaration rule reads the initialiser the way the language does:
|
|
460
|
+
*
|
|
461
|
+
* - `x = arr.last().x` and `float y = verts.get(i).y` read a FIELD called
|
|
462
|
+
* `x`/`y` off a value. A field after a dot is not the variable.
|
|
463
|
+
* - `result = if cond` + a branch that declares its own `result` reads
|
|
464
|
+
* that branch's local, not the one being declared.
|
|
465
|
+
*/
|
|
466
|
+
protected enforceNoSelfReference(name: string, value: ParseTree, ctx: SourceLocated): void;
|
|
467
|
+
/** Is `root` a namespace, user type, enum or library alias — a qualifier, not a variable? */
|
|
468
|
+
protected isQualifierRoot(root: string): boolean;
|
|
469
|
+
/**
|
|
470
|
+
* `(expr).field` and `(expr).method()` — see `atom` in PineV5Parser.g4.
|
|
471
|
+
* The receiver is a value, so it folds through the same `emitChainedMethod`
|
|
472
|
+
* a call's result does.
|
|
473
|
+
*/
|
|
474
|
+
visitAtom(ctx: any): string;
|
|
475
|
+
/** The first call in an expression, the way the base check finds it. */
|
|
476
|
+
private firstCallOf;
|
|
477
|
+
protected destructuringCallee(call: Fun_callContext): string;
|
|
478
|
+
/**
|
|
479
|
+
* `a.f().g()` — the chained method calls hanging off a call's result.
|
|
480
|
+
*
|
|
481
|
+
* Folded here rather than inside each emit path, because every one of them
|
|
482
|
+
* returns a VALUE and the chain applies to the value, whatever produced it:
|
|
483
|
+
* a built-in call, a method call, a UDT constructor, a library function.
|
|
484
|
+
*
|
|
485
|
+
* The receiver of a chained call is an expression, so it cannot go through
|
|
486
|
+
* `emitMethodCall`, which resolves a receiver NAME to a local. Dispatch is
|
|
487
|
+
* otherwise identical, and `Context.builtinMethod` was already receiving a
|
|
488
|
+
* value rather than a name.
|
|
489
|
+
*/
|
|
490
|
+
protected emitCallChain(base: string, ctx: Fun_callContext): string;
|
|
491
|
+
/** One `.method(args)` — or `.field` — applied to an already-emitted receiver. */
|
|
492
|
+
protected emitChainedMethod(receiver: string, ctx: Call_suffixContext, receiverType?: string | null): string;
|
|
493
|
+
/** A chained call to a user method, `overload` or the plain name. */
|
|
494
|
+
private emitUserChainedCall;
|
|
495
|
+
/**
|
|
496
|
+
* A dotted callee whose root is NOT a built-in namespace.
|
|
497
|
+
*
|
|
498
|
+
* Three things can be, and they are checked in this order:
|
|
499
|
+
*
|
|
500
|
+
* Point.new(…) a user type's constructor
|
|
501
|
+
* lib.f(…) a function from an imported library
|
|
502
|
+
* p.area(…) a method call, on a user type or on a built-in collection
|
|
503
|
+
*
|
|
504
|
+
* Returns null when none applies, so the caller falls through to the ordinary
|
|
505
|
+
* namespace path. A real namespace always wins — see `emitFieldChain`.
|
|
506
|
+
*/
|
|
507
|
+
protected emitDottedCall(name: string, ctx: Fun_callContext): string | null;
|
|
508
|
+
/**
|
|
509
|
+
* `label.all.size()` — a method called on a namespace member's VALUE.
|
|
510
|
+
*
|
|
511
|
+
* `id` is greedy, so the whole of `label.all.size` arrives as one dotted
|
|
512
|
+
* name and the ordinary namespace path asks the registry for a function of
|
|
513
|
+
* that name. There is none: `label.all` is an ARRAY, and `size` is a method
|
|
514
|
+
* on it.
|
|
515
|
+
*
|
|
516
|
+
* So the longest registry-known PREFIX is taken as the receiver and whatever
|
|
517
|
+
* follows is applied to it — the same split `emitChainedMethod` performs on a
|
|
518
|
+
* dotted call suffix, arrived at from the other side. Returns null when no
|
|
519
|
+
* prefix resolves, which leaves the existing "has no member" diagnostic to
|
|
520
|
+
* report the name the script actually wrote.
|
|
521
|
+
*/
|
|
522
|
+
protected emitNamespaceValueMethod(name: string, ctx: Fun_callContext): string | null;
|
|
523
|
+
/** `label.all` → `opsv2_label.opsv2_all`, the way visitId prefixes a dotted read. */
|
|
524
|
+
protected namespacedName(dotted: string): string;
|
|
525
|
+
/** `Point.new(…)` and `Point.copy(p)` — the two calls a TYPE answers. */
|
|
526
|
+
protected emitTypeCall(typeName: string, member: string, ctx: Fun_callContext): string | null;
|
|
527
|
+
/**
|
|
528
|
+
* Maps a `Type.new(…)` argument list onto field names.
|
|
529
|
+
*
|
|
530
|
+
* Positional arguments take fields in declaration order; keyword arguments
|
|
531
|
+
* name them. Both are checked, because neither mistake is visible at run
|
|
532
|
+
* time: an unknown field name would just add a property nothing reads, and a
|
|
533
|
+
* ninth positional argument to an eight-field type would be dropped.
|
|
534
|
+
*/
|
|
535
|
+
protected constructorFields(typeName: string, fields: readonly string[], ctx: Fun_callContext): Map<string, string>;
|
|
536
|
+
/**
|
|
537
|
+
* `receiver.name(args)`.
|
|
538
|
+
*
|
|
539
|
+
* A USER method becomes `name(receiver, args)`, which is what it already is —
|
|
540
|
+
* see `visitMethod_def_stmt`. Anything else is a BUILT-IN method, and which
|
|
541
|
+
* namespace owns it depends on what the receiver turns out to be at run time,
|
|
542
|
+
* so that dispatch is deferred to `Context.builtinMethod`.
|
|
543
|
+
*
|
|
544
|
+
* `.copy()` is intercepted first because three different namespaces spell it
|
|
545
|
+
* — `array.copy`, `matrix.copy`, and a UDT's shallow copy — and only the
|
|
546
|
+
* value knows which it is.
|
|
547
|
+
*/
|
|
548
|
+
protected emitMethodCall(receiver: string, method: string, ctx: Fun_callContext): string;
|
|
549
|
+
/**
|
|
550
|
+
* `<value>.method(args)` where the receiver is an already-emitted VALUE of a
|
|
551
|
+
* known type — an enum member — rather than a name.
|
|
552
|
+
*/
|
|
553
|
+
protected emitValueMethodCall(receiver: string, receiverType: string, method: string, ctx: Fun_callContext): string;
|
|
554
|
+
/** Runs `emit` with `name` on the callee stack, so visitKw_arg can see it. */
|
|
555
|
+
private withCallee;
|
|
556
|
+
/**
|
|
557
|
+
* Function OVERLOADING — several definitions of one name.
|
|
558
|
+
*
|
|
559
|
+
* SOURCING. From the v5 release notes:
|
|
560
|
+
*
|
|
561
|
+
* // Two parameters
|
|
562
|
+
* mult(x1, x2) => x1 * x2
|
|
563
|
+
* // Three parameters
|
|
564
|
+
* mult(x1, x2, x3) => x1 * x2 * x3
|
|
565
|
+
* plot(mult(7, 4))
|
|
566
|
+
* plot(mult(7, 4, 2))
|
|
567
|
+
*
|
|
568
|
+
* and, for the harder case, "When overloads have the same number of
|
|
569
|
+
* parameters, all parameters in each overload must be explicitly typified,
|
|
570
|
+
* and their type combinations must be unique".
|
|
571
|
+
*
|
|
572
|
+
* ── What it was doing instead ───────────────────────────────────────────────
|
|
573
|
+
*
|
|
574
|
+
* Both definitions were emitted as `function opsv2_$fn_mult(…)`, so the second
|
|
575
|
+
* simply replaced the first in JavaScript. `mult(7, 4)` then called the
|
|
576
|
+
* three-parameter body with one argument missing and plotted `na` on every
|
|
577
|
+
* bar — no error anywhere, because a redeclared function is legal JavaScript.
|
|
578
|
+
*/
|
|
579
|
+
protected functionOverloads: Map<string, FnOverload[]>;
|
|
580
|
+
/** The arity range and declared parameter types of one definition. */
|
|
581
|
+
protected overloadOf(inner: Fun_def_singlelineContext | Fun_def_multilineContext): FnOverload;
|
|
582
|
+
/**
|
|
583
|
+
* The suffix that separates one overload's emitted name from its siblings.
|
|
584
|
+
*
|
|
585
|
+
* Arity alone where the arities differ, and the declared types as well where
|
|
586
|
+
* they do not — which the manual requires to be unique, so the pair always
|
|
587
|
+
* is. A name with a single definition gets no suffix at all, so nothing about
|
|
588
|
+
* an ordinary script's output changes.
|
|
589
|
+
*/
|
|
590
|
+
protected overloadSuffix(o: FnOverload): string;
|
|
591
|
+
/** The definition currently being emitted, when its name is overloaded. */
|
|
592
|
+
private emittingOverload;
|
|
593
|
+
/** The function or method whose body is being emitted, overloaded or not. */
|
|
594
|
+
private enclosingDefinition;
|
|
595
|
+
private withOverload;
|
|
596
|
+
visitFun_def_singleline(ctx: Fun_def_singlelineContext): string;
|
|
597
|
+
visitFun_def_multiline(ctx: Fun_def_multilineContext): string;
|
|
598
|
+
/**
|
|
599
|
+
* The STATIC type of a call argument, as far as it can be known here.
|
|
600
|
+
*
|
|
601
|
+
* Literals carry their own type, and a variable carries whatever type its
|
|
602
|
+
* declaration wrote down. Anything else is null — unknown — and an overload
|
|
603
|
+
* set that cannot be resolved from what is known says so rather than picking
|
|
604
|
+
* one, because picking wrong calls a body written for another type and
|
|
605
|
+
* returns a plausible wrong number.
|
|
606
|
+
*/
|
|
607
|
+
protected argumentType(expr: ParseTree | null): string | null;
|
|
608
|
+
/** Does an argument of type `arg` fit a parameter declared `param`? */
|
|
609
|
+
private fits;
|
|
610
|
+
/**
|
|
611
|
+
* An enum member may not stand in for a member of a DIFFERENT enum.
|
|
612
|
+
*
|
|
613
|
+
* SOURCING. From the manual's Enums page: "Scripts cannot compare members of
|
|
614
|
+
* different enums or use such members in expressions requiring a specific
|
|
615
|
+
* enum type, EVEN IF THE FIELDS HAVE IDENTICAL NAMES AND TITLES." A parameter
|
|
616
|
+
* declared `OscType selection` is such an expression.
|
|
617
|
+
*
|
|
618
|
+
* TradingView's own `v5-language-enums-05` is written to show the refusal —
|
|
619
|
+
* it declares `OscType` and `OscType2` with identical fields, passes an
|
|
620
|
+
* `OscType2` where an `OscType` belongs, and comments the plot "Raises a
|
|
621
|
+
* compilation error because only members of `OscType` are allowed." This
|
|
622
|
+
* engine compiled it and plotted `na` on every bar instead, which is the
|
|
623
|
+
* worse of the two answers: the script looks like it ran.
|
|
624
|
+
*
|
|
625
|
+
* ⚠ The COMPARISON half of that sentence — `OscType.rsi == OscType2.rsi` —
|
|
626
|
+
* is not refused here. It is the same rule and needs the same type
|
|
627
|
+
* information on both operands of a binary expression; no corpus script
|
|
628
|
+
* witnesses it, so it is left as a stated gap rather than a guess. The
|
|
629
|
+
* runtime keeps the two members distinct regardless, which
|
|
630
|
+
* tests/v5/transpiler/enums.test.ts asserts.
|
|
631
|
+
*/
|
|
632
|
+
private checkEnumArguments;
|
|
633
|
+
protected callFunctionName(pineName: string, ctx: Fun_callContext): string;
|
|
634
|
+
/**
|
|
635
|
+
* v5 keeps the mutable-argument restriction for ONE member of the family.
|
|
636
|
+
*
|
|
637
|
+
* SOURCING. The manual's Other timeframes and data page states it only of
|
|
638
|
+
* `request.security_lower_tf`: its `expression` parameter "accepts most of the
|
|
639
|
+
* same arguments discussed in that section, excluding direct references to
|
|
640
|
+
* collections and mutable variables declared in the script's main scope". It
|
|
641
|
+
* is said of that function as a DIFFERENCE from `request.security()`, which
|
|
642
|
+
* the same page restricts only INSIDE A LOOP — "all parameters defining parts
|
|
643
|
+
* of the requested context can accept arguments that depend on variables from
|
|
644
|
+
* the loop's header or mutable variables that change within the loop's local
|
|
645
|
+
* scope. However, a request.*() call's evaluated expression cannot depend on
|
|
646
|
+
* the values of those variables."
|
|
647
|
+
*
|
|
648
|
+
* TradingView's own `v5-writing-limitations-10` settles it. That script passes
|
|
649
|
+
* the global `var x = 0`, mutated by 300 `if` blocks, straight into
|
|
650
|
+
* `request.security()`, and its comment names the only error as "compilation
|
|
651
|
+
* error since the scope count is now 602". The blanket v3 guard rejected it
|
|
652
|
+
* one rule too early, for a reason v5 does not have.
|
|
653
|
+
*
|
|
654
|
+
* v3 and v4 are untouched: nothing in either archive lifts the rule there.
|
|
655
|
+
*/
|
|
656
|
+
protected securityRestrictsMutableArgs(ctx: Fun_callContext): boolean;
|
|
657
|
+
/**
|
|
658
|
+
* `strategy.long` / `strategy.short` passed as `when` is rejected. They are
|
|
659
|
+
* direction constants, not conditions: v4 let the mistake through, where
|
|
660
|
+
* `strategy.entry("L", strategy.long, when = strategy.long)` read as true.
|
|
661
|
+
* Migration guide to v5 (https://www.tradingview.com/pine-script-docs/migration-guides/to-pine-version-5/):
|
|
662
|
+
* "strategy.long" cannot be passed to `when`; use `direction`.
|
|
663
|
+
*/
|
|
664
|
+
protected enforceWhenIsBool(name: string, ctx: Fun_callContext): void;
|
|
665
|
+
/**
|
|
666
|
+
* `strategy.exit()` with no exit condition is rejected (§4d).
|
|
667
|
+
*
|
|
668
|
+
* Positional form is admitted from the fifth argument on: the parameter order
|
|
669
|
+
* is `(id, from_entry, qty, qty_percent, profit, …)`, so a fifth positional
|
|
670
|
+
* argument IS `profit`. Checking the count rather than the names is the only
|
|
671
|
+
* option there — a positional argument carries no name to check.
|
|
672
|
+
*/
|
|
673
|
+
protected enforceExitCondition(ctx: Fun_callContext): void;
|
|
674
|
+
/**
|
|
675
|
+
* The collection constructors that take a TYPE ARGUMENT.
|
|
676
|
+
*
|
|
677
|
+
* `array.new<float>(0)` is v5's spelling of `array.new_float(0)`, and this
|
|
678
|
+
* engine implements the per-type constructors — so the type argument is not
|
|
679
|
+
* decoration here, it selects the function. `matrix.new<float>` and
|
|
680
|
+
* `map.new<string, int>` have one implementation each, so their type
|
|
681
|
+
* arguments are parsed and discarded.
|
|
682
|
+
*
|
|
683
|
+
* Returns null when this call is not a typed constructor, so the caller can
|
|
684
|
+
* fall through to the ordinary path.
|
|
685
|
+
*/
|
|
686
|
+
protected emitTypedConstructor(name: string, ctx: Fun_callContext): string | null;
|
|
687
|
+
/** Emits `ctx.call("name@L:C", ctx.builtin("name"), …args)` for a built-in. */
|
|
688
|
+
private emitCall;
|
|
689
|
+
/**
|
|
690
|
+
* The RUNTIME's name for a keyword the script spelled the v5 way, or null.
|
|
691
|
+
*
|
|
692
|
+
* Consulted from `visitId` rather than from `visitKw_arg`, which is where it
|
|
693
|
+
* started. `emitSecurity` builds its keyword object itself — it has to, so it
|
|
694
|
+
* can defer the `expression` argument as a thunk — and reads each key by
|
|
695
|
+
* visiting the id directly, so a translation living in `visitKw_arg` was
|
|
696
|
+
* bypassed for exactly the call that needs it most:
|
|
697
|
+
* `request.security(…, timeframe="D")` silently fell back to the chart's
|
|
698
|
+
* timeframe. `visitId` is the one point both paths go through.
|
|
699
|
+
*/
|
|
700
|
+
protected keywordAliasFor(written: string): string | undefined;
|
|
701
|
+
/** v5's `stmt` gained four alternatives; the base knows two. */
|
|
702
|
+
visitStmt(ctx: StmtContext): string;
|
|
703
|
+
/**
|
|
704
|
+
* `type Name` + fields → a FACTORY FUNCTION.
|
|
705
|
+
*
|
|
706
|
+
* type Point
|
|
707
|
+
* float x
|
|
708
|
+
* float y = 0.0
|
|
709
|
+
*
|
|
710
|
+
* becomes
|
|
711
|
+
*
|
|
712
|
+
* function opsv2_$type_Point(_a) {
|
|
713
|
+
* const _o = { __type: "Point" };
|
|
714
|
+
* _o.x = ctx.fieldValue(_a.x !== undefined ? _a.x : (NaN));
|
|
715
|
+
* _o.y = ctx.fieldValue(_a.y !== undefined ? _a.y : (0.0));
|
|
716
|
+
* return _o;
|
|
717
|
+
* }
|
|
718
|
+
*
|
|
719
|
+
* A function DECLARATION rather than an object template, for two reasons.
|
|
720
|
+
* Declarations hoist, so a type may be used above the line that declares it
|
|
721
|
+
* inside any function body. And a default has to be RE-EVALUATED per
|
|
722
|
+
* instance: `type Bag\n array<float> xs = array.new<float>(0)` must give
|
|
723
|
+
* every Bag its own array, and a shared template would give them all one.
|
|
724
|
+
*
|
|
725
|
+
* Fields are stored UNPREFIXED. They live inside an object of their own, so
|
|
726
|
+
* there is nothing in the sandbox for them to collide with, and `p.x` reads
|
|
727
|
+
* better in a debugger than `p.opsv2_x`.
|
|
728
|
+
*/
|
|
729
|
+
visitType_def_stmt(ctx: Type_def_stmtContext): string;
|
|
730
|
+
/**
|
|
731
|
+
* `enum Name` + fields → one frozen object of MEMBER objects.
|
|
732
|
+
*
|
|
733
|
+
* enum OscType
|
|
734
|
+
* rsi = "Relative Strength Index"
|
|
735
|
+
* mfi = "Money Flow Index"
|
|
736
|
+
*
|
|
737
|
+
* becomes
|
|
738
|
+
*
|
|
739
|
+
* const opsv2_$enum_OscType = ctx.declareEnum("OscType", {
|
|
740
|
+
* "rsi": "Relative Strength Index", "mfi": "Money Flow Index",
|
|
741
|
+
* });
|
|
742
|
+
*
|
|
743
|
+
* whose members are the STRINGS `"OscType.rsi"` and `"OscType.mfi"`. See
|
|
744
|
+
* `Context.declareEnum` for why a member is a string rather than an object —
|
|
745
|
+
* in one line, because `x == OscType.rsi` compares a Series to the member,
|
|
746
|
+
* and JavaScript compares two objects by reference while it coerces an object
|
|
747
|
+
* against a primitive.
|
|
748
|
+
*
|
|
749
|
+
* The enum NAME is part of every member, which keeps the rule the manual
|
|
750
|
+
* states: "Scripts cannot compare members of different enums ... even if the
|
|
751
|
+
* fields have identical names and titles."
|
|
752
|
+
*
|
|
753
|
+
* ── The title default ──────────────────────────────────────────────────
|
|
754
|
+
*
|
|
755
|
+
* "If the programmer does not specify a field's title, its title is the
|
|
756
|
+
* 'string' representation of its name."
|
|
757
|
+
*/
|
|
758
|
+
visitEnum_def_stmt(ctx: Enum_def_stmtContext): string;
|
|
759
|
+
/** An enum declaration's fields, in declaration order. */
|
|
760
|
+
protected enumFieldsOf(ctx: Enum_def_stmtContext): EnumField[];
|
|
761
|
+
/** The emitted name of an enum's member table. */
|
|
762
|
+
protected enumFactory(enumName: string): string;
|
|
763
|
+
/** The emitted name of a type's factory. */
|
|
764
|
+
protected typeFactory(typeName: string): string;
|
|
765
|
+
/**
|
|
766
|
+
* `method name(Receiver self, …) => …`.
|
|
767
|
+
*
|
|
768
|
+
* Emitted as an ordinary function, because that is what it is: Pine's
|
|
769
|
+
* `r.area()` and `area(r)` are the same call, and the receiver is simply the
|
|
770
|
+
* first argument. Only the CALL SITES differ, and `visitFun_call` handles
|
|
771
|
+
* those.
|
|
772
|
+
*
|
|
773
|
+
* ── The one limitation ────────────────────────────────────────────────────
|
|
774
|
+
*
|
|
775
|
+
* Pine dispatches on the receiver's declared type, so two methods may share a
|
|
776
|
+
* name if their receivers differ. This engine dispatches on the NAME alone,
|
|
777
|
+
* because it does not track types — so a second method of the same name would
|
|
778
|
+
* emit a second JavaScript function of the same name and silently win. It is
|
|
779
|
+
* rejected instead.
|
|
780
|
+
*/
|
|
781
|
+
visitMethod_def_stmt(ctx: Method_def_stmtContext): string;
|
|
782
|
+
/**
|
|
783
|
+
* A method's receiver type — the declared type of its FIRST parameter.
|
|
784
|
+
*
|
|
785
|
+
* "Since user-defined methods require explicit type specification for their
|
|
786
|
+
* first parameter (the receiver object)", this is always written down, and
|
|
787
|
+
* it is the whole basis of overload resolution. A parameter with no declared
|
|
788
|
+
* type is recorded as `?`, which no overload set can be resolved against and
|
|
789
|
+
* which reads correctly in the diagnostic.
|
|
790
|
+
*/
|
|
791
|
+
protected receiverTypeOf(inner: Fun_def_singlelineContext | Fun_def_multilineContext): string;
|
|
792
|
+
/**
|
|
793
|
+
* The JavaScript name for one method.
|
|
794
|
+
*
|
|
795
|
+
* Unqualified while the name has a single declaration, so nothing about a
|
|
796
|
+
* script without overloads changes; qualified by receiver type as soon as it
|
|
797
|
+
* has more than one, because two JavaScript functions of the same name are
|
|
798
|
+
* one function and the second silently wins.
|
|
799
|
+
*/
|
|
800
|
+
protected methodFunctionName(name: string, receiver: string | null, sig?: FnOverload | null): string;
|
|
801
|
+
/** The signature of the method definition currently being emitted. */
|
|
802
|
+
protected emittingMethodSig: FnOverload | null;
|
|
803
|
+
/** The receiver type of the method definition currently being emitted. */
|
|
804
|
+
protected emittingMethodFor: string | null;
|
|
805
|
+
/**
|
|
806
|
+
* A method's definition takes the qualified name; every other definition
|
|
807
|
+
* keeps its own. Only `visitMethod_def_stmt` sets `emittingMethodFor`, and a
|
|
808
|
+
* definition cannot nest inside another, so the flag cannot leak.
|
|
809
|
+
*/
|
|
810
|
+
protected definitionFunctionName(pineName: string): string;
|
|
811
|
+
private withMethodReceiver;
|
|
812
|
+
/**
|
|
813
|
+
* Which overload of `name` a receiver spelled `expression` resolves to.
|
|
814
|
+
*
|
|
815
|
+
* Returns null when `name` is not overloaded — the caller then takes its
|
|
816
|
+
* ordinary path. Throws when it IS overloaded and the receiver's type cannot
|
|
817
|
+
* be proved, because guessing would dispatch to a body written for another
|
|
818
|
+
* type and return a plausible wrong answer.
|
|
819
|
+
*/
|
|
820
|
+
protected resolveOverload(name: string, receiverType: string | null, ctx: SourceLocated, argCount?: number | null): string | null;
|
|
821
|
+
/**
|
|
822
|
+
* The declared type of `name` where the code being emitted sits: a
|
|
823
|
+
* parameter or typed local of the enclosing function, else a script-scope
|
|
824
|
+
* declaration.
|
|
825
|
+
*/
|
|
826
|
+
protected typeOfName(name: string): string | null;
|
|
827
|
+
/** Script-scope types settled by an initialiser, beyond the scope pass's. */
|
|
828
|
+
protected scriptTypes: Map<string, string>;
|
|
829
|
+
/** The type of `receiver.field1.field2…`, following declared field types. */
|
|
830
|
+
protected typeOfPath(receiver: string, fields: readonly string[]): string | null;
|
|
831
|
+
/**
|
|
832
|
+
* A type stated by a local's initialiser: `P.new(…)`, `lib.P.new(…)`,
|
|
833
|
+
* `array.new<T>(…)` and its `map`/`matrix` siblings, `xs.copy()`. Anything
|
|
834
|
+
* else is unknown rather than guessed.
|
|
835
|
+
*/
|
|
836
|
+
protected typeOfInitialiser(expr: ParseTree | null): string | null;
|
|
837
|
+
/**
|
|
838
|
+
* After the last declaration of an overloaded name, a function under the
|
|
839
|
+
* PLAIN name that picks the overload at run time — by argument count, then
|
|
840
|
+
* by the arguments' own types (Context.pickOverload).
|
|
841
|
+
*
|
|
842
|
+
* Calls the emitter can resolve go straight to their overload and never
|
|
843
|
+
* reach it. It serves the rest: a receiver whose type is not written down
|
|
844
|
+
* where it is used, and a library's export, which is published under the
|
|
845
|
+
* plain name and so was "not defined" whenever the name was overloaded.
|
|
846
|
+
*/
|
|
847
|
+
protected emitOverloadDispatcher(name: string): string;
|
|
848
|
+
/** How many declarations of each overloaded name have been emitted so far. */
|
|
849
|
+
private emittedOverloads;
|
|
850
|
+
/**
|
|
851
|
+
* Every declaration of `name`, functions and methods together: they share
|
|
852
|
+
* one JavaScript name, so they are one overload set. A library declaring
|
|
853
|
+
* `export dot(Vec2 a, Vec2 b, …)` and `export method dot(float[] a, …)`
|
|
854
|
+
* emitted both as the same function, and the second replaced the first.
|
|
855
|
+
*/
|
|
856
|
+
protected overloadTotal(name: string): number;
|
|
857
|
+
/** How many arguments a call passes, positional and keyword. */
|
|
858
|
+
protected argumentCount(args: any | null): number;
|
|
859
|
+
/**
|
|
860
|
+
* A local's declared type — `Point p = …`, or one its initialiser states —
|
|
861
|
+
* recorded for the method calls later in the same function body.
|
|
862
|
+
*/
|
|
863
|
+
visitVar_def(ctx: any): string;
|
|
864
|
+
/**
|
|
865
|
+
* TradingView does not evaluate a plain local whose value nothing reads, so
|
|
866
|
+
* an out-of-bounds `array.get` in one raises no error (probes: pe6 in
|
|
867
|
+
* conformance/common/tradingview-findings.md, v6-dead-local; the same read
|
|
868
|
+
* used errors: pe6b, v6-dead-local-used). Only what the probes showed is
|
|
869
|
+
* skipped: a non-`var` local inside a block, never named elsewhere in that
|
|
870
|
+
* block (a loop body re-runs, so earlier statements count), whose value is
|
|
871
|
+
* not the block's result and whose initialiser is built from nothing but
|
|
872
|
+
* reads and `array.get` / `.get`. Anything else is evaluated as before.
|
|
873
|
+
*/
|
|
874
|
+
protected isDeadLocal(ctx: any): boolean;
|
|
875
|
+
/** Is `name` (or a field path rooted at it) written anywhere under `node`, bar `skip`? */
|
|
876
|
+
protected mentionsName(node: any, name: string, skip: any): boolean;
|
|
877
|
+
/**
|
|
878
|
+
* Is the value of this statement thrown away? A block's last statement is its
|
|
879
|
+
* value, so it is only discarded when the construct holding the block is
|
|
880
|
+
* itself a statement whose own value is discarded. Unknown shapes say no.
|
|
881
|
+
*/
|
|
882
|
+
protected valueDiscarded(content: any): boolean;
|
|
883
|
+
/** Reads, operators and `array.get` / `.get` only: nothing that can have an effect. */
|
|
884
|
+
protected isPureRead(node: any): boolean;
|
|
885
|
+
/** The dispatcher, when `name`'s last declaration has just been emitted. */
|
|
886
|
+
protected afterDefinition(name: string): string;
|
|
887
|
+
/** `name/receiverType` for every method ALREADY EMITTED — the duplicate check above. */
|
|
888
|
+
private definedMethods;
|
|
889
|
+
/**
|
|
890
|
+
* `export` in front of a function, method or type.
|
|
891
|
+
*
|
|
892
|
+
* The declaration is emitted unchanged; the keyword only records that the
|
|
893
|
+
* name is part of a library's public surface. Outside a library it is an
|
|
894
|
+
* error, which is TradingView's rule and is worth enforcing: `export` in an
|
|
895
|
+
* indicator is a script that thinks it is something it is not.
|
|
896
|
+
*/
|
|
897
|
+
visitExport_stmt(ctx: Export_stmtContext): string;
|
|
898
|
+
/** The name a `fun_def_stmt` binds, whichever of its two forms it takes. */
|
|
899
|
+
protected definitionName(def: any): string;
|
|
900
|
+
/**
|
|
901
|
+
* `f(float x, int n = 2)` — v5 parameters carry a type and may carry a
|
|
902
|
+
* default.
|
|
903
|
+
*
|
|
904
|
+
* The TYPE is parsed and discarded: this engine evaluates everything as a
|
|
905
|
+
* series, so an annotation cannot change a result, and rejecting a script for
|
|
906
|
+
* carrying one would be worse than ignoring it. The DEFAULT is not — it maps
|
|
907
|
+
* straight onto a JavaScript default parameter, which has the semantics Pine
|
|
908
|
+
* describes (evaluated per call, only when the argument is absent).
|
|
909
|
+
*/
|
|
910
|
+
/** v5 parameters are a rule of their own, and may carry a default. */
|
|
911
|
+
protected paramNames(ctx: Fun_headContext): string[];
|
|
912
|
+
visitFun_head(ctx: Fun_headContext): string;
|
|
913
|
+
/**
|
|
914
|
+
* `import user/library/version as alias`.
|
|
915
|
+
*
|
|
916
|
+
* The library is compiled INLINE, into an immediately-invoked function whose
|
|
917
|
+
* result is the namespace object the alias refers to:
|
|
918
|
+
*
|
|
919
|
+
* const opsv2_$lib$mylib = (() => {
|
|
920
|
+
* function opsv2_$fn_f(opsv2_x) { … }
|
|
921
|
+
* return { "f": opsv2_$fn_f };
|
|
922
|
+
* })();
|
|
923
|
+
*
|
|
924
|
+
* ── Why inline, and why an IIFE ───────────────────────────────────────────
|
|
925
|
+
*
|
|
926
|
+
* Inline, because there is nowhere else to put it: the emitted script is one
|
|
927
|
+
* JavaScript function body that the runtime re-invokes per bar, and a library
|
|
928
|
+
* has to be inside that scope to reach `ctx`.
|
|
929
|
+
*
|
|
930
|
+
* An IIFE, because a library has its own script scope. Two scripts that both
|
|
931
|
+
* declare `len = 14` at top level are not sharing a variable, and emitted
|
|
932
|
+
* flat they would be — `let opsv2_len` twice in one scope is a redeclaration
|
|
933
|
+
* error, and with `var` it is worse: it silently works and they overwrite
|
|
934
|
+
* each other.
|
|
935
|
+
*
|
|
936
|
+
* The library's body therefore re-runs per bar exactly as the importing
|
|
937
|
+
* script's does, which is what Pine specifies: a library's top-level code is
|
|
938
|
+
* script-scope code.
|
|
939
|
+
*/
|
|
940
|
+
visitImport_stmt(ctx: Import_stmtContext): string;
|
|
941
|
+
/**
|
|
942
|
+
* `alias.EnumName` → its field names, for every enum an import exports.
|
|
943
|
+
*
|
|
944
|
+
* Keyed by the ALIAS rather than the path, because that is what a read
|
|
945
|
+
* spells: two aliases of one library are two namespaces to the script.
|
|
946
|
+
*/
|
|
947
|
+
protected libraryEnums: Map<string, string[]>;
|
|
948
|
+
/** The emitted name of an imported library's namespace object. */
|
|
949
|
+
protected libraryNamespace(alias: string): string;
|
|
950
|
+
/**
|
|
951
|
+
* Parses a library's source, and says which version it was parsed as.
|
|
952
|
+
*
|
|
953
|
+
* A seam rather than an inline `parseV5`: a v6 script can import a library
|
|
954
|
+
* written in either version, and each must be read by its OWN grammar — a v5
|
|
955
|
+
* library binding a variable called `once` is legal and must stay so. v5
|
|
956
|
+
* itself only ever had v5 libraries, so this reads everything as v5, exactly
|
|
957
|
+
* as before.
|
|
958
|
+
*/
|
|
959
|
+
protected parseLibrary(source: string): {
|
|
960
|
+
parsed: ReturnType<typeof parseV5>;
|
|
961
|
+
version: PineVersion;
|
|
962
|
+
};
|
|
963
|
+
/** The visitor a library parsed as `version` is compiled with. See parseLibrary. */
|
|
964
|
+
protected createLibraryVisitor(_version: PineVersion, options: V5Options): V5ToJsVisitor;
|
|
965
|
+
/**
|
|
966
|
+
* The JavaScript an exported name is reached by, on the library's own
|
|
967
|
+
* visitor — a type's factory, an enum's, or a function. A seam because v6
|
|
968
|
+
* adds a fourth kind of export, the constant.
|
|
969
|
+
*/
|
|
970
|
+
protected exportedMemberJs(name: string, types: ReadonlyMap<string, unknown>, enums: ReadonlyMap<string, unknown>): string;
|
|
971
|
+
/**
|
|
972
|
+
* Compiles a library's source with a visitor of its own.
|
|
973
|
+
*
|
|
974
|
+
* A fresh visitor, not this one: a library has its own types, its own
|
|
975
|
+
* methods, its own scope analysis and its own set of exported names, and
|
|
976
|
+
* threading two scripts' worth of that through one instance is how a library
|
|
977
|
+
* would end up able to see the importing script's declarations.
|
|
978
|
+
*
|
|
979
|
+
* Nested imports are allowed — the child gets the same library table. An
|
|
980
|
+
* import back into a library already being compiled (itself, or a cycle
|
|
981
|
+
* through others) is refused by `libraryChain`, and the depth is bounded so a
|
|
982
|
+
* long chain of distinct libraries cannot exhaust the stack either.
|
|
983
|
+
*/
|
|
984
|
+
protected compileLibrary(path: string, alias: string, source: string, ctx: SourceLocated): {
|
|
985
|
+
js: string;
|
|
986
|
+
exports: Set<string>;
|
|
987
|
+
types: Map<string, string[]>;
|
|
988
|
+
methods: Set<string>;
|
|
989
|
+
members: string[];
|
|
990
|
+
enums: Map<string, string[]>;
|
|
991
|
+
};
|
|
992
|
+
/**
|
|
993
|
+
* `alias.f(args)` — a call into an imported library.
|
|
994
|
+
*
|
|
995
|
+
* Only EXPORTED names resolve. A library's private helpers are inside the
|
|
996
|
+
* IIFE and unreachable from here, so an unexported name would emit a property
|
|
997
|
+
* read that is `undefined` at run time; naming it at compile time is the
|
|
998
|
+
* whole point of the export list.
|
|
999
|
+
*/
|
|
1000
|
+
protected emitLibraryCall(alias: string, path: string, member: string, ctx: Fun_callContext): string;
|
|
1001
|
+
/** `alias.Type.new(…)` and `alias.Type.copy(obj)` — a library's type. */
|
|
1002
|
+
protected emitLibraryTypeCall(alias: string, path: string, typeName: string, member: string, ctx: Fun_callContext): string;
|
|
1003
|
+
/**
|
|
1004
|
+
* v5's `global_stmt_content` / `local_stmt_content` gained `while_expr`, and
|
|
1005
|
+
* the global one gained `switch_expr` directly rather than through the
|
|
1006
|
+
* `arith_expr` wrapper. The statement dispatcher has to route both;
|
|
1007
|
+
* everything else falls through to the inherited implementation.
|
|
1008
|
+
*/
|
|
1009
|
+
protected visitContent(ctx: any): string;
|
|
1010
|
+
/** `arith_expr` gained `switch_expr` and `for_in_expr`; the base knows three. */
|
|
1011
|
+
visitArith_expr(ctx: any): string;
|
|
1012
|
+
/**
|
|
1013
|
+
* `for item in xs` and `for [index, item] in xs`.
|
|
1014
|
+
*
|
|
1015
|
+
* Emitted as a real JavaScript `for … of` over `Context.forIn`, for the same
|
|
1016
|
+
* reason `while` is emitted as a real `while`: `break` and `continue` cannot
|
|
1017
|
+
* cross a function boundary, and Pine allows both here.
|
|
1018
|
+
*
|
|
1019
|
+
* The whole thing still sits inside an IIFE, because — unlike `while` — a
|
|
1020
|
+
* `for … in` is an EXPRESSION. The manual's syntax block opens with
|
|
1021
|
+
* `[variables = | :=]`, and its body ends with a `return_expression`, so the
|
|
1022
|
+
* loop has a value: the last one its body produced. `captureLoopResult`
|
|
1023
|
+
* rewrites the block's trailing `return` into an assignment to `_res`, which
|
|
1024
|
+
* is exactly what `visitFor_expr` does for the numeric form — the two loops
|
|
1025
|
+
* differ in how they step, not in what they evaluate to.
|
|
1026
|
+
*
|
|
1027
|
+
* ── Why the pair comes from the runtime rather than the emitter ───────────
|
|
1028
|
+
*
|
|
1029
|
+
* `item` means something different per collection — an element of an array, a
|
|
1030
|
+
* ROW of a matrix, the VALUE half of a map entry — and the emitter cannot
|
|
1031
|
+
* know which it has: `xs` is an expression, and this engine does not track
|
|
1032
|
+
* element types. `Context.forIn` decides at runtime, where the value is,
|
|
1033
|
+
* and hands back a uniform `[key, value]`. It also owns the two size rules,
|
|
1034
|
+
* which differ between maps and the rest.
|
|
1035
|
+
*/
|
|
1036
|
+
visitFor_in_expr(ctx: For_in_exprContext): string;
|
|
1037
|
+
/**
|
|
1038
|
+
* `while cond` + block. A STATEMENT: it evaluates to nothing.
|
|
1039
|
+
*
|
|
1040
|
+
* Emitted as a real JavaScript `while` rather than an IIFE, so `break` and
|
|
1041
|
+
* `continue` inside it are legal — the same reason `visitIf_expr` drops the
|
|
1042
|
+
* IIFE for a statement-`if` inside a loop. `loopDepth` is raised for exactly
|
|
1043
|
+
* that: it is what makes a nested statement-`if` emit as a plain `if`.
|
|
1044
|
+
*
|
|
1045
|
+
* The trailing return that a block normally ends with is SUPPRESSED, because
|
|
1046
|
+
* there is no function boundary here to contain it — a `return` would leave
|
|
1047
|
+
* the enclosing user function, or be a syntax error at script scope.
|
|
1048
|
+
*/
|
|
1049
|
+
visitWhile_expr(ctx: While_exprContext): string;
|
|
1050
|
+
/**
|
|
1051
|
+
* `switch` — an EXPRESSION, in both of its forms.
|
|
1052
|
+
*
|
|
1053
|
+
* switch expr each label is compared to `expr`
|
|
1054
|
+
* 1 => "one"
|
|
1055
|
+
* => "other" a label-less arm is the default
|
|
1056
|
+
*
|
|
1057
|
+
* switch each label is a condition in its own right
|
|
1058
|
+
* x > 5 => "big"
|
|
1059
|
+
*
|
|
1060
|
+
* Emitted as an IIFE, because the arms have to be able to evaluate to
|
|
1061
|
+
* something and a `return` needs a function to belong to. The subject is
|
|
1062
|
+
* bound to a temporary first so it is evaluated ONCE — Pine evaluates it once,
|
|
1063
|
+
* and re-evaluating a `ta.*` call per arm would advance its history several
|
|
1064
|
+
* times a bar and quietly desynchronise it.
|
|
1065
|
+
*/
|
|
1066
|
+
/**
|
|
1067
|
+
* The loop depth at each enclosing `switch`. A `switch` is emitted as an
|
|
1068
|
+
* arrow function, so a `break` in one of its arms that belongs to a loop
|
|
1069
|
+
* OUTSIDE it cannot be a JavaScript `break`: Pine allows
|
|
1070
|
+
*
|
|
1071
|
+
* for i = 0 to n
|
|
1072
|
+
* switch kind
|
|
1073
|
+
* 1 =>
|
|
1074
|
+
* if found
|
|
1075
|
+
* break
|
|
1076
|
+
*
|
|
1077
|
+
* and the emitted `break` was an "Illegal break statement". It is thrown as
|
|
1078
|
+
* a loop signal instead, which that loop's body catches (captureLoopResult).
|
|
1079
|
+
*/
|
|
1080
|
+
protected switchLoopFloors: number[];
|
|
1081
|
+
visitLoop_break(ctx: any): string;
|
|
1082
|
+
visitLoop_continue(ctx: any): string;
|
|
1083
|
+
visitSwitch_expr(ctx: Switch_exprContext): string;
|
|
1084
|
+
/**
|
|
1085
|
+
* What a switch evaluates to when no arm matches. `na` at v5; a seam for the
|
|
1086
|
+
* same reason as `ifFallthrough` in the v1 base.
|
|
1087
|
+
*/
|
|
1088
|
+
protected switchFallthrough(_ctx: Switch_exprContext): string;
|
|
1089
|
+
/**
|
|
1090
|
+
* One arm's body, as a JavaScript block that RETURNS the arm's value.
|
|
1091
|
+
*
|
|
1092
|
+
* An indented block already ends in a `return` — that is what
|
|
1093
|
+
* `visitLocal_stmts_list` does with a block's last statement. A single-line
|
|
1094
|
+
* body does not, and cannot simply be wrapped in one either: `1 => x = 2`
|
|
1095
|
+
* emits a declaration, and `return let x = …` is a syntax error. A trailing
|
|
1096
|
+
* binding evaluates to the bound name, which is the same rule a function body
|
|
1097
|
+
* follows — so this is exactly `singleLineBody`.
|
|
1098
|
+
*/
|
|
1099
|
+
protected caseBody(arm: Switch_caseContext): string;
|
|
1100
|
+
}
|
|
1101
|
+
export {};
|