From d2e248724e74fb8cde2c114b6f326e56c090d3cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:49:32 +0000 Subject: [PATCH 1/3] Initial plan From 422af44554e3bbb019a4d067c1b9a50b8281b2fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:56:10 +0000 Subject: [PATCH 2/3] Fix semantic analyzer to handle named parameters correctly Co-authored-by: deepentropy <8287111+deepentropy@users.noreply.github.com> --- .../transpiler/semantic/SemanticAnalyzer.ts | 10 ++- packages/pine2ts/tests/semantic.test.ts | 90 +++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts b/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts index 3c57db8..12c4004 100644 --- a/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts +++ b/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts @@ -666,10 +666,16 @@ export class SemanticAnalyzer { } } - // Visit arguments + // Visit arguments - but handle named parameters specially if (node.children) { for (const arg of node.children) { - this.visitExpression(arg); + // Named parameter: Assignment node where left side is the param name + if (arg.type === 'Assignment' && arg.children && arg.children.length >= 2) { + // Only visit the right side (the value), not the left side (param name) + this.visitExpression(arg.children[1]!); + } else { + this.visitExpression(arg); + } } } break; diff --git a/packages/pine2ts/tests/semantic.test.ts b/packages/pine2ts/tests/semantic.test.ts index 3004c71..57d09f2 100644 --- a/packages/pine2ts/tests/semantic.test.ts +++ b/packages/pine2ts/tests/semantic.test.ts @@ -348,4 +348,94 @@ describe('SemanticAnalyzer', () => { expect(result.errors.some(e => e.kind === 'BREAK_OUTSIDE_LOOP')).toBe(true); }); }); + + describe('named parameters', () => { + it('should not error on named parameters in indicator()', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Test", shorttitle="T", overlay=true) + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('should not error on named parameters in input functions', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Test") + len = input.int(14, title="Length", minval=1, maxval=100) + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('should not error on named parameters in plot()', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Test") + plot(close, title="Close", color=color.red, linewidth=2) + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('should still error on actual undefined variables', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Test") + x = undefinedVar + 1 + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + expect(result.errors[0]!.kind).toBe('UNDEFINED_VARIABLE'); + expect(result.errors[0]!.message).toContain('undefinedVar'); + }); + + it('should handle complex mixed positional and named parameters', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Average Day Range", shorttitle="ADR", timeframe="", timeframe_gaps=true) + lengthInput = input.int(14, title="Length") + plot(close, title="ADR", color=color.blue) + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('should validate expressions in named parameter values', () => { + const parser = new PineParser(); + const { ast } = parser.parse(` + indicator("Test") + plot(close, title="Close", color=undefinedColor) + `); + + const analyzer = new SemanticAnalyzer(); + const result = analyzer.analyze(ast); + + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + expect(result.errors[0]!.kind).toBe('UNDEFINED_VARIABLE'); + expect(result.errors[0]!.message).toContain('undefinedColor'); + }); + }); }); From bedd1acc82c51286ad7076f8fd63888dc15416d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:58:55 +0000 Subject: [PATCH 3/3] Address code review feedback - use precise check for Assignment nodes Co-authored-by: deepentropy <8287111+deepentropy@users.noreply.github.com> --- packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts b/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts index 12c4004..b100d62 100644 --- a/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts +++ b/packages/pine2ts/src/transpiler/semantic/SemanticAnalyzer.ts @@ -670,7 +670,7 @@ export class SemanticAnalyzer { if (node.children) { for (const arg of node.children) { // Named parameter: Assignment node where left side is the param name - if (arg.type === 'Assignment' && arg.children && arg.children.length >= 2) { + if (arg.type === 'Assignment' && arg.children && arg.children.length === 2) { // Only visit the right side (the value), not the left side (param name) this.visitExpression(arg.children[1]!); } else {