25d895d 🐛 fix(gitea_workflow): clean up temp repos after job steps 2bc3b11 🐛 fix(gitea_workflow): clean up temporary repo dirs in workflows 98c3f30 📝 docs(agent_rules): allow plan execution on current branch 16c7230 📝 docs(prompts): define custom verify layering 8efc4dd 🐛 fix(skills): quote commit-message description bc8498f 🐛 fix(ci): install tomli for gitea tests 55cda3b 🐛 fix(tests): report missing toml parser clearly c0729c7 🐛 fix(playbook): import Optional for cli compatibility 63e24bf 📦 deps(skills): sync thirdparty skills d2f9356 🐛 fix(ci): isolate gitea workflow repos 588b81d 🐛 fix(ci): inline gitea workflow bootstrap e0b1c3a ♻️ refactor(skills): standardize first-party skill contracts 2c5050d ♻️ refactor(skills): rename repo skills source dir f049dfb 📦 deps(skills): drop duplicate first-party superpowers skills 234b335 ✨ feat(workflow): add superpowers planning and execution state tracking c1702a6 📝 docs(markdown): format tracked markdown and drop stale templates 2325409 📝 docs(markdown): clarify optional markdownlint usage 214c44e 🔧 chore(markdown): add markdownlint baseline and lint fixes a22b324 📝 docs(templates): add execution and memory-bank prompt templates 223a797 📝 docs(templates): update README for Claude Code and current features 4ac8672 📝 docs: simplify README + platform-agnostic tools + auto-create local rules 2431c9d 📝 docs: add claude_md config and use cross-platform paths d64b248 📝 docs: fix README.md inaccuracies and add Claude Code info c8d6bf2 🐛 fix(playbook): use relative paths in CLAUDE.md when not at project root 6518f0f ✨ feat(playbook): auto-create CLAUDE.md with path discovery 6ec9a45 ✨ feat(skills): add skill_link symlink support + platform-agnostic prompt 9f8b6b5 📝 docs: update README and config example for Claude Code support 79cff6c 📝 docs(skills): add Claude Code platform support 452c6f5 ✨ feat(playbook): auto-inject AGENTS.md into CLAUDE.md e1dbf3c 🐛 fix(skills): remove dual-path from commit-message skill f3a7259 🔧 chore(ci): use prepare_repo.sh in both workflows da08212 🔧 chore(ci): extract prepare_repo.sh and clean up workflows 7ade85e 🗑️ remove(tsl): drop syntax_book/, data/ source and build script f94dba0 ♻️ refactor(skills): update playbook.py and tests for thirdparty/ layout b3df412 ♻️ refactor(skills): separate thirdparty skills into thirdparty/ subdirectory 64950e7 📦 deps(skills): sync thirdparty skills a2e3cb0 ✨ feat(playbook): add no_backup deploy controls 8609d59 🐛 fix(docs): repair reference catalog source links 956da11 🐛 fix(playbook): publish hidden ci test fixes 3f67754 📦 deps(skills): sync thirdparty skills 08ca87b 📦 deps(skills): add karpathy thirdparty sync 96b705b 📝 docs(tsl): rebuild canonical syntax and routing manual 3ed5052 📦 deps(skills): sync thirdparty skills 60108dd 📦 deps(skills): sync thirdparty skills da85d4e 🐛 fix(thirdparty): prune nested project snapshots a2a697e 📦 deps(skills): sync thirdparty skills 9df610a 🐛 fix(thirdparty): exclude duplicated superpowers skills 33dd5bb 🐛 fix(thirdparty): preserve optional manifest fields 91b0ea7 🐛 fix(thirdparty): preserve manifest during snapshot update 2e26f98 🔧 chore(thirdparty): generalize skills sync pipeline 5b9c1e3 📦 deps(skills): sync superpowers 2f2d34a 📝 docs(readme): normalize subtree command spacing 62db7db 🐛 fix(ci): serialize superpowers update and sync 3463223 🐛 fix(ci): use literal superpowers sync paths 48f6de8 📦 deps(skills): sync superpowers 4b23529 🔧 chore(ci): merge superpowers update and sync workflow a56d75b 📦 deps(skills): sync superpowers 84bcefa 🔧 chore(ci): use ci[bot] commit author name 00a07e5 📦 deps(skills): sync superpowers 7b84daf 🐛 fix(templates): enforce main loop progress tracking 51373d7 🔧 chore(ci): automate superpowers sync workflow eaaa39c 🐛 fix(ci): prevent stale superpowers sync from restoring skills block 79755c6 📦 deps(skills): sync superpowers 836d878 📦 deps(skills): sync superpowers 8216c9f 📦 deps(skills): sync superpowers 9439505 🐛 fix(playbook): address reported repo issues git-subtree-dir: docs/standards/playbook git-subtree-split: 25d895d8b3f56624ccfe99ad7289e9eb49e0f316
12 KiB
Namespace Libpath And Unit Runtime
文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:10_units_and_scope.md、23_object_runtime_and_introspection.md、12_pitfalls.md
手册位置:第 22 篇,共 32 篇。上一篇:21_external_calls_and_threads.md。下一篇:23_object_runtime_and_introspection.md。
这一篇收拢 unit 的运行时行为,以及 .tsf / namespace / libpath 的查找规则。unit 的基础写法、uses 位置和重名解析,统一放在 10_units_and_scope.md。
这一篇解决什么问题
回答下面这些“写法已经会了,但运行起来为什么这样”的问题:
unit什么时候进入initializationfinalization在什么时候触发unit里的常量、变量和findfunction("UnitName")到底暴露哪些成员tslfilename()返回什么NameSpace "..."、tsl.conf和-LIBPATH怎样影响.tsf查找syssettsllibpath()/sysgettsllibpath()怎样在运行时改查找路径
必须记住的规则
- 完整
unit形态可以包含interface、implementation、initialization、finalization,并以end.结束。 initialization在unit第一次被实际使用时触发,不是只因为顶层写了uses就立刻执行。finalization会在脚本结束前触发。- 直接写
DemoUnit.Member时,当前解释器能读到interface和implementation里的常量、变量。 findfunction("DemoUnit")拿到的是unit对象入口,但当前只验证到它稳定暴露interface成员;implementation变量和常量没有从这个对象读出来。DemoUnit.VarName := value这种限定赋值当前会编译失败;如果要改unit状态,应导出函数或方法来改。tslfilename()在命令行解释器里返回当前执行脚本的完整路径。NameSpace "DemoNS";会选择Hello@DemoNS.tsf这类命名空间函数文件。tsl.conf的[system] Namespace=...可以设置默认命名空间;脚本里的NameSpace "..."会覆盖配置值。- 当全局
Hello.tsf和Hello@StmtNS.tsf同时存在时,启用StmtNS后会优先命中命名空间版本。 -LIBPATH必须跟在脚本文件名后面;目录项必须以/或\结尾;多路径分隔符当前实测是分号;。- 当多个
-LIBPATH目录都存在同名.tsf时,解释器按路径从前到后查找,先命中的目录优先。 tsl.conf里的绝对Libpath=可以生效;Libpath={$P}...在当前解释器下没有拿到正向结果。syssettsllibpath()/sysgettsllibpath()可以在运行时修改和读取查找路径,路径规则与-LIBPATH相同。InheritParent=1目前没有拿到稳定正例,因此不写成正式规则。
已验证语法与行为
unit 生命周期
代码块身份:配置片段 / 概念骨架
// DemoUnit.tsf
unit DemoUnit;
interface
const UnitValue = 7;
var UnitCounter;
function Ping();
implementation
var PrivateCounter;
function Ping();
begin
PrivateCounter := PrivateCounter + 1;
UnitCounter := UnitCounter + 10;
return PrivateCounter;
end;
initialization
WriteLn("INIT");
UnitCounter := 1;
PrivateCounter := 100;
finalization
WriteLn("FINAL");
end.
// main.tsl
program test;
uses DemoUnit;
begin
WriteLn("BEFORE");
WriteLn(DemoUnit.UnitValue);
WriteLn(DemoUnit.UnitCounter);
WriteLn(Ping());
WriteLn(DemoUnit.UnitCounter);
end.
已验证运行结果:
- 输出顺序是
BEFORE、INIT、7、1、101、11、FINAL。 - 这说明顶层写了
uses DemoUnit;之后,initialization不是立刻执行,而是在第一次真正读DemoUnit成员时触发。 finalization在脚本主体输出结束后触发。
unit 成员的读取边界
直接限定读取:
代码块身份:配置片段 / 概念骨架
// DemoUnit.tsf
unit DemoUnit;
interface
const PublicConst = 1;
var PublicVar;
function PublicFunc();
implementation
const ImplConst = 2;
var ImplVar;
function PublicFunc();
begin
return 10;
end;
function ImplFunc();
begin
return 20;
end;
initialization
PublicVar := 3;
ImplVar := 4;
end.
// main.tsl
uses DemoUnit;
WriteLn(DemoUnit.PublicConst);
WriteLn(DemoUnit.PublicVar);
WriteLn(DemoUnit.ImplConst);
WriteLn(DemoUnit.ImplVar);
WriteLn(PublicFunc());
WriteLn(ImplFunc());
已验证运行结果:
DemoUnit.PublicConst输出1。DemoUnit.PublicVar输出3。DemoUnit.ImplConst输出2。DemoUnit.ImplVar输出4。PublicFunc()输出10。ImplFunc()仍然报compile error or not found。- 当前解释器的真实边界是:实现段函数仍私有,但实现段常量和变量可以通过
DemoUnit.Member直接读取。
findfunction("UnitName") 拿到的 unit 对象:
代码块身份:配置片段 / 概念骨架
// DemoUnit.tsf
unit DemoUnit;
interface
const PublicConst = 1;
var PublicVar;
function Ping();
implementation
const ImplConst = 2;
var ImplVar;
function Ping();
begin
return 11;
end;
initialization
PublicVar := 3;
ImplVar := 4;
end.
// main.tsl
uses DemoUnit;
u := findfunction("DemoUnit");
WriteLn(u.PublicConst);
WriteLn(u.PublicVar);
WriteLn(u.Ping());
WriteLn(u.ImplConst);
已验证运行结果:
u.PublicConst输出1。u.PublicVar输出3。u.Ping()输出11。u.ImplConst报对象属性或方法不存在。- 当前解释器下,
findfunction("DemoUnit")拿到的对象入口只稳定暴露interface成员。
限定赋值失败:
代码块身份:反例 / 不可照写
uses DemoUnit;
DemoUnit.UnitCounter := 13;
已验证结果:
- 上面这段会编译失败,报错核心是
left side can not be assign to。
TslFileName
代码块身份:已验证可执行示例
WriteLn(tslfilename());
已验证结果:
- 在命令行解释器里,这一行会输出当前执行
.tsl文件的完整路径。 - 当前已验证的是“主脚本路径”;不要把
.tsf被调用场景的返回值提前写成已验证事实。
NameSpace 与默认命名空间
把默认命名空间、脚本覆盖、以及“全局函数与命名空间函数同名”的三组结论压成同一套最小实验:
代码块身份:配置片段 / 概念骨架
// tsl.conf
[system]
Namespace=ConfNS
Libpath=D:\path\to\lib\
// 同一目录下准备三份函数文件
// Hello.tsf
function Hello();
begin
return 10;
end;
// Hello@ConfNS.tsf
function Hello();
begin
return 20;
end;
// Hello@StmtNS.tsf
function Hello();
begin
return 30;
end;
// main_conf.tsl
program test;
begin
WriteLn(Hello());
end.
// main_stmt.tsl
program test;
begin
NameSpace "StmtNS";
WriteLn(Hello());
end.
已验证运行结果:
main_conf.tsl输出20,说明tsl.conf里的Namespace=ConfNS会默认命中Hello@ConfNS.tsf。main_stmt.tsl输出30,说明脚本里的NameSpace "StmtNS";会覆盖tsl.conf里的Namespace=ConfNS。- 在同一目录保留
Hello.tsf后,不写NameSpace且不设置Namespace时,Hello()输出10。 - 如果只保留
Hello@StmtNS.tsf而没有全局Hello.tsf,又没有启用命名空间,Hello()会报找不到函数。
-LIBPATH
查找顺序:
代码块身份:配置片段 / 概念骨架
// libA/Hello.tsf
function Hello();
begin
return 101;
end;
// libB/Hello.tsf
function Hello();
begin
return 202;
end;
// main.tsl
program test;
begin
WriteLn(Hello());
end.
// command A
tsl .\main.tsl -LIBPATH "D:\libA\;D:\libB\"
// command B
tsl .\main.tsl -LIBPATH "D:\libB\;D:\libA\"
已验证运行结果:
- 命令 A 输出
101。 - 命令 B 输出
202。 - 当前解释器按
-LIBPATH从前到后查找,先命中的目录优先。
位置与分隔符边界:
代码块身份:配置片段 / 概念骨架
tsl -LIBPATH "D:\libA\" .\main.tsl
tsl .\main.tsl -LIBPATH "D:\libA"
tsl .\main.tsl -LIBPATH "D:\libA\,D:\libB\"
已验证结果:
- 把
-LIBPATH放在脚本文件名前面时,解释器不会执行脚本,而是回到 usage 提示。 - 路径项不带结尾
/或\时,函数找不到。 - 逗号分隔多路径没有跑通;当前实测有效的是分号
;。
syssettsllibpath() 与 sysgettsllibpath()
代码块身份:配置片段 / 概念骨架
program test;
begin
WriteLn(syssettsllibpath("C:/path/to/libA/;C:/path/to/libB/"));
WriteLn(sysgettsllibpath());
WriteLn(call("FnA"));
WriteLn(call("FnB"));
end.
已验证运行结果:
syssettsllibpath(".../libA/;.../libB/")之后,call("FnA")和call("FnB")都能找到对应函数。sysgettsllibpath()返回设置后的完整路径串。- 当前实测里,逗号分隔没有跑通;路径项不带结尾
/也没有跑通。
tsl.conf 的 Libpath=
代码块身份:配置片段 / 概念骨架
[system]
Libpath=D:\path\to\funcext\
已验证结果:
- 当前目录
tsl.conf设为绝对Libpath=时,脚本可以找到该目录下的.tsf。 - 当前解释器下,
Libpath={$P}funcext/没有拿到正向结果。 - 当前解释器下,也没有拿到
InheritParent=1的稳定正例,因此不要把这两条当成可依赖规则。
Libpath= 的正向与负向对照:
代码块身份:配置片段 / 概念骨架
// case A: 绝对 Libpath
// tsl.conf
[system]
Libpath=D:/abs/path/to/funcext/
// funcext/Hello.tsf
function Hello();
begin
return 123;
end;
// main.tsl
program test;
begin
WriteLn(Hello());
end.
// case B: {$P} Libpath
// tsl.conf
[system]
Libpath={$P}funcext/
// case B 的 main.tsl 与 case A 相同
// case B 的 funcext/Hello.tsf 只把返回值改成 456
已验证结果:
- case A 输出
123 - case B 仍报
function:Hello compile error or not found - 因此当前解释器下,不能把
{$P}替换写法当成已经稳定可依赖的规则
InheritParent=1 的最小负例:
代码块身份:反例 / 不可照写
// parent/tsl.conf
[system]
Namespace=ParentNS
InheritParent=1
// parent/sub/Hello@ParentNS.tsf
function Hello();
begin
return 314;
end;
// parent/sub/main.tsl
program test;
begin
WriteLn(Hello());
end.
已验证结果:
- 在当前解释器里,这组父目录配置 + 子目录脚本的最小样例仍报
function:Hello compile error or not found - 因此
InheritParent=1现在还不能升级成正式正向结论
常见误写
- 把
DemoUnit.VarName := value当成可用的限定赋值。 - 以为
implementation里的常量和变量一定都不能从DemoUnit.Member读到。 - 以为
findfunction("DemoUnit")暴露的成员范围和DemoUnit.Member完全相同。 - 把脚本内的
NameSpace "..."当成和tsl.conf里的Namespace=...叠加,而不是覆盖。 - 把
-LIBPATH放在脚本文件名前面。 - 用逗号分隔
-LIBPATH或syssettsllibpath()的多路径。 - 忘了给
-LIBPATH或syssettsllibpath()的目录项补结尾/或\。 - 把
Libpath={$P}...和InheritParent=1直接当成当前解释器已经稳定可依赖的规则。
跳转指引
- 回看
unit/uses主线:见 10_units_and_scope.md - 查看对象反射与运行时对象:见 23_object_runtime_and_introspection.md
- 回看函数文件与顶层主体:见 03_core_model.md