词法与语法
本篇定义普通源码的解析输入及树构造。下文 式 是语法层表达式,是否为合法类型/值由下一篇决定。混合位置操作符有歧义消解规则,不能把本篇 EBNF 直接交给任意优先级默认的解析器。
词一:源文本扫描
按 UTF-8 字符顺序扫描。普通源码仅显式忽略 U+0020 空格、U+0009 制表、U+000A 换行;不假定 BOM、U+000D、全角空格等会自动消失。换行不终止表达式。标识符、字符串、注释内部的标点不作为外层操作符。
扫描器至少有五种状态:外层、名称、字符串、字符串转义、注释。外层遇 「: 先进入注释;否则 「 进入名称;『 进入字符串;其他非空白字符保存为具体字符令牌。
名称一直读到第一个 」,内容原样保留,不做反斜线转义或 Unicode 正规化。空名称能在扫描阶段形成,但不能因此承诺它在表达式或绑定位置合法;正式接口应使用非空名称;空名称在不同上下文的接受边界不作为常规可移植源码约定。字符串一直读到未转义的 』,允许实际换行及其他标点。未闭合名称、字符串、转义或注释必须拒绝。
字符串中的 「:内容:」 是转义,而非注释。允许的内容恰为:
| 原文 | 结果 |
|---|---|
「:换行:」 |
U+000A |
「:制表:」 |
U+0009 |
「:』:」 |
字符 』 |
「:「::」 |
两字符 「: |
不支持 C 风格 \n 的特殊解释;不识别的上述转义内容报错。注释支持深度计数:遇 「: 加一,遇 :」 减一;外层结束时出状态。注释对运行值无影响,但保留文字及前后方向:当前行已有语法元素则后置,否则前置;换行会清除该行状态。
词二:名称到字面量
语法绑定已经建立之后,只对自由名称做以下识别;已转为局部绑定索引的引用不能再误改为数字。
阳、阴、元为保留内建值。《《内建类型:字符串》》、整数、小数、动态分类值、有、爻、元类型这七个完整内建类型拼写(均带同样前后定界)为内建类型节点。- 完整内建值拼写为
《《内建爻:阳》》、《《内建爻:阴》》、《《内建有:元》》。内建函数拼写及类型见类型篇。 - 名称非空且每字符属于
0123456789.零一二三四五六七八九点时尝试数字解析;.、点都是小数分隔符。无分隔符按十进制逐位累加;单分隔符把两侧作为整数部和小数部;多分隔符不识别成数字。 - 其余仍是自由名称,等待名称解析。
汉字是逐位数字,一二三=123;十、负一、-1、1e3 不由上述数值规则识别。空分隔片段的兼容情况须按分隔算法和用例判断,不把“词法扫描成功”视为数值有效。数字越界不是任意精度契约。小数字面量保存规范化阿拉伯整/小部分及宿主浮点近似值;LLVM 字面生成使用前者,不使用默认六位打印值。
语一:普通源码形状
引号中的文字为终结符;花括号表示重复,方括号表示可选;这里的 EBNF 元记号不是豫言源码。名 是带 「」 的名称令牌,串 是带 『』 的字符串令牌。
文件 = { 声明 "。" | "。" } [ 声明 ] ;
声明 = 导入 | 名 ("乃" | "者" | "即" | "立") 式 "也" | 式 ;
导入 = ("寻" | "观" | "诵" | "寻观" | "寻诵" | "观诵" | "寻观诵") 路径 "之书" ;
路径 = 名 { "之" 名 } ;
式 = 名 | 串 | "(" 式 ")"
| "会" 名 "而" 式
| "遇" 式 "者" 名 "而" 式
| "受" 名 "而" 式
| "化" 式 "而" 式
| ("化" | "承") 式 "者" 名 "而" 式
| "承" 名 "而" 式
| "虑" 名 "者" 式 "而" 式
| "递归虑" 名 "其" 式 "者" 式 "而" 式
| "若" 式 "则" 式 "否则" 式
| "鉴" 式 "而" 分支 { "或" 分支 }
| 式 ("于" | "授以" | "之" | "中" | "与" | "合" | "也" | ";" | "附") 式
| 序列首 "【" [ 式 { "," 式 } [ "," ] ] "】"
| "《《C调用》》名" 式 "传" 式
| "循" 名 "以" 式 ;
分支 = "有" 式 "则" 式 ;
序列首 = 名 | "《《C调用》》名" 式 ;
文件切句在词法令牌上进行,只在圆括号深度零时以具体 。 切分;连续空句忽略,文件末尾未带句号的非空尾句仍解析。字符串/名称中的 。 已被包成令牌。普通圆括号不容内联结构声明;典/阶段扩展不是任意普通运行时语法。
鉴 本身不额外消费终结 也;例子的 也 通常属于外层声明。或有 是相邻的 或 和 有 组件,不是独立布尔或。合 不建立联合类型。
语二:操作符解析算法
每个操作符由固定字符串组件、表达式槽 〇、绑定槽 □ 组成。实现保留多个组件匹配候选,不用最长关键词匹配武断消除短候选。绑定槽只读一个名称并匹配其后固定组件;表达式槽递归参与解析。
可用有共享子树的 chart parser 或具有分支状态的移入规约器:扫描令牌;扩展所有可匹配操作符组件;完成组件模式时形成候选规约;遇两种嵌套/连续规约冲突时查询规约关系表。表返回左规约则先完成左式,右规约则让右式占据其表达式槽;无关系时保留可行状态。规约结果还须经过各操作符的形状约束,例如序列首与投影名称。
最终必须有唯一合法解析;无解析拒绝,多条不等价解析拒绝,不按容器遍历顺序选第一棵。括号强制内部式先形成,括号节点本身不进入类型树。两个后备关系按顺序应用:右为 〇也〇 且左不同,则左规约;左不是 〇 开头、右是 〇 开头,则右规约;仍无关系则不作额外承诺。
至少复现这些容易反直觉的结果:f 于 a 于 b 的应用脊为 [a,b];f 于 a 附 b 等价 f 于 (a 附 b);A 合 F 于 X 等价 A 合 (F 于 X)。词法空白已经删除,所以 寻 观 等空白分隔组件不能按英语词界处理。
语三:树构造与语法糖
| 表面式 | 树与约束 |
|---|---|
会 x 而 e |
显式无标注函数,x 只绑定 e |
遇 A 者 x 而 e |
显式带参数类型函数,x 不绑定 A |
受 a 而 e |
隐式类型参数函数 |
化 A 而 B |
若 B 已是连续箭头 [B1,…,R],前加 A;否则 [A,B] |
承 a 而 B |
隐式全称类型;相邻同形隐式量化合并计数 |
化 T 者 a 而 B、承 T 者 a 而 B |
旧式兼容;T 必须是语法上 元类型(或相应内建节点),转隐式全称类型;不会接受任意同义别名 |
f 于 x、f 授以 A |
对 f 的调用脊追加显式/隐式实参;已有调用头不再二叉包裹 |
A 也 e |
类型标注;左类型、右对象;运行时只保留 e |
虑 x 者 a 而 b |
先 a,再在 x 绑定下 b;x 不绑定 a |
递归虑 x 其 A 者 a 而 b |
局部绑定递归值;x 分别绑定 a、b,不绑定 A |
a 与 b、A 合 B |
右侧已经是元组节点时展平;左侧元组保留为嵌套项 |
x 中 n |
n 在语法期必须识别为整数字面量,存为常量索引;不是动态索引 |
m 之 n |
n 必须为名称,变为静态投影字段 |
a 附 b |
依次绑定经字符串检查的 a、b,再调用 豫言_字符串拼接;临时名须卫生 |
「列」【a,b】(源码须用中文逗号) |
缀 于 a 于 (缀 于 b 于 罄);空序列为 罄;模式位置同样展开 |
「典」【…】 |
保留静态配置树,由豫构解码;普通表达式类型检查不接受运行时字典字面量 |
「《《扩展名》》」【…】 |
留给对应 IR 阶段解释;普通源码不任意执行扩展 |
《《C调用》》名 s【…】 |
与 传 等价参数容器:零项为元,单项为自身,多项为元组 |
循 x 以 e |
过期兼容形式当前返回 e,不建立递归绑定;不得实现成不动点运算符 |
元组的展平具有方向性:a 与 b 与 c 是三项;(a 与 b)与 c 的首项是二元组;a 与(b 与 c) 仍被展平。函数箭头的右展平同理,括号不会凭空增加可部分调用边界。
语四:声明预处理
一条 乃 声明与紧随同名 者/即 定义合成一项,末尾独立签名、不同名或中间夹其他声明均拒绝。合并后先对定义体建立同名抽象;若声明类型为箭头/隐式全称,或定义为函数,则加递归节点,否则去掉该顶层绑定。无签名定义不获得这条自动递归规则。
所有普通命名声明初始公开;无普通私有声明关键字。顶层裸式获内部匿名名但仍占有序定义位置。文件操作按导入、打开、导出次序展开为内部项;不能忽略这些项后继续用源码声明序数作为 ABI 序号。
语五:最小解析用例
「整数」也「一」 的树为标注,不是声明;「整数」中「一」 语法可形成,类型检查会失败。「甲」中「序数」 若序数不是数字则语法拒绝;「甲」【「一」】 的未知头不能解释成普通调用。裸 1 不形成数字表达式;「1」 才进入数值识别。未闭合定界符须在对应扫描状态报告错误,不能继续吞到下一个文件。