@hoox-sh/pynets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +46 -0
  4. package/README.md +327 -0
  5. package/dist/browser.js +44 -0
  6. package/dist/index.d.ts +348 -0
  7. package/dist/index.js +41741 -0
  8. package/package.json +77 -0
  9. package/src/ast/builder.ts +1002 -0
  10. package/src/ast/dump.ts +43 -0
  11. package/src/ast/helper.ts +77 -0
  12. package/src/ast/nodes.ts +570 -0
  13. package/src/ast/unparse.ts +360 -0
  14. package/src/cli/rich.ts +378 -0
  15. package/src/cli.ts +627 -0
  16. package/src/generated/PinescriptLexer.interp +267 -0
  17. package/src/generated/PinescriptLexer.tokens +131 -0
  18. package/src/generated/PinescriptLexer.ts +673 -0
  19. package/src/generated/PinescriptLexerBase.ts +395 -0
  20. package/src/generated/PinescriptParser.interp +267 -0
  21. package/src/generated/PinescriptParser.tokens +131 -0
  22. package/src/generated/PinescriptParser.ts +8863 -0
  23. package/src/generated/PinescriptParserBase.ts +21 -0
  24. package/src/generated/PinescriptParserVisitor.ts +840 -0
  25. package/src/index.ts +55 -0
  26. package/src/parser/LexerBase.ts +395 -0
  27. package/src/parser/ParserBase.ts +21 -0
  28. package/src/parser/error.ts +33 -0
  29. package/src/runtime/array.ts +486 -0
  30. package/src/runtime/color.ts +164 -0
  31. package/src/runtime/drawings.ts +510 -0
  32. package/src/runtime/input.ts +102 -0
  33. package/src/runtime/interpret.ts +3897 -0
  34. package/src/runtime/library.ts +282 -0
  35. package/src/runtime/log.ts +69 -0
  36. package/src/runtime/map.ts +85 -0
  37. package/src/runtime/math.ts +231 -0
  38. package/src/runtime/matrix.ts +1154 -0
  39. package/src/runtime/provider.ts +232 -0
  40. package/src/runtime/request.ts +372 -0
  41. package/src/runtime/series.ts +44 -0
  42. package/src/runtime/str.ts +433 -0
  43. package/src/runtime/strategy.ts +1105 -0
  44. package/src/runtime/ta.ts +2093 -0
  45. package/src/runtime/ticker.ts +81 -0
  46. package/src/runtime/time.ts +439 -0
  47. package/src/runtime/timeframe.ts +85 -0
  48. package/src/runtime/udt.ts +119 -0
