Skip to content

Commit ebb0c8a

Browse files
committed
fix function overloads
1 parent f9fb631 commit ebb0c8a

File tree

9 files changed

+567
-357
lines changed

9 files changed

+567
-357
lines changed

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"Bash(npm run test:lexer:*)",
66
"Bash(npm run test:parser:*)",
77
"Bash(npm run build:*)",
8-
"Bash(npm run:*)"
8+
"Bash(npm run:*)",
9+
"Bash(npm test)",
10+
"WebSearch"
911
],
1012
"deny": [],
1113
"ask": []

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 更新日志
22

3+
## [0.1.4] - 2025-10-21
4+
5+
### 改进
6+
7+
- 修复一些函数的多签名支持
8+
39
## [0.1.3] - 2025-10-15
410

511
### 新增
@@ -40,4 +46,4 @@
4046
- `pine.showDocs` - 显示符号文档
4147

4248
### 配置
43-
- `pine.applyFileAssociation` - 自动关联 .pine 文件类型
49+
- `pine.applyFileAssociation` - 自动关联 .pine 文件类型

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"name": "Garden Yuen",
88
"url": "https://github.com/weaming"
99
},
10-
"version": "0.1.3",
10+
"version": "0.1.4",
1111
"icon": "icon.png",
1212
"repository": {
1313
"type": "git",

src/parser/astValidator.ts

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// AST-based Pine Script Validator with Type Checking and Scope Analysis
22
import { Program, Statement, Expression, CallExpression, CallArgument, Identifier, Literal } from './ast';
33
import { V6_FUNCTIONS, V6_NAMESPACES, PineItem } from '../../v6/v6-manual';
4-
import { PINE_FUNCTIONS_MERGED } from '../../v6/parameter-requirements-merged';
5-
import type { FunctionSignatureSpec, FunctionParameter } from '../../v6/parameter-requirements-generated';
4+
import { PINE_FUNCTIONS_MERGED } from '../../v6/functions';
5+
import type { FunctionSignatureSpec, FunctionParameter, FunctionOverload } from '../../v6/functions';
66
import { PineType, TypeChecker } from './typeSystem';
77
import { SymbolTable, Symbol as SymbolInfo } from './symbolTable';
88

@@ -877,6 +877,20 @@ export class AstValidator {
877877
}
878878
}
879879

880+
// Check if function has overloads
881+
const spec = PINE_FUNCTIONS_MERGED[functionName];
882+
if (spec && spec.overloads && spec.overloads.length > 0) {
883+
// Try to match against all overloads
884+
const result = this.tryMatchOverloads(call, functionName, spec.overloads, providedArgs, positionalArgs);
885+
if (!result.matched) {
886+
// Report errors from the best matching overload
887+
for (const error of result.errors) {
888+
this.addError(error.line, error.column, error.length, error.message, error.severity);
889+
}
890+
}
891+
return;
892+
}
893+
880894
// Check argument count
881895
const totalCount = signature.parameters.length;
882896

@@ -978,6 +992,122 @@ export class AstValidator {
978992
this.validateSpecialCases(call, functionName, args);
979993
}
980994

