feat: support bare name assignments in interactive and function scopes

This commit is contained in:
2026-10-20 18:16:54 +08:00
parent 6281e58647
commit df1b77fc1a
17 changed files with 259 additions and 37 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
| EndorObject、句柄、默认操作 | [对象模型](runtime/objects.md) | objects/src |
| Number、String、Dict、Cell、Function | [基本对象](runtime/builtins.md) | objects/src |
| 源码读取与来源 | [Reader](runtime/reader.md) | frontend/src/reader.rs |
| 词法、语法和诊断 | [Frontend](runtime/frontend.md) | frontend/src/lexer.peg/grammar.peg |
| 词法、语法、裸赋值和诊断 | [Frontend](runtime/frontend.md) | frontend/src/lexer.peg/grammar.peg |
| ASDL、AST 与编辑器接口 | [AST](runtime/ast.md) | frontend/Endor.asdl |
| 名称分析、编译和装载 | [Compiler](runtime/compiler.md) | compiler/src |
| 字节码、函数与限额 | [字节码 VM](runtime/bytecode.md) | objects/src、core/src |
+3 -2
View File
@@ -33,13 +33,14 @@ Rust 用 struct/enum、Vec/Option 表达结构;生成器在按值依赖图
| Program | declarations: Vec<Decl> 与 span;当前声明种类为 Function |
| Decl/DeclKind | 声明的位置及其具体种类;Function 保存参数、返回类型名称和 Block |
| Block | statements: Vec<Stmt> 与 span,不再用固定的 bindings/result 字段表示 |
| Stmt/StmtKind | Binding、Return、Expression、Declaration;每条语句有自身范围 |
| Stmt/StmtKind | Binding、Assignment、Return、Expression、Declaration;每条语句有自身范围 |
| Assignment | 名称目标、右侧表达式及原文范围;独立于 let 的 Binding 节点 |
| ReturnStatement | 可选表达式与范围;当前源码中的 return 仍要求表达式 |
| Expr/ExprKind | 名称、数字原文、字符串、符号、一元/二元运算、成员和调用 |
参数与返回类型仍为 Name,只保留语法,不实施类型或名称检查。Program::functions() 提供函数迭代便捷接口。旧的 Program.functions、Block.bindings/result/return_span 字段已移除;源码构造和使用方改为声明/语句列表。ExprKind 的既有表达式结构保留。
AST 数据模型允许一般语句列表,不代表 parser 接受了所有组合。模块仍要求一个或多个函数;函数体仍接受 let* 后跟一个带值 return。Interactive 模式接受一次函数声明、let 绑定或表达式,必须消费完整输入;不创建交互会话,不处理续行提示,不执行语言代码。多条交互提交由宿主分别解析。条件、循环、导入、类等尚未实现,也不加入 FunctionType 这种 Python 专用入口。
AST 数据模型允许一般语句列表,不代表 parser 接受了所有组合。模块仍要求一个或多个函数;函数体接受 let/裸赋值序列后跟一个带值 return。Interactive 模式接受一次函数声明、let 绑定、裸名称赋值或表达式,必须消费完整输入;不创建交互会话,不处理续行提示,不执行语言代码。多条交互提交由宿主分别解析。条件、循环、导入、类等尚未实现,也不加入 FunctionType 这种 Python 专用入口。
## 解析与位置
+6 -6
View File
@@ -15,9 +15,9 @@
## 名称与作用域
首版是模块作用域和函数作用域。模块先登记全部函数名,再分析函数体,因此支持前向引用、自递归和函数作为普通值传递。函数参数占据最前面的局部槽,let 按声明顺序分配后续槽;引用优先使用函数局部,其次使用已声明全局。
首版是模块作用域和函数作用域。模块先登记全部函数名,再分析函数体,因此支持前向引用、自递归和函数作为普通值传递。函数参数占据最前面的局部槽,let 与首次赋值目标按出现顺序分配后续槽;引用优先使用函数局部,其次使用已声明全局。
函数内先收集全部局部名称,再按语句顺序检查初始化。后面声明的 let 会遮蔽整个函数中的同名全局,但提前读取报错;let x = x 也会报未初始化。参数、局部绑定和同一模块的函数不允许在各自作用域内重复声明;参数或局部可以遮蔽模块函数。let 当前没有赋值/重绑定语法。嵌套函数/闭包分析和块级作用域尚未实现。
函数内先收集全部局部名称,再按语句顺序检查初始化。后面声明的 let 会遮蔽整个函数中的同名全局,但提前读取报错;let x = x 也会报未初始化。参数、局部绑定和同一模块的函数不允许在各自作用域内重复声明;参数或局部可以遮蔽模块函数。裸名称赋值 `x = expression` 可在交互全局创建或更新名字;函数内赋值复用已有参数/局部槽,不存在时分配局部槽。赋值右侧先求值再写入,失败不会覆盖该目标;函数内赋值目标仍遮蔽同名全局,首次初始化前读取报错。`let` 的重复声明检查不变。嵌套函数/闭包分析和块级作用域尚未实现。
CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称,默认空。不认识的名称在编译时报错;已声明的宿主名称仍必须在执行时存在,否则 VM 返回 NameNotFound。编译器不自动注入 print、默认基础类或标准库,也不读取这些名字的值。类型注解只保留语法,不参与名称绑定、运行时求值或类型检查。
@@ -27,14 +27,14 @@ CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称
| --- | --- |
| 字符串、整数、十进制常量 | 放入常量表并 LoadConst;数字经现有 EndorNumber 解析,不转换为 f64 |
| 数字字面量前的一个或多个负号 | 折叠符号为精确常量;不是任意表达式的一元算术 |
| 局部读取/let | LoadFast/StoreFast;参数和局部名称在 CodeLayout 中 |
| 全局读取 | LoadGlobal,使用该代码对象的 names 表 |
| 局部读取/let/赋值 | LoadFast/StoreFast;参数和局部名称在 CodeLayout 中 |
| 全局读取/赋值 | LoadGlobal/StoreGlobal,使用该代码对象的 names 表 |
| 函数声明 | 子代码模板 → LoadConst(code) → MakeFunction(0, 0) → StoreGlobal |
| 普通调用 | 求值 callee → PushNull → 从左到右求值位置参数 → Call(argc) |
| 成员读取 | 求值对象 → LoadAttr;沿用 VM 属性协议,无自动 self 绑定 |
| return | 求值结果 → ReturnValue;无值 return 或 AST 函数末尾无 return 时生成 nil |
源码 parser 仍要求函数体为 let* 加一个带值 return;更通用的表达式语句和空 return 可以由 AST 调用方构造,但不能因此宣称源码语法已开放。模块初始化只发布函数并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。
源码 parser 仍要求函数体为 let/裸赋值序列加一个带值 return;更通用的表达式语句和空 return 可以由 AST 调用方构造,但不能因此宣称源码语法已开放。模块初始化只发布函数并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。
二元算术、非字面量的一元负号和符号值 .name 目前产生明确 Compilation 诊断,不伪造运行结果。导入、类、Agent、条件/循环、关键字参数等仍未实现。函数参数个数和对象是否可调用由 VM 在运行时检查。
@@ -44,7 +44,7 @@ CompiledCode 是纯 Rust 模板,嵌套代码常量在模板内部使用占位
宿主为模块提供 globals 字典,调用 vm.execute(code, globals) 发布函数,然后从字典借用函数句柄并调用 vm.call。函数共享装载后的代码,反复调用不重新编译;execute/call 的对象结果持有根,用后 release_value。函数指向 globals,globals 保存函数,因此宿主需保留命名空间期间的根,并在销毁时清空字典或释放根后显式 collect_cycles。编译器不清空宿主字典。
Expression 模式返回表达式值;Interactive 模式严格编译一次声明、let 或表达式。声明/绑定返回 nil,表达式返回值;宿主可以跨提交复用 globals,并在下一次 CompileOptions 中声明可见名称。Lyra 当前没有 REPL、续行或自动会话管理。
Expression 模式返回表达式值;Interactive 模式严格编译一次声明、let、裸赋值或表达式。声明/绑定/赋值返回 nil,表达式返回值;宿主可以跨提交复用 globals,并在下一次 CompileOptions 中声明可见名称。Lyra 当前没有 REPL、续行或自动会话管理。
代码对象保存来源字符串、函数名和定义起始行,现有 VM traceback 保存该位置及指令偏移;没有每条指令对应的精确源码行/列映射。现有 ExecutionLimits 帧数/指令数限制可由嵌入方继续使用;本链路不构成不可信代码沙箱。
+5 -2
View File
@@ -52,11 +52,12 @@ AST 结构及 Visitor、Spanned 和跨工具 JSON 契约见 [AST 模型](ast.md)
program = function+
function = "function" name "(" parameters? ")" ":" name block
parameter = name ":" name
block = "{" binding* "return" expression "}"
block = "{" (binding | assignment)* "return" expression "}"
binding = "let" name "=" expression
assignment = name "=" expression
```
parameters 和调用参数列表使用逗号分隔,不接受尾随逗号。函数体要求一个末尾 return;函数体尚不支持独立表达式语句、重赋值、嵌套函数声明或默认参数源码写法。新增 Interactive 解析模式接受一次函数声明、let 绑定或表达式,完整消费输入,但不创建 REPL 或执行代码;AST 可表示的语句组合比当前源码子集更广。参数和返回类型目前只是名称节点;并不据此创建语言 type 对象或检查类型。
parameters 和调用参数列表使用逗号分隔,不接受尾随逗号。函数体要求一个末尾 return;函数体尚不支持独立表达式语句、属性赋值、嵌套函数声明或默认参数源码写法。新增 Interactive 解析模式接受一次函数声明、let 绑定、裸名称赋值或表达式,完整消费输入,但不创建 REPL 或执行代码;AST 可表示的语句组合比当前源码子集更广。参数和返回类型目前只是名称节点;并不据此创建语言 type 对象或检查类型。
表达式包括名称、数字、字符串、`.name` 符号、括号分组、成员访问与位置参数调用、一元负号以及四则运算。优先级从高到低为:
@@ -76,3 +77,5 @@ use、namespace、class、agent、完整类型语法、条件/循环、比较
`lyra parse <file>` 与 `lyra parse -` 成功时输出 Rust Debug 格式的 AST,失败时在 stderr 输出来源/行/列和诊断并返回非零退出码;`-` 表示从 stdin 读到 EOF,不是 REPL。阅读与来源包装规则见 [阅读器](reader.md)。可用 --mode 选择 module/expression/interactive,用 --json 输出带 schema 标识的 AstDocument;普通输出为 AstRoot Debug 文本,稳定交换格式见 [AST 模型](ast.md)。命令不执行源码、不初始化 VM、不连接数据库;具体运行与本地禁测约定见 [运行](../operations/README.md) 和 [验证](../operations/validation.md)。
当前 Compiler 将 AST 经模块/函数名称分析转换为现有字节码,再交给 VM 解释执行。这是解释器内部的编译/执行准备步骤,不要求使用者先构建程序,见 [执行与版本模型](../architecture/execution-and-versions.md)。MakeFunction 已接入源码函数编译,但解析接受的二元算术和符号值仍没有执行语义;编译器会给出明确诊断,见 [编译链路](compiler.md)。源码闭包尚未实现。解析器也不是不可信代码沙箱,当前没有通用输入大小或解析递归深度限额。
裸赋值 `name = expression` 创建或更新当前作用域中的名称;它不是表达式,不能链式赋值,当前也不接受属性/索引目标。`let` 仍表示声明,保留重复声明检查;名称与初始化规则见 [Compiler](compiler.md)。两种源码写法产生独立的 Binding/Assignment 节点,宿主不改写输入。