@alphacharts/script 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 +21 -0
- package/README.md +161 -2
- package/dist/builtins-BXxWK58s.js +812 -0
- package/dist/builtins-BXxWK58s.js.map +1 -0
- package/dist/editor.js +211 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.js +342 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox-p2HPP8yl.js +2416 -0
- package/dist/sandbox-p2HPP8yl.js.map +1 -0
- package/dist/sandbox-worker.js +8 -0
- package/dist/sandbox-worker.js.map +1 -0
- package/dist/types/ast.d.ts +156 -0
- package/dist/types/builtins.d.ts +67 -0
- package/dist/types/check.d.ts +78 -0
- package/dist/types/codegen.d.ts +14 -0
- package/dist/types/compile.d.ts +52 -0
- package/dist/types/diag.d.ts +45 -0
- package/dist/types/editor/index.d.ts +23 -0
- package/dist/types/feeds.d.ts +33 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/lexer.d.ts +14 -0
- package/dist/types/limits.d.ts +25 -0
- package/dist/types/parser.d.ts +3 -0
- package/dist/types/run.d.ts +69 -0
- package/dist/types/runner.d.ts +54 -0
- package/dist/types/sandbox-worker.d.ts +1 -0
- package/dist/types/sandbox.d.ts +78 -0
- package/dist/types/ui-adapter.d.ts +21 -0
- package/package.json +75 -3
|
@@ -0,0 +1,2416 @@
|
|
|
1
|
+
import { a as e, d as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./builtins-BXxWK58s.js";
|
|
2
|
+
import { SOURCE_NAMES as l, StudyEngine as u, describeJoin as d, resolveParams as f } from "@alphacharts/indicators";
|
|
3
|
+
//#region src/diag.ts
|
|
4
|
+
var p = class {
|
|
5
|
+
text;
|
|
6
|
+
starts = [0];
|
|
7
|
+
constructor(e) {
|
|
8
|
+
this.text = e;
|
|
9
|
+
for (let t = 0; t < e.length; t++) e.charCodeAt(t) === 10 && this.starts.push(t + 1);
|
|
10
|
+
}
|
|
11
|
+
at(e) {
|
|
12
|
+
let t = 0, n = this.starts.length - 1;
|
|
13
|
+
for (; t < n;) {
|
|
14
|
+
let r = t + n + 1 >>> 1;
|
|
15
|
+
this.starts[r] <= e ? t = r : n = r - 1;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
line: t + 1,
|
|
19
|
+
column: e - this.starts[t] + 1
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}, m = class {
|
|
23
|
+
max;
|
|
24
|
+
list = [];
|
|
25
|
+
map;
|
|
26
|
+
errors = 0;
|
|
27
|
+
constructor(e, t = 50) {
|
|
28
|
+
this.max = t, this.map = new p(e);
|
|
29
|
+
}
|
|
30
|
+
get errorCount() {
|
|
31
|
+
return this.errors;
|
|
32
|
+
}
|
|
33
|
+
error(e, t) {
|
|
34
|
+
this.add("error", e, t);
|
|
35
|
+
}
|
|
36
|
+
warn(e, t) {
|
|
37
|
+
this.add("warning", e, t);
|
|
38
|
+
}
|
|
39
|
+
add(e, t, n) {
|
|
40
|
+
if (this.errors > this.max) return;
|
|
41
|
+
e === "error" && (this.errors++, this.errors > this.max && (t = `too many errors; stopped after ${this.max}`));
|
|
42
|
+
let r = Math.max(n.to, n.from), i = this.map.at(n.from), a = this.map.at(r);
|
|
43
|
+
this.list.push({
|
|
44
|
+
severity: e,
|
|
45
|
+
message: t,
|
|
46
|
+
line: i.line,
|
|
47
|
+
column: i.column,
|
|
48
|
+
endLine: a.line,
|
|
49
|
+
endColumn: a.column,
|
|
50
|
+
from: n.from,
|
|
51
|
+
to: r
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}, h = class extends Error {
|
|
55
|
+
span;
|
|
56
|
+
kind;
|
|
57
|
+
constructor(e, t = null, n = "build") {
|
|
58
|
+
super(e), this.span = t, this.kind = n, this.name = "ScriptError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function g(e) {
|
|
62
|
+
return `${e.line}:${e.column}: ${e.severity}: ${e.message}`;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/parser.ts
|
|
66
|
+
var _ = /* @__PURE__ */ new Set([
|
|
67
|
+
"int",
|
|
68
|
+
"float",
|
|
69
|
+
"bool",
|
|
70
|
+
"string",
|
|
71
|
+
"color"
|
|
72
|
+
]), v = /* @__PURE__ */ new Set([
|
|
73
|
+
":=",
|
|
74
|
+
"+=",
|
|
75
|
+
"-=",
|
|
76
|
+
"*=",
|
|
77
|
+
"/=",
|
|
78
|
+
"%="
|
|
79
|
+
]), y = 100, b = class extends Error {}, x = class {
|
|
80
|
+
t;
|
|
81
|
+
rep;
|
|
82
|
+
p = 0;
|
|
83
|
+
depth = 0;
|
|
84
|
+
constructor(e, t) {
|
|
85
|
+
this.t = e, this.rep = t;
|
|
86
|
+
}
|
|
87
|
+
get tok() {
|
|
88
|
+
return this.t[this.p];
|
|
89
|
+
}
|
|
90
|
+
kind() {
|
|
91
|
+
return this.t[this.p].kind;
|
|
92
|
+
}
|
|
93
|
+
peek(e = 1) {
|
|
94
|
+
return this.t[Math.min(this.p + e, this.t.length - 1)];
|
|
95
|
+
}
|
|
96
|
+
next() {
|
|
97
|
+
let e = this.tok;
|
|
98
|
+
return e.kind !== "eof" && this.p++, e;
|
|
99
|
+
}
|
|
100
|
+
isOp(e, t = this.tok) {
|
|
101
|
+
return t.kind === "op" && t.text === e;
|
|
102
|
+
}
|
|
103
|
+
isKw(e, t = this.tok) {
|
|
104
|
+
return t.kind === "kw" && t.text === e;
|
|
105
|
+
}
|
|
106
|
+
fail(e, t = this.tok) {
|
|
107
|
+
throw this.rep.error(e, t.from === t.to && this.tok.kind === "newline" ? {
|
|
108
|
+
from: t.from,
|
|
109
|
+
to: t.from
|
|
110
|
+
} : t), new b();
|
|
111
|
+
}
|
|
112
|
+
expectOp(e, t = `"${e}"`) {
|
|
113
|
+
return this.isOp(e) || this.fail(`expected ${t}, found ${this.describe()}`), this.next();
|
|
114
|
+
}
|
|
115
|
+
describe(e = this.tok) {
|
|
116
|
+
switch (e.kind) {
|
|
117
|
+
case "eof": return "end of input";
|
|
118
|
+
case "newline": return "end of line";
|
|
119
|
+
case "indent": return "an unexpected indent";
|
|
120
|
+
case "dedent": return "end of block";
|
|
121
|
+
case "str": return "a string";
|
|
122
|
+
case "num": return `"${e.text}"`;
|
|
123
|
+
default: return `"${e.text}"`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
program() {
|
|
127
|
+
let e = [];
|
|
128
|
+
for (; this.tok.kind !== "eof";) this.tok.kind === "newline" ? this.next() : this.kind() === "indent" ? (this.rep.error("unexpected indent", this.tok), this.skipBlock()) : this.kind() === "dedent" ? this.next() : this.statementRecovering(e);
|
|
129
|
+
return { body: e };
|
|
130
|
+
}
|
|
131
|
+
statementRecovering(e) {
|
|
132
|
+
try {
|
|
133
|
+
e.push(this.statement());
|
|
134
|
+
} catch (e) {
|
|
135
|
+
if (!(e instanceof b)) throw e;
|
|
136
|
+
this.sync();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
sync() {
|
|
140
|
+
for (; this.tok.kind !== "newline" && this.tok.kind !== "eof" && this.tok.kind !== "dedent";) this.next();
|
|
141
|
+
this.tok.kind === "newline" && this.next(), this.kind() === "indent" && this.skipBlock();
|
|
142
|
+
}
|
|
143
|
+
skipBlock() {
|
|
144
|
+
let e = 0;
|
|
145
|
+
do
|
|
146
|
+
this.kind() === "indent" ? e++ : this.kind() === "dedent" && e--, this.next();
|
|
147
|
+
while (e > 0 && this.tok.kind !== "eof");
|
|
148
|
+
}
|
|
149
|
+
endStatement() {
|
|
150
|
+
this.tok.kind === "newline" ? this.next() : this.tok.kind !== "eof" && this.tok.kind !== "dedent" && this.fail(`unexpected ${this.describe()}; expected end of line`);
|
|
151
|
+
}
|
|
152
|
+
block() {
|
|
153
|
+
this.tok.kind !== "newline" && this.fail(`expected end of line before the indented block, found ${this.describe()}`), this.next(), this.kind() !== "indent" && this.fail("expected an indented block"), this.next();
|
|
154
|
+
let e = [];
|
|
155
|
+
for (; this.kind() !== "dedent" && this.kind() !== "eof";) this.tok.kind === "newline" ? this.next() : this.kind() === "indent" ? (this.rep.error("unexpected indent", this.tok), this.skipBlock()) : this.statementRecovering(e);
|
|
156
|
+
return this.kind() === "dedent" && this.next(), e.length === 0 && this.rep.error("a block needs at least one statement", this.t[Math.max(0, this.p - 1)]), e;
|
|
157
|
+
}
|
|
158
|
+
statement() {
|
|
159
|
+
let e = this.tok;
|
|
160
|
+
if (e.kind === "ident" && e.text === "_reserved") throw new b();
|
|
161
|
+
if (this.isKw("if")) return this.ifStmt();
|
|
162
|
+
if (this.isKw("for")) return this.forStmt();
|
|
163
|
+
if (this.isKw("var")) return this.next(), this.declaration(e.from, !0);
|
|
164
|
+
if (e.kind === "ident") {
|
|
165
|
+
let t = this.peek();
|
|
166
|
+
if (_.has(e.text) && t.kind === "ident" && this.isOp("=", this.peek(2)) || this.isOp("=", t)) return this.declaration(e.from, !1);
|
|
167
|
+
if (t.kind === "op" && v.has(t.text)) {
|
|
168
|
+
this.next();
|
|
169
|
+
let t = this.next().text, n = this.expr(), r = this.tok.from;
|
|
170
|
+
return this.endStatement(), {
|
|
171
|
+
k: "assign",
|
|
172
|
+
name: e.text,
|
|
173
|
+
nameSpan: e,
|
|
174
|
+
op: t,
|
|
175
|
+
value: n,
|
|
176
|
+
from: e.from,
|
|
177
|
+
to: Math.max(n.to, r)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (this.isOp("[")) {
|
|
182
|
+
let t = [];
|
|
183
|
+
for (this.next();;) {
|
|
184
|
+
this.tok.kind !== "ident" && this.fail(`expected a name, found ${this.describe()}`);
|
|
185
|
+
let e = this.next();
|
|
186
|
+
if (t.push({
|
|
187
|
+
name: e.text,
|
|
188
|
+
span: e
|
|
189
|
+
}), this.isOp(",")) this.next();
|
|
190
|
+
else break;
|
|
191
|
+
}
|
|
192
|
+
this.expectOp("]"), this.expectOp("=", "\"=\" (a tuple of names must be assigned)");
|
|
193
|
+
let n = this.expr();
|
|
194
|
+
return this.endStatement(), {
|
|
195
|
+
k: "tuple",
|
|
196
|
+
names: t,
|
|
197
|
+
init: n,
|
|
198
|
+
from: e.from,
|
|
199
|
+
to: n.to
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
let t = this.expr();
|
|
203
|
+
return t.k !== "call" && this.fail("this expression has no effect; a statement is a declaration, an assignment, if, for or a call", t), this.endStatement(), {
|
|
204
|
+
k: "expr",
|
|
205
|
+
expr: t,
|
|
206
|
+
from: t.from,
|
|
207
|
+
to: t.to
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
declaration(e, t) {
|
|
211
|
+
let n = null;
|
|
212
|
+
this.tok.kind === "ident" && _.has(this.tok.text) && this.peek().kind === "ident" && (n = this.next().text), this.tok.kind !== "ident" && this.fail(`expected a variable name, found ${this.describe()}`);
|
|
213
|
+
let r = this.next();
|
|
214
|
+
this.expectOp("=");
|
|
215
|
+
let i = this.expr();
|
|
216
|
+
return this.endStatement(), {
|
|
217
|
+
k: "decl",
|
|
218
|
+
isVar: t,
|
|
219
|
+
type: n,
|
|
220
|
+
name: r.text,
|
|
221
|
+
nameSpan: r,
|
|
222
|
+
init: i,
|
|
223
|
+
from: e,
|
|
224
|
+
to: i.to
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
ifStmt() {
|
|
228
|
+
let e = this.next(), t = this.expr(), n = this.block(), r = null;
|
|
229
|
+
return this.isKw("else") && (this.next(), r = this.isKw("if") ? [this.ifStmt()] : this.block()), {
|
|
230
|
+
k: "if",
|
|
231
|
+
cond: t,
|
|
232
|
+
then: n,
|
|
233
|
+
else: r,
|
|
234
|
+
from: e.from,
|
|
235
|
+
to: this.t[this.p - 1].to
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
forStmt() {
|
|
239
|
+
let e = this.next();
|
|
240
|
+
this.tok.kind !== "ident" && this.fail(`expected a loop variable name, found ${this.describe()}`);
|
|
241
|
+
let t = this.next();
|
|
242
|
+
this.expectOp("=");
|
|
243
|
+
let n = this.expr();
|
|
244
|
+
this.isKw("to") || this.fail(`expected "to", found ${this.describe()}`), this.next();
|
|
245
|
+
let r = this.expr(), i = null;
|
|
246
|
+
this.isKw("by") && (this.next(), i = this.expr());
|
|
247
|
+
let a = this.block();
|
|
248
|
+
return {
|
|
249
|
+
k: "for",
|
|
250
|
+
name: t.text,
|
|
251
|
+
nameSpan: t,
|
|
252
|
+
lo: n,
|
|
253
|
+
hi: r,
|
|
254
|
+
by: i,
|
|
255
|
+
body: a,
|
|
256
|
+
from: e.from,
|
|
257
|
+
to: this.t[this.p - 1].to
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
expr() {
|
|
261
|
+
++this.depth > y && this.fail("expression is nested too deeply");
|
|
262
|
+
try {
|
|
263
|
+
return this.ternary();
|
|
264
|
+
} finally {
|
|
265
|
+
this.depth--;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
ternary() {
|
|
269
|
+
let e = this.or();
|
|
270
|
+
if (!this.isOp("?")) return e;
|
|
271
|
+
this.next();
|
|
272
|
+
let t = this.expr();
|
|
273
|
+
this.expectOp(":", "\":\" of the ternary");
|
|
274
|
+
let n = this.expr();
|
|
275
|
+
return {
|
|
276
|
+
k: "tern",
|
|
277
|
+
c: e,
|
|
278
|
+
a: t,
|
|
279
|
+
b: n,
|
|
280
|
+
from: e.from,
|
|
281
|
+
to: n.to
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
or() {
|
|
285
|
+
let e = this.and();
|
|
286
|
+
for (; this.isKw("or");) {
|
|
287
|
+
this.next();
|
|
288
|
+
let t = this.and();
|
|
289
|
+
e = {
|
|
290
|
+
k: "bin",
|
|
291
|
+
op: "or",
|
|
292
|
+
a: e,
|
|
293
|
+
b: t,
|
|
294
|
+
from: e.from,
|
|
295
|
+
to: t.to
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return e;
|
|
299
|
+
}
|
|
300
|
+
and() {
|
|
301
|
+
let e = this.not();
|
|
302
|
+
for (; this.isKw("and");) {
|
|
303
|
+
this.next();
|
|
304
|
+
let t = this.not();
|
|
305
|
+
e = {
|
|
306
|
+
k: "bin",
|
|
307
|
+
op: "and",
|
|
308
|
+
a: e,
|
|
309
|
+
b: t,
|
|
310
|
+
from: e.from,
|
|
311
|
+
to: t.to
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return e;
|
|
315
|
+
}
|
|
316
|
+
not() {
|
|
317
|
+
if (this.isKw("not")) {
|
|
318
|
+
let e = this.next();
|
|
319
|
+
++this.depth > y && this.fail("expression is nested too deeply");
|
|
320
|
+
try {
|
|
321
|
+
let t = this.not();
|
|
322
|
+
return {
|
|
323
|
+
k: "un",
|
|
324
|
+
op: "not",
|
|
325
|
+
x: t,
|
|
326
|
+
from: e.from,
|
|
327
|
+
to: t.to
|
|
328
|
+
};
|
|
329
|
+
} finally {
|
|
330
|
+
this.depth--;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return this.cmp();
|
|
334
|
+
}
|
|
335
|
+
cmp() {
|
|
336
|
+
let e = this.add(), t = this.tok;
|
|
337
|
+
if (t.kind === "op" && (t.text === "==" || t.text === "!=" || t.text === "<" || t.text === "<=" || t.text === ">" || t.text === ">=")) {
|
|
338
|
+
this.next();
|
|
339
|
+
let n = this.add(), r = {
|
|
340
|
+
k: "bin",
|
|
341
|
+
op: t.text,
|
|
342
|
+
a: e,
|
|
343
|
+
b: n,
|
|
344
|
+
from: e.from,
|
|
345
|
+
to: n.to
|
|
346
|
+
}, i = this.tok;
|
|
347
|
+
return i.kind === "op" && [
|
|
348
|
+
"==",
|
|
349
|
+
"!=",
|
|
350
|
+
"<",
|
|
351
|
+
"<=",
|
|
352
|
+
">",
|
|
353
|
+
">="
|
|
354
|
+
].includes(i.text) && this.fail("comparisons do not chain; combine them with 'and'", i), r;
|
|
355
|
+
}
|
|
356
|
+
return e;
|
|
357
|
+
}
|
|
358
|
+
add() {
|
|
359
|
+
let e = this.mul();
|
|
360
|
+
for (; this.isOp("+") || this.isOp("-");) {
|
|
361
|
+
let t = this.next().text, n = this.mul();
|
|
362
|
+
e = {
|
|
363
|
+
k: "bin",
|
|
364
|
+
op: t,
|
|
365
|
+
a: e,
|
|
366
|
+
b: n,
|
|
367
|
+
from: e.from,
|
|
368
|
+
to: n.to
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return e;
|
|
372
|
+
}
|
|
373
|
+
mul() {
|
|
374
|
+
let e = this.unary();
|
|
375
|
+
for (; this.isOp("*") || this.isOp("/") || this.isOp("%");) {
|
|
376
|
+
let t = this.next().text, n = this.unary();
|
|
377
|
+
e = {
|
|
378
|
+
k: "bin",
|
|
379
|
+
op: t,
|
|
380
|
+
a: e,
|
|
381
|
+
b: n,
|
|
382
|
+
from: e.from,
|
|
383
|
+
to: n.to
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return e;
|
|
387
|
+
}
|
|
388
|
+
unary() {
|
|
389
|
+
if (this.isOp("-") || this.isOp("+")) {
|
|
390
|
+
let e = this.next();
|
|
391
|
+
++this.depth > y && this.fail("expression is nested too deeply");
|
|
392
|
+
try {
|
|
393
|
+
let t = this.unary();
|
|
394
|
+
return {
|
|
395
|
+
k: "un",
|
|
396
|
+
op: e.text,
|
|
397
|
+
x: t,
|
|
398
|
+
from: e.from,
|
|
399
|
+
to: t.to
|
|
400
|
+
};
|
|
401
|
+
} finally {
|
|
402
|
+
this.depth--;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return this.postfix();
|
|
406
|
+
}
|
|
407
|
+
postfix() {
|
|
408
|
+
let e = this.primary();
|
|
409
|
+
for (;;) if (this.isOp("(")) {
|
|
410
|
+
this.next();
|
|
411
|
+
let t = this.args(), n = this.expectOp(")", "\")\" to close the call");
|
|
412
|
+
e = {
|
|
413
|
+
k: "call",
|
|
414
|
+
callee: e,
|
|
415
|
+
args: t,
|
|
416
|
+
from: e.from,
|
|
417
|
+
to: n.to
|
|
418
|
+
};
|
|
419
|
+
} else if (this.isOp("[")) {
|
|
420
|
+
this.next();
|
|
421
|
+
let t = this.expr(), n = this.expectOp("]", "\"]\" to close the index");
|
|
422
|
+
e = {
|
|
423
|
+
k: "idx",
|
|
424
|
+
x: e,
|
|
425
|
+
i: t,
|
|
426
|
+
from: e.from,
|
|
427
|
+
to: n.to
|
|
428
|
+
};
|
|
429
|
+
} else if (this.isOp(".")) {
|
|
430
|
+
this.next(), this.tok.kind !== "ident" && this.fail(`expected a name after ".", found ${this.describe()}`);
|
|
431
|
+
let t = this.next();
|
|
432
|
+
e = {
|
|
433
|
+
k: "mem",
|
|
434
|
+
obj: e,
|
|
435
|
+
name: t.text,
|
|
436
|
+
nameSpan: t,
|
|
437
|
+
from: e.from,
|
|
438
|
+
to: t.to
|
|
439
|
+
};
|
|
440
|
+
} else return e;
|
|
441
|
+
}
|
|
442
|
+
args() {
|
|
443
|
+
let e = [];
|
|
444
|
+
if (this.isOp(")")) return e;
|
|
445
|
+
for (;;) {
|
|
446
|
+
if (this.tok.kind === "ident" && this.isOp("=", this.peek())) {
|
|
447
|
+
let t = this.next();
|
|
448
|
+
this.next(), e.push({
|
|
449
|
+
name: t.text,
|
|
450
|
+
nameSpan: t,
|
|
451
|
+
value: this.expr()
|
|
452
|
+
});
|
|
453
|
+
} else {
|
|
454
|
+
let t = this.expr();
|
|
455
|
+
e.push({
|
|
456
|
+
name: null,
|
|
457
|
+
value: t
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (this.isOp(",")) this.next();
|
|
461
|
+
else return e;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
primary() {
|
|
465
|
+
let e = this.tok;
|
|
466
|
+
switch (e.kind) {
|
|
467
|
+
case "num": return this.next(), {
|
|
468
|
+
k: "num",
|
|
469
|
+
value: e.value,
|
|
470
|
+
float: /[.eE]/.test(e.text),
|
|
471
|
+
from: e.from,
|
|
472
|
+
to: e.to
|
|
473
|
+
};
|
|
474
|
+
case "str": return this.next(), {
|
|
475
|
+
k: "str",
|
|
476
|
+
value: e.text,
|
|
477
|
+
from: e.from,
|
|
478
|
+
to: e.to
|
|
479
|
+
};
|
|
480
|
+
case "color": return this.next(), {
|
|
481
|
+
k: "color",
|
|
482
|
+
value: e.text,
|
|
483
|
+
from: e.from,
|
|
484
|
+
to: e.to
|
|
485
|
+
};
|
|
486
|
+
case "ident": return this.next(), {
|
|
487
|
+
k: "id",
|
|
488
|
+
name: e.text,
|
|
489
|
+
from: e.from,
|
|
490
|
+
to: e.to
|
|
491
|
+
};
|
|
492
|
+
case "kw":
|
|
493
|
+
if (e.text === "true" || e.text === "false") return this.next(), {
|
|
494
|
+
k: "bool",
|
|
495
|
+
value: e.text === "true",
|
|
496
|
+
from: e.from,
|
|
497
|
+
to: e.to
|
|
498
|
+
};
|
|
499
|
+
break;
|
|
500
|
+
case "op":
|
|
501
|
+
if (e.text === "(") {
|
|
502
|
+
this.next();
|
|
503
|
+
let t = this.expr(), n = this.expectOp(")", "\")\"");
|
|
504
|
+
return {
|
|
505
|
+
...t,
|
|
506
|
+
from: e.from,
|
|
507
|
+
to: n.to
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (e.text === "[") {
|
|
511
|
+
this.next();
|
|
512
|
+
let t = [];
|
|
513
|
+
if (!this.isOp("]")) for (; t.push(this.expr()), this.isOp(",");) this.next();
|
|
514
|
+
let n = this.expectOp("]", "\"]\"");
|
|
515
|
+
return {
|
|
516
|
+
k: "arr",
|
|
517
|
+
items: t,
|
|
518
|
+
from: e.from,
|
|
519
|
+
to: n.to
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
this.fail(`expected an expression, found ${this.describe()}`);
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
function S(e, n) {
|
|
527
|
+
return new x(t(e, n), n).program();
|
|
528
|
+
}
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/limits.ts
|
|
531
|
+
var C = Object.freeze({
|
|
532
|
+
maxPeriod: 5e3,
|
|
533
|
+
maxLoopIterations: 1e4,
|
|
534
|
+
maxSteps: 2e6,
|
|
535
|
+
maxColumns: 512,
|
|
536
|
+
maxBytes: 536870912,
|
|
537
|
+
maxBars: 1e7,
|
|
538
|
+
maxFeeds: 8,
|
|
539
|
+
maxSourceChars: 2e5,
|
|
540
|
+
batchMs: 8e3,
|
|
541
|
+
tickMs: 1e3
|
|
542
|
+
}), w = 1e6, T = {
|
|
543
|
+
m: 6e4,
|
|
544
|
+
h: 36e5,
|
|
545
|
+
d: 864e5,
|
|
546
|
+
w: 6048e5
|
|
547
|
+
}, E = /^([1-9][0-9]{0,2})([mhdw])$/;
|
|
548
|
+
function D(e) {
|
|
549
|
+
let t = E.exec(e);
|
|
550
|
+
return t ? Number(t[1]) * T[t[2]] : null;
|
|
551
|
+
}
|
|
552
|
+
function O(e, t) {
|
|
553
|
+
return `${e ?? ""}\u0001${t ?? ""}`;
|
|
554
|
+
}
|
|
555
|
+
function k(e) {
|
|
556
|
+
let t = /* @__PURE__ */ new Map();
|
|
557
|
+
for (let n of e) {
|
|
558
|
+
let e = O(n.symbol, n.timeframe), r = t.get(e);
|
|
559
|
+
r || t.set(e, r = {
|
|
560
|
+
key: e,
|
|
561
|
+
symbol: n.symbol,
|
|
562
|
+
timeframe: n.timeframe,
|
|
563
|
+
fields: []
|
|
564
|
+
}), r.fields.includes(n.field) || r.fields.push(n.field);
|
|
565
|
+
}
|
|
566
|
+
return [...t.values()];
|
|
567
|
+
}
|
|
568
|
+
function A(e, t) {
|
|
569
|
+
if (!(Number.isFinite(t) && t > 0)) throw RangeError(`the chart's bar length must be a positive number of milliseconds, got ${String(t)}`);
|
|
570
|
+
if (e === null) return {
|
|
571
|
+
align: "same",
|
|
572
|
+
spanMs: t
|
|
573
|
+
};
|
|
574
|
+
let n = D(e);
|
|
575
|
+
if (n === null) throw RangeError(`timeframe "${e}" is not written like 5m, 1h, 1d or 1w`);
|
|
576
|
+
if (n < t) throw RangeError(`timeframe "${e}" is shorter than the chart's bars; an outside series must be on the chart's timeframe or a longer one`);
|
|
577
|
+
return n === t ? {
|
|
578
|
+
align: "same",
|
|
579
|
+
spanMs: n
|
|
580
|
+
} : {
|
|
581
|
+
align: "closed",
|
|
582
|
+
spanMs: n
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
//#endregion
|
|
586
|
+
//#region src/check.ts
|
|
587
|
+
var j = {
|
|
588
|
+
b: "na",
|
|
589
|
+
q: "const"
|
|
590
|
+
};
|
|
591
|
+
function M(...e) {
|
|
592
|
+
let t = "~";
|
|
593
|
+
for (let n of e) if (n !== "~") {
|
|
594
|
+
if (t === "~") t = n;
|
|
595
|
+
else if (t !== n) return "@";
|
|
596
|
+
}
|
|
597
|
+
return t;
|
|
598
|
+
}
|
|
599
|
+
var N = {
|
|
600
|
+
value: 0,
|
|
601
|
+
ohlc: 1,
|
|
602
|
+
ohlcv: 2
|
|
603
|
+
}, P = {
|
|
604
|
+
const: 0,
|
|
605
|
+
input: 1,
|
|
606
|
+
series: 2
|
|
607
|
+
}, F = (...e) => e.reduce((e, t) => P[t] > P[e] ? t : e, "const"), I = (e) => e.b === "int" || e.b === "float" || e.b === "na", L = (e) => e.q !== "series", R = (e) => e === j ? "na" : e.b === "tuple" ? `a tuple of ${e.elems.length} values (destructure it: [a, b] = ...)` : `${L(e) && e.b !== "na" || e.b === "na" ? "" : "series "}${e.b}`, z = class {
|
|
608
|
+
parent;
|
|
609
|
+
names = /* @__PURE__ */ new Map();
|
|
610
|
+
constructor(e) {
|
|
611
|
+
this.parent = e;
|
|
612
|
+
}
|
|
613
|
+
find(e) {
|
|
614
|
+
for (let t = this; t; t = t.parent) {
|
|
615
|
+
let n = t.names.get(e);
|
|
616
|
+
if (n) return n;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function B(e, t) {
|
|
621
|
+
for (let n of e) n.k === "assign" ? t.add(n.name) : n.k === "if" ? (B(n.then, t), n.else && B(n.else, t)) : n.k === "for" && B(n.body, t);
|
|
622
|
+
}
|
|
623
|
+
var V = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), H = class {
|
|
624
|
+
rep;
|
|
625
|
+
limits;
|
|
626
|
+
plan = {
|
|
627
|
+
title: "Script",
|
|
628
|
+
overlay: !1,
|
|
629
|
+
scale: "right",
|
|
630
|
+
dependencies: [],
|
|
631
|
+
inputs: [],
|
|
632
|
+
plots: [],
|
|
633
|
+
body: [],
|
|
634
|
+
needs: "value",
|
|
635
|
+
slots: 0,
|
|
636
|
+
stateVars: []
|
|
637
|
+
};
|
|
638
|
+
scope = new z(null);
|
|
639
|
+
assigned = /* @__PURE__ */ new Set();
|
|
640
|
+
depth = 0;
|
|
641
|
+
sawIndicator = !1;
|
|
642
|
+
keys = /* @__PURE__ */ new Set();
|
|
643
|
+
loops = 0;
|
|
644
|
+
constructor(e, t = C) {
|
|
645
|
+
this.rep = e, this.limits = t;
|
|
646
|
+
}
|
|
647
|
+
err(e, t) {
|
|
648
|
+
this.rep.error(e, t);
|
|
649
|
+
}
|
|
650
|
+
need(e) {
|
|
651
|
+
N[e] > N[this.plan.needs] && (this.plan.needs = e);
|
|
652
|
+
}
|
|
653
|
+
run(e) {
|
|
654
|
+
B(e.body, this.assigned);
|
|
655
|
+
for (let t of e.body) this.stmt(t, !0);
|
|
656
|
+
this.plan.body = e.body;
|
|
657
|
+
let t = this.plan.plots.find((e) => e.kind === "bgcolor");
|
|
658
|
+
return t && !this.plan.overlay && !this.plan.plots.some((e) => e.kind === "line" || e.kind === "histogram" || e.kind === "area") && this.err("bgcolor shades the pane the script draws in, and with overlay=false the script has a pane only if it plots a line, histogram or area: add a plot(...), or use indicator(..., overlay=true) to shade the price chart", t.span), this.plan;
|
|
659
|
+
}
|
|
660
|
+
declare(t, n, r) {
|
|
661
|
+
this.scope.find(t) ? this.err(`"${t}" is already declared`, r) : Object.prototype.hasOwnProperty.call(c, t) || e.has(t) || t === "na" || t === "nz" || a.has(t) ? this.err(`"${t}" is a built-in name and cannot be redeclared`, r) : (n.decl = r, this.scope.names.set(t, n));
|
|
662
|
+
}
|
|
663
|
+
slot() {
|
|
664
|
+
return this.plan.slots++;
|
|
665
|
+
}
|
|
666
|
+
stmt(e, t) {
|
|
667
|
+
switch (e.k) {
|
|
668
|
+
case "decl": return this.decl(e, t);
|
|
669
|
+
case "tuple": return this.tupleDecl(e, t);
|
|
670
|
+
case "assign": return this.assign(e);
|
|
671
|
+
case "if": return this.ifStmt(e);
|
|
672
|
+
case "for": return this.forStmt(e);
|
|
673
|
+
case "expr": {
|
|
674
|
+
let n = e.expr;
|
|
675
|
+
this.call(n, t ? "stmt" : "expr-block");
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
decl(e, t) {
|
|
681
|
+
if (e.isVar && !t && this.err("'var' is only allowed at the top level of the script", e), e.init.k === "call" && this.calleeName(e.init) !== null && this.calleeName(e.init).startsWith("input.")) {
|
|
682
|
+
if (!t || e.isVar) {
|
|
683
|
+
this.err("input.* may only declare a top-level variable", e.init), this.skipInputArgs(e.init);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
this.inputDecl(e);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
let n = e.init.k === "call" ? this.calleeName(e.init) : null;
|
|
690
|
+
if (n === "plot" || n === "hline") {
|
|
691
|
+
if (!t) {
|
|
692
|
+
this.err(`${n}() is only allowed at the top level of the script`, e.init);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
(e.isVar || e.type) && this.err("a plot handle cannot be declared var or typed", e), this.call(e.init, "decl");
|
|
696
|
+
let i = {
|
|
697
|
+
name: e.name,
|
|
698
|
+
kind: "plot",
|
|
699
|
+
ty: r("plot"),
|
|
700
|
+
key: String(this.plan.plots.length - 1)
|
|
701
|
+
};
|
|
702
|
+
this.declare(e.name, i, e.nameSpan), e.sym = i;
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
let i = this.expr(e.init), a = i;
|
|
706
|
+
if (i.b === "void" && (this.err("this expression has no value", e.init), a = j), i.b === "plot" && (this.err("a plot handle can only come from plot() or hline()", e.init), a = j), i.b === "tuple" && (this.err(`this call returns ${i.elems.length} values; destructure them: [a, b, ...] = ...`, e.init), a = j), e.type) {
|
|
707
|
+
let t = r(e.type, "const");
|
|
708
|
+
if (a !== j && !this.assignable(t, a)) {
|
|
709
|
+
if (t.b === "int" && a.b === "float") {
|
|
710
|
+
let t = ` (declare it float: float ${e.name} = 0.0, or var float ${e.name} = 0.0; there is no int() conversion)`;
|
|
711
|
+
this.err(`cannot assign ${R(a)} to ${e.type} "${e.name}"${t}`, e.init);
|
|
712
|
+
} else this.err(`cannot assign ${R(a)} to ${e.type} "${e.name}"`, e.init);
|
|
713
|
+
}
|
|
714
|
+
a = {
|
|
715
|
+
b: t.b,
|
|
716
|
+
q: a === j ? "series" : a.q
|
|
717
|
+
};
|
|
718
|
+
} else a.b === "na" && a !== j && (this.err(`cannot infer the type of "${e.name}" from na; declare it, e.g. float ${e.name} = na`, e.init), a = j);
|
|
719
|
+
let o = this.assigned.has(e.name);
|
|
720
|
+
if (t) {
|
|
721
|
+
if (e.isVar || o || e.init.st) {
|
|
722
|
+
let t = {
|
|
723
|
+
name: e.name,
|
|
724
|
+
kind: "state",
|
|
725
|
+
ty: {
|
|
726
|
+
b: a.b,
|
|
727
|
+
q: "series"
|
|
728
|
+
},
|
|
729
|
+
isVar: e.isVar,
|
|
730
|
+
slot: this.slot()
|
|
731
|
+
};
|
|
732
|
+
this.declare(e.name, t, e.nameSpan), e.sym = t, this.plan.stateVars.push(t);
|
|
733
|
+
} else {
|
|
734
|
+
let t = {
|
|
735
|
+
name: e.name,
|
|
736
|
+
kind: L(a) ? "simple" : "pure",
|
|
737
|
+
ty: a
|
|
738
|
+
};
|
|
739
|
+
t.kind === "pure" && (t.dom = e.init.dom ?? "@"), this.declare(e.name, t, e.nameSpan), e.sym = t;
|
|
740
|
+
}
|
|
741
|
+
} else {
|
|
742
|
+
let t = {
|
|
743
|
+
name: e.name,
|
|
744
|
+
kind: "local",
|
|
745
|
+
ty: {
|
|
746
|
+
b: a.b,
|
|
747
|
+
q: "series"
|
|
748
|
+
},
|
|
749
|
+
slot: this.slot()
|
|
750
|
+
};
|
|
751
|
+
this.declare(e.name, t, e.nameSpan), e.sym = t;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
tupleDecl(e, t) {
|
|
755
|
+
if (!t) {
|
|
756
|
+
this.err("destructuring is only allowed at the top level of the script", e);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
let n = this.expr(e.init);
|
|
760
|
+
if (e.syms = [], n === j) for (let t of e.names) this.declare(t.name, {
|
|
761
|
+
name: t.name,
|
|
762
|
+
kind: "pure",
|
|
763
|
+
ty: o
|
|
764
|
+
}, t.span);
|
|
765
|
+
else if (n.b !== "tuple") {
|
|
766
|
+
this.err("only calls that return several values can be destructured", e.init);
|
|
767
|
+
for (let t of e.names) this.declare(t.name, {
|
|
768
|
+
name: t.name,
|
|
769
|
+
kind: "pure",
|
|
770
|
+
ty: o
|
|
771
|
+
}, t.span);
|
|
772
|
+
} else n.elems.length !== e.names.length && this.err(`this call returns ${n.elems.length} values, ${e.names.length} names given`, e), e.names.forEach((t, r) => {
|
|
773
|
+
this.assigned.has(t.name) && this.err(`"${t.name}" is destructured from a call and cannot be assigned with :=`, t.span);
|
|
774
|
+
let i = {
|
|
775
|
+
name: t.name,
|
|
776
|
+
kind: "pure",
|
|
777
|
+
ty: n.elems[r] ?? o,
|
|
778
|
+
dom: e.init.dom ?? "@"
|
|
779
|
+
};
|
|
780
|
+
this.declare(t.name, i, t.span), e.syms.push(i);
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
assign(e) {
|
|
784
|
+
let t = this.scope.find(e.name), n = this.expr(e.value);
|
|
785
|
+
if (!t) {
|
|
786
|
+
this.err(`"${e.name}" is not declared; declare it first with ${e.name} = ...`, e.nameSpan);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (e.sym = t, t.kind !== "state" && t.kind !== "local") {
|
|
790
|
+
let n = t.kind === "loop" ? "a loop counter" : t.kind === "input" ? "an input" : t.kind === "builtin" ? "a built-in series" : "not assignable here";
|
|
791
|
+
this.err(`cannot assign to "${e.name}": it is ${n}`, e.nameSpan);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (n === j) return;
|
|
795
|
+
let r = n;
|
|
796
|
+
if (e.op !== ":=") {
|
|
797
|
+
if (!I(t.ty) || !I(n)) {
|
|
798
|
+
this.err(`"${e.op}" needs numbers, got ${R(t.ty)} and ${R(n)}`, e);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
r = {
|
|
802
|
+
b: t.ty.b === "int" && n.b === "int" && e.op !== "/=" ? "int" : "float",
|
|
803
|
+
q: "series"
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
if (!this.assignable(t.ty, r)) {
|
|
807
|
+
if (t.ty.b === "int" && r.b === "float") {
|
|
808
|
+
let n = ` (declare it float: float ${e.name} = 0.0, or var float ${e.name} = 0.0; there is no int() conversion)`;
|
|
809
|
+
this.err(`cannot assign ${R(r)} to ${t.ty.b} "${e.name}"${n}`, e.value);
|
|
810
|
+
} else this.err(`cannot assign ${R(r)} to ${t.ty.b} "${e.name}"`, e.value);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
ifStmt(e) {
|
|
814
|
+
let t = this.expr(e.cond);
|
|
815
|
+
t !== j && t.b !== "bool" && this.err(`the condition of if must be bool, got ${R(t)}`, e.cond), this.block(e.then), e.else && this.block(e.else);
|
|
816
|
+
}
|
|
817
|
+
block(e) {
|
|
818
|
+
let t = this.scope;
|
|
819
|
+
this.scope = new z(t), this.depth++;
|
|
820
|
+
for (let t of e) this.stmt(t, !1);
|
|
821
|
+
this.depth--, this.scope = t;
|
|
822
|
+
}
|
|
823
|
+
forStmt(e) {
|
|
824
|
+
let t = this.expr(e.lo), n = this.expr(e.hi), r = e.by ? this.expr(e.by) : null;
|
|
825
|
+
for (let [i, a] of [
|
|
826
|
+
[t, e.lo],
|
|
827
|
+
[n, e.hi],
|
|
828
|
+
[r, e.by]
|
|
829
|
+
]) i && i !== j && i.b !== "int" && this.err(`a loop bound must be an int, got ${R(i)}`, a);
|
|
830
|
+
let i = (e) => e === null ? 1 : e.k === "num" ? e.value : e.k === "un" && e.op === "-" && e.x.k === "num" ? -e.x.value : void 0, a = i(e.lo), o = i(e.hi), s = i(e.by);
|
|
831
|
+
if (s === 0 && this.err("the loop step cannot be 0", e.by), a !== void 0 && o !== void 0 && s !== void 0 && s !== 0) {
|
|
832
|
+
let t = e.by ? Math.abs(s) : 1, n = Math.floor(Math.abs(o - a) / t) + 1;
|
|
833
|
+
n > this.limits.maxLoopIterations && this.err(`this loop runs ${n} times; the limit is ${this.limits.maxLoopIterations} iterations per loop`, e);
|
|
834
|
+
}
|
|
835
|
+
this.loops++;
|
|
836
|
+
let c = this.scope;
|
|
837
|
+
this.scope = new z(c);
|
|
838
|
+
let l = {
|
|
839
|
+
name: e.name,
|
|
840
|
+
kind: "loop",
|
|
841
|
+
ty: {
|
|
842
|
+
b: "int",
|
|
843
|
+
q: "series"
|
|
844
|
+
},
|
|
845
|
+
slot: this.slot()
|
|
846
|
+
};
|
|
847
|
+
this.declare(e.name, l, e.nameSpan), e.sym = l, this.depth++;
|
|
848
|
+
for (let t of e.body) this.stmt(t, !1);
|
|
849
|
+
this.depth--, this.scope = c;
|
|
850
|
+
}
|
|
851
|
+
assignable(e, t) {
|
|
852
|
+
return t === j || e === j || t.b === "na" || e.b === t.b || e.b === "float" && t.b === "int";
|
|
853
|
+
}
|
|
854
|
+
expr(e) {
|
|
855
|
+
let t = this.expr_(e);
|
|
856
|
+
return e.ty = t, e.dom = this.domOf(e), t;
|
|
857
|
+
}
|
|
858
|
+
domOf(e) {
|
|
859
|
+
if (e.ty === j || e.st) return "@";
|
|
860
|
+
switch (e.k) {
|
|
861
|
+
case "num":
|
|
862
|
+
case "str":
|
|
863
|
+
case "bool":
|
|
864
|
+
case "color":
|
|
865
|
+
case "arr": return "~";
|
|
866
|
+
case "mem": return e.obj.k === "id" && e.obj.name === "ta" && e.name === "tr" ? "@" : "~";
|
|
867
|
+
case "id": {
|
|
868
|
+
let t = e.sym;
|
|
869
|
+
return !t || t.kind === "simple" ? "~" : t.kind === "input" ? t.ty.b === "float" && t.ty.q === "series" ? "@" : "~" : t.kind === "pure" ? t.dom ?? "@" : "@";
|
|
870
|
+
}
|
|
871
|
+
case "un": return e.x.dom ?? "@";
|
|
872
|
+
case "bin": return M(e.a.dom ?? "@", e.b.dom ?? "@");
|
|
873
|
+
case "tern": return M(e.c.dom ?? "@", e.a.dom ?? "@", e.b.dom ?? "@");
|
|
874
|
+
case "idx": return M(e.x.dom ?? "@", e.i.dom ?? "@");
|
|
875
|
+
case "call": {
|
|
876
|
+
if (e.fn === "outside") return e.own && e.feed ? e.feed : "@";
|
|
877
|
+
let t = e.fn ? a.get(e.fn) : void 0;
|
|
878
|
+
if (!t || !e.bound) return "@";
|
|
879
|
+
let n = [];
|
|
880
|
+
t.params.forEach((t, r) => {
|
|
881
|
+
let i = e.bound[r];
|
|
882
|
+
i && t.kind === "series" && n.push(i.dom ?? "@");
|
|
883
|
+
});
|
|
884
|
+
let r = M(...n);
|
|
885
|
+
return r === "~" && (t.op === "kernel" || t.op === "study" || t.name === "ta.bb") ? "@" : r;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
expr_(e) {
|
|
890
|
+
switch (e.k) {
|
|
891
|
+
case "num": return r(e.float || !Number.isInteger(e.value) ? "float" : "int");
|
|
892
|
+
case "str": return r("string");
|
|
893
|
+
case "bool": return r("bool");
|
|
894
|
+
case "color": return r("color");
|
|
895
|
+
case "arr": return this.err("array values are only allowed as options=[...] of input.string", e), j;
|
|
896
|
+
case "id": return this.ident(e);
|
|
897
|
+
case "mem": return this.member(e);
|
|
898
|
+
case "un": return this.unary(e);
|
|
899
|
+
case "bin": return this.binary(e);
|
|
900
|
+
case "tern": return this.ternary(e);
|
|
901
|
+
case "idx": return this.index(e);
|
|
902
|
+
case "call": return this.call(e, "expr");
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
ident(t) {
|
|
906
|
+
let n = this.scope.find(t.name);
|
|
907
|
+
if (n) return t.sym = n, (n.kind === "state" || n.kind === "local" || n.kind === "loop") && (t.st = !0), n.ty;
|
|
908
|
+
let i = c[t.name];
|
|
909
|
+
return i ? (this.need(i.needs), t.sym = {
|
|
910
|
+
name: t.name,
|
|
911
|
+
kind: "builtin",
|
|
912
|
+
ty: i.ty,
|
|
913
|
+
key: t.name
|
|
914
|
+
}, i.ty) : t.name === "na" ? r("na") : e.has(t.name) ? (this.err(`"${t.name}" is a namespace; use ${t.name}.<name>`, t), j) : a.has(t.name) ? (this.err(`"${t.name}" is a function; call it: ${t.name}(...)`, t), j) : (this.err(`unknown name "${t.name}"`, t), j);
|
|
915
|
+
}
|
|
916
|
+
member(t) {
|
|
917
|
+
if (t.obj.k !== "id" || !e.has(t.obj.name) || this.scope.find(t.obj.name)) return this.err("only namespace names can be followed by '.'", t), j;
|
|
918
|
+
let r = `${t.obj.name}.${t.name}`, i = n[r];
|
|
919
|
+
return i ? (t.cv = i.value, i.ty) : r === "ta.tr" ? (this.need("ohlc"), s) : a.has(r) ? (this.err(`"${r}" is a function; call it: ${r}(...)`, t), j) : (this.err(`unknown name "${r}"`, t.nameSpan), j);
|
|
920
|
+
}
|
|
921
|
+
unary(e) {
|
|
922
|
+
let t = this.expr(e.x);
|
|
923
|
+
return e.st = e.x.st ?? !1, t === j ? j : e.op === "not" ? t.b === "bool" ? t : (this.err(`"not" needs a bool, got ${R(t)}`, e), j) : I(t) ? t.b === "na" ? {
|
|
924
|
+
b: "float",
|
|
925
|
+
q: t.q
|
|
926
|
+
} : t : (this.err(`unary "${e.op}" needs a number, got ${R(t)}`, e), j);
|
|
927
|
+
}
|
|
928
|
+
binary(e) {
|
|
929
|
+
let t = this.expr(e.a), n = this.expr(e.b);
|
|
930
|
+
if (e.st = !!(e.a.st || e.b.st), t === j || n === j) return j;
|
|
931
|
+
let r = F(t.q, n.q);
|
|
932
|
+
switch (e.op) {
|
|
933
|
+
case "and":
|
|
934
|
+
case "or": return t.b !== "bool" || n.b !== "bool" ? (this.err(`"${e.op}" needs bool operands, got ${R(t)} and ${R(n)}`, e), j) : {
|
|
935
|
+
b: "bool",
|
|
936
|
+
q: r
|
|
937
|
+
};
|
|
938
|
+
case "==":
|
|
939
|
+
case "!=": return I(t) && I(n) || t.b === n.b && (t.b === "bool" || t.b === "string" || t.b === "color") ? (t.b === "string" || t.b === "color") && r === "series" ? (this.err("strings and colors can only be compared as constants or inputs", e), j) : {
|
|
940
|
+
b: "bool",
|
|
941
|
+
q: r
|
|
942
|
+
} : (this.err(`cannot compare ${R(t)} with ${R(n)}`, e), j);
|
|
943
|
+
case "<":
|
|
944
|
+
case "<=":
|
|
945
|
+
case ">":
|
|
946
|
+
case ">=": return !I(t) || !I(n) ? (this.err(`"${e.op}" needs numbers, got ${R(t)} and ${R(n)}`, e), j) : {
|
|
947
|
+
b: "bool",
|
|
948
|
+
q: r
|
|
949
|
+
};
|
|
950
|
+
default: return !I(t) || !I(n) ? (this.err(`"${e.op}" needs numbers, got ${R(t)} and ${R(n)}${t.b === "bool" || n.b === "bool" ? " (a bool is not a number: use a ternary)" : ""}`, e), j) : {
|
|
951
|
+
b: t.b === "int" && n.b === "int" && e.op !== "/" ? "int" : "float",
|
|
952
|
+
q: r
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
ternary(e) {
|
|
957
|
+
let t = this.expr(e.c), n = this.expr(e.a), r = this.expr(e.b);
|
|
958
|
+
if (e.st = !!(e.c.st || e.a.st || e.b.st), t !== j && t.b !== "bool" && this.err(`the condition of ?: must be bool, got ${R(t)}`, e.c), n === j || r === j) return j;
|
|
959
|
+
let i = F(t === j ? "const" : t.q, n.q, r.q);
|
|
960
|
+
return n.b === "na" ? {
|
|
961
|
+
b: r.b,
|
|
962
|
+
q: i
|
|
963
|
+
} : r.b === "na" ? {
|
|
964
|
+
b: n.b,
|
|
965
|
+
q: i
|
|
966
|
+
} : I(n) && I(r) ? {
|
|
967
|
+
b: n.b === "int" && r.b === "int" ? "int" : "float",
|
|
968
|
+
q: i
|
|
969
|
+
} : n.b === "string" && r.b === "string" && i === "series" ? (this.err("a string cannot depend on a series: the condition of ?: must be a constant or an input (a string is a label, not a value that changes per bar)", e), j) : n.b === r.b ? {
|
|
970
|
+
b: n.b,
|
|
971
|
+
q: i
|
|
972
|
+
} : (this.err(`the branches of ?: have different types: ${R(n)} and ${R(r)}`, e), j);
|
|
973
|
+
}
|
|
974
|
+
historyOk(e) {
|
|
975
|
+
return !e.st || e.k === "id" && e.sym?.kind === "state";
|
|
976
|
+
}
|
|
977
|
+
index(e) {
|
|
978
|
+
let t = this.expr(e.x), n = this.expr(e.i);
|
|
979
|
+
if (e.st = !!(e.x.st || e.i.st), t === j || n === j) return j;
|
|
980
|
+
if (t.q !== "series" || !(I(t) || t.b === "bool")) return this.err(`only a series can be indexed with [ ], this is ${R(t)}`, e.x), j;
|
|
981
|
+
if (n.b !== "int") return this.err(`the history offset must be an int, got ${R(n)}`, e.i), j;
|
|
982
|
+
if (!this.historyOk(e.x)) return this.err("history [ ] needs a plain series or a top-level variable; a block variable, loop counter or an expression of those has no earlier values. Assign the value to a top-level variable first", e.x), j;
|
|
983
|
+
let r = e.i.k === "num" ? e.i.value : e.i.k === "un" && e.i.op === "-" && e.i.x.k === "num" ? -e.i.x.value : void 0;
|
|
984
|
+
return r !== void 0 && (r < 0 || r > 1e6) && this.err(`the history offset must be between 0 and ${w}, got ${r}`, e.i), {
|
|
985
|
+
b: t.b,
|
|
986
|
+
q: "series"
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
calleeName(e) {
|
|
990
|
+
return e.callee.k === "id" ? e.callee.name : e.callee.k === "mem" && e.callee.obj.k === "id" ? `${e.callee.obj.name}.${e.callee.name}` : null;
|
|
991
|
+
}
|
|
992
|
+
skipInputArgs(e) {
|
|
993
|
+
for (let t of e.args) this.expr(t.value);
|
|
994
|
+
}
|
|
995
|
+
bind(e, t) {
|
|
996
|
+
let n = e.params.map(() => null), r = 0, i = !1;
|
|
997
|
+
for (let a of t.args) if (a.name === null) {
|
|
998
|
+
if (i) {
|
|
999
|
+
this.err("positional arguments must come before named ones", a.value);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (r >= e.params.length) return this.err(`${e.name} takes at most ${e.params.length} argument${e.params.length === 1 ? "" : "s"}, got ${t.args.length}`, a.value), n;
|
|
1003
|
+
n[r++] = a.value;
|
|
1004
|
+
} else {
|
|
1005
|
+
i = !0;
|
|
1006
|
+
let t = e.params.findIndex((e) => e.name === a.name);
|
|
1007
|
+
if (t < 0) {
|
|
1008
|
+
this.err(`${e.name} has no parameter "${a.name}" (parameters: ${e.params.map((e) => e.name).join(", ") || "none"})`, a.nameSpan ?? a.value);
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
if (n[t]) {
|
|
1012
|
+
this.err(`parameter "${a.name}" is given twice`, a.nameSpan ?? a.value);
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
n[t] = a.value;
|
|
1016
|
+
}
|
|
1017
|
+
return e.params.forEach((r, i) => {
|
|
1018
|
+
!r.opt && !n[i] && !(r.kind === "series" && 0) && this.err(`${e.name} needs the argument "${r.name}"`, t);
|
|
1019
|
+
}), n;
|
|
1020
|
+
}
|
|
1021
|
+
constVal(e) {
|
|
1022
|
+
switch (e.k) {
|
|
1023
|
+
case "num": return e.value;
|
|
1024
|
+
case "str": return e.value;
|
|
1025
|
+
case "bool": return e.value;
|
|
1026
|
+
case "color": return e.value;
|
|
1027
|
+
case "mem": return e.cv;
|
|
1028
|
+
case "un": return e.op === "-" && e.x.k === "num" ? -e.x.value : void 0;
|
|
1029
|
+
default: return;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
checkParam(e, t, n) {
|
|
1033
|
+
let r = n.ty ?? this.expr(n);
|
|
1034
|
+
if (r === j) return;
|
|
1035
|
+
let a = `argument "${t.name}" of ${e.name}`, o = () => {
|
|
1036
|
+
L(r) || this.err(`${a} must be a literal or an input, not a series value`, n);
|
|
1037
|
+
};
|
|
1038
|
+
switch (t.kind) {
|
|
1039
|
+
case "series":
|
|
1040
|
+
I(r) || this.err(`${a} must be a number series, got ${R(r)}`, n);
|
|
1041
|
+
break;
|
|
1042
|
+
case "any":
|
|
1043
|
+
(r.b === "void" || r.b === "plot" || r.b === "tuple") && this.err(`${a} must be a value`, n);
|
|
1044
|
+
break;
|
|
1045
|
+
case "num":
|
|
1046
|
+
if (!I(r) || r.b === "na") {
|
|
1047
|
+
this.err(`${a} must be a number, got ${R(r)}`, n);
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
o();
|
|
1051
|
+
break;
|
|
1052
|
+
case "int":
|
|
1053
|
+
if (r.b !== "int") {
|
|
1054
|
+
this.err(`${a} must be an int, got ${R(r)}`, n);
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
1057
|
+
o();
|
|
1058
|
+
break;
|
|
1059
|
+
case "bool":
|
|
1060
|
+
case "string":
|
|
1061
|
+
case "color":
|
|
1062
|
+
if (r.b !== t.kind) {
|
|
1063
|
+
this.err(`${a} must be a ${t.kind}, got ${R(r)}`, n);
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
if (e.name === "plot" && t.name === "color" && n.k === "tern") break;
|
|
1067
|
+
if (o(), t.oneOf) {
|
|
1068
|
+
let e = this.constVal(n);
|
|
1069
|
+
typeof e == "string" && !t.oneOf.includes(e) && this.err(`${a} must be one of ${t.oneOf.map((e) => JSON.stringify(e)).join(", ")}, got ${JSON.stringify(e)}`, n);
|
|
1070
|
+
}
|
|
1071
|
+
break;
|
|
1072
|
+
case "enum": {
|
|
1073
|
+
let e = this.constVal(n);
|
|
1074
|
+
if (typeof e != "string" || !t.oneOf.includes(e) || n.k !== "mem") {
|
|
1075
|
+
let e = t.oneOf === i.plotStyle ? "plot.style_*" : t.oneOf === i.lineStyle ? "hline.style_*" : t.oneOf === i.shape ? "shape.*" : "location.*";
|
|
1076
|
+
this.err(`${a} must be one of ${e}`, n);
|
|
1077
|
+
}
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
case "strlist": break;
|
|
1081
|
+
case "plot": (n.k !== "id" || n.sym?.kind !== "plot") && this.err(`${a} must be a plot handle: p = plot(...)`, n);
|
|
1082
|
+
}
|
|
1083
|
+
let s = this.constVal(n);
|
|
1084
|
+
typeof s == "number" && (t.kind === "int" || t.kind === "num") && (t.min !== void 0 && s < t.min || t.max !== void 0 && s > t.max) && this.err(`${a} must be between ${t.min ?? "-inf"} and ${t.max ?? "inf"}, got ${s}`, n);
|
|
1085
|
+
}
|
|
1086
|
+
call(e, t) {
|
|
1087
|
+
let n = this.calleeName(e);
|
|
1088
|
+
if (n === null) {
|
|
1089
|
+
this.err("only named functions can be called", e.callee);
|
|
1090
|
+
for (let t of e.args) this.expr(t.value);
|
|
1091
|
+
return j;
|
|
1092
|
+
}
|
|
1093
|
+
let r = a.get(n);
|
|
1094
|
+
if (!r) {
|
|
1095
|
+
for (let t of e.args) this.expr(t.value);
|
|
1096
|
+
let t = [...a.keys()].filter((e) => e.endsWith(`.${n.split(".").pop()}`) || e === n.split(".").pop());
|
|
1097
|
+
return (n === "security" || n === "request.security") && t.unshift("outside"), this.err(`unknown function "${n}"${t.length ? `; did you mean ${t.slice(0, 3).join(", ")}?` : ""}`, e.callee), j;
|
|
1098
|
+
}
|
|
1099
|
+
if (e.fn = n, (n === "plot" || n === "hline" || n === "fill" || n === "plotshape" || n === "indicator" || n === "barcolor" || n === "bgcolor") && (this.depth > 0 || t === "expr" || t === "expr-block" ? this.err(`${n}() is only allowed as a statement at the top level of the script`, e) : t === "decl" && n !== "plot" && n !== "hline" && this.err(`${n}() does not return a handle`, e)), n.startsWith("input.") && t !== "decl") {
|
|
1100
|
+
this.err("input.* may only be the whole right-hand side of a top-level declaration: name = input.int(...)", e);
|
|
1101
|
+
for (let t of e.args) this.expr(t.value);
|
|
1102
|
+
return j;
|
|
1103
|
+
}
|
|
1104
|
+
let i = this.bind(r, e);
|
|
1105
|
+
e.bound = i, r.params.forEach((e, t) => {
|
|
1106
|
+
let n = i[t];
|
|
1107
|
+
n && e.kind !== "strlist" && this.expr(n);
|
|
1108
|
+
}), r.params.forEach((e, t) => {
|
|
1109
|
+
let n = i[t];
|
|
1110
|
+
n && e.kind !== "strlist" && this.checkParam(r, e, n);
|
|
1111
|
+
});
|
|
1112
|
+
let o = !1;
|
|
1113
|
+
for (let e of i) e?.st && (o = !0);
|
|
1114
|
+
e.st = o, r.needs && this.need(r.needs);
|
|
1115
|
+
let s = r.ret;
|
|
1116
|
+
switch (r.op) {
|
|
1117
|
+
case "kernel":
|
|
1118
|
+
case "study": return r.params.forEach((e, t) => {
|
|
1119
|
+
let n = i[t];
|
|
1120
|
+
n && n.st && e.kind === "series" && this.err(`argument "${e.name}" of ${r.name} depends on a variable that is declared var, assigned with :=, or is a block variable / loop counter. ${r.name} runs over whole columns, so its inputs must be known before the bar is evaluated`, n);
|
|
1121
|
+
}), e.st = !1, r.op === "study" && this.studySources(r, i), s;
|
|
1122
|
+
case "scalar": return {
|
|
1123
|
+
b: "float",
|
|
1124
|
+
q: F(...i.filter((e) => !!e).map((e) => e.ty.q))
|
|
1125
|
+
};
|
|
1126
|
+
case "special": return this.special(r, e, i, t, s);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
studySources(e, t) {}
|
|
1130
|
+
special(e, t, n, i, a) {
|
|
1131
|
+
let o = (t) => n[e.params.findIndex((e) => e.name === t)] ?? null;
|
|
1132
|
+
switch (e.name) {
|
|
1133
|
+
case "nz": {
|
|
1134
|
+
let e = o("replacement");
|
|
1135
|
+
return {
|
|
1136
|
+
b: "float",
|
|
1137
|
+
q: F(o("x").ty.q, e?.ty?.q ?? "const")
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
case "na": return {
|
|
1141
|
+
b: "bool",
|
|
1142
|
+
q: o("x").ty.q
|
|
1143
|
+
};
|
|
1144
|
+
case "math.sum": return a;
|
|
1145
|
+
case "ta.change": {
|
|
1146
|
+
let e = o("source");
|
|
1147
|
+
return this.historyOk(e) || this.err("ta.change needs a plain series or a top-level variable, not an expression of changing variables", e), t.st = !!e.st, {
|
|
1148
|
+
b: "float",
|
|
1149
|
+
q: "series"
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
case "ta.crossover":
|
|
1153
|
+
case "ta.crossunder":
|
|
1154
|
+
case "ta.cross":
|
|
1155
|
+
for (let t of [o("a"), o("b")]) this.historyOk(t) || this.err(`${e.name} needs plain series or top-level variables, not expressions of changing variables; assign the value to a variable first`, t);
|
|
1156
|
+
return t.st = !!(o("a").st || o("b").st), {
|
|
1157
|
+
b: "bool",
|
|
1158
|
+
q: "series"
|
|
1159
|
+
};
|
|
1160
|
+
case "ta.bb":
|
|
1161
|
+
for (let e of n) if (e?.st && e.ty.q === "series") {
|
|
1162
|
+
this.err("ta.bb needs inputs known before the bar is evaluated (no var / := / block variables)", e);
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
return t.st = !1, a;
|
|
1166
|
+
case "color.new": return r("color", F(...n.map((e) => e.ty.q)));
|
|
1167
|
+
case "indicator": return this.indicator(t, n);
|
|
1168
|
+
case "plot": return this.plot(t, n);
|
|
1169
|
+
case "hline": return this.hline(t, n);
|
|
1170
|
+
case "fill": return this.fill(t, n);
|
|
1171
|
+
case "plotshape": return this.plotshape(t, n);
|
|
1172
|
+
case "barcolor":
|
|
1173
|
+
case "bgcolor": return this.shade(e.name, t, n);
|
|
1174
|
+
case "outside": return this.outside(t, n, a);
|
|
1175
|
+
default: return e.name.startsWith("input.") ? r("void") : a;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
str(e) {
|
|
1179
|
+
if (!e) return;
|
|
1180
|
+
let t = this.constVal(e);
|
|
1181
|
+
if (typeof t == "string") return t;
|
|
1182
|
+
e.ty && e.ty !== j && e.ty.b === "string" && this.err("this must be a string literal", e);
|
|
1183
|
+
}
|
|
1184
|
+
int(e) {
|
|
1185
|
+
if (!e) return;
|
|
1186
|
+
let t = this.constVal(e);
|
|
1187
|
+
if (typeof t == "number") return t;
|
|
1188
|
+
e.ty && e.ty !== j && I(e.ty) && this.err("this must be a number literal", e);
|
|
1189
|
+
}
|
|
1190
|
+
offsetArg(e) {
|
|
1191
|
+
if (!e) return {};
|
|
1192
|
+
let t = this.constVal(e);
|
|
1193
|
+
return typeof t == "number" ? { offset: t } : e.ty && e.ty !== j && e.ty.b === "int" && L(e.ty) ? { offsetExpr: e } : {};
|
|
1194
|
+
}
|
|
1195
|
+
indicator(e, t) {
|
|
1196
|
+
this.sawIndicator && this.err("indicator() may be called only once", e), this.sawIndicator = !0;
|
|
1197
|
+
let n = this.str(t[0]);
|
|
1198
|
+
n !== void 0 && (this.plan.title = n);
|
|
1199
|
+
let i = this.str(t[1]);
|
|
1200
|
+
if (i !== void 0 && (this.plan.title = i), t[2]) {
|
|
1201
|
+
let e = this.constVal(t[2]);
|
|
1202
|
+
typeof e == "boolean" ? this.plan.overlay = e : t[2].ty && t[2].ty !== j && this.err("overlay must be true or false", t[2]);
|
|
1203
|
+
}
|
|
1204
|
+
let a = this.str(t[3]);
|
|
1205
|
+
return (a === "left" || a === "right") && (this.plan.scale = a), r("void");
|
|
1206
|
+
}
|
|
1207
|
+
colorRef(e, t) {
|
|
1208
|
+
if (!e || e.ty === j) return { value: t };
|
|
1209
|
+
let n = this.constVal(e);
|
|
1210
|
+
if (typeof n == "string" && e.ty?.b === "color") return { value: n };
|
|
1211
|
+
if (e.k === "id" && e.sym?.kind === "input" && e.sym.ty.b === "color") return { input: e.sym.key };
|
|
1212
|
+
if (e.k === "call" && e.fn === "color.new") {
|
|
1213
|
+
let n = this.colorRef(e.bound[0], t), r = e.bound[1] ? this.constVal(e.bound[1]) : void 0;
|
|
1214
|
+
if ("value" in n && typeof r == "number") return { value: U(n.value, r) };
|
|
1215
|
+
}
|
|
1216
|
+
return this.err("a color here must be a constant (color.red, #rrggbb, color.new(...)) or an input.color variable", e), { value: t };
|
|
1217
|
+
}
|
|
1218
|
+
nextKey(e, t) {
|
|
1219
|
+
let n = V(e) || `plot${t}`;
|
|
1220
|
+
/^[0-9]/.test(n) && (n = `p-${n}`);
|
|
1221
|
+
let r = n, i = 2;
|
|
1222
|
+
for (; this.keys.has(r);) r = `${n}-${i++}`;
|
|
1223
|
+
return this.keys.add(r), r;
|
|
1224
|
+
}
|
|
1225
|
+
plot(e, t) {
|
|
1226
|
+
let n = this.plan.plots.length;
|
|
1227
|
+
n >= 64 && this.err("a script may draw at most 64 plots", e);
|
|
1228
|
+
let i = this.str(t[1]) ?? `Plot ${n + 1}`, a = (t[4] ? this.constVal(t[4]) : void 0) ?? "line", o = this.int(t[3]), s = this.offsetArg(t[5]), c = t[6] ? this.colorRef(t[6], "#f23645") : void 0;
|
|
1229
|
+
c && "input" in c && this.err("color_down must be a constant color", t[6]);
|
|
1230
|
+
let l = [
|
|
1231
|
+
"#2962ff",
|
|
1232
|
+
"#ff6d00",
|
|
1233
|
+
"#089981",
|
|
1234
|
+
"#f23645",
|
|
1235
|
+
"#9c27b0",
|
|
1236
|
+
"#fbc02d"
|
|
1237
|
+
][n % 6], u = t[2], d, f;
|
|
1238
|
+
if (u && u.k === "tern" && u.c.ty && u.c.ty !== j && u.c.ty.q === "series" && u.ty && u.ty.b === "color") {
|
|
1239
|
+
let e = this.colorRef(u.a, l), t = this.colorRef(u.b, l);
|
|
1240
|
+
a === "line" ? (!("value" in e) || !("value" in t)) && this.err("both colors of a color that depends on the bar must be constants (color.red, #rrggbb, color.new(...)), not an input.color", u) : this.err("a color that depends on the bar is for line plots; histogram and area take a constant color (a histogram can use color_down for negative values)", u), d = "value" in t ? t : { value: l }, "value" in e && "value" in t && a === "line" && (f = {
|
|
1241
|
+
cond: u.c,
|
|
1242
|
+
color: e.value
|
|
1243
|
+
});
|
|
1244
|
+
} else d = this.colorRef(u, l);
|
|
1245
|
+
return this.plan.plots.push({
|
|
1246
|
+
kind: a,
|
|
1247
|
+
key: this.nextKey(i, n + 1),
|
|
1248
|
+
title: i,
|
|
1249
|
+
color: d,
|
|
1250
|
+
expr: t[0],
|
|
1251
|
+
...o !== void 0 && { width: o },
|
|
1252
|
+
...s,
|
|
1253
|
+
...c && "value" in c && { colorDown: c.value },
|
|
1254
|
+
...f && { colorWhen: f },
|
|
1255
|
+
span: e
|
|
1256
|
+
}), r("plot");
|
|
1257
|
+
}
|
|
1258
|
+
shade(e, t, n) {
|
|
1259
|
+
let i = this.plan.plots.length;
|
|
1260
|
+
i >= 64 && this.err("a script may draw at most 64 plots", t);
|
|
1261
|
+
let a = n[0];
|
|
1262
|
+
a.ty && a.ty !== j && a.ty.b !== "bool" && this.err(`${e} needs a bool condition, got ${R(a.ty)}`, a);
|
|
1263
|
+
let o = `${e === "barcolor" ? "Bar color" : "Background"} ${i + 1}`;
|
|
1264
|
+
return this.plan.plots.push({
|
|
1265
|
+
kind: e,
|
|
1266
|
+
key: this.nextKey(o, i + 1),
|
|
1267
|
+
title: o,
|
|
1268
|
+
color: this.colorRef(n[1], e === "barcolor" ? "#fdd835" : "#2962ff33"),
|
|
1269
|
+
expr: a,
|
|
1270
|
+
span: t
|
|
1271
|
+
}), r("void");
|
|
1272
|
+
}
|
|
1273
|
+
outside(e, t, n) {
|
|
1274
|
+
let r = (e, t) => {
|
|
1275
|
+
if (!e || e.k === "id" && e.name === "na" && !e.sym) return null;
|
|
1276
|
+
if (e.k === "str") return e.value;
|
|
1277
|
+
e.ty && e.ty !== j && this.err(`the ${t} of outside() must be a string literal${t === "timeframe" ? " like \"1d\"" : ""}, or na for this chart's, not a computed value`, e);
|
|
1278
|
+
}, i = r(t[0], "symbol"), a = r(t[2], "timeframe");
|
|
1279
|
+
if (i === void 0 || a === void 0) return n;
|
|
1280
|
+
if (i !== null && (i.length < 1 || i.length > 128 || /[\u0000-\u001f]/.test(i))) return this.err("the symbol of outside() must be 1 to 128 printable characters", t[0]), n;
|
|
1281
|
+
if (a !== null && D(a) === null) return this.err(`the timeframe "${a}" is not written like 5m, 15m, 1h, 4h, 1d or 1w`, t[2]), n;
|
|
1282
|
+
if (i === null && a === null) return this.err("outside() needs a symbol or a timeframe; this chart's own series are close, open, high, low and volume", e), n;
|
|
1283
|
+
let o = (t[1] ? this.constVal(t[1]) : void 0) ?? "close";
|
|
1284
|
+
return e.feed = O(i, a), e.own = a !== null, this.plan.dependencies.some((e) => e.symbol === i && e.timeframe === a && e.field === o) || (this.plan.dependencies.push({
|
|
1285
|
+
symbol: i,
|
|
1286
|
+
field: o,
|
|
1287
|
+
timeframe: a
|
|
1288
|
+
}), k(this.plan.dependencies).length > this.limits.maxFeeds && this.err(`a script may read at most ${this.limits.maxFeeds} outside series (distinct symbol and timeframe pairs)`, e)), n;
|
|
1289
|
+
}
|
|
1290
|
+
hline(e, t) {
|
|
1291
|
+
let n = this.plan.plots.length, i = this.str(t[1]) ?? `Level ${n + 1}`, a = t[3] ? this.constVal(t[3]) : void 0;
|
|
1292
|
+
return this.plan.plots.push({
|
|
1293
|
+
kind: "hline",
|
|
1294
|
+
key: this.nextKey(i, n + 1),
|
|
1295
|
+
title: i,
|
|
1296
|
+
color: this.colorRef(t[2], "#787b86"),
|
|
1297
|
+
expr: t[0],
|
|
1298
|
+
...a && { lineStyle: a },
|
|
1299
|
+
span: e
|
|
1300
|
+
}), r("plot");
|
|
1301
|
+
}
|
|
1302
|
+
fill(e, t) {
|
|
1303
|
+
let n = this.plan.plots.length, i = this.str(t[3]) ?? `Fill ${n + 1}`, a = (e) => e && e.k === "id" && e.sym?.kind === "plot" ? Number(e.sym.key) : void 0, o = a(t[0]), s = a(t[1]);
|
|
1304
|
+
for (let [e, n] of [[o, t[0]], [s, t[1]]]) {
|
|
1305
|
+
if (e === void 0) continue;
|
|
1306
|
+
let t = this.plan.plots[e];
|
|
1307
|
+
t.kind === "hline" ? t.column = !0 : t.kind !== "line" && this.err("fill needs line plots or hline levels (not histogram, area or markers)", n);
|
|
1308
|
+
}
|
|
1309
|
+
return o !== void 0 && o === s && this.err("fill needs two different plots", e), this.plan.plots.push({
|
|
1310
|
+
kind: "fill",
|
|
1311
|
+
key: this.nextKey(i, n + 1),
|
|
1312
|
+
title: i,
|
|
1313
|
+
color: this.colorRef(t[2], "#2962ff33"),
|
|
1314
|
+
...o !== void 0 && { a: o },
|
|
1315
|
+
...s !== void 0 && { b: s },
|
|
1316
|
+
span: e
|
|
1317
|
+
}), r("void");
|
|
1318
|
+
}
|
|
1319
|
+
plotshape(e, t) {
|
|
1320
|
+
let n = this.plan.plots.length, i = t[0], a = this.str(t[1]) ?? `Shape ${n + 1}`, o = (t[2] ? this.constVal(t[2]) : void 0) ?? "circle", s = (t[3] ? this.constVal(t[3]) : void 0) ?? "above", c = i.ty;
|
|
1321
|
+
c && c !== j && (c.b === "bool" && s === "value" ? this.err("location.absolute needs a number (the price to mark), not a bool", i) : c.b !== "bool" && s !== "value" ? this.err("plotshape marks bars where a bool is true; for a number use location=location.absolute", i) : c.b !== "bool" && !I(c) && this.err(`plotshape needs a bool or a number, got ${R(c)}`, i));
|
|
1322
|
+
let l = this.offsetArg(t[5]), u = this.int(t[6]), d = this.int(t[7]);
|
|
1323
|
+
return this.plan.plots.push({
|
|
1324
|
+
kind: "markers",
|
|
1325
|
+
key: this.nextKey(a, n + 1),
|
|
1326
|
+
title: a,
|
|
1327
|
+
color: this.colorRef(t[4], "#2962ff"),
|
|
1328
|
+
expr: i,
|
|
1329
|
+
shape: o,
|
|
1330
|
+
position: s,
|
|
1331
|
+
...l,
|
|
1332
|
+
...u !== void 0 && { last: u },
|
|
1333
|
+
...d !== void 0 && { within: d },
|
|
1334
|
+
span: e
|
|
1335
|
+
}), r("void");
|
|
1336
|
+
}
|
|
1337
|
+
inputDecl(e) {
|
|
1338
|
+
let t = e.init, n = this.calleeName(t), i = a.get(n);
|
|
1339
|
+
if (!i) {
|
|
1340
|
+
this.err(`unknown function "${n}"`, t.callee), this.skipInputArgs(t);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
t.fn = n;
|
|
1344
|
+
let s = this.bind(i, t);
|
|
1345
|
+
t.bound = s;
|
|
1346
|
+
let c = n.slice(6), u = (e) => i.params.findIndex((t) => t.name === e), d = (e) => s[u(e)] ?? null;
|
|
1347
|
+
i.params.forEach((e, t) => {
|
|
1348
|
+
let r = s[t];
|
|
1349
|
+
r && (e.kind === "strlist" ? (r.k !== "arr" || r.items.length === 0 || r.items.some((e) => e.k !== "str")) && this.err("options must be a non-empty list of string literals: options=[\"a\", \"b\"]", r) : (n !== "input.source" || e.name !== "defval") && (this.expr(r), this.checkParam(i, e, r), r.ty && r.ty !== j && r.ty.q !== "const" && e.kind !== "series" && this.err(`${e.name} of ${n} must be a literal`, r)));
|
|
1350
|
+
});
|
|
1351
|
+
let f = 0, p = d("defval"), m = this.str(d("title")) ?? e.name, h = {
|
|
1352
|
+
key: e.name,
|
|
1353
|
+
kind: c,
|
|
1354
|
+
label: m,
|
|
1355
|
+
default: 0,
|
|
1356
|
+
span: e.nameSpan
|
|
1357
|
+
};
|
|
1358
|
+
if (p) {
|
|
1359
|
+
if (n === "input.source") p.k === "id" && l.includes(p.name) && !this.scope.find(p.name) ? (f = p.name, p.ty = o, this.need(p.name === "close" ? "value" : "ohlc")) : this.err(`the default of input.source must be one of ${l.join(", ")}`, p);
|
|
1360
|
+
else {
|
|
1361
|
+
let e = this.constVal(p);
|
|
1362
|
+
e !== void 0 && (f = e);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (h.default = f, c === "int" || c === "float") {
|
|
1366
|
+
let e = d("minval") ? this.int(d("minval")) : void 0, n = d("maxval") ? this.int(d("maxval")) : void 0, r = d("step") ? this.int(d("step")) : void 0;
|
|
1367
|
+
e !== void 0 && (h.min = e), n !== void 0 && (h.max = n), r !== void 0 && (r <= 0 ? this.err("step must be positive", d("step")) : h.step = r), typeof f == "number" && (e !== void 0 && f < e || n !== void 0 && f > n) && this.err(`the default ${f} is outside [${e ?? "-inf"}, ${n ?? "inf"}]`, p), e !== void 0 && n !== void 0 && e > n && this.err("minval is above maxval", t);
|
|
1368
|
+
}
|
|
1369
|
+
if (c === "string") {
|
|
1370
|
+
let e = d("options"), t = e && e.k === "arr" ? e.items.filter((e) => e.k === "str").map((e) => e.value) : [];
|
|
1371
|
+
h.options = t, t.length > 0 && !t.includes(f) && this.err(`the default "${String(f)}" is not one of the options`, p);
|
|
1372
|
+
}
|
|
1373
|
+
c === "color" && typeof f == "string" && !/^#[0-9a-f]{6}([0-9a-f]{2})?$/.test(f) && this.err("the default must be a color", p), this.plan.inputs.push(h);
|
|
1374
|
+
let g = c === "source" ? o : r(c === "string" ? "string" : c, "input"), _ = {
|
|
1375
|
+
name: e.name,
|
|
1376
|
+
kind: "input",
|
|
1377
|
+
ty: g,
|
|
1378
|
+
key: e.name
|
|
1379
|
+
};
|
|
1380
|
+
this.declare(e.name, _, e.nameSpan), e.sym = _, e.init.ty = g;
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
function U(e, t) {
|
|
1384
|
+
let n = e.slice(1);
|
|
1385
|
+
return `rgba(${parseInt(n.slice(0, 2), 16)},${parseInt(n.slice(2, 4), 16)},${parseInt(n.slice(4, 6), 16)},${Math.round((1 - t / 100) * 1e3) / 1e3})`;
|
|
1386
|
+
}
|
|
1387
|
+
function W(e, t, n = C) {
|
|
1388
|
+
return new H(t, n).run(e);
|
|
1389
|
+
}
|
|
1390
|
+
//#endregion
|
|
1391
|
+
//#region src/codegen.ts
|
|
1392
|
+
var G = NaN, K = (e) => e === e && e !== 0;
|
|
1393
|
+
function q(e) {
|
|
1394
|
+
let t = 2166136261;
|
|
1395
|
+
for (let n = 0; n < e.length; n++) t = Math.imul(t ^ e.charCodeAt(n), 16777619) >>> 0;
|
|
1396
|
+
return t.toString(16).padStart(8, "0");
|
|
1397
|
+
}
|
|
1398
|
+
function ee(e, t, n = C) {
|
|
1399
|
+
let r = [], i = [];
|
|
1400
|
+
for (let t of e.inputs) switch (t.kind) {
|
|
1401
|
+
case "int":
|
|
1402
|
+
r.push({
|
|
1403
|
+
key: t.key,
|
|
1404
|
+
label: t.label,
|
|
1405
|
+
type: "int",
|
|
1406
|
+
default: t.default,
|
|
1407
|
+
...t.min !== void 0 && { min: t.min },
|
|
1408
|
+
...t.max !== void 0 && { max: t.max },
|
|
1409
|
+
step: t.step ?? 1
|
|
1410
|
+
});
|
|
1411
|
+
break;
|
|
1412
|
+
case "float":
|
|
1413
|
+
r.push({
|
|
1414
|
+
key: t.key,
|
|
1415
|
+
label: t.label,
|
|
1416
|
+
type: "float",
|
|
1417
|
+
default: t.default,
|
|
1418
|
+
...t.min !== void 0 && { min: t.min },
|
|
1419
|
+
...t.max !== void 0 && { max: t.max },
|
|
1420
|
+
...t.step !== void 0 && { step: t.step }
|
|
1421
|
+
});
|
|
1422
|
+
break;
|
|
1423
|
+
case "bool":
|
|
1424
|
+
r.push({
|
|
1425
|
+
key: t.key,
|
|
1426
|
+
label: t.label,
|
|
1427
|
+
type: "bool",
|
|
1428
|
+
default: t.default
|
|
1429
|
+
});
|
|
1430
|
+
break;
|
|
1431
|
+
case "string":
|
|
1432
|
+
r.push({
|
|
1433
|
+
key: t.key,
|
|
1434
|
+
label: t.label,
|
|
1435
|
+
type: "select",
|
|
1436
|
+
default: t.default,
|
|
1437
|
+
options: t.options
|
|
1438
|
+
});
|
|
1439
|
+
break;
|
|
1440
|
+
case "source":
|
|
1441
|
+
r.push({
|
|
1442
|
+
key: t.key,
|
|
1443
|
+
label: t.label,
|
|
1444
|
+
type: "source",
|
|
1445
|
+
default: t.default,
|
|
1446
|
+
options: l
|
|
1447
|
+
});
|
|
1448
|
+
break;
|
|
1449
|
+
case "color": i.push({
|
|
1450
|
+
key: t.key,
|
|
1451
|
+
label: t.label,
|
|
1452
|
+
default: t.default,
|
|
1453
|
+
plotKeys: []
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
let a = (t) => e.inputs.find((e) => e.key === t).default, o = [], s = [], c = /* @__PURE__ */ new Map();
|
|
1457
|
+
return e.plots.forEach((t, r) => {
|
|
1458
|
+
let l = "value" in t.color ? t.color.value : a(t.color.input);
|
|
1459
|
+
"input" in t.color && i.find((e) => e.key === t.color.input).plotKeys.push(t.key);
|
|
1460
|
+
let u = {
|
|
1461
|
+
key: t.key,
|
|
1462
|
+
title: t.title
|
|
1463
|
+
};
|
|
1464
|
+
switch (t.kind) {
|
|
1465
|
+
case "line": {
|
|
1466
|
+
o.push(t.key);
|
|
1467
|
+
let i = t.colorWhen ? `${t.key}~when` : void 0;
|
|
1468
|
+
i && o.push(i), c.set(r, t.key), s.push({
|
|
1469
|
+
kind: "line",
|
|
1470
|
+
...u,
|
|
1471
|
+
col: t.key,
|
|
1472
|
+
color: l,
|
|
1473
|
+
...t.width !== void 0 && { width: t.width },
|
|
1474
|
+
...J(t, e, n),
|
|
1475
|
+
...i && { colorBy: {
|
|
1476
|
+
col: i,
|
|
1477
|
+
color: t.colorWhen.color
|
|
1478
|
+
} }
|
|
1479
|
+
});
|
|
1480
|
+
break;
|
|
1481
|
+
}
|
|
1482
|
+
case "barcolor":
|
|
1483
|
+
o.push(t.key), s.push({
|
|
1484
|
+
kind: "barcolor",
|
|
1485
|
+
...u,
|
|
1486
|
+
col: t.key,
|
|
1487
|
+
color: l
|
|
1488
|
+
});
|
|
1489
|
+
break;
|
|
1490
|
+
case "bgcolor":
|
|
1491
|
+
o.push(t.key), s.push({
|
|
1492
|
+
kind: "bgcolor",
|
|
1493
|
+
...u,
|
|
1494
|
+
col: t.key,
|
|
1495
|
+
color: l
|
|
1496
|
+
});
|
|
1497
|
+
break;
|
|
1498
|
+
case "histogram":
|
|
1499
|
+
o.push(t.key), s.push({
|
|
1500
|
+
kind: "histogram",
|
|
1501
|
+
...u,
|
|
1502
|
+
col: t.key,
|
|
1503
|
+
color: l,
|
|
1504
|
+
...t.colorDown && { colorDown: t.colorDown }
|
|
1505
|
+
});
|
|
1506
|
+
break;
|
|
1507
|
+
case "area":
|
|
1508
|
+
o.push(t.key), s.push({
|
|
1509
|
+
kind: "area",
|
|
1510
|
+
...u,
|
|
1511
|
+
col: t.key,
|
|
1512
|
+
color: l
|
|
1513
|
+
});
|
|
1514
|
+
break;
|
|
1515
|
+
case "markers":
|
|
1516
|
+
o.push(t.key), s.push({
|
|
1517
|
+
kind: "markers",
|
|
1518
|
+
...u,
|
|
1519
|
+
col: t.key,
|
|
1520
|
+
shape: t.shape,
|
|
1521
|
+
position: t.position,
|
|
1522
|
+
color: l,
|
|
1523
|
+
...J(t, e, n),
|
|
1524
|
+
...t.last !== void 0 && { last: t.last },
|
|
1525
|
+
...t.within !== void 0 && { within: t.within }
|
|
1526
|
+
});
|
|
1527
|
+
break;
|
|
1528
|
+
case "hline":
|
|
1529
|
+
if (t.column) {
|
|
1530
|
+
let e = `${t.key}~level`;
|
|
1531
|
+
o.push(e), c.set(r, e);
|
|
1532
|
+
}
|
|
1533
|
+
s.push({
|
|
1534
|
+
kind: "hline",
|
|
1535
|
+
...u,
|
|
1536
|
+
value: (r) => new Y(null, r, e, n).simpleNumber(t.expr),
|
|
1537
|
+
color: l,
|
|
1538
|
+
style: t.lineStyle ?? "dashed"
|
|
1539
|
+
});
|
|
1540
|
+
break;
|
|
1541
|
+
case "fill": s.push({
|
|
1542
|
+
kind: "fill",
|
|
1543
|
+
...u,
|
|
1544
|
+
a: c.get(t.a),
|
|
1545
|
+
b: c.get(t.b),
|
|
1546
|
+
color: l
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
}), {
|
|
1550
|
+
def: {
|
|
1551
|
+
id: `script-${q(t)}`,
|
|
1552
|
+
name: e.title,
|
|
1553
|
+
family: "tv",
|
|
1554
|
+
inputs: r,
|
|
1555
|
+
needs: e.needs,
|
|
1556
|
+
pane: e.overlay ? "overlay" : "new",
|
|
1557
|
+
...e.scale === "left" && { scale: "left" },
|
|
1558
|
+
columns: o,
|
|
1559
|
+
plots: s,
|
|
1560
|
+
build: (t, r) => new Y(t, r, e, n).build(),
|
|
1561
|
+
reference: "alphacharts script"
|
|
1562
|
+
},
|
|
1563
|
+
colorInputs: i
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function J(e, t, n) {
|
|
1567
|
+
if (e.offsetExpr) {
|
|
1568
|
+
let r = e.offsetExpr;
|
|
1569
|
+
return { offset: (i) => {
|
|
1570
|
+
let a = new Y(null, i, t, n).simpleNumber(r);
|
|
1571
|
+
if (!Number.isInteger(a) || a < -5e3 || a > 5e3) throw new h(`offset must be a whole number between -5000 and 5000, got ${a}`, e.span, "runtime");
|
|
1572
|
+
return a;
|
|
1573
|
+
} };
|
|
1574
|
+
}
|
|
1575
|
+
return e.offset === void 0 ? {} : { offset: e.offset };
|
|
1576
|
+
}
|
|
1577
|
+
var Y = class {
|
|
1578
|
+
g;
|
|
1579
|
+
p;
|
|
1580
|
+
plan;
|
|
1581
|
+
limits;
|
|
1582
|
+
simple = /* @__PURE__ */ new Map();
|
|
1583
|
+
pure = /* @__PURE__ */ new Map();
|
|
1584
|
+
stateCols = /* @__PURE__ */ new Map();
|
|
1585
|
+
hoisted = /* @__PURE__ */ new Map();
|
|
1586
|
+
mats = /* @__PURE__ */ new Map();
|
|
1587
|
+
inits = /* @__PURE__ */ new Map();
|
|
1588
|
+
S;
|
|
1589
|
+
ctx = { steps: 0 };
|
|
1590
|
+
tr = null;
|
|
1591
|
+
bpy = null;
|
|
1592
|
+
fx;
|
|
1593
|
+
joinedCols = /* @__PURE__ */ new Map();
|
|
1594
|
+
sameCols = /* @__PURE__ */ new Map();
|
|
1595
|
+
constructor(e, t, n, r) {
|
|
1596
|
+
this.g = e, this.p = t, this.plan = n, this.limits = r, this.fx = {
|
|
1597
|
+
g: e,
|
|
1598
|
+
key: null
|
|
1599
|
+
}, this.S = new Float64Array(n.slots);
|
|
1600
|
+
for (let e of n.body) e.k === "decl" && e.sym && this.inits.set(e.sym, e.init);
|
|
1601
|
+
}
|
|
1602
|
+
G() {
|
|
1603
|
+
if (!this.fx.g) throw new h("a series is not available here");
|
|
1604
|
+
return this.fx.g;
|
|
1605
|
+
}
|
|
1606
|
+
feedDom(e) {
|
|
1607
|
+
let t = e.dom;
|
|
1608
|
+
return t !== void 0 && t !== "@" && t !== "~" ? t : null;
|
|
1609
|
+
}
|
|
1610
|
+
inFeed(e, t) {
|
|
1611
|
+
let n = this.fx;
|
|
1612
|
+
this.fx = {
|
|
1613
|
+
g: this.g.feed(e).graph,
|
|
1614
|
+
key: e
|
|
1615
|
+
};
|
|
1616
|
+
try {
|
|
1617
|
+
return t();
|
|
1618
|
+
} finally {
|
|
1619
|
+
this.fx = n;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
joinedCol(e, t) {
|
|
1623
|
+
let n = this.inFeed(e, () => this.colOf(t)), r = this.joinedCols.get(n);
|
|
1624
|
+
return r || this.joinedCols.set(n, r = this.G().joined(e, n)), r;
|
|
1625
|
+
}
|
|
1626
|
+
rawCol(e) {
|
|
1627
|
+
let t = e.bound[1], n = t && t.k === "str" ? t.value : "close";
|
|
1628
|
+
if (this.fx.key !== null) return this.G()[n];
|
|
1629
|
+
let r = `${e.feed}\u0002${n}`, i = this.sameCols.get(r);
|
|
1630
|
+
return i || this.sameCols.set(r, i = this.G().external(e.feed, n)), i;
|
|
1631
|
+
}
|
|
1632
|
+
build() {
|
|
1633
|
+
let e = this.G(), t = [];
|
|
1634
|
+
for (let e of this.plan.body) switch (e.k) {
|
|
1635
|
+
case "decl": {
|
|
1636
|
+
let n = e.sym;
|
|
1637
|
+
if (n.kind === "state") t.push(e);
|
|
1638
|
+
else if (n.kind === "pure") {
|
|
1639
|
+
let t = this.feedDom(e.init);
|
|
1640
|
+
this.pure.set(n, t ? this.inFeed(t, () => this.colOf(e.init)) : this.colOf(e.init));
|
|
1641
|
+
}
|
|
1642
|
+
break;
|
|
1643
|
+
}
|
|
1644
|
+
case "tuple": {
|
|
1645
|
+
let t = this.feedDom(e.init), n = t ? this.inFeed(t, () => this.hoist(e.init)) : this.hoist(e.init);
|
|
1646
|
+
e.syms.forEach((e, t) => this.pure.set(e, n[t]));
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
case "assign":
|
|
1650
|
+
case "if":
|
|
1651
|
+
case "for": t.push(e);
|
|
1652
|
+
}
|
|
1653
|
+
(t.length > 0 || this.plan.stateVars.length > 0) && this.buildExec(t);
|
|
1654
|
+
let n = [];
|
|
1655
|
+
for (let t of this.plan.plots) if (t.kind === "hline" && t.column) {
|
|
1656
|
+
let r = this.simpleNumber(t.expr);
|
|
1657
|
+
n.push(e.calc(() => r));
|
|
1658
|
+
} else t.kind !== "hline" && t.kind !== "fill" && (t.kind === "markers" || t.kind === "barcolor" || t.kind === "bgcolor" ? n.push(this.markers(t)) : (n.push(this.colOf(t.expr)), t.colorWhen && n.push(this.colOf(t.colorWhen.cond))));
|
|
1659
|
+
let r = e.cols.length;
|
|
1660
|
+
for (let t of e.feeds.values()) r += t.graph.cols.length;
|
|
1661
|
+
if (r > this.limits.maxColumns) throw new h(`the script needs ${r} columns; the limit is ${this.limits.maxColumns}`);
|
|
1662
|
+
return n;
|
|
1663
|
+
}
|
|
1664
|
+
markers(e) {
|
|
1665
|
+
let t = e.expr, n = this.ev(t, "col"), r = e.position === "value";
|
|
1666
|
+
return this.G().calc(r ? n : (e) => K(n(e)) ? 1 : G);
|
|
1667
|
+
}
|
|
1668
|
+
buildExec(e) {
|
|
1669
|
+
let t = this.G();
|
|
1670
|
+
for (let e of this.plan.stateVars) this.stateCols.set(e, t.col());
|
|
1671
|
+
let n = e.map((e) => this.stmt(e)), r = this.S, i = this.ctx;
|
|
1672
|
+
this.limits.maxSteps;
|
|
1673
|
+
let a = this.plan.stateVars.map((e) => ({
|
|
1674
|
+
slot: e.slot,
|
|
1675
|
+
col: this.stateCols.get(e),
|
|
1676
|
+
restore: !!e.isVar
|
|
1677
|
+
}));
|
|
1678
|
+
t.node((e, t, o) => {
|
|
1679
|
+
for (let e = t; e < o; e++) {
|
|
1680
|
+
i.steps = 0;
|
|
1681
|
+
for (let t of a) t.restore && (r[t.slot] = e > 0 ? t.col.v[e - 1] : G);
|
|
1682
|
+
for (let t of n) t(e);
|
|
1683
|
+
for (let t of a) t.col.v[e] = r[t.slot];
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
step(e) {
|
|
1688
|
+
if (++this.ctx.steps > this.limits.maxSteps) throw new h(`execution budget exceeded: more than ${this.limits.maxSteps} loop steps in one bar`, e, "budget");
|
|
1689
|
+
}
|
|
1690
|
+
stmt(e) {
|
|
1691
|
+
let t = this.S;
|
|
1692
|
+
switch (e.k) {
|
|
1693
|
+
case "decl": {
|
|
1694
|
+
let n = this.ev(e.init, "exec"), r = e.sym.slot;
|
|
1695
|
+
return e.isVar ? (e) => {
|
|
1696
|
+
e === 0 && (t[r] = n(e));
|
|
1697
|
+
} : (e) => {
|
|
1698
|
+
t[r] = n(e);
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
case "assign": {
|
|
1702
|
+
let n = this.ev(e.value, "exec"), r = e.sym.slot;
|
|
1703
|
+
switch (e.op) {
|
|
1704
|
+
case ":=": return (e) => {
|
|
1705
|
+
t[r] = n(e);
|
|
1706
|
+
};
|
|
1707
|
+
case "+=": return (e) => {
|
|
1708
|
+
t[r] = t[r] + n(e);
|
|
1709
|
+
};
|
|
1710
|
+
case "-=": return (e) => {
|
|
1711
|
+
t[r] = t[r] - n(e);
|
|
1712
|
+
};
|
|
1713
|
+
case "*=": return (e) => {
|
|
1714
|
+
t[r] = t[r] * n(e);
|
|
1715
|
+
};
|
|
1716
|
+
case "/=": return (e) => {
|
|
1717
|
+
let i = n(e);
|
|
1718
|
+
t[r] = i === 0 ? G : t[r] / i;
|
|
1719
|
+
};
|
|
1720
|
+
default: return (e) => {
|
|
1721
|
+
let i = n(e);
|
|
1722
|
+
t[r] = i === 0 ? G : t[r] % i;
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
case "if": {
|
|
1727
|
+
let t = this.ev(e.cond, "exec"), n = e.then.map((e) => this.stmt(e)), r = (e.else ?? []).map((e) => this.stmt(e));
|
|
1728
|
+
return (e) => {
|
|
1729
|
+
for (let i of K(t(e)) ? n : r) i(e);
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
case "for": {
|
|
1733
|
+
let n = this.ev(e.lo, "exec"), r = this.ev(e.hi, "exec"), i = e.by ? this.ev(e.by, "exec") : null, a = e.body.map((e) => this.stmt(e)), o = e.sym.slot, s = this.limits.maxLoopIterations;
|
|
1734
|
+
return (c) => {
|
|
1735
|
+
let l = n(c), u = r(c), d = i ? i(c) : l <= u ? 1 : -1;
|
|
1736
|
+
if (!Number.isFinite(l) || !Number.isFinite(u) || !Number.isFinite(d) || d === 0) throw new h("loop bounds must be finite and the step non-zero", e, "runtime");
|
|
1737
|
+
if (i && (d > 0 && l > u || d < 0 && l < u)) return;
|
|
1738
|
+
!i && l > u && (d = -1);
|
|
1739
|
+
let f = Math.floor(Math.abs(u - l) / Math.abs(d)) + 1;
|
|
1740
|
+
if (f > s) throw new h(`this loop would run ${f} times; the limit is ${s} iterations per loop`, e, "budget");
|
|
1741
|
+
let p = l;
|
|
1742
|
+
for (let n = 0; n < f; n++, p += d) {
|
|
1743
|
+
this.step(e), t[o] = p;
|
|
1744
|
+
for (let e of a) e(c);
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
default: return () => {};
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
srcCol(e) {
|
|
1752
|
+
let t = this.G();
|
|
1753
|
+
switch (e) {
|
|
1754
|
+
case "volume": return t.volume;
|
|
1755
|
+
case "time": return t.time;
|
|
1756
|
+
case "bar_index": return t.calc((e) => e);
|
|
1757
|
+
case "bars_per_year": return this.fx.key === null ? this.bpy ??= t.barsPerYear() : t.barsPerYear();
|
|
1758
|
+
default: return t.src(e);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
colOf(e) {
|
|
1762
|
+
let t = this.feedDom(e);
|
|
1763
|
+
if (t && this.fx.key === null) return this.joinedCol(t, e);
|
|
1764
|
+
if (e.k === "call" && e.fn === "outside") return this.rawCol(e);
|
|
1765
|
+
let n = this.G();
|
|
1766
|
+
if (e.k === "id" && e.sym) {
|
|
1767
|
+
let t = e.sym;
|
|
1768
|
+
if (t.kind === "builtin") return this.memo(e, () => this.srcCol(t.key));
|
|
1769
|
+
if (t.kind === "pure") return this.pure.get(t);
|
|
1770
|
+
if (t.kind === "state") return this.stateCols.get(t);
|
|
1771
|
+
if (t.kind === "input" && t.ty.b === "float" && t.ty.q === "series") return this.srcCol(String(this.p[t.key]));
|
|
1772
|
+
}
|
|
1773
|
+
return e.k === "call" && this.isHoisted(e) ? this.hoist(e)[0] : this.memo(e, () => {
|
|
1774
|
+
let t = this.ev(e, "col");
|
|
1775
|
+
return n.calc(t);
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
memo(e, t) {
|
|
1779
|
+
let n = this.mats.get(e);
|
|
1780
|
+
return n || this.mats.set(e, n = t()), n;
|
|
1781
|
+
}
|
|
1782
|
+
isHoisted(e) {
|
|
1783
|
+
let t = a.get(e.fn);
|
|
1784
|
+
return !!t && (t.op === "kernel" || t.op === "study" || t.name === "ta.bb");
|
|
1785
|
+
}
|
|
1786
|
+
num(e, t, n, r, i = !0) {
|
|
1787
|
+
let a = this.ev(e, "col")(0);
|
|
1788
|
+
if (!Number.isFinite(a) || i && !Number.isInteger(a) || a < n || a > r) throw new h(`${t} must be ${i ? "an integer" : "a number"} between ${n} and ${r}, got ${a}`, e, "build");
|
|
1789
|
+
return a;
|
|
1790
|
+
}
|
|
1791
|
+
hoist(e) {
|
|
1792
|
+
let t = this.hoisted.get(e);
|
|
1793
|
+
if (t) return t;
|
|
1794
|
+
let n = a.get(e.fn), r = e.bound, i = this.G(), o = this.limits.maxPeriod, s, c = (e) => this.num(r[e], `the length of ${n.name}`, 1, o);
|
|
1795
|
+
switch (n.name) {
|
|
1796
|
+
case "ta.sma":
|
|
1797
|
+
s = [i.sma(this.colOf(r[0]), c(1))];
|
|
1798
|
+
break;
|
|
1799
|
+
case "ta.ema":
|
|
1800
|
+
s = [i.ema(this.colOf(r[0]), c(1))];
|
|
1801
|
+
break;
|
|
1802
|
+
case "ta.wma":
|
|
1803
|
+
s = [i.wma(this.colOf(r[0]), c(1))];
|
|
1804
|
+
break;
|
|
1805
|
+
case "ta.rma":
|
|
1806
|
+
s = [i.rma(this.colOf(r[0]), c(1))];
|
|
1807
|
+
break;
|
|
1808
|
+
case "ta.highest":
|
|
1809
|
+
s = [i.highest(this.colOf(r[0]), c(1))];
|
|
1810
|
+
break;
|
|
1811
|
+
case "ta.lowest":
|
|
1812
|
+
s = [i.lowest(this.colOf(r[0]), c(1))];
|
|
1813
|
+
break;
|
|
1814
|
+
case "ta.highestbars":
|
|
1815
|
+
s = [i.highestBars(this.colOf(r[0]), c(1))];
|
|
1816
|
+
break;
|
|
1817
|
+
case "ta.lowestbars":
|
|
1818
|
+
s = [i.lowestBars(this.colOf(r[0]), c(1))];
|
|
1819
|
+
break;
|
|
1820
|
+
case "ta.stdev":
|
|
1821
|
+
case "ta.variance": {
|
|
1822
|
+
let e = !!r[2] && K(this.ev(r[2], "col")(0)), t = e ? this.num(r[1], `the length of ${n.name} with sample=true`, 2, o) : c(1);
|
|
1823
|
+
s = [n.name === "ta.stdev" ? i.stdev(this.colOf(r[0]), t, e) : i.variance(this.colOf(r[0]), t, e)];
|
|
1824
|
+
break;
|
|
1825
|
+
}
|
|
1826
|
+
case "ta.sum":
|
|
1827
|
+
case "math.sum":
|
|
1828
|
+
s = [i.sum(this.colOf(r[0]), c(1))];
|
|
1829
|
+
break;
|
|
1830
|
+
case "ta.linreg": {
|
|
1831
|
+
let e = this.num(r[1], "the length of ta.linreg", 2, o);
|
|
1832
|
+
s = [i.linreg(this.colOf(r[0]), e).value];
|
|
1833
|
+
break;
|
|
1834
|
+
}
|
|
1835
|
+
case "ta.atr":
|
|
1836
|
+
s = [i.atr(c(0))];
|
|
1837
|
+
break;
|
|
1838
|
+
case "ta.bb": {
|
|
1839
|
+
let e = a.get("ta.bbands").study, t = this.num(r[2], "mult of ta.bb", 0, 100, !1), n = this.runStudy(e, {
|
|
1840
|
+
length: c(1),
|
|
1841
|
+
multUp: t,
|
|
1842
|
+
multDn: t
|
|
1843
|
+
}, this.colOf(r[0]));
|
|
1844
|
+
s = [
|
|
1845
|
+
n[1],
|
|
1846
|
+
n[0],
|
|
1847
|
+
n[2]
|
|
1848
|
+
];
|
|
1849
|
+
break;
|
|
1850
|
+
}
|
|
1851
|
+
default: s = this.hoistStudy(n, e);
|
|
1852
|
+
}
|
|
1853
|
+
return this.hoisted.set(e, s), s;
|
|
1854
|
+
}
|
|
1855
|
+
hoistStudy(e, t) {
|
|
1856
|
+
let n = e.study, r = {}, i = null;
|
|
1857
|
+
e.params.forEach((n, a) => {
|
|
1858
|
+
let o = t.bound[a];
|
|
1859
|
+
if (!o) return;
|
|
1860
|
+
let s = n.input;
|
|
1861
|
+
if (s.type === "source") {
|
|
1862
|
+
i = this.colOf(o);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
if (s.type === "select") {
|
|
1866
|
+
r[s.key] = this.str(o);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (s.type === "bool") {
|
|
1870
|
+
r[s.key] = K(this.ev(o, "col")(0));
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
let c = this.ev(o, "col")(0);
|
|
1874
|
+
if (!Number.isFinite(c) || s.type === "int" && !Number.isInteger(c) || s.min !== void 0 && c < s.min || s.max !== void 0 && c > s.max) throw new h(`${s.key} of ${e.name} must be ${s.type === "int" ? "an integer" : "a number"}${s.min === void 0 ? "" : ` >= ${s.min}`}${s.max === void 0 ? "" : ` <= ${s.max}`}, got ${c}`, o);
|
|
1875
|
+
r[s.key] = c;
|
|
1876
|
+
});
|
|
1877
|
+
try {
|
|
1878
|
+
return this.runStudy(n, r, i);
|
|
1879
|
+
} catch (n) {
|
|
1880
|
+
throw n instanceof h ? n : new h(`${e.name}: ${n instanceof Error ? n.message : String(n)}`, t);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
runStudy(e, t, n) {
|
|
1884
|
+
let r = f(e, t), i = Object.create(this.G());
|
|
1885
|
+
return n && (i.src = () => n), [...e.build(i, r)];
|
|
1886
|
+
}
|
|
1887
|
+
str(e) {
|
|
1888
|
+
switch (e.k) {
|
|
1889
|
+
case "str": return e.value;
|
|
1890
|
+
case "color": return e.value;
|
|
1891
|
+
case "mem": return String(e.cv);
|
|
1892
|
+
case "id": {
|
|
1893
|
+
let t = e.sym;
|
|
1894
|
+
if (t.kind === "input") return String(this.p[t.key]);
|
|
1895
|
+
if (t.kind === "simple") return String(this.simpleVal(t));
|
|
1896
|
+
break;
|
|
1897
|
+
}
|
|
1898
|
+
case "tern": return K(this.ev(e.c, "col")(0)) ? this.str(e.a) : this.str(e.b);
|
|
1899
|
+
case "call": if (e.fn === "color.new") return String(this.simpleVal2(e));
|
|
1900
|
+
}
|
|
1901
|
+
throw new h("this string cannot be evaluated here", e);
|
|
1902
|
+
}
|
|
1903
|
+
simpleVal2(e) {
|
|
1904
|
+
return String(e.fn);
|
|
1905
|
+
}
|
|
1906
|
+
simpleVal(e) {
|
|
1907
|
+
let t = this.simple.get(e);
|
|
1908
|
+
if (t !== void 0) return t;
|
|
1909
|
+
let n = this.inits.get(e), r = n.ty.b === "string" || n.ty.b === "color" ? this.str(n) : this.ev(n, "col")(0);
|
|
1910
|
+
return this.simple.set(e, r), r;
|
|
1911
|
+
}
|
|
1912
|
+
simpleNumber(e) {
|
|
1913
|
+
return this.ev(e, "col")(0);
|
|
1914
|
+
}
|
|
1915
|
+
hist(e, t, n, r) {
|
|
1916
|
+
let i = this.S, a = (e) => {
|
|
1917
|
+
throw new h(`the history offset must be an integer between 0 and ${w}, got ${e}`, r, "runtime");
|
|
1918
|
+
};
|
|
1919
|
+
if (e.k === "id" && e.sym?.kind === "state" && n === "exec") {
|
|
1920
|
+
let n = e.sym.slot, r = this.stateCols.get(e.sym);
|
|
1921
|
+
return (e) => {
|
|
1922
|
+
let o = t(e);
|
|
1923
|
+
return !Number.isInteger(o) || o < 0 || o > 1e6 ? a(o) : o === 0 ? i[n] : e - o >= 0 ? r.v[e - o] : G;
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
let o = this.colOf(e);
|
|
1927
|
+
return (e) => {
|
|
1928
|
+
let n = t(e);
|
|
1929
|
+
return !Number.isInteger(n) || n < 0 || n > 1e6 ? a(n) : e - n >= 0 ? o.v[e - n] : G;
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
ev(e, t) {
|
|
1933
|
+
let n = this.S, r = this.feedDom(e);
|
|
1934
|
+
if (r && this.fx.key === null) {
|
|
1935
|
+
let t = this.joinedCol(r, e);
|
|
1936
|
+
return (e) => t.v[e];
|
|
1937
|
+
}
|
|
1938
|
+
switch (e.k) {
|
|
1939
|
+
case "num": {
|
|
1940
|
+
let t = e.value;
|
|
1941
|
+
return () => t;
|
|
1942
|
+
}
|
|
1943
|
+
case "bool": {
|
|
1944
|
+
let t = +!!e.value;
|
|
1945
|
+
return () => t;
|
|
1946
|
+
}
|
|
1947
|
+
case "str":
|
|
1948
|
+
case "color":
|
|
1949
|
+
case "arr": throw new h("a string or color is not a number", e);
|
|
1950
|
+
case "id": {
|
|
1951
|
+
let r = e.sym;
|
|
1952
|
+
if (!r) return () => G;
|
|
1953
|
+
switch (r.kind) {
|
|
1954
|
+
case "builtin": {
|
|
1955
|
+
if (r.key === "bar_index") return (e) => e;
|
|
1956
|
+
let e = this.srcCol(r.key);
|
|
1957
|
+
return (t) => e.v[t];
|
|
1958
|
+
}
|
|
1959
|
+
case "input": {
|
|
1960
|
+
if (r.ty.b === "float" && r.ty.q === "series") {
|
|
1961
|
+
let e = this.srcCol(String(this.p[r.key]));
|
|
1962
|
+
return (t) => e.v[t];
|
|
1963
|
+
}
|
|
1964
|
+
let t = this.p[r.key];
|
|
1965
|
+
if (typeof t == "string") throw new h("a string is not a number", e);
|
|
1966
|
+
let n = typeof t == "boolean" ? +!!t : t;
|
|
1967
|
+
return () => n;
|
|
1968
|
+
}
|
|
1969
|
+
case "simple": {
|
|
1970
|
+
let t = this.simpleVal(r);
|
|
1971
|
+
if (typeof t == "string") throw new h("a string is not a number", e);
|
|
1972
|
+
return () => t;
|
|
1973
|
+
}
|
|
1974
|
+
case "pure": {
|
|
1975
|
+
let e = this.pure.get(r);
|
|
1976
|
+
return (t) => e.v[t];
|
|
1977
|
+
}
|
|
1978
|
+
case "state": {
|
|
1979
|
+
if (t === "exec") {
|
|
1980
|
+
let e = r.slot;
|
|
1981
|
+
return () => n[e];
|
|
1982
|
+
}
|
|
1983
|
+
let e = this.stateCols.get(r);
|
|
1984
|
+
return (t) => e.v[t];
|
|
1985
|
+
}
|
|
1986
|
+
case "local":
|
|
1987
|
+
case "loop": {
|
|
1988
|
+
let e = r.slot;
|
|
1989
|
+
return () => n[e];
|
|
1990
|
+
}
|
|
1991
|
+
default: throw new h("this name has no numeric value", e);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
case "mem":
|
|
1995
|
+
if (typeof e.cv == "number") {
|
|
1996
|
+
let t = e.cv;
|
|
1997
|
+
return () => t;
|
|
1998
|
+
}
|
|
1999
|
+
if (e.obj.k === "id" && e.name === "tr") {
|
|
2000
|
+
let e = this.tr ??= this.G().tr();
|
|
2001
|
+
return (t) => e.v[t];
|
|
2002
|
+
}
|
|
2003
|
+
throw new h("this name has no numeric value", e);
|
|
2004
|
+
case "un": {
|
|
2005
|
+
let n = this.ev(e.x, t);
|
|
2006
|
+
return e.op === "-" ? (e) => -n(e) : e.op === "+" ? n : (e) => +!K(n(e));
|
|
2007
|
+
}
|
|
2008
|
+
case "bin": return this.binary(e, t);
|
|
2009
|
+
case "tern": {
|
|
2010
|
+
let n = this.ev(e.c, t), r = this.ev(e.a, t), i = this.ev(e.b, t);
|
|
2011
|
+
return (e) => K(n(e)) ? r(e) : i(e);
|
|
2012
|
+
}
|
|
2013
|
+
case "idx": {
|
|
2014
|
+
let n = this.ev(e.i, t);
|
|
2015
|
+
return this.hist(e.x, n, t, e);
|
|
2016
|
+
}
|
|
2017
|
+
case "call": return this.callEv(e, t);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
binary(e, t) {
|
|
2021
|
+
let n = e.a.ty, r = e.b.ty;
|
|
2022
|
+
if ((e.op === "==" || e.op === "!=") && (n.b === "string" || n.b === "color") && n.b === r.b) {
|
|
2023
|
+
let t = this.str(e.a) === this.str(e.b), n = +(e.op === "==" === t);
|
|
2024
|
+
return () => n;
|
|
2025
|
+
}
|
|
2026
|
+
let i = this.ev(e.a, t), a = this.ev(e.b, t);
|
|
2027
|
+
switch (e.op) {
|
|
2028
|
+
case "+": return (e) => i(e) + a(e);
|
|
2029
|
+
case "-": return (e) => i(e) - a(e);
|
|
2030
|
+
case "*": return (e) => i(e) * a(e);
|
|
2031
|
+
case "/": return (e) => {
|
|
2032
|
+
let t = a(e);
|
|
2033
|
+
return t === 0 ? G : i(e) / t;
|
|
2034
|
+
};
|
|
2035
|
+
case "%": return (e) => {
|
|
2036
|
+
let t = a(e);
|
|
2037
|
+
return t === 0 ? G : i(e) % t;
|
|
2038
|
+
};
|
|
2039
|
+
case "<": return (e) => +(i(e) < a(e));
|
|
2040
|
+
case "<=": return (e) => +(i(e) <= a(e));
|
|
2041
|
+
case ">": return (e) => +(i(e) > a(e));
|
|
2042
|
+
case ">=": return (e) => +(i(e) >= a(e));
|
|
2043
|
+
case "==": return (e) => +(i(e) === a(e));
|
|
2044
|
+
case "!=": return (e) => {
|
|
2045
|
+
let t = i(e), n = a(e);
|
|
2046
|
+
return +(t === t && n === n && t !== n);
|
|
2047
|
+
};
|
|
2048
|
+
case "and": return (e) => K(i(e)) && K(a(e)) ? 1 : 0;
|
|
2049
|
+
case "or": return (e) => K(i(e)) || K(a(e)) ? 1 : 0;
|
|
2050
|
+
default: throw new h(`unknown operator ${e.op}`, e);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
callEv(e, t) {
|
|
2054
|
+
let n = a.get(e.fn), r = e.bound;
|
|
2055
|
+
if (n.name === "outside") {
|
|
2056
|
+
let t = this.rawCol(e);
|
|
2057
|
+
return (e) => t.v[e];
|
|
2058
|
+
}
|
|
2059
|
+
if (this.isHoisted(e)) {
|
|
2060
|
+
let t = this.hoist(e)[0];
|
|
2061
|
+
return (e) => t.v[e];
|
|
2062
|
+
}
|
|
2063
|
+
switch (n.name) {
|
|
2064
|
+
case "nz": {
|
|
2065
|
+
let e = this.ev(r[0], t), n = r[1] ? this.ev(r[1], t) : () => 0;
|
|
2066
|
+
return (t) => {
|
|
2067
|
+
let r = e(t);
|
|
2068
|
+
return r === r ? r : n(t);
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
case "na": {
|
|
2072
|
+
let e = this.ev(r[0], t);
|
|
2073
|
+
return (t) => {
|
|
2074
|
+
let n = e(t);
|
|
2075
|
+
return n === n ? 0 : 1;
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
case "ta.change": {
|
|
2079
|
+
let n = this.ev(r[0], t), i = this.hist(r[0], r[1] ? this.ev(r[1], t) : () => 1, t, e);
|
|
2080
|
+
return (e) => n(e) - i(e);
|
|
2081
|
+
}
|
|
2082
|
+
case "ta.crossover":
|
|
2083
|
+
case "ta.crossunder":
|
|
2084
|
+
case "ta.cross": {
|
|
2085
|
+
let i = this.ev(r[0], t), a = this.ev(r[1], t), o = this.hist(r[0], () => 1, t, e), s = this.hist(r[1], () => 1, t, e), c = (e) => i(e) > a(e) && o(e) <= s(e), l = (e) => i(e) < a(e) && o(e) >= s(e);
|
|
2086
|
+
return n.name === "ta.crossover" ? (e) => +!!c(e) : n.name === "ta.crossunder" ? (e) => +!!l(e) : (e) => c(e) || l(e) ? 1 : 0;
|
|
2087
|
+
}
|
|
2088
|
+
case "color.new": throw new h("a color is not a number", e);
|
|
2089
|
+
default:
|
|
2090
|
+
if (n.op === "scalar") {
|
|
2091
|
+
let e = n.fn, i = r.map((e) => this.ev(e, t));
|
|
2092
|
+
if (i.length === 1) {
|
|
2093
|
+
let t = i[0];
|
|
2094
|
+
return (n) => e(t(n));
|
|
2095
|
+
}
|
|
2096
|
+
let a = i[0], o = i[1];
|
|
2097
|
+
return (t) => e(a(t), o(t));
|
|
2098
|
+
}
|
|
2099
|
+
throw new h(`${n.name} cannot be used as a value here`, e);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
2103
|
+
//#endregion
|
|
2104
|
+
//#region src/compile.ts
|
|
2105
|
+
function X(e, t = C) {
|
|
2106
|
+
if (typeof e != "string") return {
|
|
2107
|
+
ok: !1,
|
|
2108
|
+
diagnostics: [{
|
|
2109
|
+
severity: "error",
|
|
2110
|
+
message: "the script must be a string",
|
|
2111
|
+
line: 1,
|
|
2112
|
+
column: 1,
|
|
2113
|
+
endLine: 1,
|
|
2114
|
+
endColumn: 1,
|
|
2115
|
+
from: 0,
|
|
2116
|
+
to: 0
|
|
2117
|
+
}]
|
|
2118
|
+
};
|
|
2119
|
+
if (e.length > t.maxSourceChars) return {
|
|
2120
|
+
ok: !1,
|
|
2121
|
+
diagnostics: [{
|
|
2122
|
+
severity: "error",
|
|
2123
|
+
message: `the script is ${e.length} characters; the limit is ${t.maxSourceChars}`,
|
|
2124
|
+
line: 1,
|
|
2125
|
+
column: 1,
|
|
2126
|
+
endLine: 1,
|
|
2127
|
+
endColumn: 1,
|
|
2128
|
+
from: 0,
|
|
2129
|
+
to: 0
|
|
2130
|
+
}]
|
|
2131
|
+
};
|
|
2132
|
+
let n = new m(e), r = S(e, n), i = null;
|
|
2133
|
+
if (n.errorCount === 0 && (i = W(r, n, t)), n.errorCount === 0 && i && i.plots.length === 0 && n.error("the script draws nothing: add plot(...)", {
|
|
2134
|
+
from: 0,
|
|
2135
|
+
to: Math.min(e.length, 1)
|
|
2136
|
+
}), n.errorCount > 0 || !i) return {
|
|
2137
|
+
ok: !1,
|
|
2138
|
+
diagnostics: n.list
|
|
2139
|
+
};
|
|
2140
|
+
let { def: a, colorInputs: o } = ee(i, e, t);
|
|
2141
|
+
return {
|
|
2142
|
+
ok: !0,
|
|
2143
|
+
study: a,
|
|
2144
|
+
colorInputs: o,
|
|
2145
|
+
inputs: i.inputs,
|
|
2146
|
+
title: i.title,
|
|
2147
|
+
diagnostics: n.list,
|
|
2148
|
+
source: e,
|
|
2149
|
+
plan: {
|
|
2150
|
+
overlay: i.overlay,
|
|
2151
|
+
scale: i.scale,
|
|
2152
|
+
dependencies: i.dependencies,
|
|
2153
|
+
needs: i.needs,
|
|
2154
|
+
plots: i.plots.map((e) => ({
|
|
2155
|
+
kind: e.kind,
|
|
2156
|
+
title: e.title,
|
|
2157
|
+
expr: e.expr ?? null
|
|
2158
|
+
})),
|
|
2159
|
+
body: i.body
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
//#endregion
|
|
2164
|
+
//#region src/sandbox.ts
|
|
2165
|
+
var Z = [
|
|
2166
|
+
"fetch",
|
|
2167
|
+
"XMLHttpRequest",
|
|
2168
|
+
"WebSocket",
|
|
2169
|
+
"WebTransport",
|
|
2170
|
+
"EventSource",
|
|
2171
|
+
"importScripts",
|
|
2172
|
+
"indexedDB",
|
|
2173
|
+
"caches",
|
|
2174
|
+
"BroadcastChannel",
|
|
2175
|
+
"SharedWorker",
|
|
2176
|
+
"Worker",
|
|
2177
|
+
"MessageChannel",
|
|
2178
|
+
"Request",
|
|
2179
|
+
"Response",
|
|
2180
|
+
"localStorage",
|
|
2181
|
+
"sessionStorage",
|
|
2182
|
+
"FileReader",
|
|
2183
|
+
"FileReaderSync",
|
|
2184
|
+
"Notification",
|
|
2185
|
+
"navigator",
|
|
2186
|
+
"location",
|
|
2187
|
+
"document",
|
|
2188
|
+
"window",
|
|
2189
|
+
"XMLHttpRequestUpload",
|
|
2190
|
+
"RTCPeerConnection"
|
|
2191
|
+
];
|
|
2192
|
+
function te(e = globalThis) {
|
|
2193
|
+
let t = [], n = [];
|
|
2194
|
+
for (let r of Z) try {
|
|
2195
|
+
Object.defineProperty(e, r, {
|
|
2196
|
+
value: void 0,
|
|
2197
|
+
writable: !1,
|
|
2198
|
+
configurable: !1,
|
|
2199
|
+
enumerable: !1
|
|
2200
|
+
}), t.push(r);
|
|
2201
|
+
} catch {
|
|
2202
|
+
n.push(r);
|
|
2203
|
+
}
|
|
2204
|
+
let r = e, i = (e) => () => {
|
|
2205
|
+
throw Error(`${e} is not available to scripts`);
|
|
2206
|
+
};
|
|
2207
|
+
try {
|
|
2208
|
+
Object.defineProperty(e, "eval", {
|
|
2209
|
+
value: i("eval"),
|
|
2210
|
+
writable: !1,
|
|
2211
|
+
configurable: !1
|
|
2212
|
+
});
|
|
2213
|
+
} catch {
|
|
2214
|
+
n.push("eval");
|
|
2215
|
+
}
|
|
2216
|
+
if (r.Math) try {
|
|
2217
|
+
Object.defineProperty(r.Math, "random", {
|
|
2218
|
+
value: i("Math.random"),
|
|
2219
|
+
writable: !1,
|
|
2220
|
+
configurable: !1
|
|
2221
|
+
});
|
|
2222
|
+
} catch {
|
|
2223
|
+
n.push("Math.random");
|
|
2224
|
+
}
|
|
2225
|
+
if (r.Date) try {
|
|
2226
|
+
Object.defineProperty(r.Date, "now", {
|
|
2227
|
+
value: i("Date.now"),
|
|
2228
|
+
writable: !1,
|
|
2229
|
+
configurable: !1
|
|
2230
|
+
});
|
|
2231
|
+
} catch {
|
|
2232
|
+
n.push("Date.now");
|
|
2233
|
+
}
|
|
2234
|
+
let a = e;
|
|
2235
|
+
for (let e of [
|
|
2236
|
+
"Object",
|
|
2237
|
+
"Array",
|
|
2238
|
+
"Function",
|
|
2239
|
+
"String",
|
|
2240
|
+
"Number",
|
|
2241
|
+
"Boolean"
|
|
2242
|
+
]) {
|
|
2243
|
+
let t = a[e]?.prototype;
|
|
2244
|
+
t && Object.freeze(t);
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
blocked: t,
|
|
2248
|
+
missing: n
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
var ne = () => new Promise((e) => setTimeout(e, 0)), re = 65536;
|
|
2252
|
+
function Q(e) {
|
|
2253
|
+
return e instanceof h ? {
|
|
2254
|
+
message: e.message,
|
|
2255
|
+
...e.span && {
|
|
2256
|
+
from: e.span.from,
|
|
2257
|
+
to: e.span.to
|
|
2258
|
+
},
|
|
2259
|
+
kind: e.kind
|
|
2260
|
+
} : {
|
|
2261
|
+
message: e instanceof Error ? e.message : String(e),
|
|
2262
|
+
kind: "build"
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
function $(e, t, n = !1) {
|
|
2266
|
+
return {
|
|
2267
|
+
...e,
|
|
2268
|
+
build: (r, i) => {
|
|
2269
|
+
let a = r;
|
|
2270
|
+
n && (a = Object.create(r), a.node = () => r.node(() => {}));
|
|
2271
|
+
let o = e.build(a, i);
|
|
2272
|
+
return r.node((e, n, r) => {
|
|
2273
|
+
t && r - n === 1 && t(n);
|
|
2274
|
+
}), o;
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
function ie(e) {
|
|
2279
|
+
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set();
|
|
2280
|
+
return (a) => {
|
|
2281
|
+
let o = a;
|
|
2282
|
+
switch (o.type) {
|
|
2283
|
+
case "cancel":
|
|
2284
|
+
r.has(o.id) && n.add(o.id);
|
|
2285
|
+
return;
|
|
2286
|
+
case "dispose":
|
|
2287
|
+
t.delete(o.sid), i.add(o.sid);
|
|
2288
|
+
return;
|
|
2289
|
+
case "bar": {
|
|
2290
|
+
let n = t.get(o.sid);
|
|
2291
|
+
if (!n) {
|
|
2292
|
+
e({
|
|
2293
|
+
type: "tick-error",
|
|
2294
|
+
sid: o.sid,
|
|
2295
|
+
error: {
|
|
2296
|
+
message: "the script has no computed state in this worker (it was restarted); change an input to run it again",
|
|
2297
|
+
kind: "runtime"
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
try {
|
|
2303
|
+
let t = n.length, r = t > 0 ? n.graph.time.v[t - 1] : NaN;
|
|
2304
|
+
if (t > 0 && o.bar.time === r ? n.reviseLast(o.bar) : n.push(o.bar), n.length - 1 !== o.index) throw new h(`out of sync with the chart: bar ${o.index} arrived with ${n.length} bars computed`);
|
|
2305
|
+
let i = new Float64Array(n.outputs.length);
|
|
2306
|
+
n.outputs.forEach((e, t) => i[t] = e.v[o.index]), e({
|
|
2307
|
+
type: "values",
|
|
2308
|
+
sid: o.sid,
|
|
2309
|
+
index: o.index,
|
|
2310
|
+
values: i
|
|
2311
|
+
}, [i.buffer]);
|
|
2312
|
+
} catch (t) {
|
|
2313
|
+
e({
|
|
2314
|
+
type: "tick-error",
|
|
2315
|
+
sid: o.sid,
|
|
2316
|
+
error: Q(t)
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
case "feed-bar": {
|
|
2322
|
+
let n = t.get(o.sid);
|
|
2323
|
+
if (!n) {
|
|
2324
|
+
e({
|
|
2325
|
+
type: "tick-error",
|
|
2326
|
+
sid: o.sid,
|
|
2327
|
+
error: {
|
|
2328
|
+
message: "the script has no computed state in this worker (it was restarted); change an input to run it again",
|
|
2329
|
+
kind: "runtime"
|
|
2330
|
+
}
|
|
2331
|
+
});
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
try {
|
|
2335
|
+
if (!n.graph.feeds.has(o.key)) throw new h(`this script reads no outside series "${o.key}"`);
|
|
2336
|
+
if (!n.pushFeed(o.key, o.bar)) {
|
|
2337
|
+
e({
|
|
2338
|
+
type: "idle",
|
|
2339
|
+
sid: o.sid
|
|
2340
|
+
});
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
let t = n.length, r = new Float64Array(n.outputs.length);
|
|
2344
|
+
n.outputs.forEach((e, n) => r[n] = e.v[t - 1]), e({
|
|
2345
|
+
type: "values",
|
|
2346
|
+
sid: o.sid,
|
|
2347
|
+
index: t - 1,
|
|
2348
|
+
values: r
|
|
2349
|
+
}, [r.buffer]);
|
|
2350
|
+
} catch (t) {
|
|
2351
|
+
e({
|
|
2352
|
+
type: "tick-error",
|
|
2353
|
+
sid: o.sid,
|
|
2354
|
+
error: Q(t)
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
case "run":
|
|
2360
|
+
r.add(o.id), (async () => {
|
|
2361
|
+
try {
|
|
2362
|
+
let r = {
|
|
2363
|
+
...C,
|
|
2364
|
+
...o.limits
|
|
2365
|
+
}, a = X(o.source, r);
|
|
2366
|
+
if (!a.ok) throw new h(a.diagnostics.map((e) => `${e.line}:${e.column}: ${e.message}`).join("; "));
|
|
2367
|
+
let s = o.bars.time.length;
|
|
2368
|
+
if (s > r.maxBars) throw new h(`${s} bars exceed the limit of ${r.maxBars}`, null, "budget");
|
|
2369
|
+
let c = new u($(a.study), o.params), l = Object.entries(o.feeds ?? {});
|
|
2370
|
+
if (l.length > r.maxFeeds) throw new h(`${l.length} outside series exceed the limit of ${r.maxFeeds}`, null, "budget");
|
|
2371
|
+
let d = 0, f = 0, p = 0;
|
|
2372
|
+
for (let [e, t] of l) {
|
|
2373
|
+
let n = t.bars.time.length;
|
|
2374
|
+
if (n > r.maxBars) throw new h(`an outside series has ${n} bars; the limit is ${r.maxBars}`, null, "budget");
|
|
2375
|
+
let i = c.graph.feeds.get(e)?.graph.cols.length ?? 6;
|
|
2376
|
+
f += n, p += i, d += n * i * 8 * 3 + n * 6 * 8;
|
|
2377
|
+
}
|
|
2378
|
+
let m = c.graph.cols.length * s * 8 * 3 + d;
|
|
2379
|
+
if (m > r.maxBytes) {
|
|
2380
|
+
let e = `${(m / 2 ** 20).toFixed(0)} MiB`, t = `${(r.maxBytes / 2 ** 20).toFixed(0)} MiB`;
|
|
2381
|
+
throw l.length === 0 ? new h(`the script needs ${e} (3 x ${c.graph.cols.length} columns x ${s} bars x 8); the limit is ${t}`, null, "budget") : new h(`the script and its ${l.length} outside series need ${e} (3 x ${c.graph.cols.length} columns x ${s} bars x 8, and 3 x ${p} columns of ${f} outside bars in all, x 8, with a copy of their bars); the limit is ${t}`, null, "budget");
|
|
2382
|
+
}
|
|
2383
|
+
for (let [e, t] of l) c.setFeed(e, t.bars, t);
|
|
2384
|
+
if (!await c.loadChunked(o.bars, re, ne, () => n.has(o.id))) {
|
|
2385
|
+
e({
|
|
2386
|
+
type: "cancelled",
|
|
2387
|
+
id: o.id
|
|
2388
|
+
});
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
i.has(o.sid) || t.set(o.sid, c);
|
|
2392
|
+
let g = c.graph.cols.map((e) => e.v.slice());
|
|
2393
|
+
e({
|
|
2394
|
+
type: "result",
|
|
2395
|
+
id: o.id,
|
|
2396
|
+
n: c.length,
|
|
2397
|
+
buffers: g
|
|
2398
|
+
}, g.map((e) => e.buffer));
|
|
2399
|
+
} catch (t) {
|
|
2400
|
+
e({
|
|
2401
|
+
type: "error",
|
|
2402
|
+
id: o.id,
|
|
2403
|
+
error: Q(t)
|
|
2404
|
+
});
|
|
2405
|
+
} finally {
|
|
2406
|
+
n.delete(o.id), r.delete(o.id);
|
|
2407
|
+
}
|
|
2408
|
+
})();
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
//#endregion
|
|
2414
|
+
export { X as a, O as c, C as d, p as f, $ as i, k as l, g as m, te as n, A as o, h as p, ie as r, d as s, Z as t, D as u };
|
|
2415
|
+
|
|
2416
|
+
//# sourceMappingURL=sandbox-p2HPP8yl.js.map
|