Skip to content

脚本模式互操作设计(Script-Mode Interop) ​

Status: Draft(裁决已闭环,待立项实施) 路径:本文件 | 上游:python-parity-roadmap §7、auto-as-rust-script-strategy 起点:Plan 539(PyTorch FFI)沉淀的桥接面 + 设计讨论(2026-09-04/05,与用户逐条裁决)

0. 目标与非目标 ​

目标:让 Auto 脚本模式(AutoScript,.as)达到"作为 Python/JS/GDScript/ArkTS 替代"的表面体验——直接写 t.sum(dim: 0)、model(x)、with torch.no_grad() {}, 编译器自动 lowering 到 Plan 539 建成的 py_xxx 桥接函数族。

非目标:不改变桥接层语义(py_xxx 是语义本体,糖只是改写);不在本期适配 JS/GDScript/ArkTS(只预留 ForeignRef 协议位);不提速(易用性/可读性 >> 效率)。

1. 核心架构:糖 → 桥 lowering ​

.as 源码(糖)                    正常模式 Auto(桥)           VM
─────────────────────           ─────────────────────       ────────
t.sum(dim: 0)        ──改写──▶   py_call_kw(t, "sum", …)  ──▶  452
model(x)             ──改写──▶   py_call0(model, x)       ──▶  460
with ng { … }        ──改写──▶   py_enter(ng); try{…}finally{py_exit(ng)}
x.foo / x[i] / x[a..b] ─改写──▶  py_getattr / py_getitem / py_slice
  • 单一真相源:语义全在桥上(450-466 + 待新增 setattr/contains 等)。 糖有 bug 看改写产物即知;py 行为分歧修在桥上,糖自动继承。
  • 改写器做成独立 source-to-source 工具先行(Auto 糖 → 正常模式 .at), 像 Babel 独立演进、产出直接跑现有门禁;稳定后折进编译器模式管线 (multi_mode.rs),不塞进 codegen 热臂(Plan 539 三次栈溢出教训)。
  • 分发组合子:send/get/set/call/len/iter 等约十个正常模式函数组成 通用对象协议(内部查运行期 tag:ForeignRef → py 桥,Auto 值 → 原生方法表)。 静态已知 py 的调用点(use.py 导入、py 调用返回值流型)窥孔直达 py_xxx, 零分发开销。这组组合子即未来 JS/ArkTS 适配的承接点(换 ForeignObject 实现)。
  • 语句级糖:with/for-in 工作在语句层(表达式级改写覆盖不了控制流)。
  • 可调试:--dump-lowered 展示改写产物(AI 生成-验证循环的审查面)。

2. 模式体系(裁决:.as 扩展名) ​

信号效果
.at 文件正常模式(存量语料默认):静态类型、null 禁令、显式 .?()
.as 文件AutoScript:动态分派糖、隐式 Err 传播、null 是值
#[script] / #[rust] pragma显式覆盖(罕见场景)
.at 含 use.py/null 字面量诊断错误("疑似脚本内容,改名 .as 或加 #[script]")——推断只做提示不做语义
  • 助记:.as = AutoScript。与 ActionScript 撞扩展名(已死语言,可接受)。
  • 先例:.ts/.js 方言对 + 渐进关系。
  • 跨模式 import 双向允许:.as→.at 按签名检查;.at→.as 导出函数 自动物化为显式 !T(隐式可失败跨类型边界变显式)。
  • 迁移:含 use.py 的 .at(py parity 套件等)一次性改名 .as, parity runner glob 兼容两者。

3. 边界目录(lowering 规则表) ​

A. 调用 ​

#糖lowering状态
A1t.sum(a)py_call539 ✅
A2t.sum(dim: 0)py_call_kw539 ✅
A3model(x)py_call0539 ✅
A4Linear(2,3, bias: false)py_item_kw539 ✅
A5闭包作 py 实参自动包 py_callable539 手动,糖自动

B. 访问 ​

#糖lowering状态
B1w.shapepy_getattr539 ✅
B2m.weight = w新增 py_setattr待建
B3/B4x[i] / x[i,j] = vpy_getitem/py_setitem539 ✅
B5x[a..b] / x[..b] / x[a..] / x[a..=b] / x[a..b..c]py_slice(Nil 端↔None;..=→b+1)词法已在(含步长注释位),实现步长
B6len(x)GIL len()(动态分派)随组合子
B7k in d新增 py_contains(__contains__)待建
B8var torch = use.py torch裸模块绑句柄(重做 Plan 300 腐烂面)待建
B9torch.nn.LinearB1 递归依赖 B8

C. 运算符 ​

