基石语言规格(v1.0)
本文件是基石语言的正式语言规格,描述当前已实现的全部语法、语义与 标准库 API 边界。自 v1.0 起,语法/语义/标准库 API 冻结:后续改动须在
docs/设计决策.md补充决策记录,并遵循语义化版本规则(主版本=不兼容变更, 次版本=新特性,修订=修 bug)。 历史版本:v0.2(M1 阶段)→ v1.0(M15 收敛,冻结于 M0–M14 全部特性)。
1. 文件与运行
- 源文件后缀:
.jsh,UTF-8 编码 - 运行:
jishi 文件.jsh;交互式:jishi -i - 调试:
jishi 文件.jsh --ast查看语法树 - 编译为跨语言字节码:
jishi 文件.jsh --dump-bytecode(M13) - 执行字节码:
jishi 文件.json --load-bytecode
2. 关键字
| 关键字 | 作用 |
|---|---|
令 | 声明/赋值变量 |
如果 / 否则如果 / 否则 | 条件分支 |
遍历 … 在 … | for 循环 |
循环 N 次 | 固定次数循环(次 可省略) |
当 | while 循环 |
中断 / 继续 | 跳出/跳过循环 |
函数 / 返回 | 定义函数 / 返回值 |
导入 模块名 | 导入基石标准库 |
真 / 假 / 空 | 布尔值与空值 |
与 / 或 / 非 | 逻辑运算 |
尝试 / 捕获 … 为 … / 最终 | 异常处理 |
抛出 | 抛出异常 |
类 / 继承 / 新建 | 定义类 / 继承 / 创建实例 |
用 … 为 … | 上下文管理器(M26.2),见 §7.12 |
匹配 / 情形 | 模式匹配(M34.1),见 §8.11 |
枚举 | 枚举(M34.2),见 §8.12 |
注:匹配 / 枚举 是软前缀——匹配组、枚举值 这些仍可作普通标识符 (见 §5 标识符)。另外 . 后面的属性名允许是关键字(M34 起), 所以 正则.匹配(...) 照常可用。
3. 标点与空白
- 全角标点(
:,()=+…)与半角标点等价,写作时无需切换输入法 - 行首缩进使用半角空格(推荐 4 格);全角空格(U+3000)出现在缩进中会报错
- Tab 与空格混用会报错
- 字符串与注释内部不进行标点归一化
4. 注释
python# 单行注释
#- 多行
注释 -#
5. 标识符
- 支持中文、英文、数字、下划线混写:
分数、total_数量、_x - 不能以数字开头
- 关键字保留,不可作标识符;关键字后必须跟空白或符号分隔
6. 字面量
| 类型 | 示例 |
|---|---|
| 整数 | 42 |
| 小数 | 3.14、2e3、.5 |
| 文本 | "你好"、'单引号'、“中文引号”、'''多行''' |
| 布尔 | 真、假 |
| 空 | 空 |
| 列表 | [1, 2, 3](支持尾随逗号 [1, 2, 3,];括号内可跨行书写) |
| 字典 | {"语文":95} |
| 插值文本(M7) | ` 你好 {名字},今年 {年龄} 岁 ` |
插值文本用反引号 ` ` 包裹,{表达式} 内的内容会被求值并转成文本插入; {{ / }}` 表示字面花括号:
python令 名字 = "小明"
令 年龄 = 18
打印(`你好 {名字},今年 {年龄} 岁`) # 你好 小明,今年 18 岁
打印(`{3} + {4} = {3 + 4}`) # 3 + 4 = 7
7. 运算符
- 算术:
+-*/(真除法)//(整除)%(取余)**(幂);文本可用+拼接、*重复 - 比较:
==!=<><=>=;支持链式比较60 < 分数 <= 90(中间表达式只求值一次,短路) - 成员测试(M23):
在/不在——名字 在 名单、9 不在 范围(5)。
可用于列表(按值)、字典(按键)、文本(找子串),与 列表.包含() 语义一致
- 同一性(M23):
是/不是——x 是 空、结果 不是 假。
空/真/假按单例判断;数字与文本按值判断(避免 Python is 那种 「有时真有时假」的随机感);列表/字典/实例/函数按对象身份判断
- 逻辑:
与或非(短路求值) - 赋值:
=+=-=*=/=//=%=**=
(//= 与 **= 在 M25 之前因词法器最长匹配出错而不可用,已修复)
- 成员访问:
模块.函数、对象.方法(...)、列表[下标]、字典[键]
优先级(松 → 紧,同一行同级):
或 < 与 < 非 < 比较(== != < > <= >= 在 不在 是 不是) < + - < * / // % < **
非的优先级在 M23 修正为「比比较松、比与或紧」,与 Python 的not一致。 修正前非 年龄 >= 18会被解析成(非 年龄) >= 18(恒为假,静默给错答案); 现在解析为非 (年龄 >= 18),符合阅读直觉。 常见写法:非 甲 与 乙=(非 甲) 与 乙;非 1 == 2=非 (1 == 2)。
链式比较里也可以混用关键字运算符:0 <= x 在 [1, 2] 等价于 0 <= x 且 x 在 [1, 2](中间值只求值一次)。
7.5 集合(M24)
python令 甲 = {1, 2, 2, 3} # 字面量,自动去重 → {1, 2, 3}
令 乙 = 集合([3, 1, 3]) # 从列表造 → {3, 1}
令 空集 = 集合() # 空集合(注意 {} 是空字典)
令 平方集 = {x * x 遍历 x 在 范围(4)} # 集合推导式
- 有序:元素按首次出现的顺序排列。
集合([3, 1, 3, 2])得到{3, 1, 2},
打印与遍历顺序稳定可复现(这也是为什么不用「无序」的哈希集合)。
- 元素必须是不可变的(数字、文本…);列表、字典放不进去,会报中文错。
{}仍是空字典(与 Python 一致),空集合写集合()。- 用
长度()、在/不在、==(与顺序无关)、遍历都直接可用。 - 集合方法:
| 方法 | 说明 |
|---|---|
添加(值) / 移除(值) / 丢弃(值) | 增删;移除 找不到会报错,丢弃 不报错 |
包含(值) | 与 值 在 集合 等价 |
并集(可迭代) / 交集(可迭代) | 返回新集合 |
差集(可迭代) / 对称差(可迭代) | 返回新集合 |
转列表() / 复制() / 清空() | 转换与清空 |
去重的常见写法:令 唯一 = 集合(原列表) 或 令 唯一 = 集合(原列表).转列表()。
7.6 切片赋值(M24)
python令 分数 = [90, 55, 70, 88, 60]
分数[1:3] = [95, 96] # 替换一段
分数[:2] = [100] # 替换开头
分数[1:] = [] # 删掉末尾
分数[::2] = [1, 2, 3] # 带步长(要求两边个数正好相等)
读取切片(M18)与写入切片共用同一条求值路径,分量同样可以省略、可以用变量。 带步长赋值时两边个数必须相等,否则报中文错(提示长度对不上)。 文本只能读切片,不能写。
7.7 匿名函数与数据流水线(M24)
python令 加倍 = 函数(x):x * 2 # 单表达式函数体,自动作为返回值
令 加基数 = 函数(x):x + 基数 # 捕获外层变量(闭包)
令 加 = 函数(x):函数(y):x + y # 柯里化,加(3)(4) → 7
数 = [1, 2, 3, 4, 5]
数.映射(函数(x):x * x) # [1, 4, 9, 16, 25]
数.过滤(函数(x):x % 2 == 1) # [1, 3, 5]
数.排序按(函数(x):-x) # 从大到小
数.归约(函数(累计, x):累计 + x, 0) # 15
- 匿名函数写成一行:
函数(参数):表达式。参数支持默认值与类型标注;
需要多行逻辑就定义具名函数(函数 名字(参数): 后面换行缩进)。
- 列表新增四个流水线方法:
映射/过滤/排序按/归约,
既可以传匿名函数,也可以传具名函数。
排序按的键要能互相比较(都是数字或都是文本),混着来会报中文错。
7.8 迭代协议(M24)
自定义对象只要定义 迭代(自身) 方法,就能用于 遍历、推导式, 以及 总和 / 最大 / 最小 / 长度 等会遍历的内建:
python类 成绩表:
函数 初始化(自身, 记录):
自身.记录 = 记录
函数 迭代(自身):
返回 自身.记录
令 表 = 新建 成绩表([90, 80])
遍历 分 在 表:
打印(分)
打印(总和(表), 长度(表)) # 170 2
迭代 应返回一个可遍历的东西(列表、文本、集合、字典…)。返回自身会 无限循环,解释器会拦下并报中文错。
7.9 类型标注(M24)
python函数 折扣(价格: 小数, 比例: 小数) -> 小数:
返回 价格 * 比例
- 参数标注会被校验:调用时若实参类型不符,报中文错并指出是哪个函数、
哪个参数、期望什么、实际是什么。三执行器行为一致。
- 认识的类型名:
整数/小数/文本/列表/字典/集合/
布尔 / 空 / 函数 / 任意(值)。布尔 不算 整数(刻意区分)。
- 不认识的标注名一律跳过(例如
订单: 订单),这样标注可以同时当文档用,
自定义类型名不会被误报。
- 返回标注目前只作文档,不做校验(这条边界是明确的)。
- 定位是「运行时校验 + 文档」,不是静态类型系统:不做类型推导、
不做编译期检查,没写标注的参数零开销。
7.10 变长参数(M25)
python函数 求和(*数): # 收下所有位置实参,是个列表
令 总 = 0
遍历 x 在 数:
总 += x
返回 总
打印(求和()) # 0
打印(求和(1, 2, 3)) # 6
函数 配置(**选项): # 收下未匹配的关键字,是个字典
返回 选项
打印(配置(甲 = 1, 乙 = 2)) # {'甲': 1, '乙': 2}
函数 混合(甲, *余, **选): # 三者可以组合
返回 [甲, 余, 选]
*参数收集多余的位置实参,选项收集未匹配的关键字**;
都没收到时是空列表 / 空字典(不会报错)。
- 参数顺序固定:
普通参数 → *参数 → **选项。顺序不对会报中文错。 *参数/**选项不能有默认值,也不能写类型标注(元素类型不固定,
要检查请在函数体里用 断言)。
- 暂不支持「
*参数之后再加普通参数」(那属于只能关键字传参的进阶写法)。 - 与默认值共存时,默认值只属于普通参数的尾部:
函数 f(甲, 乙 = 10, *余) 调用 f(1) 得到 甲=1、乙=10、余=[]。
- 没有「星号展开」实参(Python 的
f(*列表)/f(**字典)):
传列表就是「一个参数」。需要展开时先自己转换。
7.11 星号解包(M25)
python令 甲, *余 = [1, 2, 3, 4] # 甲=1,余=[2, 3, 4]
令 首, *中, 尾 = [1, 2, 3, 4, 5] # 首=1,中=[2, 3, 4],尾=5
令 x, *y = [1] # x=1,y=[](可以为空)
令 头, *尾 = "你好世界" # 头='你',尾=['好', '世', '界']
a, *b = [9, 8, 7] # 不加「令」也一样
- 星号位置收集「其余」元素成列表,可以在中间或末尾;只能有一个。
- 长度要求:星号以外的固定名字都要有对应的值,否则报中文错
(提示可以写「*余」来收剩下的)。
- 右值可以是列表、文本、集合(字典解出来的是键)。
7.12 上下文管理器 用 … 为(M26)
python用 打开("数据.txt") 为 文件:
遍历 行 在 文件:
打印(行)
# 走到这里文件已经自动关闭
用后面是「上下文管理器」,为后面接一个名字。- 离开代码块时自动收拾:先找对象的
退出(),没有就找关闭()并调用。
正常结束、返回、中断、抛出 都会走到(尝试/最终 的语义)。
- 自定义类定义
进入()可以决定绑定什么值;不写就绑定对象本身。 - 退出不会吞掉异常(有意不做):出错了就让它照常报出来,
否则「出问题快速找到」会变难。
它等价于下面这段(解析期就是这样脱糖的,所以三个执行器行为必然一致):
python__用_1__ = 打开("数据.txt") # 临时变量存原对象
文件 = 进入上下文(__用_1__)
尝试:
……你的代码……
最终:
退出上下文(__用_1__)
退出作用在原来的对象上,而不是绑定后的名字——
进入完全可以返回 别的东西(Python 的__enter__也允许),拿返回值去退出会找错对象。
文件对象(打开)
python用 打开("a.txt", "写") 为 f:
f.写行("第一行")
用 打开("a.txt", "追加") 为 f:
f.写("追加的内容")
用 打开("a.txt") 为 f:
遍历 行 在 f: # 文件本身可遍历(按行)
打印(行)
模式:读 / 写 / 追加 / 读写(也认 r w a r+ w+ a+)。 一律按 UTF-8 读写——Windows 上的默认编码是 GBK,中文会写乱。
| 方法 | 作用 |
|---|---|
读(数量?) | 读整篇;给了数量就读前 N 个字符 |
读行() | 读一行(不含换行符);已经到末尾给「空」 |
读所有行() | 一次读成列表 |
写(内容) | 写文本(不加换行) |
写行(内容) | 写一行(自动加换行) |
关闭() | 关闭(重复调用不报错) |
刷新() / 位置() / 定位(偏移) | 落盘 / 当前位置 / 跳到位置 |
打印文件对象显示 <文件 路径(状态)>,便于排错。
跨语言宿主(Node / Rust)都已同步
打开与进入上下文/退出上下文(Node 在 M27、Rust 在 M29,两边都有对拍测试),所以用 … 为与文件 操作在那边照常可用。
7.13 自定义对象打印:文本()(M26)
python类 分数:
函数 初始化(自身, 分子, 分母):
自身.分子 = 分子
自身.分母 = 分母
函数 文本(自身):
返回 文本(自身.分子) + "/" + 文本(自身.分母)
打印(新建 分数(1, 3)) # 1/3
- 类里定义
文本(自身)后,打印(对象)与文本(对象)都用它的返回值;
放进列表、字典里打印也一样生效。
- 没定义时保持
<类名 实例>,子类继承父类的文本。 文本必须返回文本,否则报中文错(说清「要返回文本」)。
7.14 精确小数(M26)
python打印(0.1 + 0.2 == 0.3) # 假(二进制浮点)
打印(精确("0.1") + 精确("0.2") == 精确("0.3")) # 真
令 价 = 精确("19.99")
打印(价 * 3) # 59.97
打印(价.舍入(2)) # 19.99(四舍五入)
精确(值)把数字或数字文本转成十进制小数。从普通小数转时走str,
所以 精确(0.1) 就是 0.1,不会把二进制尾巴带进来。
- 四则运算、比较、
总和/最大/最小/排序都支持;与普通整数/小数
混算时按十进制处理(所以 精确("0.3") == 0.3 为真)。
- 方法:
舍入(位数 = 2)(四舍五入;负位数表示舍到十位、百位)、
绝对值()、转文本()。
- 普通小数
0.1仍是二进制浮点,与 Python 一致——不偷偷改语义,
需要精确的地方显式写 精确(...)。
- 跨语言宿主都已同步这个内建:Node 侧用 BigInt 自己实现十进制(M27),
Rust 侧零依赖手写 BigInt(M29),两边的 精确("1")/精确("3") 都与 Python 逐字节一致(有对拍)。
7.15 值的显示规则(M27 统一)
一个值只有一种写法。打印(值)、文本(值)、字符串插值 ` {值} 、 打印([值])、REPL 回显,全部走同一套规则(runtime.jishi_repr`):
| 值 | 顶层显示 | 容器内显示 |
|---|---|---|
空 | 空 | 空 |
真 / 假 | 真 / 假 | 真 / 假 |
| 文本 | 原样:甲 | 带引号:'甲' |
| 列表 | [1, 真, 空, '甲'] | 同左 |
| 字典 | {'键': 1} | 同左 |
| 自定义对象 | 文本() 的返回值,否则 <甲 实例> | 同左 |
| 精确小数 | 1.50 | 同左 |
| 文件 | <文件 路径(状态)> | 同左 |
| 字节 | 字节(0C FF 00) | 同左 |
python打印(空, 真, 假) # 空 真 假
打印([1, 真, 空, "甲"]) # [1, 真, 空, '甲']
打印(`结果是 {真}`) # 结果是 真
- 空 / 真 / 假 一律中文化,与源码里的写法一致;从 Python 抄来
True/False/None 会直接报错并提示中文写法(见 §11 容错解析)。
- 文本在顶层不加引号(用户要的就是那串字),在容器里加引号
(否则分不清 ['甲'] 里的是文本还是变量)——与 Python 的 print/repr 分工一致。REPL 回显算「容器内」这一侧:>>> "甲" 回显 '甲'。
- 字典用半角
:,与源码字面量{"甲": 1}写法一致——打印结果能直接抄回代码。 - 标准库/桥接返回的原生 Python 值(
bool、None、list…)也走这套规则,
所以 路径.存在(...) 显示「真/假」而不是 True/False。
7.16 字节(M113)
一串 0–255 的整数,用来装图片、压缩包、加密盐这类二进制数据。
python导入 编码
令 图 = 编码.十六进制解码("89504E47") # 源码里造一串字节就用它
打印(类型(图)) # 字节
打印(图) # 字节(89 50 4E 47)
打印(长度(图), 图[0]) # 4 128
打印(图[0:2]) # 字节(89 50)
打印(图 + 编码.十六进制解码("0D0A")) # 字节(89 50 4E 47 0D 0A)
打印(图 * 2) # 字节(89 50 4E 47 89 50 4E 47)
遍历 x 在 图: # 逐个给整数
打印(x) # 137 / 80 / 78 / 71
打印(编码.十六进制编码(图, 真)) # 89504E47(要全量内容时用它)
字节(0C FF 00)是它唯一的写法:空字节是字节();超过 16 个只列前 16 个
(字节(00 01 … 共 1024 个字节))。要全量内容用 编码.十六进制编码(...) —— 显示是有损的,那是有意的(读一个 1 MB 文件不该刷屏)。
- 没有字节字面量(源码里不写
b"…")。字节只能由标准库产生:
编码.文本字节(文本)、编码.十六进制解码(文本)、编码.base64解码(文本)、 加密.安全随机字节(个数)、文件.读字节(路径)。
- 不自动变文本:
文本(图)给的就是字节(89 50 4E 47)。要真文本用
编码.字节文本(图)(解不出合法 UTF-8 会明确报错,不返回乱码)。
- 下标给整数、切片给字节;只能字节拼字节(和文本拼要显式转)。
- 比大小只在字节之间(按字典序);
字节 == 文本给假(不报错),
但 字节 < 文本 会报错——两种东西不该混着比。
- 可以当集合元素 / 字典键(不可变、可哈希)。
- ⚠️ 不许当数字用:
总和(字节)/最大(字节)会明确报错,不会静默
给你「所有字节值之和」。
json.转文本(字节)明确报错(JSON 没有二进制)——先用
编码.base64编码(...) 转成文本。数据库 里字节走 BLOB,原样往返。
7.17 并发(M114)
⚠️ 基石没有「语言级的线程 / 锁 / 队列 / 异步」,而且这是有意的 (见「不做清单」与 docs/设计决策.md D80)。有的是标准库 并发 模块:
python导入 并发
令 摘要们 = 并发.批量摘要(路径们) # 一批文件同时算(默认 sha256,4 路)
令 内容们 = 并发.批量读(路径们, 8) # 一批文件同时读成「字节」
令 正文们 = 并发.并行取(网址们) # 一批网址同时取(GET)
- 结果顺序 = 输入顺序(严格一一对应)。所以并发不影响可复现性 ——
并发.批量摘要(路径们)[3] 就是 路径们[3] 那一份的摘要。
- 任何一项失败就整体报错,报的是输入顺序里最靠前的那一项。
- 线程数默认 4(等的是 IO,不是算力),上限 32。
- 📌 它只在「大量小 IO」上划算。实测:2000 个 4 KB 文件 2.0 倍;
但大文件、纯计算(算数、字符串处理)不要用它 —— 线程只是白加调度开销。 收益取决于瓶颈在哪:IO 等待能重叠才有用(详见 docs/设计决策.md D80 的实测表)。
- ⚠️ 平台的诚实边界:浏览器试玩页(wasm)与 Node 宿主顺序执行 ——
结果一模一样,只是不快(不是「不支持」,所以不会报错)。
- ⚠️ 它不跑你的函数:
并发.映射(函数, 列表)这种形状没有(那会引出
「共享状态被两个线程同时改」这类问题,代价见 D80)。想并行跑自己的逻辑, 把它放进一批文件 / 网址里,用上面三个函数。
7.18 HTTP 服务端(M115)
python导入 HTTP服务端
函数 处理(请求):
如果 请求["方法"] == "GET" 与 请求["路径"] == "/":
返回 {"正文": "<h1>你好</h1>", "内容类型": "text/html; charset=utf-8"}
返回 {"状态": 404, "正文": "没有这一页"}
HTTP服务端.启动(8000, 处理, 最多请求数=2) # 收 2 个请求后干净退出
处理函数收一个「请求」字典:{方法, 路径, 查询, 版本, 请求头, 正文};
返回要么是文本(自己用 构造响应 拼好的完整响应), 要么是字典 {状态, 正文, 内容类型, 头}(框架替你拼,日常用这种)。
最多请求数:0(默认)= 一直跑;给正数 = 处理够那么多就干净退出。
⚠️ 数的是真正处理过的请求(连上就断的探测连接不算)。
处理函数出错 ⇒ 该请求回 500(正文就是那句错),服务继续 ——
不会因为一条坏请求把整个本地服务带走。
端口 = 0⇒ 由系统分配(实际端口在返回的字典里);想先知道端口用空闲端口()。- ⚠️ 只监听
127.0.0.1(安全默认,局域网连不上)、一问一答就关、
正文按 UTF-8。
- ⚠️ 平台边界:
启动/空闲端口在 Node 宿主与浏览器试玩页上
起不了(会报一句说清原因的话);解析请求 / 构造响应 是纯函数, 哪里都能用。理由见 docs/设计决策.md D81。
7.19 给模型看的两份事实(M8 / M116)
语言事实有两份投影,都从同一个来源(build_lang_spec())生成,不另写一套说辞:
| 命令 | 形态 | 给谁 |
|---|---|---|
jishi --lang-spec | JSON(关键字 / 内建 / 标准库含签名与说明 / 异常 / 指纹) | 程序化对接(MCP、agent 自己解析) |
jishi --ai-card | Markdown 语言卡(贴进系统提示词就能用) | 直接当提示词 |
语言卡(M116 起:带签名 + 可分片)
bashjishi --ai-card # 全量卡(「一贴就能用」)
jishi --ai-card 文本 # 只要「文本」这一个模块的卡
jishi --ai-card --任务 表格 # 按任务打包:常用模块 + 该任务的惯用法
jishi --ai-card --统计 # 只看各种形态多大(字符数),不打印卡
- 标准库段每个函数都带完整签名,可选参数写成
参数=默认值;
认不出值的默认值写成 参数=…(表明「可以省」,但不把内部常量名漏给你)。
- ⚠️ 可选参数请按顺序传:跳过中间那个只在部分平台支持 —— 别跳。
- 可用的任务包:
表格/文本/文件/网络/数据/并发/测试。 - 📌 卡片永远是语言规格的投影:加了模块或函数,两份一起长,不用手改渲染代码。
- ⚠️ 报分(
tools/eval_ai.py)默认喂的是--lang-spec的 JSON(与历史跑分可比);
想量语言卡这一路,加 --规格 card [--卡分片 模块或任务]。
8. 语句
变量
python令 分数 = 60
分数 += 5 # 复合赋值
数据[0] = 100 # 下标赋值
下标(含负索引,R6.5)
python令 列 = [1, 2, 3]
列[0] # 1 第一个(从 0 数)
列[-1] # 3 最后一个(**负号从末尾数**)
列[-3] # 1
列[-4] # 索引错误 折算完仍越界 → 报「列表下标越界」(**不绕回开头**)
列[-1] = 9 # [1, 2, 9] 负索引也能赋值
列.弹出(-1) # 弹最后一个
列.插入(-1, 9) # [1, 2, 9, 3] 插到最后一个元素**之前**(负数先 + 长度,再夹到 [0, 长度])
文 = "甲乙丙"
文[-1] # 丙 文本同样支持,且按**码点**取
⚠️ 负索引是 R6.5 才在两个宿主上补齐的:三个 Python 执行器底下就是 Python 的
list/str,一直支持;两个宿主原先显式要求「下标 ≥ 0」——于是同一个程序 在 Python 上给3、在宿主上报「索引错误」。规格里本来就写着弹出(位置=-1),文档与实现不一致就是 bug。 📌 折算里的「长度」是元素的个数:文本按码点数("a😀b"[-1]是b), 不是字节数、也不是 UTF-16 码元数。 📌 字典的下标是键,不做负索引:{1: "甲"}[-1]报「找不到这个键」。
切片(M18.1)
python令 a = [1, 2, 3, 4, 5]
a[1:3] # [2, 3] 起:止
a[::-1] # [5, 4, 3, 2, 1] 倒序
a[:2] # [1, 2] 省略起
a[2:] # [3, 4, 5] 省略止
a[::2] # [1, 3, 5] 步长
a[:] # [1, 2, 3, 4, 5] 全复制
"你好世界"[1:3] # "好世" 字符串同样支持
- 起/止/步长分量都可省略(省略为「不指定」),也可用表达式(
a[i:j]) - 读取与赋值都支持(切片赋值见 §7.6)
多赋值与解包(M7)
python令 a, b = [1, 2] # 列表解包(数目须精确匹配)
a, b = b, a # 交换两个变量
令 x, y = 计算坐标() # 函数多值返回 → 解包
返回 1, 2 # 多值返回:等价于 返回 [1, 2]
- 解包右值限列表(含 Python 桥接的元组),数目不符会中文报错
- 多值返回「返回 a, b」编译为「返回 [a, b]」(基石多值即列表)
表达式语句
以值开头的行都是合法语句(REPL 中会回显结果,非空值时显示):
python1 + 2 # 顶层表达式
"你好"
[1, 2] + [3]
条件
python如果 分数 >= 90:
打印("优秀")
否则如果 分数 >= 60:
打印("及格")
否则:
打印("不及格")
循环
python遍历 分数 在 [85, 92, 78]:
打印(分数)
遍历 名字, 年龄 在 [["小明", 8], ["小红", 9]]: # 多变量解包(M41)
打印(名字, 年龄)
遍历 序, 名 在 配对(["甲", "乙"], [1, 2]): # 常与「配对」「带下标」连用
打印(序, 名)
循环 3 次:
打印("你好")
当 分数 < 100:
分数 += 1
遍历 支持多变量解包(M41):写成 遍历 甲, 乙 在 可迭代:,每次迭代把 元素解成两个名字(余 也能用,语义与 令 甲, 余 = … 一致)。实现走 解析期脱糖(临时循环变量 + 循环体首句解包赋值),所以三个执行器与两个 宿主的行为必然一致。
推导式(M7)
python[x * 2 遍历 x 在 [1, 2, 3]] # [2, 4, 6]
[x 遍历 x 在 [1, 2, 3, 4] 如果 x > 2] # [3, 4]
{x: x * x 遍历 x 在 [1, 2, 3]} # {1:1, 2:4, 3:9}
- 列表推导与字典推导均支持;
如果条件子句可选 - 循环变量与「遍历」语句一致(写入当前作用域)
函数
python函数 求和(a, b):
返回 a + b
打印(求和(3, 4))
默认参数(M7)
python函数 打招呼(名字, 语气 = "你好"):
打印(语气, 名字)
打招呼("小明") # 你好 小明
打招呼("小明", "欢迎") # 欢迎 小明
- 默认值在定义处求值一次(与 Python 一致)
- 有默认值的参数必须排在最后
导入
python导入 随机 # 基石标准库(中文 API)
令 n = 随机.随机整数(1, 100)
导入 math 从 python # Python 生态桥接(M3)
打印(math.sqrt(16)) # 4.0
导入 math 从 python 为 数学 # 起别名
打印(数学.sqrt(9)) # 3.0
导入 数学工具 从 本地包 # 本地包(M10.2)
打印(数学工具.平方(8))
导入 文本 为 t # 普通导入也支持别名(M18.4)
打印(t.计数("香蕉苹果", "香")) # 1
- 标准库导入也支持
为 别名(M18.4),避免模块名遮蔽内建(如导入 文本
会遮蔽内建 文本();用 导入 文本 为 t 可两全,导入时会打印一次中文提示)
Python 桥接规则(M3):
- 导入的 Python 模块成员原样可用(函数、类、常量);下划线开头的内部成员不可见
- Python 对象的公开属性/方法可直接访问:
datetime.datetime.now().year - 基石函数可以直接传给 Python 高阶函数(如
builtins.map(基石函数, 列表)) - Python 侧抛出的异常自动翻译成中文错误(找不到文件、权限不足、递归过深…)
本地包规则(M10.2):
- 包是
.jishi/packages/名字/目录,含包.json(名字/版本/依赖)与若干.jsh模块 - 执行所有模块后,顶层定义的函数/变量作为包成员导出
- 搜索顺序:项目目录
.jishi/packages/→ 用户目录~/.jishi/packages/
包清单 包.json 字段:
json{
"名字": "某包",
"版本": "1.0.0",
"依赖": {"基础库": ">=1.0.0"},
"描述": "可选说明"
}
依赖:名字 → 版本约束。导入某包时会先递归加载其依赖,校验依赖版本
满足约束后,把依赖包对象注入到该包模块的命名空间(模块里可直接 依赖名.成员, 无需重复导入)。
- 版本约束语法:
*任意;1.2.3精确;>=1.0.0/<=2.0.0/>/<;
^1.2.3 主版本锁定(>=1.2.3 且 <2.0.0)。约束不满足报中文错误。
- 循环依赖(甲依赖乙、乙依赖甲)会报「检测到循环依赖:甲 → 乙 → 甲」。
远端索引格式(M10.2,先静态 JSON 文件托管,不建站):
json{
"格式": "jishi-package-index",
"版本": 1,
"包": {
"某包": {
"版本": "1.0.0",
"下载地址": "https://例/某包-1.0.0.zip",
"依赖": {"基础库": ">=1.0.0"},
"描述": "……"
}
}
}
格式固定为jishi-package-index,版本为索引格式版本号(当前 1)。- 校验逻辑在
jishi/packages.py(parse_index/resolve_package/
version_satisfies),供 jishi 安装 命令(M14)复用。
包管理命令(M14.2):
bashjishi 安装 包名 --local-dir 目录 # 从本地目录安装(含 包.json)
jishi 安装 包名 --local-zip 包.zip # 从本地 zip 安装(内含 包名/ 顶层目录)
jishi 安装 包名 --from-index 索引.json # 从远端索引拉包(本地文件或 URL)
jishi 安装 包名 --force # 覆盖已安装的同名包
jishi 卸载 包名 # 卸载本地包
jishi 列表 # 列出已安装的包(名字/版本/描述)
⚠️ 卸载 有两个含义(M110 起):
bashjishi 卸载 包名 # 卸**包**(`.jishi/packages/` 里的东西)
jishi 卸载 --自身 # 卸**语言本体**(解释器自己)
jishi 卸载 # 不带包名 = 同上(必须二次确认,非交互要 --是)
jishi 升级 # 把语言本体升到最新版(--演练 / --本地包 / --源)
- 安装目标:项目
.jishi/packages/(不存在时自动创建);搜索顺序与导入一致
(先项目后用户目录)。
- 从索引安装会「读索引 → 取下载地址 → 下载 zip → 解压安装」全自动完成。
关键字参数(M3)
python导入 json 从 python
打印(json.dumps({"姓名": "小明"}, ensure_ascii=假)) # 中文不被转义
函数 介绍(姓名, 年龄):
返回 姓名 + "今年" + 文本(年龄) + "岁"
打印(介绍(姓名 = "小红", 年龄 = 8)) # 基石函数同样支持
打印(介绍("小刚", 年龄 = 20)) # 位置参数 + 关键字参数
规则:
- 调用中「名字 = 值」一律按关键字参数处理(调用内部没有赋值语义)
- 基石函数按参数名绑定;缺参数会明确指出缺哪个,多传/未知参数名会报错
- 布尔值写
真/假,空值写空;从 Python 抄来True/False/None时,
报错会提示对应的中文写法
- 第三方库(pandas 等)先在 Python 环境里 pip 安装,之后可直接导入
8.9 异常处理
python尝试:
令 结果 = 整数("不是数字")
捕获 类型错误 为 e:
打印("输入不对:", e.消息)
捕获 异常 为 e: # 「异常」是所有错误的基类,抓得住一切
打印("其他错误:", e.类型)
最终:
打印("无论如何都会执行")
捕获可以有多个,从上往下匹配第一个符合的;最终最多一个且可选。- 捕获条件按错误类型名匹配,基类能抓子类:
异常>运行期错误>
类型错误/值错误/索引错误/键错误/除零错误/文件错误/断言错误。
断言(条件, 消息)失败抛出的就是「断言错误」(M23),因此它既能用
捕获 断言错误 精确接住,也能用 捕获 运行期错误 兜住。
- 不写类型就是全抓:
捕获 为 e:(或干脆捕获:)。 - 作用域:
捕获 为 e:里的那个名字、以及捕获体里的赋值,都写在当前作用域
—— 与 如果 / 循环 / 遍历 / 当 / 尝试 体 / 最终 一样, 基石没有块级作用域(文档里「循环变量写入当前作用域」是同一条口径)。 所以捕获体里赋的值,出了捕获体照样读得到;捕获名也会覆盖外层同名变量。
抛出 表达式抛出任意值;非异常对象会被自动包成「异常」,原值留在e.消息:
python函数 取折扣价(原价, 折扣):
如果 折扣 <= 0 或 折扣 > 1:
抛出 值错误("折扣必须在 0 到 1 之间")
返回 原价 * 折扣
- 异常对象可读属性:
e.消息(中文说明)、e.类型(如「值错误」)。 - 未被捕获的异常照常渲染成中文报错(含文件行列),程序终止。
8.10 类与继承
python类 动物:
函数 初始化(自身, 名字):
自身.名字 = 名字
函数 叫(自身):
打印(自身.名字, "在叫")
类 猫 继承 动物:
函数 叫(自身): # 覆盖基类方法
打印(自身.名字, "喵喵")
令 咪咪 = 新建 猫("咪咪")
咪咪.叫()
类 名字:定义类;类体里写方法和类变量(M23)。- 类体里只能写三样(M35):方法(
函数 …)、类变量(名字 = 值)、
空类占位(单独一行 :——基石没有 pass 关键字)。写别的(表达式语句、 += 这类增强赋值、下标/属性赋值)在解析期就报错并给出正确写法。 以前这条不统一:树遍历会报错,而两个字节码执行器会静默忽略 (x += 5 甚至被当成 x = 5 静默算错),所以统一挪到解析期拦下。
- 方法第一个参数约定叫
自身(普通标识符,不是关键字),表示实例本身,
调用 实例.方法(...) 时自动绑定。
初始化是约定俗成的构造方法,新建 类(参数)时自动调用;
没定义 初始化 则 新建 类() 造空实例,传参报错。
继承 基类让子类复用基类方法,子类同名方法覆盖;基类必须确实是类。- 实例字段用
自身.名字 = 值创建,实例.字段读取;不同实例互不影响。 - 类型名:类显示为「类 名字」,实例显示为「名字 实例」。
类变量(M23)——写在类体里、所有实例共享的名字:
python类 计数器:
总数 = 0 # 类变量
名字 = "计数器"
函数 加(自身, n):
计数器.总数 += n # 用类名读写,或 自身.总数 读也行
- 值在定义类的外层作用域求值,所以
值 = 外层常量 * 2能用外层名字; - 查找顺序与 Python 一致:实例字段 → 类变量(含基类)→ 方法,
同名时实例字段遮蔽类变量;
类名.变量 = 值写在类自己的类变量表里,不影响已造出的实例字段;- 继承时类变量浅拷贝合并,子类可覆盖;可变对象(列表等)两边共享同一份。
超() 调父类方法(M23):
python类 动物:
函数 初始化(自身, 名字):
自身.名字 = 名字
函数 叫(自身):
返回 自身.名字 + "叫了一声"
类 狗 继承 动物:
函数 初始化(自身, 名字, 品种):
超().初始化(名字) # 调基类的构造方法
自身.品种 = 品种
函数 叫(自身):
返回 超().叫() + ",汪汪" # 调基类同名方法再追加
超()指向「定义这个方法的类的基类」,因此在多级继承里不会绕回自己
(小狗 → 狗 → 动物 三级链能正确逐层向上,与 Python 的零参数 super() 一致);
- 只能在方法里用(方法必须有第一个参数
自身);写在类外或没有自身
参数的方法里,解析期即报错并给出写法提示;
- 找不到基类或基类没有该方法时,报错会列出基类可用的方法。
8.11 模式匹配 匹配 / 情形(M34)
python匹配 分数:
情形 90: # 单值
打印("优秀")
情形 80, 85: # 多值:命中任一个即可
打印("良好")
情形 如果 分数 >= 60: # 只有条件(守卫链,最常见的写法)
打印("及格")
情形 其他: # 兜底,要放最后
打印("继续努力")
四种分支写法:
| 写法 | 含义 |
|---|---|
情形 值: | 待匹配值与「值」相等即命中 |
情形 值1, 值2: | 命中任一个即可 |
情形 值 如果 条件: | 先比较值,再看条件 |
情形 如果 条件: | 只有条件(相当于一个 如果 分支) |
情形 其他: | 兜底(不带条件的通配,必须放最后) |
- 待匹配值只求值一次:
匹配 取下一个():不会因为分支多而跑多次; - 模式可以是任意表达式(变量、运算、容器字面量):
情形 基准 + 5:; - 分支体里
中断/继续/返回行为与普通块一致; - 通配用的是
其他这个词(_也行);想匹配一个名叫「其他」的变量,
写 情形 (其他):(加括号走普通表达式);
- 实现是解析期脱糖成
如果/否则如果/否则链,所以三个执行器
(树遍历 / Python VM / C VM)与两个跨语言宿主(Node / Rust)行为必然一致。
8.12 枚举 枚举(M34)
python枚举 颜色:
红 = 1
绿 = 2
蓝 = 3
枚举 方向: # 不写值:自动接着编号(从 1 起)
上
下
左
右
- 成员是实例(不是裸值),所以
打印(颜色.红)给颜色.红
(容器里也一样),.名字 / .值 分别取名字与值;
==按身份比:颜色.红 == 颜色.红为真、颜色.红 == 1为假
(与 Python 的 Enum 一致);
类型(颜色.红)给颜色 实例、类型(颜色)给类 颜色;- 遍历所有成员用
颜色.全部(一份清单):
遍历 c 在 颜色.全部:;
- 成员值可以是文本等任意值(
待办 = "todo"),此时后面的成员不能再省值
(没法自动编号,报错会说清);
- 枚举体里只写成员,不写方法(要方法就用
类); - 实现同样是解析期脱糖:
类 颜色:+ 若干颜色.红 = 新建 颜色(…, …)。
8.13 条件表达式 甲 如果 条件 否则 乙(M36)
「在一行里二选一」的表达式写法(三元表达式):
python令 年龄 = 20
令 等级 = "成年" 如果 年龄 >= 18 否则 "未成年"
打印(等级) # 成年
# 可嵌套(**右结合**):先判第一个条件,不成立再往后判
打印("优" 如果 分数 >= 90 否则 "良" 如果 分数 >= 60 否则 "差")
- 语序与 Python 的
A if C else B一致(甲 如果 条件 否则 乙),
选它是因为零新增关键字——如果/否则 本来就是语句关键字, 词法器、粘连检查、既有 API 都不用动(加关键字的成本与风险见 AGENTS.md 的「加关键字的检查清单」)。
- 优先级比「与/或」低、比「令」这类语句高:
假 或 真 如果 假 否则 假
是「先算 假 或 真,再拿它当条件」。
- 只求值选中的那一边:
f("甲") 如果 真 否则 f("乙")只会调用f("甲")。 - 条件是完整表达式,所以
1 如果 非 (2 在 [3]) 否则 0、x 如果 "k" 在 d 否则 -1
都成立。
- 少写「否则」会明确报错(不是静默给
空)。 - 实现是解析期脱糖成「匿名函数立即调用」:
`` 甲 如果 条件 否则 乙 ⇓ (函数(): 如果 条件: 返回 甲 返回 乙 )() ``
所以三个执行器与 Node / Rust 两个宿主一行都没改(本项目第六次用这个手法)。
- ⚠️ 与另外两种「如果」不冲突(这是实现时真踩到的坑,两处都要显式让路):
- 推导式的过滤子句:
[x 遍历 x 在 y 如果 条件]里的「如果」仍是过滤子句
——可迭代对象那一层不认条件表达式;
- 匹配的守卫:
情形 90 如果 加分:里的「如果」仍是守卫
——值那一层不认条件表达式。
两处都跟 Python 同一个思路:把「可迭代对象 / 情形值」限制在不含 conditional_expression 的层级(Python 的 comp_for 只允许到 or_test)。 想让这些位置用条件表达式,加括号即可:[x 遍历 x 在 (a 如果 c 否则 b)]、 情形 (a 如果 c 否则 b):。
9. 内建函数
| 函数 | 说明 | |
|---|---|---|
打印(...) | 输出内容(空/真/假 显示为中文,见 §7.15) | |
输入("提示") | 读取一行输入(返回文本) | |
整数(x) / 小数(x) / 文本(x) | 类型转换 | |
长度(x) | 列表/文本/字典的长度 | |
范围(起, 止, 步长) | 生成整数序列,1~3 个参数:范围(5)、范围(1, 5)、范围(1, 10, 2) | |
带下标(可迭代, 起始) | 造 [下标, 元素] 对(M41,同 Python 的 enumerate):带下标(["甲","乙"]) → [[0,'甲'],[1,'乙']] | |
配对(甲, 乙, …) | 按位置配成 [甲[i], 乙[i], …](M41,同 Python 的 zip,取最短) | |
最大(列表) / 最小(列表) | 取最大值 / 最小值;也支持多参数 最小(a, b)、最小(a, b, c)(M18.3) | |
总和(列表) | 求和 | |
类型(x) | 值的类型名:整数/小数/文本/列表/字典/布尔/空/函数… | |
异常(msg) / 类型错误(msg) / 值错误(msg) / 索引错误(msg) / 键错误(msg) / 除零错误(msg) / 文件错误(msg) | 构造异常对象(配合 抛出 使用) | |
反转(列表或文本) | 反转 | |
断言(条件, 消息=空) | 条件为假就抛「断言错误」(M23),消息可省 | |
是实例(值, 类) | 值是不是这个类(或其子类)的实例(M23) | |
超() | 在方法里调基类的同名方法(M23),见「类与继承」 | |
集合(可迭代=空) | 去重造集合(M24),见「集合」 | |
位与(a, b) / 位或(a, b) / 位异或(a, b) / 位取反(a) | 按位运算(M36)。**没有 `& \ | ^ 语法**:日常业务代码罕见,加运算符要动三执行器 + 两宿主,按「关 0」做成内建;只收整数,真/假`/小数/文本一律明确报错 |
左移(值, 位数) / 右移(值, 位数) | 位移(M36)。位数须是非负整数且 ≤ 1000000(防 1 << 10**9 申请 125 MB);右移 对负数是算术右移(向下取整) |
9.5 对象方法(M1)
方法通过 对象.方法(...) 调用;列表与字典的方法为原地修改,文本方法返回新值。
| 对象 | 方法 |
|---|---|
| 列表 | 追加(v) 插入(位置, v) 移除(v) 弹出(位置=-1) 排序() 反转() 清空() 索引(v) 计数(v) 包含(v) 映射(f) 过滤(f) 排序按(f) 归约(f, 初值)(后四个是 M24 起就有的,这份表以前漏列了) |
| 字典 | 获取(键, 默认=空) 键() 值() 包含(键) 更新(字典) 合并(字典)(返回新字典,不动双方,M36) 弹出(键, 默认=空) 清空() |
| 文本 | 拆分(分隔符=" ") 替换(旧, 新) 查找(子串)(找不到返回 -1) 大写() 小写() 去空白() 开头是(前缀) 结尾是(后缀) 包含(子串) 转整数() 转小数() |
10. 标准库(M2)
全部为中文 API,底层薄封装 Python 成熟实现。导入 模块名 引入。
| 模块 | 主要功能 |
|---|---|
随机 | 随机整数 / 随机小数 / 随机选择 / 洗牌 |
数学 | 开方 / 平方 / 幂 / 绝对值 / 向上向下取整 / 四舍五入 / 最大公约数 / 最小公倍数 / 阶乘 / 圆周率 / 正弦余弦正切 / 角度转弧度 / 对数 / 常用对数 |
时间 | 现在 / 今天 / 此刻(返回年月日时分秒星期字典)/ 时间戳 / 格式化时间戳 / 星期名 / 加天数 / 高精度时间 / 睡眠 |
文件 | 写文本 / 读文本 / 追加文本 / 写行 / 按行读 / 读字节(M113:整个文件 → 字节)/ 写字节(M113)/ 文件存在 / 是文件 / 是目录 / 列出目录 / 创建目录 / 删除文件 / 复制 / 文件大小 / 原子写(M108:先写临时再改名) / 路径拼接 / 文件名 / 扩展名 |
编码 | base64编码 / base64解码(M113:→ 字节)/ 十六进制编码 / 十六进制解码(M113:→ 字节,源码里造字节就用它)/ url编码 / url解码 / 文本字节(M113:文本 → 字节)/ 字节文本(M113:字节 → 文本,解不出 UTF-8 明确报错) |
表格 | 读表格(表头+数据行)/ 写表格 / 写字典表格 / 读字典表格 / 追加 / 挑选(一列或多列)/ 筛选 / 排序按 / 汇总(个数总和平均最大最小)/ 分组汇总(M106:按列分组再统计)/ 连接(M106:按键内连接两表)/ 转置 / 从记录(M107:字典列表→表格)/ 转记录(M107:表格→字典列表) |
文本 | 拼接 / 居中 / 左右对齐(M106 起按「显示宽度」补:一个汉字算 2 列)/ 填充 / 显示宽度(M106)/ 补零 / 重复 / 计数 / 是数字 / 是字母 / 是空白 / 首字母大写 / 去前缀 / 去后缀 / 格式化 / 按行拆分 / 截断 |
json | 转文本(中文不转义)/ 解析 / 读文件 / 写文件 |
数据库 | 打开 / 关闭 / 表们 / 有表 / 建表 / 建索引 / 插入 / 批量插入(事务)/ 查询 / 条数 / 汇总 / 修改 / 删除 / 执行 / 执行查询(M112;字节列按 BLOB 原样往返,M113) |
并发 | 批量摘要(M114:并行算一批文件的摘要)/ 批量读(并行读成 字节)/ 并行取(并行 GET 一批网址)—— 结果顺序与输入严格一致 |
HTTP服务端 | 启动(M115:起一个只监听本机的小服务)/ 空闲端口 / 解析请求 / 构造响应 —— 前两个要 socket,Node 宿主与浏览器试玩页用不了 |
日期 | 今天 / 解析 / 格式化 / 加天数 / 减天数 / 加月(M109:按日历月,月末收口)/ 加年 / 解析ISO(M109:只取字面日期)/ 相差天数 / 早于 / 晚于 / 相等 / 星期名 / 今年 / 本月 / 本年天数 |
正则 | 匹配 / 搜索 / 查找全部 / 替换 / 拆分 / 分组 |
网络 | 获取 / 提交 / 获取JSON(默认 10 秒超时,都能带 请求头 字典);提交JSON(M107,发 JSON);获取详情(M107,返回状态码/响应头/正文,非 2xx 不抛);下载(M107,存文件给字节数) |
html | 转义 / 原样 / 标签 / 空元素 / 链接 / 属性文本 / 有序无序列表(生成方向);取文本 / 取链接 / 取表格(M107,解析方向) |
路径 | 存在 / 是文件 / 是目录 / 绝对路径 / 父目录 / 文件名 / 后缀 / 无后缀名 / 连接 / 当前目录 / 主目录 / 创建目录 / 删除 / 递归删除(M108)/ 列出 / 递归列出(M41)/ 按模式找(M108:报告*.md 这类通配)/ 重命名 / 大小 / 修改时间 / 系统临时目录 / 临时目录 / 临时文件(M108) |
加密 | 摘要 / md5 / sha1 / sha256 / sha512 / 文件摘要 / HMAC(M109:带密钥的签名)/ 口令摘要(M109:PBKDF2,默认 10 万次)/ 安全随机字节(M113:系统熵源 → 字节;M109 起有,返回类型 M113 改对了)/ 支持的算法 |
压缩 | 打包(文件或目录→zip)/ 解压 / 列出内容 / 压缩文件(M109:单文件 → .gz)/ 解压文件(M109) |
系统 | 系统名 / 系统版本 / 机器架构 / 处理器 / 当前目录 / 改目录 / 环境变量 / 设环境变量 / 执行(外部命令)/ 退出码 / 打开(M108:默认程序开文件/网址)/ 主机名 / 参数(命令行参数,M18.2) |
容器 | 计数 / 最多 / 按值排序 / 分组 / 取前 / 取后 / 分块(M41;模块名不叫「集合」——那是内建函数名) |
迭代 | 分组 / 滑窗 / 去重 / 展开 / 取前 / 取后 / 求和 / 计数(M41)/ 归约(M106:通用 reduce)/ 分区(M106:按判定分两堆)/ 累积 / 组合 / 排列 / 笛卡尔积 |
参数 | 新建 / 选项 / 标志 / 用法 / 解析(M41,命令行选项解析) |
日志 | 级别们 / 设级别 / 取级别 / 设前缀 / 调试 / 信息 / 警告 / 错误(M41) |
测试 | 用例 / 相等 / 不等 / 为真 / 为假 / 近似 / 包含 / 抛出 / 统计 / 汇总 / 重置(M41) |
命令行参数(M18.2):
jishi 脚本.jsh 参数1 参数2中,脚本名之后的参数 传给脚本,脚本内系统.参数()返回这些参数组成的列表(不含脚本名); 用--分隔可把--开头的内容也当脚本参数(jishi 脚本.jsh -- --foo)。 注意:导入 文本后,内建转换函数文本(...)会被模块遮蔽,导入时会打印一次 中文提示;如需两全,用别名导入 文本 为 t(M18.4)。 正则模式写"\d+"(基石的未知转义会保留反斜杠,无需 r 前缀)。路径/压缩涉及文件系统访问、系统涉及执行外部命令,沙箱默认拦截 (需显式开启 allow_write / allow_network);加密纯摘要计算,沙箱默认放行。
11. 中文报错
所有错误以中文呈现,带错误码、行列定位与修正建议:
错误 E0106:关键字与后面的内容粘连了(「如果分数」被当成了一个名字,但它的开头是关键字「如果」)
┌─ 文件.jsh:1:1
│
1 │ 如果分数 > 60:
│ ~^
│
提示:你是不是想写「如果 分数」?关键字和后面的内容之间要留空格
错误码分段:E01xx 词法、E02xx 语法、E03xx 语义、E2xxx 运行期。
11.0 调用栈回溯(M17)
运行期错误若发生在函数调用深处,报错下方会附带「调用链(从外到内)」,标明 每一层是「哪个函数」在「第几行」被调用:
错误 E2003:索引越界(列表下标越界)
┌─ 脚本.jsh:3:9
3 │ 打印(x[10])
│ ^
│
调用链(从外到内):
外层 ← 第 8 行
中层 ← 第 7 行
里层 ← 第 5 行
- ⚠️ 文案一律是中文(R6.3 起):
e.消息里不会再出现
list index out of range / '键'(键的 repr)这种宿主语言的原文 —— 与「一个字符 = 一个码点」「不把内部结构泄进文案」是同一条规矩。
- 四个执行器的渲染逐字相同:树遍历 / Python VM / C VM 共用
render(),
独立宿主(Rust 单二进制)按 filename 现读源文件画出同一份 (源码行 + ^ + 调用链),由 tools/check_engines.py 守着。
- 递归超限(
RecursionError)同样走中文翻译 + 调用链 + 「是不是忘了结束条件」建议; - 协议/沙箱的 JSON 错误里也带
trace字段(agent 可程序化读取); - 调用链最多显示最近 20 帧,避免深层递归刷屏。
11.1 容错解析(M7.5)
基石会识别「用英文写了 Python 关键字/内建」的常见错误(大模型写中文代码时 容易 fallback 回英文),并直接给出中文对应建议:
pythondef f(x): # → 「def」是英文关键字,你是不是想写「函数」?
print(1) # → 「print」是 Python 内建,基石里叫「打印」
令 x = True # → 「True」是英文写法,你是不是想写「真」?
令 y = 1 and 2 # → 「and」是英文写法,你是不是想写「与」?
覆盖三类:英文语句关键字(def/if/for/while/return/import/class/try…)、 值/运算符关键字(True/False/None/and/or/not/in/is)、内建函数 (print/len/range/int/float/str/type/min/max/sum/reversed)。 英文普通变量名(a、data 等)不受影响。
12. 已知边界(v1.0 冻结时的诚实声明)
以下特性当前不支持,已诚实标注,留待后续版本评估:
- Rust 宿主的剩余边界(M33 后:标准库已补齐到 14 个模块):
整数到 i128 就到边界(越界明确报错)、绑定方法的 是/不是 明确报错、 精确小数的 只支持整数指数、压缩.打包 写 stored(不压缩, 文件大一点但完全合规)、非 Windows 平台上 https:// 明确报错。 已支持的(方法调用/高阶方法/集合/文件与上下文/精确小数/类型标注/同一性/ 切片/输入/排序/14 个标准库模块**)全部有对拍测试; 详见 docs/embed.md 的支持程度表与 rust/README.md。
- JS 宿主的剩余边界:
node/已补齐到 M34(含 M32 的大整数
BigInt、类与函数显示、精确小数精度,以及 M34 的类变量与 模式匹配/枚举),剩下的是 导入 … 从 python 不支持(无 Python 运行时)、 网络 是子进程同步封装(每次请求多约 40ms)。
以上边界均属「已知、诚实标注、留待演进」,不影响 v1.0 的核心语言特性冻结。