pinets 0.9.13 → 0.9.14

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.
@@ -132,6 +132,23 @@ export declare class Context {
132
132
  * @param value - The value to set
133
133
  */
134
134
  set(target: any, value: any): void;
135
+ /**
136
+ * Resolve an iterable for `for x in collection` codegen.
137
+ * Handles PineArrayObject (unwrap to inner JS array) and plain JS arrays uniformly.
138
+ * Returns the value itself if it's already iterable (Map, Set, etc.).
139
+ *
140
+ * Centralizing this here means the transpiler can emit a uniform shape regardless of
141
+ * whether the iterable is a built-in returning a plain array (e.g. box.all) or a UDT
142
+ * field holding a PineArrayObject — and future collection types only need to update
143
+ * this helper, not the codegen.
144
+ */
145
+ iter(source: any): any;
146
+ /**
147
+ * Resolve an iterable yielding [index, value] tuples for `for [i, x] in collection`
148
+ * destructuring codegen. PineArrayObject's [Symbol.iterator] yields scalar values, so
149
+ * we must explicitly call `.entries()` on the underlying array.
150
+ */
151
+ entries(source: any): IterableIterator<[number, any]>;
135
152
  private _callStack;
136
153
  /**
137
154
  * Pushes a call ID onto the stack
@@ -20,5 +20,11 @@ export declare class Str {
20
20
  match(source: string, pattern: string): RegExpMatchArray;
21
21
  split(source: string, separator: string): PineArrayObject;
22
22
  substring(source: string, begin_pos: number, end_pos: number): string;
23
+ /**
24
+ * Format a UNIX millisecond timestamp using Java SimpleDateFormat-style tokens
25
+ * (yyyy, MM, dd, HH, mm, ss, EEE, EEEE, MMM, MMMM, a, h, S, Z, etc.).
26
+ * Text inside single quotes is treated as a literal; '' produces a literal '.
27
+ */
28
+ format_time(time: any, format?: string, timezone?: string): string;
23
29
  format(message: string, ...args: any[]): string;
24
30
  }