995+
/**
996+
* 尝试匹配所有函数重载
997+
* 返回第一个匹配的重载,或所有重载都不匹配时返回最佳匹配的错误信息
998+
*/
999+
private tryMatchOverloads(
1000+
call: CallExpression,
1001+
functionName: string,
1002+
overloads: FunctionOverload[],
1003+
providedArgs: Map<string, { arg: CallArgument; type: PineType }>,
1004+
positionalArgs: { arg: CallArgument; type: PineType }[]
1005+
): { matched: boolean; errors: Array<{ line: number; column: number; length: number; message: string; severity: DiagnosticSeverity }> } {
1006+
1007+
interface OverloadMatchResult {
1008+
overload: FunctionOverload;
1009+
errors: Array<{ line: number; column: number; length: number; message: string; severity: DiagnosticSeverity }>;
1010+
score: number; // 匹配分数,越低越好
1011+
}
1012+
1013+
const results: OverloadMatchResult[] = [];
1014+
1015+
// 尝试每个重载
1016+
for (const overload of overloads) {
1017+
const errors: Array<{ line: number; column: number; length: number; message: string; severity: DiagnosticSeverity }> = [];
1018+
let score = 0;
1019+
1020+
// 构建参数列表
1021+
const allParams = [
1022+
...overload.requiredParams.map(name => ({ name, optional: false })),
1023+
...overload.optionalParams.map(name => ({ name, optional: true }))
1024+
];
1025+
1026+
// 检查参数数量
1027+
const providedCount = positionalArgs.length + providedArgs.size;
1028+
const requiredCount = overload.requiredParams.length;
1029+
const maxCount = allParams.length;
1030+
1031+
if (positionalArgs.length > maxCount) {
1032+
errors.push({
1033+
line: call.line,
1034+
column: call.column,
1035+
length: functionName.length,
1036+
message: `Too many arguments for '${functionName}'. Expected ${maxCount}, got ${positionalArgs.length}`,
1037+
severity: DiagnosticSeverity.Error
1038+
});
1039+
score += 100; // 严重错误
1040+
}
1041+
1042+
// 检查每个参数
1043+
for (let i = 0; i < allParams.length; i++) {
1044+
const param = allParams[i];
1045+
1046+
// 检查命名参数
1047+
const namedArg = providedArgs.get(param.name);
1048+
if (namedArg) {
1049+
continue; // 命名参数提供了
1050+
}
1051+
1052+
// 检查位置参数
1053+
if (i < positionalArgs.length) {
1054+
continue; // 位置参数提供了
1055+
}
1056+
1057+
// 参数未提供
1058+
if (!param.optional) {
1059+
errors.push({
1060+
line: call.line,
1061+
column: call.column,
1062+
length: functionName.length,
1063+
message: `Missing required parameter '${param.name}' for function '${functionName}'`,
1064+
severity: DiagnosticSeverity.Error
1065+
});
1066+
score += 10; // 缺少必需参数
1067+
}
1068+
}
1069+
1070+
// 检查无效的命名参数
1071+
for (const [name] of providedArgs.entries()) {
1072+
if (!allParams.some(p => p.name === name)) {
1073+
errors.push({
1074+
line: call.line,
1075+
column: call.column,
1076+
length: name.length,
1077+
message: `Invalid parameter '${name}' for this overload`,
1078+
severity: DiagnosticSeverity.Error
1079+
});
1080+
score += 5; // 无效参数
1081+
}
1082+
}
1083+
1084+
results.push({ overload, errors, score });
1085+
1086+
// 如果找到完美匹配,立即返回
1087+
if (errors.length === 0) {
1088+
return { matched: true, errors: [] };
1089+
}
1090+
}
1091+
1092+
// 没有完美匹配,返回最佳匹配的错误
1093+
results.sort((a, b) => a.score - b.score);
1094+
const bestMatch = results[0];
1095+
1096+
// 如果有多个重载,在错误消息中提示所有可能的签名
1097+
if (overloads.length > 1 && bestMatch.errors.length > 0) {
1098+
const signatures = overloads.map(o => o.signature).join('\n ');
1099+
bestMatch.errors.push({
1100+
line: call.line,
1101+
column: call.column,
1102+
length: functionName.length,
1103+
message: `Available overloads:\n ${signatures}`,
1104+
severity: DiagnosticSeverity.Information
1105+
});
1106+
}
1107+
1108+
return { matched: false, errors: bestMatch.errors };
1109+
}
1110+
9811111
private validateSpecialCases(
9821112
call: CallExpression,
9831113
functionName: string,

src/parser/patternValidator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import * as vscode from 'vscode';
8-
import { PINE_FUNCTIONS_MERGED as ALL_FUNCTION_SIGNATURES } from '../../v6/parameter-requirements-merged';
8+
import { PINE_FUNCTIONS_MERGED as ALL_FUNCTION_SIGNATURES } from '../../v6/functions';
99
import { isValidNamespaceMember, CONSTANT_NAMESPACES } from '../../v6/pine-constants-complete';
1010
import {
1111
VARIABLE_NAMESPACES,

v6/check-parameter-conflicts.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* 检查 parameter-requirements.ts (MANUAL) 和 parameter-requirements-generated.ts (GENERATED) 之间的冲突
3+
*
4+
* 这个脚本帮助发现手动定义与自动生成定义之间的差异,确保参数定义的一致性。
5+
*
6+
* 用法: node v6/check-parameter-conflicts.js
7+
*/
8+
9+
const GENERATED = require('../dist/v6/parameter-requirements-generated.js').PINE_FUNCTIONS;
10+
const MANUAL = require('../dist/v6/parameter-requirements.js').ALL_FUNCTION_SIGNATURES;
11+
12+
console.log('🔍 检查手动定义与自动生成定义之间的冲突...\n');
13+
14+
let conflictCount = 0;
15+
const conflicts = [];
16+
17+
// 检查所有手动定义的函数
18+
for (const [funcName, manualSpec] of Object.entries(MANUAL)) {
19+
const generatedSpec = GENERATED[funcName];
20+
21+
if (!generatedSpec) {
22+
console.log(`ℹ️ ${funcName}: 仅存在于手动定义中(自动生成中不存在)`);
23+
continue;
24+
}
25+
26+
// 比较必需参数
27+
const manualRequired = new Set(manualSpec.requiredParams);
28+
const generatedRequired = new Set(generatedSpec.requiredParams);
29+
30+
const manualOptional = new Set(manualSpec.optionalParams);
31+
const generatedOptional = new Set(generatedSpec.optionalParams);
32+
33+
let hasConflict = false;
34+
const issues = [];
35+
36+
// 检查参数分类是否一致
37+
for (const param of manualRequired) {
38+
if (generatedOptional.has(param)) {
39+
hasConflict = true;
40+
issues.push(` ❌ 参数 '${param}': MANUAL定义为必需,GENERATED定义为可选`);
41+
} else if (!generatedRequired.has(param)) {
42+
hasConflict = true;
43+
issues.push(` ⚠️ 参数 '${param}': MANUAL定义为必需,GENERATED中不存在`);
44+
}
45+
}
46+
47+
for (const param of manualOptional) {
48+
if (generatedRequired.has(param)) {
49+
hasConflict = true;
50+
issues.push(` ❌ 参数 '${param}': MANUAL定义为可选,GENERATED定义为必需`);
51+
}
52+
}
53+
54+
if (hasConflict) {
55+
conflictCount++;
56+
console.log(`\n🔴 ${funcName}:`);
57+
console.log(` MANUAL: requiredParams: [${Array.from(manualRequired).join(', ')}]`);
58+
console.log(` optionalParams: [${Array.from(manualOptional).join(', ')}]`);
59+
console.log(` GENERATED: requiredParams: [${Array.from(generatedRequired).join(', ')}]`);
60+
console.log(` optionalParams: [${Array.from(generatedOptional).join(', ')}]`);
61+
issues.forEach(issue => console.log(issue));
62+
63+
conflicts.push({
64+
function: funcName,
65+
manual: {
66+
required: Array.from(manualRequired),
67+
optional: Array.from(manualOptional)
68+
},
69+
generated: {
70+
required: Array.from(generatedRequired),
71+
optional: Array.from(generatedOptional)
72+
}
73+
});
74+
}
75+
}
76+
77+
console.log('\n' + '='.repeat(80));
78+
if (conflictCount === 0) {
79+
console.log('✅ 没有发现冲突!所有手动定义与自动生成定义一致。');
80+
} else {
81+
console.log(`⚠️ 发现 ${conflictCount} 个冲突函数`);
82+
console.log('\n建议:');
83+
console.log('1. 检查 TradingView 官方文档确认正确的参数定义');
84+
console.log('2. 更新 v6/parameter-requirements.ts 中的手动定义');
85+
console.log('3. 重新编译并运行测试');
86+
}
87+
console.log('='.repeat(80) + '\n');
88+
89+
process.exit(conflictCount > 0 ? 1 : 0);

0 commit comments

Comments
 (0)