diff --git a/skills/tsl-api-reference/SKILL.md b/skills/tsl-api-reference/SKILL.md index 9b2219f9..92c66d98 100644 --- a/skills/tsl-api-reference/SKILL.md +++ b/skills/tsl-api-reference/SKILL.md @@ -29,12 +29,21 @@ description: "当编写、修改或审查 TSL 代码时需要确认 builtin、do - 未知名称,但知道其行为或中文关键词: ```bash - python /scripts/lookup.py --kw 数组 排序 + python /scripts/lookup.py --kw 最大值 位置 ``` -- 关键词采用 AND 语义;增加关键词可缩小结果范围。 -- 关键词会搜索原有字段及 `qualified_name`、`owner`、`kind`、`binding`、 - `visibility`;因此可用 `--kw protected method` 等组合缩小成员范围。 +- 只查询一个 scope: + + ```bash + python /scripts/lookup.py --scope builtin --name boolToStr + python /scripts/lookup.py --scope dotnet --kw argmax + ``` + +- 关键词采用字面 AND 语义;增加关键词可缩小结果范围。常用同义词由 API 条目的 + Tags 提供,例如“位置/下标”“列表/数组”“打印/输出”。 +- 关键词会搜索 `name`、`signature`、`tags`、`summary`、`module`、`scope` 及 + `qualified_name`、`owner`、`kind`、`binding`、`visibility`,并按字段加权稳定排序; + 因此也可用 `--kw dotnet argmax` 或 `--kw protected method` 缩小范围。 - 使用 `--limit N` 调整打印的候选行数量,N 必须 >= 1。 精确查询会打印完整的条目正文和来源标记。关键词查询打印候选行; @@ -52,8 +61,8 @@ method 显示 `function`,类方法显示 `class function`,静态字段与常 `static field`、`static const`。 同名 API 存在于多个 scope 或模块时(如 `host`、`login`、`port`),`--name` -会依次打印全部同名条目,每条带自己的来源标记。按当前代码所处的 scope 和模块 -选择条目,不要默认取第一条。 +会依次打印全部同名条目,每条带自己的来源标记。已知 scope 时先用 `--scope` +过滤;否则按当前代码所处的 scope 和模块选择条目,不要默认取第一条。 查询脚本兼容旧 8 列和新 13 列 `function_index.tsv`。旧 8 列索引仍可查询原有 function;完全限定名称、owner、kind、binding、visibility 依赖新索引中的扩展列。 diff --git a/skills/tsl-api-reference/data/function_index.tsv b/skills/tsl-api-reference/data/function_index.tsv index b78489b2..b07eeeea 100644 --- a/skills/tsl-api-reference/data/function_index.tsv +++ b/skills/tsl-api-reference/data/function_index.tsv @@ -182,7 +182,7 @@ arcTan builtin math arcTan(x) builtin/math/numeric.md arctan 反正切函数, arcTan2 builtin math arcTan2(x, y) builtin/math/numeric.md arctan2 反正切函数,求正切值等于Y/X的弧度值,[-π,π] function arcTan2 arcTanh builtin math arcTanh(x) builtin/math/numeric.md arctanh 反双曲正切函数,计算参数X的反双曲正切值,是双曲正切函数TanH(X)的反函数 function arcTanh argconfirst dotnet runtime argconfirst(a, v, _type) dotnet/runtime/array.md argconfirst 第一次满足条件的位置, 原数组必须为有序表且元素应可比较。 function argconfirst -argmax dotnet runtime argmax(x) dotnet/runtime/array.md argmax 1、返回一维数组中最大值所在下标 function argmax +argmax dotnet runtime argmax(x) dotnet/runtime/array.md argmax 数组 列表 array list 最大值 最大 maximum max 下标 索引 位置 index position 1、返回一维数组中最大值所在下标 function argmax argmin dotnet runtime argmin(x) dotnet/runtime/array.md argmin 1、返回一维数组中最小值所在下标 function argmin argsort dotnet runtime argsort(value, sortdir) dotnet/runtime/array.md argsort 返回可以用于排序的一维数组下标 function argsort arma dotnet runtime arma(series, p, q) dotnet/runtime/math_stats.md arma 自回归移动平均模型,ARMA(p,q)模型,采用逆函数去估计自回归系数和移动平均系数,返回结果有自回归系数、移动平均系数、残差方差等信息 ARMA模型: 其中:为原序列零均值化的新序列,是自回归方程系数,为移动平均系数,是零均值、方差为的平稳白噪声 function arma @@ -197,7 +197,7 @@ arrayinsert dotnet runtime arrayinsert(a, ind, v) dotnet/runtime/array.md arrayi arraysetdefault dotnet runtime arraysetdefault(r, k, v) dotnet/runtime/array.md arraysetdefault 带默认值的数组赋值(字段未使用则赋值) function arraysetdefault arraysetdefault2 dotnet runtime arraysetdefault2(r, k, v) dotnet/runtime/array.md arraysetdefault2 带默认值的数组赋值(值为nil则赋值) function arraysetdefault2 arrayToFm builtin base arrayToFm(value, sample_value) builtin/base/array.md arraytofm 将数组转换为 FMArray,并按样例值确定单元格类型。 function arrayToFm -arrDropDuplicate dotnet runtime arrDropDuplicate(data) dotnet/runtime/array.md arrdropduplicate 数组去重(包含NAN)。 function arrDropDuplicate +arrDropDuplicate dotnet runtime arrDropDuplicate(data) dotnet/runtime/array.md arrdropduplicate 数组 列表 array list 去重 删除重复 deduplicate duplicate dropduplicate 数组去重(包含NAN)。 function arrDropDuplicate AsBinary builtin language AsBinary builtin/language/legacy_pending.md asbinary 消息的原始内容,二进制数据类型。 function AsBinary AsBinary builtin language AsBinary builtin/language/legacy_pending.md asbinary-1 内容以二进制类型的原始值。 function AsBinary asBlank dotnet document asBlank(col, row) dotnet/document/excel_class.md asblank 是否为空白单元格 function asBlank @@ -12287,7 +12287,7 @@ WR_v dotnet quant WR_v(n) dotnet/quant/ma_indicator.md wr_v 威廉指标WR, wrapText builtin base wrapText(max_col) builtin/base/string.md wraptext 按照最大行长为MaxCol自动分行。 function wrapText wrapText dotnet document wrapText(col, row) dotnet/document/excel_class.md wraptext 单元格的是否换行 function wrapText wrcoef dotnet runtime wrcoef(o, c, l, wname, n) dotnet/runtime/math_stats.md wrcoef 一维离散小波单支重构,小波分析,小波重构中间函数或者细节系数(高频)提取 function wrcoef -write builtin external write(p1, pn) builtin/external/cgi.md write 输出字符串,如果在平台运行,会输出信息到客户端。 function write +write builtin external write(p1, pn) builtin/external/cgi.md write 打印 输出 output print write writeln 输出字符串,如果在平台运行,会输出信息到客户端。 function write WriteBinaryStream builtin language WriteBinaryStream(section, ident, value) builtin/language/object_tsl.md writebinarystream 写INI内容。 function WriteBinaryStream WriteBoolean builtin language WriteBoolean(section, ident, value) builtin/language/object_tsl.md writeboolean 写INI内容。 function WriteBoolean WriteClient builtin language WriteClient(buffer, count) builtin/language/object_tsl.md writeclient 向客户端发送的数据。 function WriteClient @@ -12297,7 +12297,7 @@ writeFile dotnet runtime writeFile(data_type, alias, file_name, pos, length, dat WriteFloat builtin language WriteFloat(section, ident, value) builtin/language/object_tsl.md writefloat 写INI内容。 function WriteFloat WriteHeaders builtin language WriteHeaders(status_code, headers) builtin/language/object_tsl.md writeheaders 向客户端发送头部。 function WriteHeaders WriteInteger builtin language WriteInteger(section, ident, value) builtin/language/object_tsl.md writeinteger 写INI内容。 function WriteInteger -writeln builtin external writeln(p1, pn) builtin/external/cgi.md writeln 带回车地输出字符串,如果在平台运行,会输出信息到客户端。 function writeln +writeln builtin external writeln(p1, pn) builtin/external/cgi.md writeln 打印 输出 output print write writeln 带回车地输出字符串,如果在平台运行,会输出信息到客户端。 function writeln WriteString builtin language WriteString(s) builtin/language/object_tsl.md writestring 向客户端发送的数据。 function WriteString WriteString builtin language WriteString(section, ident, value) builtin/language/object_tsl.md writestring-1 写INI内容。 function WriteString writeToLog dotnet runtime writeToLog(dir, sub_dir, value) dotnet/runtime/file_dir.md writetolog 向虚拟目录写日志。其中Dir为虚拟目录名,SubDir为文件名,成功写入日志后,会生成命名为’SubDir’+写入日期+’.log’的TXT文件,文件内容为写入时间->Value。用户使用其应当申请好服务器权限。 20250512升级:优化多个线程同时写入时存在丢失情况: 1、增加重试次数,重试20次间隔0.5秒。 2、增加返回内容。 注意:在极端情况下(超多多线程下同时写入)可能还会存在写入失败情况,此时可通过返回结果继续处理 function writeToLog diff --git a/skills/tsl-api-reference/references/codegen/builtin/external/cgi.md b/skills/tsl-api-reference/references/codegen/builtin/external/cgi.md index d40a8f61..a1adcaba 100644 --- a/skills/tsl-api-reference/references/codegen/builtin/external/cgi.md +++ b/skills/tsl-api-reference/references/codegen/builtin/external/cgi.md @@ -30,6 +30,8 @@ 带回车地输出字符串,如果在平台运行,会输出信息到客户端。 + + | 参数 | 类型 | 说明 | | ---- | ------ | -------------- | | `p1` | string | string 字符串1 | @@ -40,13 +42,9 @@ ### 示例 ```tsl -// 先输出111222,接着另起一行显示空白,再接着另起一行输出333 +// 需平台运行环境查看客户端输出 writeln("111", "222"); writeln("333"); -{结果页面中,运行信息下打印: -111222 -333 -} ``` ## `write(p1, pn)` @@ -55,6 +53,8 @@ writeln("333"); 输出字符串,如果在平台运行,会输出信息到客户端。 + + | 参数 | 类型 | 说明 | | ---- | ------ | -------------- | | `p1` | string | string 字符串1 | @@ -65,13 +65,10 @@ writeln("333"); ### 示例 ```tsl -// 先输出123456,接着另起一行输出789 +// 需平台运行环境查看客户端输出 write("123", "456"); write("789"); return; -{在结果页面中打印: -123456 -789} ``` ## `httpGetContent()` diff --git a/skills/tsl-api-reference/references/codegen/dotnet/runtime/array.md b/skills/tsl-api-reference/references/codegen/dotnet/runtime/array.md index 620f8ced..da64a13c 100644 --- a/skills/tsl-api-reference/references/codegen/dotnet/runtime/array.md +++ b/skills/tsl-api-reference/references/codegen/dotnet/runtime/array.md @@ -1459,6 +1459,8 @@ return fieldCountOfStrSubscript(s); 1、返回一维数组中最大值所在下标 + + | 参数 | 类型 | 说明 | | ---- | ---------------- | ------------ | | `x` | array,tablearray | 一维数字数组 | @@ -1469,7 +1471,8 @@ return fieldCountOfStrSubscript(s); ```tsl x := array(9, 8, 10); -return argmax(x); // 2 +return argmax(x); +// 输出:2 ``` ## `argmin(x)` @@ -2688,6 +2691,8 @@ return groupDemeanIt(t, 'G', array('V1', 'V2')); 数组去重(包含NAN)。 + + | 参数 | 类型 | 说明 | | ------ | ----- | -------------------- | | `data` | array | 数据表类型。数据表。 | @@ -2699,7 +2704,7 @@ return groupDemeanIt(t, 'G', array('V1', 'V2')); ```tsl data := array(("A": 1, "B": nan), ("A": 1, "B": 2), ("A": 1, "B": nan), ("A": 1, "B": 2)); return arrDropDuplicate(data); -// 结果:array(("A":1,"B":NAN),("A":1,"B":2)) +// 输出:array(("A":1,"B":NAN),("A":1,"B":2)) ``` ## `getSubTableByField(t)` diff --git a/skills/tsl-api-reference/scripts/lookup.py b/skills/tsl-api-reference/scripts/lookup.py index a5596600..c2b89c5e 100644 --- a/skills/tsl-api-reference/scripts/lookup.py +++ b/skills/tsl-api-reference/scripts/lookup.py @@ -3,6 +3,7 @@ import argparse import re import sys +import unicodedata from pathlib import Path TOP_LEVEL_RE = re.compile(r"^##(?!#)\s+`(.+?)`\s*$") @@ -43,6 +44,25 @@ OPTIONAL_COLUMNS = ( "owner", "qualified_name", ) +SEARCH_FIELD_WEIGHTS = ( + ("name", 120), + ("qualified_name", 120), + ("signature", 90), + ("tags", 70), + ("summary", 50), + ("module", 30), + ("scope", 30), + ("owner", 25), + ("kind", 20), + ("binding", 20), + ("visibility", 20), +) +# Complete coverage in curated semantic text must outrank incidental short +# aliases in composite identifiers, such as "index" and "max". +SEMANTIC_COVERAGE_BONUSES = ( + ("tags", 240), + ("summary", 200), +) HELP_EPILOG = """\ 检索分两步,--kw 的摘要不能直接作为 API 事实: @@ -53,6 +73,7 @@ HELP_EPILOG = """\ lookup.py --name arrDropDuplicate 已知确切名称时可以直接用 --name,跳过第 1 步。 +只查询一个 API scope 时使用 --scope,例如 --scope builtin 或 --scope dotnet。 退出码: 0 取回成功;--name 无匹配也是 0(打印提示,不算错误) @@ -67,20 +88,6 @@ def non_empty(value): return value -def find_tsv(start): - """Walk upward from start and find a bundled or legacy function_index.tsv.""" - for directory in [start, *start.parents]: - candidates = [ - directory / "data" / "function_index.tsv", - directory / "function_index.tsv", - directory / "docs" / "tsl" / "codegen" / "function_index.tsv", - ] - for candidate in candidates: - if candidate.is_file(): - return candidate - return None - - def load_rows(tsv_path): lines = tsv_path.read_text(encoding="utf-8").splitlines() if not lines: @@ -108,6 +115,45 @@ def load_rows(tsv_path): return rows +def normalize(value): + return unicodedata.normalize("NFKC", value).casefold().strip() + + +def field_match_score(value, variant, weight): + normalized = normalize(value) + if not normalized or not variant: + return 0 + if normalized == variant: + return weight + 20 + if variant in normalized: + return weight + return 0 + + +def term_score(row, term): + best = 0 + for field, weight in SEARCH_FIELD_WEIGHTS: + score = field_match_score(row.get(field, ""), term, weight) + if score: + best = max(best, score) + return best + + +def field_covers_terms(row, field, terms): + value = normalize(row.get(field, "")) + return bool(value) and all(term in value for term in terms) + + +def keyword_sort_key(row): + return ( + normalize(row.get("qualified_name", "") or row.get("name", "")), + normalize(row.get("signature", "")), + normalize(row.get("scope", "")), + row.get("page", ""), + row.get("anchor", ""), + ) + + def search_exact(rows, name): key = name.casefold() return [ @@ -122,26 +168,19 @@ def search_exact(rows, name): def search_keyword(rows, terms): - lowered_terms = [term.lower() for term in terms] - matches = [] + normalized_terms = [normalize(term) for term in terms] + ranked = [] for row in rows: - haystack = " ".join( - [ - row.get("name", ""), - row.get("signature", ""), - row.get("module", ""), - row.get("tags", ""), - row.get("summary", ""), - row.get("kind", ""), - row.get("binding", ""), - row.get("visibility", ""), - row.get("owner", ""), - row.get("qualified_name", ""), - ] - ).casefold() - if all(term in haystack for term in lowered_terms): - matches.append(row) - return matches + scores = [term_score(row, term) for term in normalized_terms] + if scores and all(scores): + coverage_bonus = sum( + bonus + for field, bonus in SEMANTIC_COVERAGE_BONUSES + if field_covers_terms(row, field, normalized_terms) + ) + ranked.append((sum(scores) + coverage_bonus, row)) + ranked.sort(key=lambda item: (-item[0], *keyword_sort_key(item[1]))) + return [row for _, row in ranked] def slug(text): @@ -334,12 +373,6 @@ def codegen_root_for_tsv(tsv_path): return DEFAULT_CODEGEN_ROOT -def default_tsv_path(): - if DEFAULT_TSV.is_file(): - return DEFAULT_TSV - return find_tsv(Path.cwd()) - - def main(argv=None): if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8") @@ -372,15 +405,24 @@ def main(argv=None): "--kw", nargs="+", type=non_empty, - help="按关键词检索候选清单,多个词是 AND 关系(全部命中才返回);" - "逐行搜索 name、signature、module、tags、summary、kind、binding、" - "visibility、owner 和 qualified_name 这些列的拼接文本," - "大小写不敏感且按子串匹配。只返回摘要表格,不含条目正文", + help="按关键词检索候选清单:多个查询词是 AND 关系;" + "逐词在 name、signature、tags、summary、module、scope、kind、binding、" + "visibility、owner 和 qualified_name 中做字面子串匹配,按字段加权并稳定排序。" + "同义词由索引的 tags 字段提供。" + "大小写不敏感,只返回摘要表格,不含条目正文", ) parser.add_argument( "--tsv", metavar="PATH", - help="显式指定 function_index.tsv;默认使用本 skill 内置的 data/function_index.tsv", + help="显式指定 function_index.tsv;默认只使用本 skill 内置的 " + "data/function_index.tsv,不从工作目录回退查找旧索引", + ) + parser.add_argument( + "--scope", + type=non_empty, + metavar="SCOPE", + help="只查询指定 scope,大小写不敏感;作用于 --name 和 --kw。" + "内置索引当前提供 builtin 与 dotnet", ) parser.add_argument( "--limit", @@ -394,8 +436,8 @@ def main(argv=None): if args.limit < 1: parser.error("--limit must be >= 1") - tsv_path = Path(args.tsv) if args.tsv else default_tsv_path() - if not tsv_path or not tsv_path.is_file(): + tsv_path = Path(args.tsv) if args.tsv else DEFAULT_TSV + if not tsv_path.is_file(): print( "ERROR: function_index.tsv not found; reinstall tsl-api-reference " "or pass --tsv PATH", @@ -415,6 +457,20 @@ def main(argv=None): file=sys.stderr, ) return 1 + if args.scope is not None: + requested_scope = normalize(args.scope) + available_scopes = sorted( + {row["scope"] for row in rows if row.get("scope")}, + key=normalize, + ) + if requested_scope not in {normalize(scope) for scope in available_scopes}: + print( + f"ERROR: unknown scope {args.scope!r}; available scopes: " + f"{', '.join(available_scopes)}", + file=sys.stderr, + ) + return 2 + rows = [row for row in rows if normalize(row["scope"]) == requested_scope] codegen_root = codegen_root_for_tsv(tsv_path) if args.name is not None: @@ -450,6 +506,8 @@ def main(argv=None): print(format_row(row)) if len(matches) > args.limit: print(f"... {len(matches) - args.limit} more; refine keywords or raise --limit") + if not matches: + print("No matching API candidates; do not infer an API name.") return 0