package/src/index.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright (C) 2024-2026 jango_blockchained
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ *
5
+ * Stable public surface — TypeScript counterpart of `pynescript`.
6
+ * parse / unparse / dump / Runtime.run match Python names. Do not re-export
7
+ * generated ANTLR, parser internals, or CLI helpers from this barrel.
8
+ */
9
+ export { dump, parse, tokenize, unparse, PinescriptSyntaxError } from "./ast/helper.ts";
10
+ export type { ParseMode } from "./ast/helper.ts";
11
+ export type { AST, Script, Expression } from "./ast/nodes.ts";
12
+ export { Runtime, RuntimeStream, interpret, interpretTree } from "./runtime/interpret.ts";
13
+ export type {
14
+ DrawingEvent,
15
+ InputOverrides,
16
+ OHLCVBar,
17
+ RuntimeFill,
18
+ RuntimeOptions,
19
+ RuntimeResult,
20
+ StrategyEvent,
21
+ StreamEvent,
22
+ } from "./runtime/interpret.ts";
23
+ export { NA, PineSeries } from "./runtime/series.ts";
24
+ export { TaEngine } from "./runtime/ta.ts";
25
+ export type { BrokerSettings, StrategySummary } from "./runtime/strategy.ts";
26
+ export { PineArray } from "./runtime/array.ts";
27
+ export { PineMap } from "./runtime/map.ts";
28
+ export { PineMatrix } from "./runtime/matrix.ts";
29
+ export { DrawingBook } from "./runtime/drawings.ts";
30
+ export { MemoryProvider, StaticMapProvider, JsonBarProvider, ProviderError, mapJsonBars } from "./runtime/provider.ts";
31
+ export type { BarProvider, ProviderBar, JsonBarProviderOptions } from "./runtime/provider.ts";
32
+ export {
33
+ LibraryModule,
34
+ LibraryRegistry,
35
+ createStubModule,
36
+ applyStubExport,
37
+ STUB_KNOWN_EXPORTS,
38
+ } from "./runtime/library.ts";
39
+ export { timestamp, timestampFromComponents, parseTimestampString, weekOfYear, timeTradingDay, utcPartsFromMs } from "./runtime/time.ts";
40
+ export type { UtcParts } from "./runtime/time.ts";
41
+ export { LogBook } from "./runtime/log.ts";
42
+ export type { LogLevel, LogRecord } from "./runtime/log.ts";
43
+ export {
44
+ TickerId,
45
+ tickerNew,
46
+ tickerHeikinashi,
47
+ tickerStandard,
48
+ tickerRenko,
49
+ tickerKagi,
50
+ tickerLinebreak,
51
+ tickerPointfigure,
52
+ tickerModify,
53
+ } from "./runtime/ticker.ts";
54
+ export { RuntimeError, runtimeError } from "./runtime/log.ts";
55
+ export { UdtType, UdtInstance, EnumType, EnumMember } from "./runtime/udt.ts";
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Copyright (C) 2024-2026 jango_blockchained
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ *
5
+ * Port of src/pynescript/ast/grammar/antlr4/resource/PinescriptLexerBase.py
6
+ *
7
+ * Token type ids live as statics on the generated PinescriptLexer subclass.
8
+ * Read them via `this.constructor` — `this.AND` is not populated on TS instances.
9
+ */
10
+ import { CommonToken, Lexer, Token, type CharStream } from "antlr4";
11
+
12
+ const STRING_COLLAPSE_NL = /(\r?\n)+/g;
13
+ const STRING_STRIP_WRAP_INDENT = /(\r?\n)(\s)+/g;
14
+
15
+ type LexerCtor = typeof Lexer & {
16
+ AND: number;
17
+ COLON: number;
18
+ COLONEQUAL: number;
19
+ COMMA: number;
20
+ EQEQUAL: number;
21
+ EQUAL: number;
22
+ GREATER: number;
23
+ GREATEREQUAL: number;
24
+ LESS: number;
25
+ LESSEQUAL: number;
26
+ MINEQUAL: number;
27
+ MINUS: number;
28
+ NOTEQUAL: number;
29
+ OR: number;
30
+ PERCENT: number;
31
+ PERCENTEQUAL: number;
32
+ PLUS: number;
33
+ PLUSEQUAL: number;
34
+ QUESTION: number;
35
+ SLASH: number;
36
+ SLASHEQUAL: number;
37
+ STAR: number;
38
+ STAREQUAL: number;
39
+ AMP: number;
40
+ PIPE: number;
41
+ CARET: number;
42
+ LSHIFT: number;
43
+ RSHIFT: number;
44
+ TILDE: number;
45
+ LPAR: number;
46
+ LSQB: number;
47
+ RPAR: number;
48
+ RSQB: number;
49
+ NEWLINE: number;
50
+ STRING: number;
51
+ ERROR_TOKEN: number;
52
+ WS: number;
53
+ COMMENT: number;
54
+ INDENT: number;
55
+ DEDENT: number;
56
+ symbolicNames: Array<string | null>;
57
+ };
58
+
59
+ function tokenType(tok: Token | null | undefined): number {
60
+ return tok == null ? Token.INVALID_TYPE : tok.type;
61
+ }
62
+
63
+ export default class PinescriptLexerBase extends Lexer {
64
+ static _operator_types: ReadonlySet<number> | null = null;
65
+
66
+ private _operators: ReadonlySet<number>;
67
+ private _tabLength = 4;
68
+ private _indentLength = 4;
69
+ private _currentToken: CommonToken | null = null;
70
+ private _followingToken: CommonToken | null = null;
71
+ private _pendingTokens: CommonToken[] = [];
72
+ private _pendingHead = 0;
73
+ private _lastPendingTokenType = 0;
74
+ private _lastPendingTokenTypeFromDefaultChannel = 0;
75
+ private _numOpens = 0;
76
+ private _indentLengthStack: number[] = [];
77
+ private _inputStarted = false;
78
+
79
+ constructor(input: CharStream) {
80
+ super(input);
81
+ const cls = this.constructor as LexerCtor;
82
+ let ops = (cls as unknown as typeof PinescriptLexerBase)._operator_types;
83
+ if (ops == null) {
84
+ ops = new Set([
85
+ cls.AND,
86
+ cls.COLON,
87
+ cls.COLONEQUAL,
88
+ cls.COMMA,
89
+ cls.EQEQUAL,
90
+ cls.EQUAL,
91
+ cls.GREATER,
92
+ cls.GREATEREQUAL,
93
+ cls.LESS,
94
+ cls.LESSEQUAL,
95
+ cls.MINEQUAL,
96
+ cls.MINUS,
97
+ cls.NOTEQUAL,
98
+ cls.OR,
99
+ cls.PERCENT,
100
+ cls.PERCENTEQUAL,
101
+ cls.PLUS,
102
+ cls.PLUSEQUAL,
103
+ cls.QUESTION,
104
+ cls.SLASH,
105
+ cls.SLASHEQUAL,
106
+ cls.STAR,
107
+ cls.STAREQUAL,
108
+ cls.AMP,
109
+ cls.PIPE,
110
+ cls.CARET,
111
+ cls.LSHIFT,
112
+ cls.RSHIFT,
113
+ cls.TILDE,
114
+ ]);
115
+ (cls as unknown as typeof PinescriptLexerBase)._operator_types = ops;
116
+ }
117
+ this._operators = ops;
118
+ }
119
+
120
+ private get C(): LexerCtor {
121
+ return this.constructor as LexerCtor;
122
+ }
123
+
124
+ private _resetInternalStates(): void {
125
+ this._currentToken = null;
126
+ this._followingToken = null;
127
+ this._pendingTokens = [];
128
+ this._pendingHead = 0;
129
+ this._lastPendingTokenType = 0;
130
+ this._lastPendingTokenTypeFromDefaultChannel = 0;
131
+ this._numOpens = 0;
132
+ this._indentLengthStack = [];
133
+ this._inputStarted = false;
134
+ }
135
+
136
+ override nextToken(): Token {
137
+ this._checkNextToken();
138
+ return this._popPendingToken();
139
+ }
140
+
141
+ private _checkNextToken(): void {
142
+ if (this._lastPendingTokenType === Token.EOF) return;
143
+
144
+ this._setNextInternalTokens();
145
+ if (!this._inputStarted) this._handleStartOfInputIfNecessary();
146
+
147
+ const tok = this._currentToken!;
148
+ const tokType = tok.type;
149
+ const C = this.C;
150
+ if (tokType === C.LPAR || tokType === C.LSQB) {
151
+ this._numOpens += 1;
152
+ this._addPendingToken(tok);
153
+ } else if (tokType === C.RPAR || tokType === C.RSQB) {
154
+ this._numOpens -= 1;
155
+ this._addPendingToken(tok);
156
+ } else if (tokType === C.NEWLINE) {
157
+ this._handleNEWLINEToken();
158
+ } else if (tokType === C.STRING) {
159
+ this._handleSTRINGToken();
160
+ } else if (tokType === C.ERROR_TOKEN) {
161
+ const message = "token recognition error at: '" + tok.text + "'";
162
+ this._reportLexerError(message, tok);
163
+ this._addPendingToken(tok);
164
+ } else if (tokType === Token.EOF) {
165
+ this._handleEOFToken();
166
+ } else {
167
+ this._addPendingToken(tok);
168
+ }
169
+ }
170
+
171
+ private _setNextInternalTokens(): void {
172
+ this._currentToken =
173
+ this._followingToken == null
174
+ ? (super.nextToken() as CommonToken)
175
+ : this._followingToken;
176
+ this._followingToken =
177
+ this._currentToken.type === Token.EOF
178
+ ? this._currentToken
179
+ : (super.nextToken() as CommonToken);
180
+ }
181
+
182
+ private _handleStartOfInputIfNecessary(): void {
183
+ if (this._inputStarted) return;
184
+ this._inputStarted = true;
185
+ this._indentLengthStack.push(0);
186
+ while (this._currentToken!.type !== Token.EOF) {
187
+ if (this._currentToken!.channel === Token.DEFAULT_CHANNEL) {
188
+ if (this._currentToken!.type === this.C.NEWLINE) {
189
+ this._hideAndAddPendingToken(this._currentToken!);
190
+ } else {
191
+ this._checkLeadingIndentIfAny();
192
+ return;
193
+ }
194
+ } else {
195
+ this._addPendingToken(this._currentToken!);
196
+ }
197
+ this._setNextInternalTokens();
198
+ }
199
+ }
200
+
201
+ private _checkLeadingIndentIfAny(): void {
202
+ if (this._lastPendingTokenType === this.C.WS) {
203
+ const prev = this._pendingTokens[this._pendingTokens.length - 1]!;
204
+ if (this._getIndentationLength(prev.text ?? "") !== 0) {
205
+ const message = "first statement indented";
206
+ this._reportLexerError(message, this._currentToken!);
207
+ this._createAndAddPendingToken(
208
+ this.C.INDENT,
209
+ Token.DEFAULT_CHANNEL,
210
+ message,
211
+ this._currentToken!,
212
+ );
213
+ }
214
+ }
215
+ }
216
+
217
+ private _getIndentationLength(text: string): number {
218
+ let length = 0;
219
+ const tab = this._tabLength;
220
+ for (const ch of text) {
221
+ if (ch === " ") length += 1;
222
+ else if (ch === "\t") length += tab;
223
+ else if (ch === "\f") length = 0;
224
+ }
225
+ return length;
226
+ }
227
+
228
+ private _createAndAddPendingToken(
229
+ type: number,
230
+ channel: number,
231
+ text: string | null,
232
+ baseToken: CommonToken,
233
+ ): void {
234
+ const token = baseToken.clone() as CommonToken;
235
+ token.type = type;
236
+ token.channel = channel;
237
+ token.stop = baseToken.start - 1;
238
+ const names = this.C.symbolicNames ?? this.getSymbolicNames();
239
+ token.text = text == null ? "<" + String(names?.[type] ?? type) + ">" : text;
240
+ this._addPendingToken(token);
241
+ }
242
+
243
+ private _addPendingToken(token: CommonToken): void {
244
+ this._lastPendingTokenType = token.type;
245
+ if (token.channel === Token.DEFAULT_CHANNEL) {
246
+ this._lastPendingTokenTypeFromDefaultChannel = this._lastPendingTokenType;
247
+ }
248
+ this._pendingTokens.push(token);
249
+ }
250
+
251
+ private _hideAndAddPendingToken(token: CommonToken): void {
252
+ token.channel = Token.HIDDEN_CHANNEL;
253
+ this._addPendingToken(token);
254
+ }
255
+
256
+ private _popPendingToken(): CommonToken {
257
+ const tok = this._pendingTokens[this._pendingHead]!;
258
+ this._pendingHead += 1;
259
+ if (this._pendingHead > 32 && this._pendingHead * 2 > this._pendingTokens.length) {
260
+ this._pendingTokens = this._pendingTokens.slice(this._pendingHead);
261
+ this._pendingHead = 0;
262
+ }
263
+ return tok;
264
+ }
265
+
266
+ private _handleNEWLINEToken(): void {
267
+ if (this._numOpens > 0 || this._operators.has(this._lastPendingTokenTypeFromDefaultChannel)) {
268
+ this._hideAndAddPendingToken(this._currentToken!);
269
+ return;
270
+ }
271
+
272
+ const nlToken = this._currentToken!;
273
+ let followingType = tokenType(this._followingToken);
274
+ let isLookingAhead = followingType === this.C.WS;
275
+
276
+ if (isLookingAhead) {
277
+ this._setNextInternalTokens();
278
+ followingType = tokenType(this._followingToken);
279
+ }
280
+
281
+ if (followingType === this.C.NEWLINE || followingType === this.C.COMMENT) {
282
+ this._hideAndAddPendingToken(nlToken);
283
+ if (isLookingAhead) this._addPendingToken(this._currentToken!);
284
+ } else if (isLookingAhead) {
285
+ const indentationLength =
286
+ followingType === Token.EOF
287
+ ? 0
288
+ : this._getIndentationLength(this._currentToken!.text ?? "");
289
+ if (indentationLength % this._indentLength === 0) {
290
+ this._addPendingToken(nlToken);
291
+ this._addPendingToken(this._currentToken!);
292
+ this._insertIndentOrDedentToken(indentationLength);
293
+ } else {
294
+ this._hideAndAddPendingToken(nlToken);
295
+ this._addPendingToken(this._currentToken!);
296
+ }
297
+ } else {
298
+ this._addPendingToken(nlToken);
299
+ this._insertIndentOrDedentToken(0);
300
+ }
301
+ }
302
+
303
+ private _insertIndentOrDedentToken(indentLength: number): void {
304
+ let prev = this._indentLengthStack[this._indentLengthStack.length - 1] ?? 0;
305
+ if (indentLength > prev) {
306
+ this._createAndAddPendingToken(
307
+ this.C.INDENT,
308
+ Token.DEFAULT_CHANNEL,
309
+ null,
310
+ this._followingToken!,
311
+ );
312
+ this._indentLengthStack.push(indentLength);
313
+ } else {
314
+ while (indentLength < prev) {
315
+ this._indentLengthStack.pop();
316
+ prev = this._indentLengthStack[this._indentLengthStack.length - 1] ?? 0;
317
+ if (indentLength <= prev) {
318
+ this._createAndAddPendingToken(
319
+ this.C.DEDENT,
320
+ Token.DEFAULT_CHANNEL,
321
+ null,
322
+ this._followingToken!,
323
+ );
324
+ } else {
325
+ const message = "inconsistent dedent";
326
+ this._reportLexerError(message, this._followingToken!);
327
+ this._createAndAddPendingToken(
328
+ this.C.ERROR_TOKEN,
329
+ Token.DEFAULT_CHANNEL,
330
+ message,
331
+ this._followingToken!,
332
+ );
333
+ }
334
+ }
335
+ }
336
+ }
337
+
338
+ private _handleSTRINGToken(): void {
339
+ const text = this._currentToken!.text ?? "";
340
+ if (text.startsWith('"""') || text.startsWith("'''")) {
341
+ this._addPendingToken(this._currentToken!);
342
+ return;
343
+ }
344
+ if (!text.includes("\n") && !text.includes("\r")) {
345
+ this._addPendingToken(this._currentToken!);
346
+ return;
347
+ }
348
+ let replaced = text.replace(STRING_COLLAPSE_NL, "$1");
349
+ replaced = replaced.replace(STRING_STRIP_WRAP_INDENT, "");
350
+ if (text.length === replaced.length) {
351
+ this._addPendingToken(this._currentToken!);
352
+ } else {
353
+ const original = this._currentToken!.clone() as CommonToken;
354
+ this._currentToken!.text = replaced;
355
+ this._addPendingToken(this._currentToken!);
356
+ this._hideAndAddPendingToken(original);
357
+ }
358
+ }
359
+
360
+ private _insertTrailingTokens(): void {
361
+ const last = this._lastPendingTokenTypeFromDefaultChannel;
362
+ if (last !== this.C.NEWLINE && last !== this.C.DEDENT) {
363
+ this._createAndAddPendingToken(
364
+ this.C.NEWLINE,
365
+ Token.DEFAULT_CHANNEL,
366
+ null,
367
+ this._followingToken!,
368
+ );
369
+ }
370
+ this._insertIndentOrDedentToken(0);
371
+ }
372
+
373
+ private _handleEOFToken(): void {
374
+ if (this._lastPendingTokenTypeFromDefaultChannel > 0) {
375
+ this._insertTrailingTokens();
376
+ }
377
+ this._addPendingToken(this._currentToken!);
378
+ }
379
+
380
+ private _reportLexerError(message: string, token: Token): void {
381
+ this.getErrorListener().syntaxError(
382
+ this as never,
383
+ token.type,
384
+ token.line,
385
+ token.column,
386
+ message,
387
+ undefined,
388
+ );
389
+ }
390
+
391
+ override reset(): void {
392
+ this._resetInternalStates();
393
+ super.reset();
394
+ }
395
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (C) 2024-2026 jango_blockchained
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ *
5
+ * Port of src/pynescript/ast/grammar/antlr4/resource/PinescriptParserBase.py
6
+ */
7
+ import { Parser, type TokenStream } from "antlr4";
8
+
9
+ export default class PinescriptParserBase extends Parser {
10
+ constructor(input: TokenStream) {
11
+ super(input);
12
+ }
13
+
14
+ isEqualToCurrentTokenText(tokenText: string): boolean {
15
+ return this.getCurrentToken().text === tokenText;
16
+ }
17
+
18
+ isNotEqualToCurrentTokenText(tokenText: string): boolean {
19
+ return !this.isEqualToCurrentTokenText(tokenText);
20
+ }
21
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (C) 2024-2026 jango_blockchained
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ */
5
+ import { ErrorListener } from "antlr4";
6
+
7
+ export class PinescriptSyntaxError extends Error {
8
+ override name = "PinescriptSyntaxError";
9
+ lineno: number | null;
10
+ col_offset: number | null;
11
+
12
+ constructor(message: string, lineno: number | null = null, col_offset: number | null = null) {
13
+ super(message);
14
+ this.lineno = lineno;
15
+ this.col_offset = col_offset;
16
+ }
17
+ }
18
+
19
+ /** Shared lexer/parser listener. Must extend ErrorListener (ambiguity hooks). */
20
+ export class PinescriptErrorListener extends ErrorListener<unknown> {
21
+ static readonly INSTANCE = new PinescriptErrorListener();
22
+
23
+ override syntaxError(
24
+ _recognizer: unknown,
25
+ _offendingSymbol: unknown,
26
+ line: number,
27
+ column: number,
28
+ msg: string,
29
+ _e: unknown,
30
+ ): void {
31
+ throw new PinescriptSyntaxError(`${msg} (${line}:${column})`, line, column);
32
+ }
33
+ }