@@ -10,9 +10,11 @@ export declare class BoxHelper {
10
10
  private _resolvePoint;
11
11
  private _resolve;
12
12
  /**
13
- * Resolve a color value, preserving NaN (na) so renderers can detect "no color".
14
- * The regular `_resolve(val) || fallback` pattern treats NaN as falsy and replaces
15
- * it with the default, losing the explicit `border_color = na` intent.
13
+ * Resolve a color value, preserving na markers so renderers can detect "no color".
14
+ * Pine emits na either as NaN (from `bgcolor = na`) or as null (from
15
+ * `bgcolor = color(na)` `color(na)` returns null per PineColor.any). Both
16
+ * must survive — replacing them with a default would force renderers to paint
17
+ * a visible color where the script asked for none.
16
18
  */
17
19
  private _resolveColor;
18
20
  private _createBox;
@@ -22,7 +24,7 @@ export declare class BoxHelper {
22
24
  */
23
25
  private _enforceMaxCount;
24
26
  new(...args: any[]): BoxObject;
25
- any(...args: any[]): BoxObject;
27
+ any(...args: any[]): BoxObject | null;
26
28
  set_left(id: BoxObject, left: number): void;
27
29
  set_right(id: BoxObject, right: number): void;
28
30
  set_top(id: BoxObject, top: number): void;
@@ -17,7 +17,7 @@ export declare class LabelHelper {
17
17
  private _createLabel;
18
18
  private _enforceMaxCount;
19
19
  new(...args: any[]): LabelObject;
20
- any(...args: any[]): LabelObject;
20
+ any(...args: any[]): LabelObject | null;
21
21
  set_x(id: LabelObject, x: number): void;
22
22
  set_y(id: LabelObject, y: number): void;
23
23
  set_xy(id: LabelObject, x: number, y: number): void;
@@ -18,7 +18,7 @@ export declare class LineHelper {
18
18
  private _createLine;
19
19
  private _enforceMaxCount;
20
20
  new(...args: any[]): LineObject;
21
- any(...args: any[]): LineObject;
21
+ any(...args: any[]): LineObject | null;
22
22
  set_x1(id: LineObject, x: number): void;
23
23
  set_y1(id: LineObject, y: number): void;
24
24
  set_x2(id: LineObject, x: number): void;
@@ -12,7 +12,7 @@ export declare class LinefillHelper {
12
12
  */
13
13
  private _resolve;
14
14
  new(line1: LineObject, line2: LineObject, color: any): LinefillObject;
15
- any(...args: any[]): LinefillObject;
15
+ any(...args: any[]): LinefillObject | null;
16
16
  set_color(id: LinefillObject, color: any): void;
17
17
  get_line1(id: LinefillObject): LineObject | undefined;
18
18
  get_line2(id: LinefillObject): LineObject | undefined;
@@ -11,8 +11,9 @@ export declare class PolylineHelper {
11
11
  */
12
12
  private _resolve;
13
13
  /**
14
- * Resolve a color value, preserving NaN (na = no color) instead of
15
- * letting it fall through to a default via the || operator.
14
+ * Resolve a color value, preserving na markers (NaN from `na`, null from
15
+ * `color(na)`) so renderers can detect "no color" instead of forcing a
16
+ * default via the `||` operator.
16
17
  */
17
18
  private _resolveColor;
18
19
  /**
@@ -21,7 +22,7 @@ export declare class PolylineHelper {
21
22
  private _extractPoints;
22
23
  new(...args: any[]): PolylineObject;
23
24
  private _enforceMaxCount;
24
- any(...args: any[]): PolylineObject;
25
+ any(...args: any[]): PolylineObject | null;
25
26
  delete(id: PolylineObject): void;
26
27
  get all(): PolylineObject[];
27
28
  /**
@@ -1 +1 @@
1
- export declare function security(context: any): (symbol: any, timeframe: any, expression: any, gaps?: boolean | any[], lookahead?: boolean | any[], ignore_invalid_symbol?: boolean, currency?: any, calc_bars_count?: any) => Promise<any>;
1
+ export declare function security(context: any): (...rawArgs: any[]) => Promise<any>;
@@ -7,4 +7,4 @@ import { PineArrayObject } from '../../array/PineArrayObject';
7
7
  * @param context
8
8
  * @returns
9
9
  */
10
- export declare function security_lower_tf(context: any): (symbol: any, timeframe: any, expression: any, ignore_invalid_symbol?: boolean | any[], currency?: any, ignore_invalid_timeframe?: boolean | any[], calc_bars_count?: number | any[]) => Promise<number | PineArrayObject | any[][]>;
10
+ export declare function security_lower_tf(context: any): (...rawArgs: any[]) => Promise<number | PineArrayObject | any[][]>;
@@ -8,7 +8,7 @@ export declare class TableHelper {
8
8
  syncToPlot(): void;
9
9
  private _resolve;
10
10
  new(...args: any[]): TableObject;
11
- any(...args: any[]): TableObject;
11
+ any(...args: any[]): TableObject | null;
12
12
  cell(...args: any[]): void;
13
13
  delete(id: TableObject): void;
14
14
  clear(...args: any[]): void;
@@ -1,5 +1,23 @@
1
1
  import ScopeManager from './ScopeManager';
2
2
  export declare function transformNestedArrowFunctions(ast: any): void;
3
+ /**
4
+ * Pre-walk the AST to populate the UDT registry on the ScopeManager.
5
+ *
6
+ * Two registries are populated:
7
+ * 1. UDT type names — collected from `const X = Type({field: ['type', default], ...})`
8
+ * which pine2js emits from Pine `type X` declarations. The field-type metadata
9
+ * is stored alongside (V2 data model) for future use-site type-aware rewrites.
10
+ *
11
+ * 2. UDT instance variables — variables initialized via `<X>.new(...)` or
12
+ * `<X>.copy(...)` where X ∈ udtTypeNames. Each instance is tagged with its
13
+ * UDT type name (V2 shape).
14
+ *
15
+ * The instance check intentionally consults `isUdtTypeName(X)` rather than just
16
+ * "X is an Identifier", so built-in factory calls like `array.from(...)`,
17
+ * `polyline.new(...)`, `chart.point.from_index(...)` are excluded — those are
18
+ * handled by their own runtime layers and must NOT be treated as UDT instances.
19
+ */
20
+ export declare function preProcessUdtRegistry(ast: any, scopeManager: ScopeManager): void;
3
21
  export declare function preProcessContextBoundVars(ast: any, scopeManager: ScopeManager): void;
4
22
  export declare function transformArrowFunctionParams(node: any, scopeManager: ScopeManager, isRootFunction?: boolean): void;
5
23
  export declare function runAnalysisPass(ast: any, scopeManager: ScopeManager): string | undefined;
@@ -22,6 +22,72 @@ export declare class ScopeManager {
22
22
  private reservedNames;
23
23
  private userFunctions;
24
24
  private userMethods;
25
+ /**
26
+ * Regular user-declared functions (i.e. NOT methods). Tracked separately
27
+ * from `userFunctions` so a UFCS-style direct call to a method-only
28
+ * declaration (`foo(receiver, args)` where `foo` was declared as
29
+ * `method foo(...)`) can be retargeted to the prefixed JS name.
30
+ *
31
+ * If a Pine name has both a regular function and a method form, the
32
+ * regular function takes precedence for direct `name(args)` calls and
33
+ * the method is reachable via dot-syntax `obj.name(args)`.
34
+ */
35
+ private regularUserFunctions;
36
+ /**
37
+ * Registry of user-defined UDT type names → their field map (fieldName → fieldType).
38
+ * Populated from `const X = Type({fieldA: ['type', default], ...})` declarations
39
+ * (which pine2js emits from Pine `type X` declarations).
40
+ *
41
+ * V2 data model: stores field-type metadata. V1 logic only consults
42
+ * `isUdtTypeName` for now; field metadata is ready for future use-site
43
+ * type-aware rewrites (nested UDT chains, mixed scalar/array fields, etc.).
44
+ */
45
+ private udtTypeNames;
46
+ /**
47
+ * Registry of user variables that hold UDT instances → the UDT type name.
48
+ * Populated from `let bar = X.new(...)` / `bar = X.copy(...)` where
49
+ * `X ∈ udtTypeNames`. Stores the type name (V2 shape) so future passes
50
+ * can do typed-field lookups via `getUdtTypeFields(typeName)` without
51
+ * a refactor. V1 logic only consults `isUdtInstance`.
52
+ */
53
+ private udtInstances;
54
+ /**
55
+ * Registry of user-defined function names → UDT type they return.
56
+ * Populated by inspecting each FunctionDeclaration's return paths during
57
+ * the UDT pre-pass. A function is registered only when ALL return paths
58
+ * unambiguously produce the SAME UDT type.
59
+ *
60
+ * Used by the instance populator so that `bar = makeBar()` registers
61
+ * `bar` as a UDT instance when `makeBar` is known to return one.
62
+ */
63
+ private functionReturnTypes;
64
+ /**
65
+ * Registry of user-defined function names → tuple of UDT type names they
66
+ * return. Each slot holds either the UDT type name at that position, or
67
+ * `undefined` when that position is not (unambiguously) a UDT instance.
68
+ *
69
+ * Populated when ALL return paths of a function are ArrayExpressions of
70
+ * the SAME length and each position resolves to the SAME UDT (or to
71
+ * something non-UDT, which becomes `undefined`).
72
+ *
73
+ * Used by the instance populator so that `[a, b] = makeBars()` registers
74
+ * `a` and `b` as UDT instances at their respective tuple positions.
75
+ */
76
+ private functionReturnTupleTypes;
77
+ /**
78
+ * Registry of user-defined function names → map of {paramName → UDT type}.
79
+ * Populated from `<funcName>.__pineParamTypes__ = {...}` markers emitted
80
+ * by pine2js codegen for parameters that carried a Pine type annotation
81
+ * (e.g. `readField(BAR b)`). Filtered to UDT-known types so non-UDT
82
+ * annotations like `int` / `float` / `string` never enter the map.
83
+ *
84
+ * Consumed by `transformFunctionDeclaration`: when entering a function's
85
+ * body, each typed param is temporarily registered as a UDT instance
86
+ * (`markVariableAsUdtInstance`) so the use-site rewrite for `b.field[N]`
87
+ * fires inside the body. The registration is removed when leaving the
88
+ * function scope, keeping the global registry clean.
89
+ */
90
+ private functionParamUdtTypes;
25
91
  get nextParamIdArg(): any;
26
92
  get nextCacheIdArg(): any;
27
93
  getNextTACallId(): any;
@@ -37,6 +103,41 @@ export declare class ScopeManager {
37
103
  addLocalSeriesVar(name: string): void;
38
104
  removeLocalSeriesVar(name: string): void;
39
105
  isLocalSeriesVar(name: string): boolean;
106
+ addUdtTypeName(typeName: string, fields?: Record<string, string>): void;
107
+ isUdtTypeName(name: string): boolean;
108
+ getUdtTypeFields(typeName: string): Record<string, string> | undefined;
109
+ markVariableAsUdtInstance(varName: string, typeName: string): void;
110
+ getVariableUdtType(varName: string): string | undefined;
111
+ isUdtInstance(varName: string): boolean;
112
+ /**
113
+ * Record a user-defined function as returning a specific UDT type.
114
+ * Idempotent — re-registering with the same type is a no-op; conflicting
115
+ * registrations (different type) drop back to "unknown" by removing the
116
+ * entry, so an ambiguous function never falsely promotes a caller.
117
+ */
118
+ setFunctionReturnType(funcName: string, typeName: string): void;
119
+ getFunctionReturnType(funcName: string): string | undefined;
120
+ /**
121
+ * Record a user-defined function as returning a tuple whose positions
122
+ * carry specific UDT types (or `undefined` for non-UDT positions).
123
+ * Idempotent — re-registering with the same shape is a no-op; conflicting
124
+ * registrations (different length OR different type at any position) drop
125
+ * the entry, so an ambiguous function never falsely promotes a caller.
126
+ */
127
+ setFunctionReturnTupleType(funcName: string, tupleTypes: (string | undefined)[]): void;
128
+ getFunctionReturnTupleType(funcName: string): (string | undefined)[] | undefined;
129
+ /**
130
+ * Record a user-defined function's UDT-typed parameters. The argument
131
+ * is a `paramName → UDT type` map filtered down to UDT-known types only.
132
+ */
133
+ setFunctionParamUdtTypes(funcName: string, paramTypes: Record<string, string>): void;
134
+ getFunctionParamUdtTypes(funcName: string): Record<string, string> | undefined;
135
+ /**
136
+ * Remove a previously-registered UDT instance entry. Used to roll back
137
+ * scope-local registrations (e.g. UDT-typed function parameters) when
138
+ * leaving the function body, so the global registry stays clean.
139
+ */
140
+ unmarkVariableAsUdtInstance(varName: string): void;
40
141
  addContextBoundVar(name: string, isRootParam?: boolean): void;
41
142
  removeContextBoundVar(name: any): void;
42
143
  addArrayPatternElement(name: string): void;
@@ -52,6 +153,8 @@ export declare class ScopeManager {
52
153
  isUserFunction(name: string): boolean;
53
154
  addUserMethod(name: string): void;
54
155
  isUserMethod(name: string): boolean;
156
+ addRegularUserFunction(name: string): void;
157
+ isRegularUserFunction(name: string): boolean;
55
158
  addVariable(name: string, kind: string): string;
56
159
  getVariable(name: string): [string, string];
57
160
  /**
@@ -16,27 +16,45 @@ export declare class CodeGenerator {
16
16
  generate(ast: any): string;
17
17
  private preProcessAST;
18
18
  /**
19
- * Scan the program body for variable declarations and assignments whose
20
- * names collide with Pine namespace/built-in names (e.g., `fill`, `size`,
21
- * `color`, `line`). Rename them with a `_$N` suffix so they don't shadow
22
- * the namespace destructured from `$.pine`.
19
+ * Scan the program body for declarations whose names would collide with
20
+ * either Pine namespaces or JavaScript reserved keywords. Rename them
21
+ * with a `_$N` suffix.
23
22
  *
24
- * Only renames **user variables** function parameters are handled
25
- * separately in generateFunctionDeclaration().
23
+ * Two collision classes, one rename pass:
26
24
  *
27
- * Renaming rules:
28
- * - Variable declaration target (let fill = ...) → renamed
29
- * - Assignment target (fill := ...) renamed
30
- * - Bare identifier read (return fill) renamed
31
- * - Call callee (fill(...)) → NOT renamed (namespace call)
32
- * - MemberExpression object (size.tiny) NOT renamed (namespace access)
33
- * - MemberExpression property (array.size) NOT renamed (method name)
34
- * - Object property key ({size: ...}) → NOT renamed (named arg key)
25
+ * 1. Pine namespace collisions (NAMESPACE_COLLISION_NAMES — e.g. `fill`,
26
+ * `size`, `color`, `line`): user variable would shadow the namespace
27
+ * destructured from `$.pine`. The CALL SITE `fill(...)` is the
28
+ * namespace, NOT the renamed variable, so callees are NOT renamed.
29
+ *
30
+ * 2. JS reserved keyword collisions (JS_RESERVED_WORDS e.g. `delete`,
31
+ * `super`, `static`): the generated JS would fail to parse
32
+ * (`function delete()`"Unexpected keyword 'delete'"). The CALL SITE
33
+ * `delete(arg)` IS the user function, so callees MUST be renamed.
34
+ *
35
+ * The walker checks the original name's source list at each call site to
36
+ * pick the right behavior.
37
+ *
38
+ * Renaming rules (common):
39
+ * - Variable declaration target (let fill = ...) → renamed
40
+ * - Function declaration name (function delete()) → renamed (class 2 only)
41
+ * - Assignment target (fill := ...) → renamed
42
+ * - Bare identifier read (return fill) → renamed
43
+ * - MemberExpression object (size.tiny) → NOT renamed
44
+ * - MemberExpression property (obj.delete) → NOT renamed
45
+ * - Object property key ({size: ...}) → NOT renamed
35
46
  */
36
47
  private renameConflictingVariables;
37
48
  /**
38
- * Walk the AST and collect variable declarations / assignment targets
39
- * whose names conflict with CONFLICTING_NAMES.
49
+ * True if `name` requires renaming either a Pine namespace collision
50
+ * or a JS reserved keyword (which would make the generated JS invalid).
51
+ */
52
+ private isReservedName;
53
+ /**
54
+ * Walk the AST and collect declarations whose names conflict with either
55
+ * Pine namespaces (NAMESPACE_COLLISION_NAMES) or JS reserved keywords
56
+ * (JS_RESERVED_WORDS). Both collision classes are renamed with the same
57
+ * `_$N` suffix scheme.
40
58
  */
41
59
  private collectConflictingVarNames;
42
60
  /**
@@ -58,6 +76,7 @@ export declare class CodeGenerator {
58
76
  generateStatement(node: any): void;
59
77
  generateTypeDefinition(node: any): void;
60
78
  private renameIdentifiersInAST;
79
+ private renameParamRefsInBody;
61
80
  generateFunctionDeclaration(node: any): void;
62
81
  generateVariableDeclaration(node: any): void;
63
82
  generateExpressionStatement(node: any): void;
@@ -4,12 +4,21 @@ export declare class Parser {
4
4
  private tokens;
5
5
  private pos;
6
6
  private functionNames;
7
+ private paramScopes;
7
8
  private noLineContinuation;
8
9
  constructor(tokens: Token[]);
9
10
  peek(offset?: number): Token;
11
+ private isCurrentFunctionParam;
10
12
  advance(): Token;
11
13
  match(type: any, value?: any): boolean;
12
14
  expect(type: any, value?: any): Token;
15
+ private static readonly CONTEXTUAL_KEYWORDS;
16
+ /**
17
+ * Consume an identifier OR a contextual keyword used as an identifier.
18
+ * Used in positions where Pine permits soft keywords as names — most notably
19
+ * UDT field names like `int type = 0`.
20
+ */
21
+ expectIdentifierOrContextual(): Token;
13
22
  matchEx(type: any, value?: any, allowLineContinuation?: boolean): boolean;
14
23
  peekOperatorEx(validOps: string[]): any;
15
24
  skipNewlines(allowIndent?: boolean): void;
@@ -3,6 +3,7 @@ export declare const NAMESPACES_LIKE: string[];
3
3
  export declare const ASYNC_METHODS: string[];
4
4
  export declare const FACTORY_METHODS: string[];
5
5
  export declare const NAMESPACE_COLLISION_NAMES: Set<string>;
6
+ export declare const JS_RESERVED_WORDS: Set<string>;
6
7
  export declare const CONTEXT_DATA_VARS: string[];
7
8
  export declare const CONTEXT_PINE_VARS: string[];
8
9
  export declare const CONTEXT_CORE_VARS: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinets",
3
- "version": "0.9.13",
3
+ "version": "0.9.14",
4
4
  "description": "Run Pine Script anywhere. PineTS is an open-source transpiler and runtime that brings Pine Script logic to Node.js and the browser with 1:1 syntax compatibility. Reliably write, port, and run indicators or strategies on your own infrastructure.",
5
5
  "keywords": [
6
6
  "Pine Script",