141 lines
11 KiB
Markdown
141 lines
11 KiB
Markdown
# Endor 基本对象与统一 Number
|
||
|
||
这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,通常由宿主显式注册到[托管名义类型](types.md),其中函数支持配置后自动分类,不定义解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。
|
||
|
||
## 对应关系
|
||
|
||
| CPython 中的结构 | Endor 对应结构 | 当前实现 |
|
||
| --- | --- | --- |
|
||
| `PyFunctionObject` | `EndorFunctionObject` | 名称、共享代码对象、全局/内建字典、默认参数和闭包 Cell;由 Vm::call 执行 |
|
||
| `PyDictObject` | `EndorDictObject` | 保留插入顺序的键值容器,VM 管理查询、写入、删除和清空 |
|
||
| `PyCFunctionObject` | `EndorNativeFunctionObject` | Rust 宿主函数指针、名称与调用入口;旧名 NativeFunction 保留为别名 |
|
||
| `PyBaseExceptionObject` | `EndorBaseExceptionObject` | 异常名称、参数、cause、context、上下文抑制、备注与回溯位置 |
|
||
| `PyASCIIObject` 等 Unicode 布局 | `EndorStringObject` | 统一 UTF-8 字符串,支持字符数、字符读取、表示、真值、比较与哈希 |
|
||
| `PyLongObject`、`PyFloatObject` 等 | `EndorNumberObject` / `EndorNumber` | 一个对外数字接口,内部保留不同数字的无损表示 |
|
||
| `PyCellObject` | `EndorCellObject` | 可被多个闭包共享的可变槽;区分空槽与 Nil |
|
||
|
||
`EndorCode` 保存经过验证的 Endor wordcode 和静态元数据,`EndorCodeObject` 将它放进受管理的堆;函数持有代码对象句柄并可共享同一份代码。指令格式、执行帧与入口见 [字节码 VM](bytecode.md)。不是 CPython 字节码格式。
|
||
|
||
## Function 与 NativeFunction
|
||
|
||
Python 代码中的 `def greet(name): ...` 创建的是语言函数。函数需要关联代码,以及代码执行时使用的全局环境、默认参数和闭包。宿主可调用 new_function 工厂,执行中的字节码也可使用 [MakeFunction](bytecode.md#在字节码中创建函数) 创建函数;两条路径使用相同校验。`EndorFunctionObject` 保存这些信息;分配时验证 globals 是字典、closure 中每一项是 Cell,并检查默认参数数量、闭包数量和代码的参数信息一致。
|
||
|
||
CPython 的 `len`、`print` 等宿主实现的内建函数则通过 C 函数入口调用。Endor 使用 Rust,所以命名为 `EndorNativeFunctionObject`,不把 C 写入名称。
|
||
|
||
```rust
|
||
use endor_core::{EndorNativeFunctionObject, Value, Vm, VmError};
|
||
|
||
fn echo(arguments: &[Value]) -> Result<Value, VmError> {
|
||
match arguments {
|
||
[value] => Ok(value.clone()),
|
||
_ => Err(VmError::InvalidArgument("expected one value")),
|
||
}
|
||
}
|
||
|
||
fn main() -> Result<(), VmError> {
|
||
let mut vm = Vm::new()?;
|
||
let function = vm.allocate(EndorNativeFunctionObject::new("echo", echo))?;
|
||
let value = vm.call(function, &[Value::Text("hello".into())])?;
|
||
assert_eq!(value, Value::Text("hello".into()));
|
||
vm.release_value(value)?;
|
||
vm.release(function)?;
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
Native 函数和语言函数均通过 `Vm::call(&mut self, ...)` 调用。语言函数用显式帧绑定位置参数、默认值和闭包,再由取指循环执行。Native 回调仍接收借用参数、返回借用对象值,不能在回调里修改当前 VM;VM 会将对象结果 retain 后返回,因此宿主需对 call/execute 的对象结果配对 release_value。
|
||
|
||
函数名义分类可由宿主配置 `set_function_type`,源码、原生函数及绑定方法使用同一类型;可调用的类型定义仍保持 Type 身份。配置和源码注解检查的唯一契约见[类型机制](types.md#源码函数注解检查)。
|
||
|
||
## Number:优先表示,不先做重计算
|
||
|
||
数字统一表示为 `Value::Number(EndorNumber)`。宿主仍可直接保存数字值;需要对象身份时才使用 `vm.new_number(number)` 装箱成 `EndorNumberObject`。
|
||
|
||
| 内部表示 | 适用数据 | 保留的内容 |
|
||
| --- | --- | --- |
|
||
| Integer | 超大整数、计数、编号 | 任意长度十进制数字和符号;去掉多余前导零,零统一为非负 |
|
||
| Decimal | 金额、精确小数、科学计数 | 原始数字、尾随零、正负号和任意长度十进制指数;完全不经过 f64 |
|
||
| Rational | 比例、分数 | 任意长度整数分子与非零分母;统一分母为正,不自动约分 |
|
||
| BinaryFloat | 宿主已有的 IEEE-754 数值 | 全部 64 位,包括负零、无穷、NaN 及其 payload |
|
||
| Complex | 复数记录 | 实部与虚部各自的 Integer、Decimal、Rational 或 BinaryFloat 表示 |
|
||
|
||
```rust
|
||
use endor_core::{EndorNumber, Value};
|
||
|
||
fn main() -> Result<(), endor_core::VmError> {
|
||
let id = EndorNumber::integer("1234567890123456789012345678901234567890")?;
|
||
let amount = EndorNumber::decimal("12345678901234567890.000100")?;
|
||
let ratio = EndorNumber::rational("1", "3")?;
|
||
let tiny = EndorNumber::decimal("1e-999999999999999999999999999999")?;
|
||
let host_float = EndorNumber::binary_float(f64::INFINITY);
|
||
let value = Value::Number(amount);
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
`EndorInteger`、`EndorDecimal`、`EndorRational` 是内部表示结构,不是三种语言数字类。构造器验证输入;分数不能有零分母;十进制指数不转换为机器整数,所以不会因指数很长而溢出。构造任意大整数或精确小数时传文本;已经被宿主转换为 f64 而丢失的精度无法恢复。整数的 `as_i64` 是显式、可失败的窄化读取,不会静默截断。
|
||
|
||
首版不实现四则运算、排序、跨表示数值相等和数值哈希。Rust 的 PartialEq / Eq 只检查表示是否相同,不能用于解释语言的 `==`:`1.0` 与 `1.00` 保留不同小数位数;`2/6` 与 `1/3` 尚未求数值等价;NaN 位模式也被保留。VM 对两个数字对象的数值比较与哈希显式报不支持。因此数字现在可以作为字典的值,暂不能作为字典键,避免先定义错误的键相等规则。没有用“全部转换 f64”的方式补齐这些操作。
|
||
|
||
这里的“各类数字”指上述五种可表示形式,不包括符号表达式、无限展开的精确 π、带单位数量、任意位宽二进制浮点等全部数学对象;这些可在未来扩展表示协议。
|
||
|
||
## Dict 与 Cell 的引用所有权
|
||
|
||
字典使用 `Vec<(Value, Value)>` 保留插入顺序,查询目前为 O(n),没有假装已经实现 CPython 哈希表性能。键须通过哈希能力检查;支持 Nil、Bool、文本及可哈希的对象。堆字符串与直接文本按内容匹配;覆盖已有键保留首次插入的键对象和位置。可变字典、Cell 与尚未定义数值哈希的数字不能当键。
|
||
|
||
`dict_items`、`dict_get`、`cell_get` 返回借用内容;这与现在返回持有式结果的 call/execute 不同。宿主需要跨越删除、覆盖或容器释放继续持有一个对象时,应先 `retain`。删除字典条目不返回一个可能已经被回收的借用对象,而是返回是否删除成功。
|
||
|
||
字典键和值、Cell 内容、函数环境与闭包、异常参数与因果链都作为强引用参与 GC。可变容器不暴露内部可变引用:VM 先构造新载荷,验证并保留所有新边,再替换载荷和引用快照,最后释放旧边。对象自身的 Box、身份、属性和外部引用数不变。非法/跨 VM 句柄使更新失败,原值及引用计数不变。同对象替换不会在中间短暂释放它,自环、字典与 Cell 互相引用、函数与全局字典的环都能由现有循环回收处理。
|
||
|
||
## Cell 为什么存在
|
||
|
||
```python
|
||
def outer():
|
||
x = "first"
|
||
def read():
|
||
return x
|
||
def write(value):
|
||
nonlocal x
|
||
x = value
|
||
return read, write
|
||
|
||
read, write = outer()
|
||
write("updated")
|
||
print(read()) # updated
|
||
```
|
||
|
||
`read` 与 `write` 必须引用同一个变量位置。若各自复制一份 `x` 的初始值,write 重新赋值后 read 就看不到变化。Cell 是这个共享位置:函数持有 Cell,Cell 持有当前值。外层函数返回后,Cell 仍被闭包持有。
|
||
|
||
```rust
|
||
use endor_core::{Value, Vm};
|
||
|
||
fn main() -> Result<(), endor_core::VmError> {
|
||
let mut vm = Vm::new()?;
|
||
let cell = vm.new_cell(None)?; // 空槽,不是 Nil。
|
||
vm.cell_set(cell, Value::Text("first".into()))?;
|
||
vm.cell_set(cell, Value::Text("updated".into()))?;
|
||
assert_eq!(vm.cell_get(cell)?, Value::Text("updated".into()));
|
||
vm.cell_clear(cell)?;
|
||
vm.release(cell)?;
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
当前已能通过字节码读取、写入和删除共享 Cell,并创建捕获参数的 Cell。LoadClosure 与 MakeFunction 可以在外围函数运行时创建共享这些 Cell 的嵌套函数,外围帧返回后 Cell 仍由闭包保活。上面的 Python 代码说明闭包语义;独立 frontend 解析基础函数,由 [Compiler](compiler.md) 生成代码对象;尚无源码闭包语法或捕获分析。
|
||
|
||
## 字符串与异常的准确含义
|
||
|
||
`PyASCIIObject` 是 CPython Unicode 字符串实现的公共头部,不是 Python 中另外一种 ASCII 字符串类。Endor 不复刻其紧凑布局,统一用 `EndorStringObject` 表示字符串。索引按 Unicode 标量值,不按 UTF-8 字节,也不按人眼字形簇。Rust String 不接受孤立代理码点,所以并非完整复制 Python str 的所有边界行为。repr 用 Rust 的转义文本显示,不承诺 Python repr 的逐字兼容。
|
||
|
||
Python 语言层所有异常的顶级基类叫 `BaseException`,其下除了普通 `Exception` 分支,还有 `SystemExit`、`KeyboardInterrupt` 等。`PyBaseExceptionObject` 是这些异常的公共 C 实例数据布局,不能把这个结构名直接当成语言类名。
|
||
|
||
Endor 当前用 `EndorBaseExceptionObject` 保存公共数据,名称只是错误标签;尚无异常继承和按类匹配机制,也没有 raise / try / except 执行流程。异常数据中的回溯保存独立位置记录;执行器另通过 last_traceback 提供已经清理的执行帧位置元信息。宿主 API 的 `VmError` 仍表示操作失败,不会自动分配语言异常或把全部错误强制变成堆对象。
|
||
|
||
来源:[CPython 函数对象](https://docs.python.org/3.14/c-api/function.html)、[Cell 对象](https://docs.python.org/3.14/c-api/cell.html)、[异常说明](https://docs.python.org/3.14/library/exceptions.html)、[Unicode 对象](https://docs.python.org/3.14/c-api/unicode.html)。实现不承诺 CPython ABI 或默认 Python 兼容性。
|
||
|
||
## 验证入口
|
||
|
||
独立存储行为在 [objects/tests](../../crates/objects/tests/README.md) 检查;既有 Vm 对象入口在 [core/tests](../../crates/core/tests/README.md) 的 builtins.rs 检查,执行行为在 execution.rs;验证方式见 [验证](../operations/validation.md)。文档描述当前契约,不代表某次 CI 已通过。CPython 284 项研究表保持为工作区外的研究材料,不纳入项目文档或 Git。
|
||
|
||
原生函数可通过 NativeFunction::with_arity 提供位置参数数量;源码、原生、绑定方法的统一查询与兼容边界见[原生调用](native-calls.md#统一位置参数数量)。
|