Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## [0.8.3] - 2026-01-13 - Transpiler Critical Fixes

### Fixed

- **Scientific Notation Parsing**: Fixed Pine Script lexer to correctly parse scientific notation literals (e.g., `10e10`, `1.2e-5`, `1E+5`). Previously, these were incorrectly tokenized as separate tokens, causing syntax errors in transpiled code.
- **Namespace Function Calls in Return Statements**: Fixed critical bug where namespace function calls (e.g., `math.max()`, `ta.sma()`) in single-expression return statements were incorrectly transpiled with double parentheses (e.g., `math.max()()`), resulting in runtime errors. Removed redundant AST traversal in `transformReturnStatement`.

## [0.8.2] - 2026-01-13 - Plot Fill Method & Transpiler Fixes

### Added
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pinets",
"version": "0.8.2",
"version": "0.8.3",
"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.",
"keywords": [
"Pine Script",
Expand Down
27 changes: 27 additions & 0 deletions src/transpiler/pineToJS/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,33 @@ export class Lexer {
}
}

// Check for scientific notation (e.g. 1e10, 1.5e-5)
if (this.pos < this.source.length) {
const ch = this.peek();
if (ch === 'e' || ch === 'E') {
const nextCh = this.peek(1);
if (this.isDigit(nextCh)) {
// Case: 10e5
value += this.advance(); // consume 'e'
// consume digits
while (this.pos < this.source.length && this.isDigit(this.peek())) {
value += this.advance();
}
} else if (nextCh === '+' || nextCh === '-') {
// Case: 10e+5 or 10e-5
const nextNextCh = this.peek(2);
if (this.isDigit(nextNextCh)) {
value += this.advance(); // consume 'e'
value += this.advance(); // consume sign
// consume digits
while (this.pos < this.source.length && this.isDigit(this.peek())) {
value += this.advance();
}
}
}
}
}

this.addToken(TokenType.NUMBER, parseFloat(value));
}

Expand Down
13 changes: 0 additions & 13 deletions src/transpiler/transformers/StatementTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -636,19 +636,6 @@ export function transformReturnStatement(node: any, scopeManager: ScopeManager):
type: 'ArrayExpression',
elements: [node.argument],
};
} else if (node.argument.type === 'BinaryExpression') {
// Transform both operands of the binary expression
walk.recursive(node.argument, scopeManager, {
Identifier(node: any, state: ScopeManager) {
transformIdentifier(node, state);
if (node.type === 'Identifier') {
addArrayAccess(node, state);
}
},
MemberExpression(node: any) {
transformMemberExpression(node, '', scopeManager);
},
});
} else if (node.argument.type === 'ObjectExpression') {
// Handle object expressions
node.argument.properties = node.argument.properties.map((prop: any) => {
Expand Down
23 changes: 23 additions & 0 deletions tests/transpiler/pinescript-to-js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,29 @@ plot(result)
expect(jsCode).toContain('$.get(close, 0) > $.get(open, 0)');
});

it('should transpile scientific notation literals', () => {
const code = `
//@version=6
indicator("Scientific Notation Test")

a = 10e10
b = 1.2e-5
c = 1E+5

plot(a)
`;

const result = transpile(code);
const jsCode = result.toString();

// 10e10 -> 100000000000
expect(jsCode).toContain('100000000000');
// 1.2e-5 -> 0.000012
expect(jsCode).toContain('0.000012');
// 1E+5 -> 100000
expect(jsCode).toContain('100000');
});

it('should reject Pine Script version < 5', () => {
const code = '//@version=4\nindicator("Test")';

Expand Down
4 changes: 2 additions & 2 deletions tests/transpiler/pinets-source-to-js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ let src_open = input.any({ title: 'Open Source', defval: open });
if ($.math.__eq($.get(avg_len, 0), 0)) {
$.set($.let.fn2_ret_val, $.get($.let.fn2_cc, 1));
}
return $.precision($.get($.let.fn2_ret_val, 0) / $.get($.get(avg_len, 0), 0));
return $.precision($.get($.let.fn2_ret_val, 0) / $.get(avg_len, 0));
}
const p2 = $.param(close, undefined, 'p2');
const p3 = $.param(14, undefined, 'p3');
Expand Down Expand Up @@ -1127,7 +1127,7 @@ let src_open = input.any({ title: 'Open Source', defval: open });
if ($.math.__eq($.get(avg_len, 0), 0)) {
$.set($.let.fn1_ret_val, $.get($.let.fn1_cc, 1));
}
return $.precision($.get($.let.fn1_ret_val, 0) / $.get($.get(avg_len, 0), 0));
return $.precision($.get($.let.fn1_ret_val, 0) / $.get(avg_len, 0));
}
const p0 = $.param(close, undefined, 'p0');
const p1 = $.param(14, undefined, 'p1');
Expand Down