#糖lowering状态
C1/C2+ - * / % 一元 - 比较dunder 臂(反射协议)539 ✅ 逐元素语义钉死
C3/C4if x: / and or notGIL bool(x)(多元素张量按 Python 抛)待建(JMP 臂)
C5a @ ba.matmul(b) → __matmul__词法位置可分(@T 引用类型不动)
C6a ** bMath.pow / 句柄 __pow__(dunder 表加 POW 臂)定案
C7a is bGIL is待建
—^保留给未来 XOR(与 Python 分工一致)定案

D. 值/封送 ​

#规则状态
D1-D3标量/list/对象 → PyObject539 ✅
D4注解预言机驱动标量强制:use.py 导入时读返回类型注解(-> float 等),有承诺的自动 py_float 强制;无注解走保守规则(比较/print 强制,赋值/传参不强制保 backward)定案(typing.get_type_hints 运行期可读;注解撒谎最坏退回 Python 行为,不做正确性地基)
D5list→TAG_LIST(py_call 面)/tuple→List(TAG_OBJECT):维持双通道,统一需先清 P539-D1在案
D6Py_None ↔ null(T18)539 ✅
D7句柄 print/f-string → GIL str()待建
D8type_name(x) 动态分派待建

E. 控制流 ​

#规则状态
E1错误模型(见 §4)定案
E2with expr { } / with expr as x { } → py_enter + try-finally + py_exit;异常抑制默认不做(py_exit 固定传 None×3;需要时显式 try-catch)定案;with 关键字零冲突(仅 #[with(...)] 注解参数位,方括号封闭)。无 as 形态已落地(Plan 560 T09,parser 直产 py_with);as 绑定归 Plan 567 Phase C
E3for-in 双通道:有 __getitem__+len 走索引(快),否则 __iter__/__next__;py_iter() 强制迭代器语义定案

F. 生命周期 ​

F1 导入(B8 补裸模块);F2 句柄 rc(债务区非语法面);F3 闭包捕获句柄退化 (DIV-PY-CLOSURE-1 在案);F4 回调窗口约束文档化(num_workers=0)。

4. 错误模型(定案) ​

隐式 !T + Err 单通道 + 边界 catch:

  1. 脚本模式函数全部默认可失败(隐式 !T,不写签名);调用点隐式短路传播 (编译器自动补 ERROR_PROPAGATE——机器已在);
  2. None/null 是值,永不传播(Python 模型:d.get(k)→None 是值, d[k]→KeyError 是 Err;None + 1→TypeError 归 Err);
  3. catch 拦值传播(帧边界拦 Err,catch e 绑定结构化载荷; py 异常即 PyException TypeError: ...)——try-catch 已存在(Plan 010/012), 值通道拦截是唯一新机制;
  4. 隐式传播作用域:调用 + 索引 + 属性 + 含 null 操作数的运算;
  5. main 边界:未捕获传播 → 脚本带错退出(= Python traceback);
  6. a2py 映射:隐式传播零代码(Python 原生)、.?(d)→x if x is not None else d、 catch→except。

落地现状(2026-09-05,Plan 567 T05-T10):上述 1-6 已全部落地——may 变体桥 453/476/477/478(call/getattr/getitem/kwargs);s2s rule_err_propagate 在 .as 内自动补 .?(桥调用 may 化;用户函数调用点;无 use.py 文件零改写);引擎 ERROR_PROPAGATE 值通道拦截(Err 遇当前帧 try → catch_pc 绑 PyException <Type>: <msg>;null 是值不进 catch);主边界未捕获 Err 带错退出(exit 1)。已知遗留: .?(d) 表达式位链式消费缺陷(存量,567 待澄清⑥)。

5. Null 体系(定案) ​

运行期一个家族,模式两套纪律:

  • TAG_NULL 家族 = ?T 的 None 的运行期表示(niche 优化视角);
  • 正字法 null(JSON/JS/GDScript/ArkTS 3/4 对齐 + JSON 字面量零转换); nil 退役(parser 别名 + 警告);None 保留为 Option 构造器(Some/Ok/Err 家族,?T 体系表面);运行期三拼写 null-family 判等(P-053-2 现状);
  • 正常模式三层禁令:①生产者门控(null/nil 字面量解析错误、use.py 解析错误、未初始化 var 禁止)②类型纪律(裸 T 不可空、?T 未解包流入 T 位 = 类型错误——强制度需盘存加固;泛型擦除处按非空不透明处理) ③运行期金丝雀(null 误用 → TypeError → 类型良好代码永不触发,触发即 编译器漏检报警);
  • None↔null 零转换 + 两条类型规则:null 可流入 ?T 槽位(即 None); Some(null) 非法(Some 载荷非空);
  • *T 裸指针:可空性显式(deref 前 .ok() 判定或 unsafe 块),与 ?T None 不隐式互通(词法半存在,规则先行)。

6. W0 审计清单(null 家族正确性与术语统一) ​

项现状目标
null + 1 等算术族静默垃圾(实测 -2147483646)TypeError(Python 格式消息,可 catch)
"a" + null垃圾数字入串(实测)同上
null[0] / null(x) / for x in null未验证/疑垃圾TypeError(not subscriptable/callable/iterable)
null.to(int)T05 加的 null→-1 臂(翻案:int(None) 应 TypeError)改错误;查 ?. 内部依赖
数组越界静默返回 0 哨兵Err(对标 IndexError)
print(null)待定"None"(a2py parity)
nil 关键字与 null 并存别名 + deprecated 警告
未初始化 var x需探针正常模式禁止

验收用例 = 上述探针翻转(null+1 从 -2147483646 变可捕获 TypeError)。

7. 注解预言机(Python 元数据 = 免费的 d.ts) ​

use.py 导入时经 inspect/typing.get_type_hints 读参数/返回注解 (Plan 300 的 param_count 内省同钩子扩展):

  • -> float/int 承诺 → D4 自动标量强制获静态授权;
  • -> T | None → nullability 知识(lint/微优化);
  • kwargs/参数类型知识同理;
  • 定位是优化与糖的授权,不是正确性地基(注解可撒谎,撒谎最坏退回 Python 行为;覆盖率 torch 约半数,typeshed 补 stdlib);
  • 对标:TS 的 .d.ts / C# 程序集元数据——跨语言无缝感的来源是元数据。

落地现状(2026-09-06,Plan 567 T16-T19):注册期 typing.get_type_hints 内省已落地(inspect_return_annotation,恒等标量 + T | None/Optional[T] 剥 None 双形态);知识灌 PySignature.returns + 全局 PY_RETURN_ANNOTATIONS 表(py_ffi_types,无 pyo3 依赖)→ codegen py_return_types 同源灌注。 D4 授权强制:-> float/int 的 shim 出口走 GIL float()/int()(撒谎注解 退回 Python 行为——fakey 用例实证);-> T | None:None 封 null 值 + W0010 py-nullable-unguarded lint(log::warn + 收集器,语句位裸消费形态)。 显式通道补 py_int(480,对齐 py_float 465)。无注解路径零变化 (20 个 py 套件全量回归面)。

8. 跨语言矩阵(预留) ​

维度Python(首)JSGDScriptArkTS
宿主PyO3 嵌入 ✅QuickJS/deno_core引擎绑定难ets 运行时
空值None(↔null)null+undefined 双空nullundefined
运算符dunder 全家无 @/** 中缀无重载无
异步同步为主Promise→future信号/awaitPromise

可移植层 = ForeignRef + send/get/set/call 组合子;差异落各自 ForeignObject 实现与强制规则表,不回渗语法。/** 走函数/方法形式为跨语言默认。

9. 实施波次(立项骨架) ​

  1. W0 null 家族审计与术语统一(§6 全表 + 三层禁令的生产者门控; 可独立小计划先行,收益立竿见影);
  2. W1 动态分派地基(ForeignRef 协议 + 分发组合子 + .as 模式管线
    • s2s 改写器骨架 + --dump-lowered);
  3. W2 语法糖批(A1-A5/B1-B9/C3-C7/D7/E2-E3 lowering 全表 + 迁移 py 套件改名 .as);
  4. W3 注解预言机(D4 授权强制 + nullability lint)。

收官注记(2026-09-06,Plan 567):W0-W3 四波全部落地——W2.5 收尾 批(p7 双红清偿 / Err 通道汇合 D2+D3 / with-as 绑定 D1)与 W3(注解 预言机)由 Plan 567 收束,设计线闭环。遗留子面见 KNOWN-DEBT(567 执行期新登记:.?(d) 表达式位链式消费存量缺陷等)。

每波独立折叠(539 先例);改写器每条规则配 source-to-source 单测, 产出跑现有三方门禁。

10. 裁决存档 ​

项终裁
切片x[a..b] 家族已在,纯 lowering;步长 a..b..c 实现
with新关键字;lower 成 enter+try-finally+exit;异常抑制默认不做
@脚本模式糖 → matmul;@T 引用类型不动
**脚本模式糖 → __pow__/Math.pow;^ 留 XOR
None 传播None 是值(无条件);失败=Err 单通道;运行期类型错误归 Err
catch拦值传播,绑定结构化 Err;与隐式传播并存(Python 模型)
传播作用域调用+索引+属性+null 运算
D4注解预言机驱动 + 保守回退
E3索引快路径/迭代器双通道
模式边界.as/.at 扩展名为主 + pragma 覆盖 + 推断降级诊断
null 正字null;nil 退役;None=Option 构造器;print "None"
正常模式三层 null 禁令(生产者/类型/金丝雀)
越界0 哨兵 → Err
未初始化 var正常模式禁止
架构糖→桥 lowering;组合子分派;s2s 工具先行;不进 codegen 热臂

Released under the MIT License.