@coderyo/pine-lite 1.0.0-rc.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,16 +1,768 @@
1
- // src/vm.ts
2
- function runPineVm(ir, length) {
3
- const plots = Array.from({ length }, (_, i) => Math.sin(i * 0.05));
4
- void ir;
5
- return { plots };
1
+ import {
2
+ runPineIr
3
+ } from "./chunk-JLVAE3JC.js";
4
+
5
+ // src/builtins.ts
6
+ var SERIES_IDENTIFIERS = /* @__PURE__ */ new Set([
7
+ "close",
8
+ "open",
9
+ "high",
10
+ "low",
11
+ "volume",
12
+ "hl2",
13
+ "hlc3"
14
+ ]);
15
+ var INDICATOR_BUILTINS = /* @__PURE__ */ new Set([
16
+ "sma",
17
+ "ema",
18
+ "rsi",
19
+ "highest",
20
+ "lowest",
21
+ "crossover",
22
+ "crossunder"
23
+ ]);
24
+ function isBuiltin(name) {
25
+ return SERIES_IDENTIFIERS.has(name) || INDICATOR_BUILTINS.has(name);
26
+ }
27
+ function builtinArity(name) {
28
+ if (SERIES_IDENTIFIERS.has(name)) return 0;
29
+ if (name === "sma" || name === "ema" || name === "rsi" || name === "highest" || name === "lowest") {
30
+ return 2;
31
+ }
32
+ if (name === "crossover" || name === "crossunder") return 2;
33
+ return null;
34
+ }
35
+
36
+ // src/compile.ts
37
+ var Emitter = class {
38
+ ops = [];
39
+ plotIndex = 0;
40
+ emit(op) {
41
+ this.ops.push(op);
42
+ return this.ops.length - 1;
43
+ }
44
+ patchJump(index, target) {
45
+ const op = this.ops[index];
46
+ if (op.op === "jump" || op.op === "jump_if_false") op.target = target;
47
+ }
48
+ emitExpr(expr, errors, vars) {
49
+ switch (expr.kind) {
50
+ case "number":
51
+ this.emit({ op: "push", value: expr.value });
52
+ return true;
53
+ case "bool":
54
+ this.emit({ op: "push", value: expr.value ? 1 : 0 });
55
+ return true;
56
+ case "ident":
57
+ if (vars.has(expr.name)) {
58
+ this.emit({ op: "load_var", name: expr.name });
59
+ return true;
60
+ }
61
+ if (isBuiltin(expr.name) && builtinArity(expr.name) === 0) {
62
+ this.emit({ op: "load_series", name: expr.name });
63
+ return true;
64
+ }
65
+ errors.push(`Unknown identifier '${expr.name}'`);
66
+ return false;
67
+ case "unary":
68
+ if (!this.emitExpr(expr.arg, errors, vars)) return false;
69
+ this.emit({ op: expr.op === "not" ? "not" : "neg" });
70
+ return true;
71
+ case "binary":
72
+ if (expr.op === "and" || expr.op === "or") {
73
+ if (!this.emitExpr(expr.left, errors, vars) || !this.emitExpr(expr.right, errors, vars)) {
74
+ return false;
75
+ }
76
+ this.emit({ op: expr.op });
77
+ return true;
78
+ }
79
+ if (!this.emitExpr(expr.left, errors, vars) || !this.emitExpr(expr.right, errors, vars)) {
80
+ return false;
81
+ }
82
+ if (expr.op === "+" || expr.op === "-" || expr.op === "*" || expr.op === "/") {
83
+ this.emit({
84
+ op: expr.op === "+" ? "add" : expr.op === "-" ? "sub" : expr.op === "*" ? "mul" : "div"
85
+ });
86
+ return true;
87
+ }
88
+ this.emit({
89
+ op: "cmp",
90
+ mode: expr.op === "==" ? "eq" : expr.op === "!=" ? "ne" : expr.op === "<" ? "lt" : expr.op === ">" ? "gt" : expr.op === "<=" ? "le" : "ge"
91
+ });
92
+ return true;
93
+ case "call": {
94
+ const arity = builtinArity(expr.name);
95
+ if (arity == null) {
96
+ errors.push(`Unknown function '${expr.name}'`);
97
+ return false;
98
+ }
99
+ if (expr.args.length !== arity) {
100
+ errors.push(`'${expr.name}' expects ${arity} arguments, got ${expr.args.length}`);
101
+ return false;
102
+ }
103
+ const fn = expr.name;
104
+ if ((fn === "crossover" || fn === "crossunder") && expr.args[0]?.kind === "ident" && expr.args[1]?.kind === "ident" && SERIES_IDENTIFIERS.has(expr.args[0].name) && SERIES_IDENTIFIERS.has(expr.args[1].name)) {
105
+ this.emit({
106
+ op: "call_ind",
107
+ fn,
108
+ series: expr.args[0].name,
109
+ series2: expr.args[1].name
110
+ });
111
+ return true;
112
+ }
113
+ if ((fn === "sma" || fn === "ema" || fn === "rsi" || fn === "highest" || fn === "lowest") && expr.args[0]?.kind === "ident" && SERIES_IDENTIFIERS.has(expr.args[0].name)) {
114
+ if (!this.emitExpr(expr.args[1], errors, vars)) return false;
115
+ this.emit({ op: "call_ind", fn, series: expr.args[0].name });
116
+ return true;
117
+ }
118
+ for (const arg of expr.args) {
119
+ if (!this.emitExpr(arg, errors, vars)) return false;
120
+ }
121
+ return true;
122
+ }
123
+ default:
124
+ return false;
125
+ }
126
+ }
127
+ emitStmt(stmt, errors, vars, plots) {
128
+ switch (stmt.kind) {
129
+ case "block":
130
+ for (const s of stmt.body) this.emitStmt(s, errors, vars, plots);
131
+ break;
132
+ case "var":
133
+ vars.add(stmt.name);
134
+ if (this.emitExpr(stmt.init, errors, vars)) this.emit({ op: "store_var", name: stmt.name });
135
+ break;
136
+ case "assign":
137
+ if (!vars.has(stmt.name)) vars.add(stmt.name);
138
+ if (this.emitExpr(stmt.value, errors, vars)) this.emit({ op: "store_var", name: stmt.name });
139
+ break;
140
+ case "plot": {
141
+ if (!this.emitExpr(stmt.expr, errors, vars)) break;
142
+ const title = stmt.title ?? `plot_${this.plotIndex++}`;
143
+ plots.push(title);
144
+ this.emit({ op: "plot", title });
145
+ break;
146
+ }
147
+ case "expr":
148
+ if (this.emitExpr(stmt.expr, errors, vars)) this.emit({ op: "pop" });
149
+ break;
150
+ case "if": {
151
+ if (!this.emitExpr(stmt.cond, errors, vars)) break;
152
+ const jmpFalse = this.emit({ op: "jump_if_false", target: 0 });
153
+ for (const s of stmt.then) this.emitStmt(s, errors, vars, plots);
154
+ if (stmt.else?.length) {
155
+ const jmpEnd = this.emit({ op: "jump", target: 0 });
156
+ this.patchJump(jmpFalse, this.ops.length);
157
+ for (const s of stmt.else) this.emitStmt(s, errors, vars, plots);
158
+ this.patchJump(jmpEnd, this.ops.length);
159
+ } else {
160
+ this.patchJump(jmpFalse, this.ops.length);
161
+ }
162
+ break;
163
+ }
164
+ case "while": {
165
+ const loopStart = this.ops.length;
166
+ if (!this.emitExpr(stmt.cond, errors, vars)) break;
167
+ const jmpEnd = this.emit({ op: "jump_if_false", target: 0 });
168
+ for (const s of stmt.body) this.emitStmt(s, errors, vars, plots);
169
+ this.emit({ op: "jump", target: loopStart });
170
+ this.patchJump(jmpEnd, this.ops.length);
171
+ break;
172
+ }
173
+ case "for": {
174
+ vars.add(stmt.name);
175
+ if (!this.emitExpr(stmt.from, errors, vars)) break;
176
+ this.emit({ op: "store_var", name: stmt.name });
177
+ const loopStart = this.ops.length;
178
+ this.emit({ op: "load_var", name: stmt.name });
179
+ if (!this.emitExpr(stmt.to, errors, vars)) break;
180
+ this.emit({ op: "cmp", mode: "le" });
181
+ const jmpEnd = this.emit({ op: "jump_if_false", target: 0 });
182
+ for (const s of stmt.body) this.emitStmt(s, errors, vars, plots);
183
+ this.emit({ op: "load_var", name: stmt.name });
184
+ this.emit({ op: "push", value: 1 });
185
+ this.emit({ op: "add" });
186
+ this.emit({ op: "store_var", name: stmt.name });
187
+ this.emit({ op: "jump", target: loopStart });
188
+ this.patchJump(jmpEnd, this.ops.length);
189
+ break;
190
+ }
191
+ }
192
+ }
193
+ };
194
+ function compileAst(program) {
195
+ const errors = [];
196
+ const vars = /* @__PURE__ */ new Set();
197
+ const plots = [];
198
+ const emitter = new Emitter();
199
+ for (const stmt of program.body) {
200
+ emitter.emitStmt(stmt, errors, vars, plots);
201
+ }
202
+ if (errors.length) return { ir: null, errors };
203
+ return {
204
+ ir: { version: 2, ops: emitter.ops, vars: [...vars], plots },
205
+ errors
206
+ };
207
+ }
208
+
209
+ // src/diagnostics.ts
210
+ function offsetToLineCol(source, offset) {
211
+ let line = 1;
212
+ let col = 1;
213
+ for (let i = 0; i < offset && i < source.length; i++) {
214
+ if (source[i] === "\n") {
215
+ line++;
216
+ col = 1;
217
+ } else {
218
+ col++;
219
+ }
220
+ }
221
+ return { line, col };
222
+ }
223
+ function diagnosticFromMessage(source, message) {
224
+ const at = message.match(/\bat\s+(\d+)\b/);
225
+ if (at) {
226
+ const pos = Number(at[1]);
227
+ const { line, col } = offsetToLineCol(source, pos);
228
+ return { line, col, message: message.replace(/\s+at\s+\d+\s*$/, ""), severity: "error" };
229
+ }
230
+ return { line: 1, col: 1, message, severity: "error" };
231
+ }
232
+ function diagnosticsFromMessages(source, messages) {
233
+ return messages.map((m) => diagnosticFromMessage(source, m));
234
+ }
235
+
236
+ // src/lexer.ts
237
+ var KEYWORDS = {
238
+ var: "var",
239
+ plot: "plot",
240
+ if: "if",
241
+ else: "else",
242
+ while: "while",
243
+ for: "for",
244
+ to: "to",
245
+ and: "and",
246
+ or: "or",
247
+ not: "not",
248
+ true: "true",
249
+ false: "false"
250
+ };
251
+ function tokenize(source) {
252
+ const tokens = [];
253
+ const errors = [];
254
+ let i = 0;
255
+ let line = 1;
256
+ let col = 1;
257
+ const push = (type, value, pos, startLine, startCol) => {
258
+ tokens.push({ type, value, pos, line: startLine, col: startCol });
259
+ };
260
+ while (i < source.length) {
261
+ const pos = i;
262
+ const startLine = line;
263
+ const startCol = col;
264
+ const ch = source[i];
265
+ if (ch === " " || ch === " " || ch === "\r") {
266
+ i++;
267
+ col++;
268
+ continue;
269
+ }
270
+ if (ch === "\n") {
271
+ i++;
272
+ line++;
273
+ col = 1;
274
+ continue;
275
+ }
276
+ if (ch === "/" && source[i + 1] === "/") {
277
+ i += 2;
278
+ col += 2;
279
+ while (i < source.length && source[i] !== "\n") {
280
+ i++;
281
+ col++;
282
+ }
283
+ continue;
284
+ }
285
+ if (ch >= "0" && ch <= "9") {
286
+ let j = i + 1;
287
+ while (j < source.length && /[0-9.]/.test(source[j])) j++;
288
+ const raw = source.slice(i, j);
289
+ push("number", raw, pos, startLine, startCol);
290
+ col += j - i;
291
+ i = j;
292
+ continue;
293
+ }
294
+ if (/[A-Za-z_]/.test(ch)) {
295
+ let j = i + 1;
296
+ while (j < source.length && /[A-Za-z0-9_]/.test(source[j])) j++;
297
+ const word = source.slice(i, j);
298
+ const kw = KEYWORDS[word];
299
+ push(kw ?? "ident", word, pos, startLine, startCol);
300
+ col += j - i;
301
+ i = j;
302
+ continue;
303
+ }
304
+ if (ch === ":" && source[i + 1] === "=") {
305
+ push("assign", ":=", pos, startLine, startCol);
306
+ i += 2;
307
+ col += 2;
308
+ continue;
309
+ }
310
+ if (ch === "=" && source[i + 1] === "=") {
311
+ push("eq", "==", pos, startLine, startCol);
312
+ i += 2;
313
+ col += 2;
314
+ continue;
315
+ }
316
+ if (ch === "!" && source[i + 1] === "=") {
317
+ push("ne", "!=", pos, startLine, startCol);
318
+ i += 2;
319
+ col += 2;
320
+ continue;
321
+ }
322
+ if (ch === "<" && source[i + 1] === "=") {
323
+ push("le", "<=", pos, startLine, startCol);
324
+ i += 2;
325
+ col += 2;
326
+ continue;
327
+ }
328
+ if (ch === ">" && source[i + 1] === "=") {
329
+ push("ge", ">=", pos, startLine, startCol);
330
+ i += 2;
331
+ col += 2;
332
+ continue;
333
+ }
334
+ if (ch === "<") {
335
+ push("lt", "<", pos, startLine, startCol);
336
+ i++;
337
+ col++;
338
+ continue;
339
+ }
340
+ if (ch === ">") {
341
+ push("gt", ">", pos, startLine, startCol);
342
+ i++;
343
+ col++;
344
+ continue;
345
+ }
346
+ if (ch === "{") {
347
+ push("lbrace", "{", pos, startLine, startCol);
348
+ i++;
349
+ col++;
350
+ continue;
351
+ }
352
+ if (ch === "}") {
353
+ push("rbrace", "}", pos, startLine, startCol);
354
+ i++;
355
+ col++;
356
+ continue;
357
+ }
358
+ if (ch === "(") {
359
+ push("lparen", "(", pos, startLine, startCol);
360
+ i++;
361
+ col++;
362
+ continue;
363
+ }
364
+ if (ch === ")") {
365
+ push("rparen", ")", pos, startLine, startCol);
366
+ i++;
367
+ col++;
368
+ continue;
369
+ }
370
+ if (ch === ",") {
371
+ push("comma", ",", pos, startLine, startCol);
372
+ i++;
373
+ col++;
374
+ continue;
375
+ }
376
+ if (ch === "+") {
377
+ push("plus", "+", pos, startLine, startCol);
378
+ i++;
379
+ col++;
380
+ continue;
381
+ }
382
+ if (ch === "-") {
383
+ push("minus", "-", pos, startLine, startCol);
384
+ i++;
385
+ col++;
386
+ continue;
387
+ }
388
+ if (ch === "*") {
389
+ push("star", "*", pos, startLine, startCol);
390
+ i++;
391
+ col++;
392
+ continue;
393
+ }
394
+ if (ch === "/") {
395
+ push("slash", "/", pos, startLine, startCol);
396
+ i++;
397
+ col++;
398
+ continue;
399
+ }
400
+ if (ch === "=") {
401
+ push("eq", "=", pos, startLine, startCol);
402
+ i++;
403
+ col++;
404
+ continue;
405
+ }
406
+ errors.push(`Unexpected '${ch}' at line ${startLine}, col ${startCol}`);
407
+ i++;
408
+ col++;
409
+ }
410
+ tokens.push({ type: "eof", value: "", pos: i, line, col });
411
+ return { tokens, errors };
412
+ }
413
+
414
+ // src/parser.ts
415
+ function parseProgram(tokens) {
416
+ const errors = [];
417
+ let i = 0;
418
+ const peek = () => tokens[i];
419
+ const at = (t) => peek().type === t;
420
+ const err = (msg) => {
421
+ errors.push({ message: msg, line: peek().line, col: peek().col });
422
+ };
423
+ const eat = (t) => {
424
+ if (!at(t)) {
425
+ err(`Expected ${t}, got ${peek().type}`);
426
+ return false;
427
+ }
428
+ i++;
429
+ return true;
430
+ };
431
+ const parseBlock = () => {
432
+ if (at("lbrace")) {
433
+ i++;
434
+ const body2 = [];
435
+ while (!at("rbrace") && !at("eof")) {
436
+ const stmt2 = parseStmt();
437
+ if (!stmt2) break;
438
+ body2.push(stmt2);
439
+ }
440
+ if (!eat("rbrace")) return body2;
441
+ return body2;
442
+ }
443
+ const stmt = parseStmt();
444
+ return stmt ? [stmt] : [];
445
+ };
446
+ const parseOr = () => {
447
+ let left = parseAnd();
448
+ if (!left) return null;
449
+ while (at("or")) {
450
+ i++;
451
+ const right = parseAnd();
452
+ if (!right) return null;
453
+ left = { kind: "binary", op: "or", left, right };
454
+ }
455
+ return left;
456
+ };
457
+ const parseAnd = () => {
458
+ let left = parseCompare();
459
+ if (!left) return null;
460
+ while (at("and")) {
461
+ i++;
462
+ const right = parseCompare();
463
+ if (!right) return null;
464
+ left = { kind: "binary", op: "and", left, right };
465
+ }
466
+ return left;
467
+ };
468
+ const parseCompare = () => {
469
+ let left = parseAdd();
470
+ if (!left) return null;
471
+ const cmpTypes = ["eq", "ne", "lt", "gt", "le", "ge"];
472
+ while (cmpTypes.some((t) => at(t))) {
473
+ const t = peek().type;
474
+ const opMap = {
475
+ eq: "==",
476
+ ne: "!=",
477
+ lt: "<",
478
+ gt: ">",
479
+ le: "<=",
480
+ ge: ">="
481
+ };
482
+ i++;
483
+ const right = parseAdd();
484
+ if (!right) return null;
485
+ left = { kind: "binary", op: opMap[t], left, right };
486
+ }
487
+ return left;
488
+ };
489
+ const parseAdd = () => {
490
+ let left = parseMul();
491
+ if (!left) return null;
492
+ while (at("plus") || at("minus")) {
493
+ const op = peek().type === "plus" ? "+" : "-";
494
+ i++;
495
+ const right = parseMul();
496
+ if (!right) return null;
497
+ left = { kind: "binary", op, left, right };
498
+ }
499
+ return left;
500
+ };
501
+ const parseMul = () => {
502
+ let left = parseUnary();
503
+ if (!left) return null;
504
+ while (at("star") || at("slash")) {
505
+ const op = peek().type === "star" ? "*" : "/";
506
+ i++;
507
+ const right = parseUnary();
508
+ if (!right) return null;
509
+ left = { kind: "binary", op, left, right };
510
+ }
511
+ return left;
512
+ };
513
+ const parseUnary = () => {
514
+ if (at("not")) {
515
+ i++;
516
+ const arg = parseUnary();
517
+ return arg ? { kind: "unary", op: "not", arg } : null;
518
+ }
519
+ if (at("minus")) {
520
+ i++;
521
+ const arg = parseUnary();
522
+ return arg ? { kind: "unary", op: "-", arg } : null;
523
+ }
524
+ return parsePrimary();
525
+ };
526
+ const parsePrimary = () => {
527
+ const t = peek();
528
+ if (t.type === "number") {
529
+ i++;
530
+ return { kind: "number", value: Number(t.value) };
531
+ }
532
+ if (t.type === "true" || t.type === "false") {
533
+ i++;
534
+ return { kind: "bool", value: t.type === "true" };
535
+ }
536
+ if (t.type === "ident") {
537
+ const name = t.value;
538
+ i++;
539
+ if (at("lparen")) {
540
+ i++;
541
+ const args = [];
542
+ if (!at("rparen")) {
543
+ const first = parseOr();
544
+ if (!first) return null;
545
+ args.push(first);
546
+ while (at("comma")) {
547
+ i++;
548
+ const next = parseOr();
549
+ if (!next) return null;
550
+ args.push(next);
551
+ }
552
+ }
553
+ if (!eat("rparen")) return null;
554
+ return { kind: "call", name, args };
555
+ }
556
+ return { kind: "ident", name };
557
+ }
558
+ if (at("lparen")) {
559
+ i++;
560
+ const inner = parseOr();
561
+ if (!inner || !eat("rparen")) return null;
562
+ return inner;
563
+ }
564
+ err(`Unexpected token ${t.type}`);
565
+ return null;
566
+ };
567
+ const parseStmt = () => {
568
+ if (at("if")) {
569
+ i++;
570
+ if (!eat("lparen")) return null;
571
+ const cond = parseOr();
572
+ if (!cond || !eat("rparen")) return null;
573
+ const then = parseBlock();
574
+ let elseBody;
575
+ if (at("else")) {
576
+ i++;
577
+ elseBody = parseBlock();
578
+ }
579
+ return { kind: "if", cond, then, else: elseBody };
580
+ }
581
+ if (at("while")) {
582
+ i++;
583
+ if (!eat("lparen")) return null;
584
+ const cond = parseOr();
585
+ if (!cond || !eat("rparen")) return null;
586
+ const body2 = parseBlock();
587
+ return { kind: "while", cond, body: body2 };
588
+ }
589
+ if (at("for")) {
590
+ i++;
591
+ if (!at("ident")) {
592
+ err("Expected loop variable after for");
593
+ return null;
594
+ }
595
+ const name = peek().value;
596
+ i++;
597
+ if (!eat("eq")) return null;
598
+ const from = parseOr();
599
+ if (!from || !at("to")) {
600
+ err('Expected "to" in for loop');
601
+ return null;
602
+ }
603
+ i++;
604
+ const to = parseOr();
605
+ if (!to) return null;
606
+ const body2 = parseBlock();
607
+ return { kind: "for", name, from, to, body: body2 };
608
+ }
609
+ if (at("var")) {
610
+ i++;
611
+ if (!at("ident")) {
612
+ err("Expected identifier after var");
613
+ return null;
614
+ }
615
+ const name = peek().value;
616
+ i++;
617
+ if (!eat("eq")) return null;
618
+ const init = parseOr();
619
+ return init ? { kind: "var", name, init } : null;
620
+ }
621
+ if (at("plot")) {
622
+ i++;
623
+ if (!eat("lparen")) return null;
624
+ const expr = parseOr();
625
+ if (!expr || !eat("rparen")) return null;
626
+ return { kind: "plot", expr };
627
+ }
628
+ if (at("ident")) {
629
+ const name = peek().value;
630
+ i++;
631
+ if (at("assign") || at("eq")) {
632
+ i++;
633
+ const value = parseOr();
634
+ return value ? { kind: "assign", name, value } : null;
635
+ }
636
+ i--;
637
+ const expr = parseOr();
638
+ return expr ? { kind: "expr", expr } : null;
639
+ }
640
+ if (at("lbrace")) {
641
+ return { kind: "block", body: parseBlock() };
642
+ }
643
+ err(`Unexpected statement ${peek().type}`);
644
+ return null;
645
+ };
646
+ const body = [];
647
+ while (!at("eof")) {
648
+ const stmt = parseStmt();
649
+ if (!stmt) break;
650
+ body.push(stmt);
651
+ }
652
+ if (!at("eof")) err("Unexpected tokens after program end");
653
+ return { program: { kind: "program", body }, errors };
654
+ }
655
+
656
+ // src/worker-client.ts
657
+ var worker = null;
658
+ var seq = 0;
659
+ var pending = /* @__PURE__ */ new Map();
660
+ function ensureWorker() {
661
+ if (typeof Worker === "undefined") return null;
662
+ if (worker) return worker;
663
+ try {
664
+ worker = new Worker(new URL("./pine.worker.js", import.meta.url), { type: "module" });
665
+ worker.onmessage = (ev) => {
666
+ const msg = ev.data;
667
+ const p = pending.get(msg.id);
668
+ if (!p) return;
669
+ pending.delete(msg.id);
670
+ if (msg.ok && msg.result) p.resolve(msg.result);
671
+ else p.reject(new Error(msg.error ?? "Pine worker failed"));
672
+ };
673
+ worker.onerror = () => {
674
+ for (const [, p] of pending) p.reject(new Error("Pine worker error"));
675
+ pending.clear();
676
+ worker?.terminate();
677
+ worker = null;
678
+ };
679
+ return worker;
680
+ } catch {
681
+ return null;
682
+ }
683
+ }
684
+ function runPineLiteAsync(ir, bars, opts) {
685
+ if (opts?.useWorker === false) {
686
+ return Promise.resolve(runPineIr(ir, bars));
687
+ }
688
+ const w = ensureWorker();
689
+ if (!w) return Promise.resolve(runPineIr(ir, bars));
690
+ const id = ++seq;
691
+ const req = { id, ir, bars };
692
+ return new Promise((resolve, reject) => {
693
+ pending.set(id, { resolve, reject });
694
+ w.postMessage(req);
695
+ });
696
+ }
697
+ function terminatePineWorker() {
698
+ worker?.terminate();
699
+ worker = null;
700
+ pending.clear();
6
701
  }
7
702
 
8
703
  // src/index.ts
9
- function compilePineLite(_source) {
10
- return { ok: true, errors: [], ir: { version: 0, plots: [] } };
704
+ var MAX_SOURCE_LEN = 32 * 1024;
705
+ function compilePineLite(source) {
706
+ const diagnostics = [];
707
+ const pushDiag = (line, col, message) => {
708
+ diagnostics.push({ line, col, message, severity: "error" });
709
+ };
710
+ if (source.length > MAX_SOURCE_LEN) {
711
+ const msg = `Source exceeds ${MAX_SOURCE_LEN} bytes`;
712
+ pushDiag(1, 1, msg);
713
+ return { ok: false, errors: [msg], diagnostics };
714
+ }
715
+ const { tokens, errors: lexErr } = tokenize(source);
716
+ for (const m of lexErr) diagnostics.push(diagnosticFromMessage(source, m));
717
+ if (lexErr.length) {
718
+ return { ok: false, errors: lexErr, diagnostics };
719
+ }
720
+ const { program, errors: parseErr } = parseProgram(tokens);
721
+ for (const e of parseErr) pushDiag(e.line, e.col, e.message);
722
+ if (parseErr.length || !program) {
723
+ return { ok: false, errors: parseErr.map((e) => e.message), diagnostics };
724
+ }
725
+ const { ir, errors: compileErr } = compileAst(program);
726
+ for (const m of compileErr) diagnostics.push(diagnosticFromMessage(source, m));
727
+ if (compileErr.length || !ir) {
728
+ return { ok: false, errors: compileErr, diagnostics };
729
+ }
730
+ if (ir.plots.length > 16) {
731
+ const msg = "Too many plot() calls (max 16)";
732
+ pushDiag(1, 1, msg);
733
+ return { ok: false, errors: [msg], diagnostics };
734
+ }
735
+ if (ir.vars.length > 256) {
736
+ const msg = "Too many variables (max 256)";
737
+ pushDiag(1, 1, msg);
738
+ return { ok: false, errors: [msg], diagnostics };
739
+ }
740
+ return { ok: true, errors: [], diagnostics, ir };
741
+ }
742
+ function runPineLite(ir, bars) {
743
+ return runPineIr(ir, bars);
744
+ }
745
+ var PINE_SAMPLE_SCRIPT = `// Pine-lite \u2014 if / while / for\u3001\u6BD4\u8F03\u3001and/or/not
746
+ var len = 20
747
+ if (close > 0) {
748
+ plot(sma(close, len))
749
+ } else {
750
+ plot(open)
751
+ }
752
+ for i = 1 to 2 {
753
+ plot(rsi(close, 14))
11
754
  }
755
+ `;
756
+ var PINE_EDITOR_DEFAULT = PINE_SAMPLE_SCRIPT;
12
757
  export {
758
+ PINE_EDITOR_DEFAULT,
759
+ PINE_SAMPLE_SCRIPT,
13
760
  compilePineLite,
14
- runPineVm
761
+ diagnosticFromMessage,
762
+ diagnosticsFromMessages,
763
+ offsetToLineCol,
764
+ runPineLite,
765
+ runPineLiteAsync,
766
+ terminatePineWorker
15
767
  };
16
768
  //# sourceMappingURL=index.js.map