渲染zh-Hans

Grammar

Faber 编程语言的形文法(grammar)。现行实现是根 Rust workspace:crates/faber 负责包/工程工具,crates/radix 负责编译流水线。

---

程序结构#

Faber 源文件是纯文本,由驱动在词法分析前剥离。可选的 TOML frontmatter 不属于 token 语法。

fabFile       := frontmatter? program
frontmatter   := '+++' NEWLINE tomlBody NEWLINE '+++' NEWLINE?
program       := statement*
statement     := importDecl | varDecl | funcDecl | genusDecl | implendumDecl
               | typeAliasDecl | enumDecl | discretioDecl
               | ifStmt | whileStmt | iteraStmt
               | eligeStmt | discerneStmt | guardStmt | curaStmt | facBlockStmt
               | returnStmt | breakStmt | continueStmt | noopStmt | throwStmt
               | assertStmt | outputStmt | adStmt | incipitStmt
               | incipietStmt | extractStmt
               | probandumDecl | probaStmt | blockStmt | incDecStmt | exprStmt
blockStmt     := '{' statement* '}'

文件 frontmatter(+++#

若存在,frontmatter 必须在第 1 行以恰好 +++ 开头。后续某行在去除空白后恰好等于 +++ 即结束该块。闭合分隔符之后的字节是 Faber program。空正文(仅空白)是合法的空程序。

Frontmatter 由编译器驱动按通用 TOML 文档解析,而非按 Faber 语句解析。作者可附加任意元数据键;工具通过访问器读取已知键,例如 groupsectio[probanda]faber 包工具消费这些包键。[package][paths][build] 的包权威仍属 faber.toml;包模式下会拒绝相互冲突的 frontmatter 值。

示例:

+++
group = "exempla.directiva"
sectio = "smoke"
+++

入口 {}

行首 § 文件指令已移除。请把文件元数据放进 +++ frontmatter。在引号字符串内部,§ 仍是字符串模板占位符(见下文调用与成员访问)。

---

声明#

变量#

varDecl      := ('常量' | '变量') typeAnnotation IDENTIFIER (('←' expression) | ('↤' assignment inlineRecovery?))?
sitDecl      := '设' IDENTIFIER ('←' expression)?
arrayDestruct := ('常量' | '变量') arrayPattern '←' expression
objectDestruct := ('常量' | '变量') objectPattern '←' expression
  • 常量 = 不可变绑定(只写一次):可以不带初值声明,之后赋值恰好一次,随即冻结。变量 = 可变绑定(可重新赋值),类似 let
  • 当初值决定类型时,用 _ 作为类型标注:常量 _ name ← value
  • 设 name ← value常量 _ name ← value 的语法糖(推断型不可变局部量)
  • 设 name(无初值)是 常量 _ name 的语法糖——推断型延后不可变量。在任意读取之前必须赋值恰好一次。
  • 延后初始化:常量 整数 x设 x 声明一个未初始化的不可变槽位,在任意读取之前必须赋值恰好一次;第二次赋值会被拒绝。确定性赋值分析(语义阶段 3a)负责强制执行。

函数#

funcDecl     := '函数' IDENTIFIER genericParams? '(' paramList ')' funcModifier* returnClause? alternateExitClause? blockStmt?
paramList    := (parameter (',' parameter)*)?
genericParams := '<' genericParam (',' genericParam)* '>'
genericParam  := IDENTIFIER | '维度' IDENTIFIER
typeArgs      := '<' typeAnnotation (',' typeAnnotation)* '>'
parameter    := ('借自' | '传入' | '取自')? '其余'? typeAnnotation IDENTIFIER '可选'? ('作为' IDENTIFIER)? ('兜底' expression)?
funcModifier := '参数' IDENTIFIER | '委派' IDENTIFIER ('作为' IDENTIFIER)? | '勘误' IDENTIFIER | '退出' (IDENTIFIER | NUMBER) | '不变' | '可抛' | '选项' IDENTIFIER
returnClause := '→' typeAnnotation
alternateExitClause := '⇥' typeAnnotation
stmtBodyJoint  := '则'
clausuraJoint  := '∴'
clausuraExpr   := compactClausuraExpr | legacyClausuraExpr
compactClausuraExpr := clausuraSignature clausuraJoint (expression | closureFacBlock)
clausuraSignature := (clausuraParam | '(' clausuraParams? ')') returnClause? alternateExitClause?
closureFacBlock := '执行' blockStmt catchClause?
legacyClausuraExpr := '闭包' clausuraParams? ('→' typeAnnotation)? (':' expression | blockStmt)
clausuraParams := clausuraParam (',' clausuraParam)*
clausuraParam  := typeAnnotation IDENTIFIER
  • 返回语法: 声明正常成功类型。带函数体但没有 的函数是仅副作用型(无值),且不得包含 返回。语句体闭包(执行 { ... } 或旧式块体)若要使用 返回,也必须显式写 → T;表达式体闭包可从表达式推断结果。
  • 可恢复的备用出口语法: 声明错误通道类型。它可出现在 → T 之后,或单独出现在仅副作用且可失败的函数或闭包上。若闭包体使用了逃逸型 抛错,则必须声明自己的 ⇥ E;它不能继承外层函数的错误通道。局部的 执行 { ... } 捕获 err { ... } 可以在没有外层 的情况下捕获 抛错。在声明了 的函数内部,可失败的函数调用(→ T ⇥ E)会传播到该函数的备用出口,无需 执行/捕获 包裹;这与裸 conversio 和 抛错 抛出本来的行为一致;该调用会降级为 Rust ?。闭包若要传播可失败调用,仍必须声明自己的 ——外层函数的错误通道不会跨越闭包边界。
  • 参数前缀:借自(读)、传入(改)、取自(消费)
  • 名称后标记:可选(自愿/可选供给)
  • 其余 标记剩余参数
  • 委派 NAME ('作为' LOCAL)? 声明一个分配器要求;LOCAL 是函数体内的别名
  • 仅作为紧凑的语句体连接符(单语句的 如果//情况/… 分支体)。
  • 仅作为紧凑的闭包连接符。两者并非别名。
  • 紧凑闭包块体必须使用 执行 { ... };闭包局部的 执行 体可附加 捕获,但不能使用后缀

#

genusDecl    := '抽象'? '类' IDENTIFIER typeParams? ('继承' IDENTIFIER)? ('实现' IDENTIFIER (',' IDENTIFIER)*)? '{' genusMember* '}'
genusMember  := annotation* (fieldDecl | methodDecl)
fieldDecl    := '静态'? '属性'? typeAnnotation IDENTIFIER '可选'? ('=' expression)?
methodDecl   := '函数' IDENTIFIER '(' paramList ')' funcModifier* returnClause? alternateExitClause? blockStmt?

注解#

annotation            := '@' annotationName annotationBody?
annotationName        := IDENTIFIER | '公开' | '保护' | '私有' | 'futura' | 'cursor'
                        | '标签' | '仅' | '跳过' | '计量'
annotationBody        := bracedAnnotation | annotationArgs
bracedAnnotation      := '{' annotationFieldList? '}'
annotationFieldList   := annotationField (',' annotationField)* ','?
annotationField       := IDENTIFIER '=' (expression | typeAnnotation)

cliProgramAnnotation := '@' cli STRING
imperiumAnnotation := '@' imperium STRING
optioAnnotation    := '@' optio IDENTIFIER optioModifier*
optioModifier      := 'brevis' STRING | 'longum' STRING | '类型' typeAnnotation
                    | 'descriptio' STRING | 'ubique' | '兜底' expression
operandusAnnotation := '@' operandus ('其余')? typeAnnotation IDENTIFIER operandusModifier*
operandusModifier  := 'descriptio' STRING | 'ubique' | '兜底' expression
annotationArgs     := (STRING | IDENTIFIER | expression)+

(* 注解契约——编译期元数据模式 *)
annotatioMarker     := '@' annotatio ( '{' annotatioFieldList? '}' )?
annotatioFieldList  := annotatioField (',' annotatioField)* ','?
annotatioField      := 'target' '=' annotatioTarget
annotatioTarget     := '函数' | STRING | IDENTIFIER
contractApplication := '@' IDENTIFIER ( '{' annotationFieldList? '}' )?

jsonGenusAnnotation := '@' json
jsonFieldAnnotation := '@' json '{' 'nomen' '=' STRING '}'

Radix 解析花括号注解记录(@ futura { }@ optio { binding = verbose, ... })以及既有的语法糖写法。语法糖形式与花括号形式会降级为同样的 HirAnnotation 记录,用于已提升的族。某些未提升的 token 族可能仍保留原始注解实参,待消费者迁移后再处理。

注解契约:@ annotatio(可选 @ annotatio { target = 函数 })把一个顶层 标记为编译期注解契约。普通类不是注解模式。应用时使用 @ ContractName { field = constant },并通过本地声明或导入的文件接口导出来解析。解析后的应用会降级为 HirAnnotation,其中 contract_id: Some(DefId),字段为常量值。v1 的附着目标仅限 函数;载荷标量类型为 文本整数小数布尔(可通过 可选T ∪ 空 设为可选)。不存在编译器自有的 @ web / controller / route 族。

JSON 类:genus 上的 @ json 是编译器自有的数据模型契约,而非通用注解模式。字段必须 JSON 安全(文本窄字串整数小数布尔时刻列表<T>映射<文本, T>、可空 T ∪ 空,或另一个 @ json 类)。字段元数据 @ json { nomen = "wire_name" } 会改变 value ↦ 动态值value ↦ jsonjson ↦ 类 所输出的对象键;JSON 文本仍是 Norma 的线上操作,例如 json.pange(value ↦ json)

  • @ radix 保留给编译器自有的元数据。历史上的词干派生含义已废弃;词干派生仍是一种源码命名约定,而非编译器生成的变位。被接受的指令形式是顶层函数上的 @ radix lane "air" / "mir" / "hir-direct",用于显式编译器通道路由;不支持的通道/目标组合会以诊断拒绝,而非被忽略。
  • @ verte 定义代码生成变换(方法名或模板)
  • @ nondum [TARGET] ["REASON"] 标记某声明存在于接口中,但当前目标不可用
  • @ cli "NAME" 把一个 入口 标记为 CLI 程序
  • @ imperium "NAME" 把一个函数标记为 CLI 命令入口
  • @ optio NAME ... 定义一个 CLI 选项;布尔标志使用 类型 布尔
  • @ operandus [其余] TYPE NAME ... 定义一个 CLI 位置参数
  • @ futura 标记函数为异步
  • @ cursor 标记函数为生成器
  • @ 公开@ 私有 会作为注解解析,但不被强制;编译器会发出 WARN012(装饰性可见性),以免作者误以为存在访问控制
  • @ 保护 被保留并以语义诊断拒绝;它没有包、子类或同级文件可见性含义
  • 继承 = 继承自,实现 = 实现
  • 静态 = static,属性 = 绑定/属性

接口#

implendumDecl   := '契约' IDENTIFIER typeParams? '{' implendumMethod* '}'
implendumMethod := annotation* '函数' IDENTIFIER '(' paramList ')' funcModifier* returnClause? alternateExitClause?

契约契约构造:供 实现 使用的纯签名方法(implendumimplere 的动形词,意为“必须被满足者”)。导入命名空间以 .fab 文件边界为准;导出声明位于文件顶层。

类型别名#

typeAliasDecl := '类型' IDENTIFIER genericParams? '=' typeAnnotation

枚举#

enumDecl   := '枚举' IDENTIFIER '{' enumMember (',' enumMember)* ','? '}'
enumMember := IDENTIFIER ('=' ('-'? NUMBER | STRING))?

标签联合#

discretioDecl := '判别' IDENTIFIER typeParams? '{' variant (',' variant)* ','? '}'
variant       := IDENTIFIER ('{' variantFields '}')?
variantFields := (typeAnnotation IDENTIFIER (',' typeAnnotation IDENTIFIER)*)?

标识符命名#

混 合大小写且小写起首的名称在语法上被接受,但在语言、标准库、宿主路由或编译器自有内建 API 中并非 Faber 推荐写法。优先使用单个词。若单个词无法承载含义,仅在少数情况下使用 snake_case。若两种形态都不合适,那该方法多半不属于核心表面,除非它至关重要。标准库的编解码在所有模块中使用机械动词三件套 pange / solve / tempta——见 docs/stdlib/stdlib-mechanical-verbs.md。公开文本库是 norma:chorda——见 docs/stdlib/chorda-methods.md

导入#

importDecl     := importRecord | importSugar
importRecord   := '导入' '{' importFieldList? '}'
importFieldList := importField (',' importField)* ','?
importField    := importSourceField | importVisibilityField | importNameField
                | importAliasField | importWildcardField
importSourceField := '取自' '=' STRING
importVisibilityField := 'visibilitas' '=' visibility
importNameField := 'nomen' '=' IDENTIFIER
importAliasField := '作为' '=' IDENTIFIER
importWildcardField := '全部' '=' IDENTIFIER

importSugar    := '导入' '取自' STRING visibility? (namedImport | wildcardImport)?
visibility    := '私有' | '公开'
namedImport   := IDENTIFIER ('作为' IDENTIFIER)?
wildcardImport := '*' '作为' IDENTIFIER

示例:

导入 取自 "hono" 私有 Hono
导入 取自 "hono" 私有 Context
导入 取自 "norma:chorda"                         # 默认为 私有 chorda
导入 { 取自 = "norma:json/solve", 作为 = solve_mod }
导入 取自 "norma:consolum" 私有 consolum
导入 取自 "faber:*" 私有 faber              # 内核清单 glob
导入 取自 "lodash" 私有 * 作为 _
导入 取自 "./types" 公开 User               # 重导出

缺省可见性默认为 私有。缺省命名绑定时,若导入路径末段是合法且不冲突的标识符,则默认取该段为绑定名。若推断名非法或与既有顶层绑定冲突,请显式写出 nomen作为 绑定。

导入 取自 "faber:*" 私有 faber 是内核专属语法糖:glob 位于导入路径字符串内部,把发布二进制文件中的内核清单展开为 faber.<module>.<verb> 调用。它不是 私有 * 作为 name 通配形式,也不会创建运行期聚合值。

---

类型#

typeAnnotation := ('借自' | '传入')? baseType ( '∪' typeAnnotation )*
baseType       := holeType | functionType | qualifiedType typeParams? | '(' typeAnnotation ')'
holeType       := '_' | '∪'
qualifiedType  := IDENTIFIER ('.' IDENTIFIER)*
functionType   := '(' typeList? ')' '→' typeAnnotation alternateExitClause?
typeList       := typeAnnotation (',' typeAnnotation)*
typeParams     := genericParams
  • 数组写作 列表<T>。后缀 T[] 不被接受。
  • 借自/传入 作为类型前缀,标记所有权(借用/可变借用)。
  • 内联联合 T ∪ U(cup)用于即席值联合;T ∪ 空 是规范的可空类型形态(降级为 Option<T>)。
  • 联合在语法中是右结合,但按扁平方式解析;在语义降级时会对重复项与仅 的情况发出诊断。
  • 可选 是声明标记(参数/字段名称之后),绝不是类型前缀。
  • 限定类型路径(如 terminus.Terminus)通过导入的命名空间绑定来命名类型。前缀必须解析为命名空间;末段必须解析为承载类型的声明。

函数类型支持高阶函数签名:

函数 filtrata((T) → 布尔 pred) → 列表<T>
函数 compose((A) → B f, (B) → C g) → (A) → C
函数 apply((整数) → 整数 ⇥ 文本 op, 整数 n) → 整数 ⇥ 文本

基础类型#

Faber含义
文本Unicode 字符串
窄字串仅 ASCII 字符串
形式捕获的模板与参数
整数整数(默认 i64
模数<W>无符号模数字;算术按 2^W 取模回绕
小数浮点(默认 f64
布尔布尔
null
无值void
永不never
未知unknown
字节字节序列

带尺寸的基础类型接受一个可选的宽度标记(不是用户类型参数):

标记非法示例
整数<W>i8, i16, i32, i64, u8, u16, u32, u64整数<f32> → 改用 小数<f32>
小数<W>f16, f32, f64小数<i32> → 改用 整数<i32>bf16 暂缓
模数<W>u8, u16, u32, u64模数<i32> → 有符号宽度不是模数字

整数 / 小数 仍是 整数<i64> / 小数<f64> 的简写。

模数<W> 是独立的语义族:算术不会与 整数<W> 隐式混用,但显式的同宽转换仍可用。字面量必须在 0..=2^W-1 范围内(对于 模数<u64>,上限为 18446744073709551615)。移位计数本身是模数的:x ⇐ W 是完整回绕。跨宽度的模数算术被拒绝。

泛型集合#

Faber含义
列表<T>数组
映射<K,V>map
集合<T>set
期约<T>promise
游标<T>迭代器
tensor<T, Figura>静态形状 Figura 的稠密同质缓冲;数值方法要求数值元素类型
vector<T, N>寄存器级数值向量,静态宽度 N(单一维度,非缓冲支持)
matrix<T, [R, C]>寄存器级数值矩阵,恰好两个静态维度(非缓冲支持,也非 tensor 别名)
atomic<T>对存储敏感的原子单元;v1 仅接受 i32 / u32 元素,且访问必须经过原子方法
sparsa<T, Figura>静态形状 Figura 的稀疏同质缓冲;省略的坐标视为零;数值方法要求数值元素类型

Figura := _ | 自然数 | 标识符 | [ figura-list ](空 [] 为秩 0)。裸 tensor<T> 不完整——秩 0 请用 tensor<T, []>,推断形状请用 tensor<T, _>

无值 用于 tensor<T, []> 会生成秩 0 张量(一个默认初始化的元素槽)。 无值 用于 sparsa<T, Figura>(任意形状)会生成全零稀疏张量,无任何存储项。 matrix<T, Figura> 要求恰好两个维度;裸 matrix<T> 以及一维或三维 matrix 形状会被拒绝。 atomic<T> 在 v1 中要求 Ti32u32。原子单元不可与其元素类型互换;请使用 loadstoreexchangecompare_exchange 接收者方法。 通过 crea / structa / 构造多维张量。 Type(...) 不是构造形式:vector<f32, 4>(...)matrix<f32, [2, 2]>(...)tensor<f32, [2, 2]>(...) 以及诸如 整数("42") 的标量形式都会被拒绝。请使用 value ↦ Type、命名的库构造器,或 类 { field = value } 记录。

张量索引/形状内建槽(accipepondeformacreastructa)接受在调用点符合规范 列表<整数> / &[i64] 运行边界的整数列表(例如 GPU 线程 id 用 列表<u32>;不可用 列表<u64>)。这是仅限于这些槽的结构性例外,不会放宽有符号↔无符号的数值格(详见 tensor-intrinsics.md 中的 Index vector parameter policy)。

值联合使用内联 T ∪ U(可空:T ∪ 空)。标签联合使用 判别集合.unio() 是一个 set 方法,不是类型构造器。

类型语法糖#

类型语法糖是数值与集合类型的另一种写法。它仅出现在类型位置,且语义上完全等同于长形式——编译器对二者一视同仁。这是语法糖的唯一权威参考;规范的其余部分使用长形式。

语法糖把一个宽度标记与一个可选的单字母族前缀组合。宽度标记有 i8/i16/i32/i64(有符号)、u8/u16/u32/u64(无符号),以及 f16/f32/f64(浮点)。裸宽度标记(无前缀)糖化标量数值类型;族前缀糖化对应宽度的集合。

语法糖长形式方括号规则
i8u64, f16/f32/f64整数<W>, 小数<W>无(裸标记)
lf32, lu32, li64, …列表<f32>, 列表<u32>, 列表<i64>, …
tf32, tf32[2, 3], ti64[N]tensor<f32, _>, tensor<f32, [2, 3]>, tensor<i64, [N]>可选 Figura
sf32, sf32[2, 3], si64[N]sparsa<f32, _>, sparsa<f32, [2, 3]>, sparsa<i64, [N]>可选 Figura
vf32, vf32[4], vu32[3]vector<f32, _>, vector<f32, 4>, vector<u32, 3>可选单一宽度
mf32[4, 4], mf16[2, 2], mu32[3, 3]matrix<f32, [4, 4]>, matrix<f16, [2, 2]>, matrix<u32, [3, 3]>必填,两个维度

方括号形状:[] 为秩 0,[2, 3] 为固定形状,无方括号则推断形状(_)。matrix 要求恰好两个维度。语法糖从不使用 <>。对于非宽度的元素类型(如 tensor<文本, [3]>),请使用完整形式。

语法糖仅在类型语法中保留——名为 tf32lf32 等的值标识符不受影响。

模数<W> 没有语法糖;请写完整的 模数<u32>

拼写偏好(作者约定,非语法):一般 Faber 代码倾向于长形式以利可读性;以数值/张量为主的模块可偏好语法糖。请按模块或文件选择。

---

控制流#

条件#

ifStmt     := '如果' expression arm ('否则如果' ifStmt | elseClause)?
elseClause := '否则' elseArm
arm        := (blockStmt | stmtBodyJoint statement) catchClause?
elseArm    := (blockStmt | stmtBodyJoint statement) catchClause?
  • 如果 = if,否则如果 = else-if,否则 = else
  • 用于单语句体,包括 则 返回则 抛错则 崩溃则 静默(此处不接受
  • 静默 用于显式空操作(源自乐谱记号:意为“静默”)

循环#

whileStmt  := '当' expression (blockStmt | stmtBodyJoint statement) catchClause?
iteraStmt  := '遍历' (('取自' | '借自') expression | 'ab' expression) ('常量' | '变量') IDENTIFIER (blockStmt | stmtBodyJoint statement) catchClause?
  • = while
  • 遍历 取自...常量/遍历 取自...变量 = for-of(取值)
  • 遍历 借自...常量/遍历 借自...变量 = for-in(取键)
  • 遍历 ab range 常量/变量 i = 区间迭代(如 遍历 ab 0‥10 步 2 常量 i { 显示 i } 属于区间表达式)

开关 / 分派#

eligeStmt    := '选择' expression '{' eligeCase* defaultCase? '}' catchClause?
eligeCase    := '情况' expression (blockStmt | stmtBodyJoint statement)
defaultCase  := '默认' (blockStmt | stmtBodyJoint statement)

模式匹配#

discerneStmt := '匹配' '全部'? discriminants '{' variantCase* defaultCase? '}'
discriminants := expression (',' expression)*
variantCase  := '情况' patterns (blockStmt | stmtBodyJoint statement)
patterns     := pattern ((',' | '且') pattern)*
pattern      := '_' | literal | (IDENTIFIER patternBind?)
patternBind  := ('作为' IDENTIFIER) | (('常量' | '变量') patternBinding (',' patternBinding)*)
patternBinding := IDENTIFIER ('作为' IDENTIFIER)?

守卫#

guardStmt   := '守护' '{' guardClause+ '}'
guardClause := '如果' expression (blockStmt | stmtBodyJoint statement)

资源管理#

curaStmt    := '管护' STRING ('常量' | '变量') typeAnnotation IDENTIFIER blockStmt catchClause?

解构提取#

extractStmt   := '取自' expression ('常量' | '变量') extractFields
extractFields := extractField (',' extractField)* (',' restField)? | restField
extractField  := IDENTIFIER ('作为' IDENTIFIER)?
restField     := '其余' IDENTIFIER

控制转移#

returnStmt   := '返回' expression?
breakStmt    := '中断'
continueStmt := '继续'
noopStmt     := '静默'

---

错误处理#

throwStmt   := ('抛错' | '崩溃') expression ['如果' expression]
catchClause := '捕获' IDENTIFIER blockStmt
assertStmt  := '断言' expression ('secus' expression)?
  • 捕获 附着于结构化语句与条件分支。它不附着于任意的裸块。
  • 执行 { ... } 捕获 err { ... } 是规范的一次性局部可恢复错误边界。
  • 尝试 是旧式 try/catch 接口,会以迁移诊断拒绝。
  • 最终 是旧式 finally 接口,会以迁移诊断拒绝。
  • 抛错 = throw(可恢复),崩溃 = panic(致命)。
  • 抛错崩溃 上的可选 如果 <expr> 守卫是解析器语法糖:抛错 val 如果 cond 在解析期脱糖为 如果 cond { 抛错 val }。缺省守卫时该语句无条件执行(行为不变)。
  • 断言 是运行时不变量检查,概念上脱糖为 崩溃 "msg" 如果 !cond,源码中保留正向条件。secus 引入假路径消息,与其在 si/secussic/secus 三元运算中的角色一致。

---

表达式#

运算符(按优先级,由低到高)#

expression := assignment
assignment := ternary ('←' assignment | '↤' assignment inlineRecovery?)?
incDecStmt := place ('⊕' | '⊖')
ternary    := or (('?' expression ':' | '乃' expression '否则') ternary)?
or         := and (('或') and)*
and        := equality (('且') equality)*
equality   := comparison (('≡' | '≠' | '≈' | '≉' | '是' | '非' '是') comparison)*
comparison := bitwiseOr (('<' | '>' | '≤' | '≥' | '内' | '间') bitwiseOr)*
# 排序运算符使用 Unicode 字形;成员关系使用拉丁关键字 内/间
# (Faber 评注中的身份判断)。字形别名如 ∈ 不在现行契约中。
bitwiseOr  := bitwiseXor ('∨' bitwiseXor)*
bitwiseXor := bitwiseAnd ('⊻' bitwiseAnd)*
bitwiseAnd := shift ('∧' shift)*
shift      := range (('⇐' | '⇒') range)*
range      := additive (('‥' | '…' | '迄' | '到') additive ('步' additive)?)?
additive   := multiplicative (('+' | '-') multiplicative)*
multiplicative := coalesce (('*' | '/' | '%') coalesce)*
# 兜底 是局部可空消除(T ∪ 空 兜底 T → T),不是逻辑 或。
# 它比算术绑得更紧,故 prefix + item 兜底 "" 等价于 prefix + (item 兜底 "")。
# 兜底 的右端可构成一个区间构造(maybeRange 兜底 0‥0)。
coalesce   := unary ('兜底' velRhs)*
velRhs     := unary (('‥' | '…' | '迄' | '到') unary ('步' unary)?)?
unary      := ('-' | '¬' | '非' | '等候' | '构造') unary | cast
cast       := call ('∷' typeAnnotation | conversio)*
conversio        := '↦' typeAnnotation typeParams? inlineRecovery?
inlineRecovery   := '⇥' unary

是定向转换赋值:先求右侧值,通过 路径转换为左侧位置的静态类型,再赋值。 内联恢复仅在 上合法, 之后不允许。

已退役的谓词关键字不是前缀一元语法。请使用 expr 是 真expr 是 假expr 是 空expr 非 是 空expr < 0expr > 0

静态类型指派(`∷` / verte):

字形(U+2237,proportion)为表达式显式指派目标类型。当源表达式已存在、且编译器需要一个静态目标形状时使用:

  • 基础/别名 → 转型(无运行期效果):data ∷ 文本 → TypeScript:(data as string)
  • 内建集合 → 目标形状的集合值:[1, 2, 3] ∷ 列表<整数>
  • variant 表达式 → enum/接口目标指派:构造 Click { x = 10 } ∷ Event

对于普通 值,请优先使用类型化构造;对于普通空集合值,请优先使用 无值

常量 _ point ← Point { x = 10 }
常量 列表<整数> xs ← 无值

只有 字形被接受为后缀静态类型指派运算符。拉丁形式 quainnatumnovum 曾是别名,现已移除(见 verte-alias-clean-break)。

运行期转换(`↦` / conversio):

字形(U+21A6,rightwards arrow from bar)是运行期值转换运算符。与 (编译期转型)不同,它执行可能失败的实际解析/转换:

  • "22" ↦ 整数 → Rust:"22".parse::<i64>().unwrap()
  • "bad" ↦ 整数 ⇥ 0 → Rust:"bad".parse::<i64>().unwrap_or(0)
  • 42 ↦ 文本 → Rust:42.to_string()

内联失败恢复使用 ,紧跟在 conversio 目标之后(↦ T ⇥ recovery-expr)。恢复表达式必须是 T 类型的值。

兜底 用作 conversio 恢复会以迁移诊断拒绝。兜底 仅用于局部可空消除(x 兜底 y、参数默认值),不是逻辑 。带括号的 conversio 结果仍可与 兜底 组合为普通默认。

调用与成员访问#

call          := primary (callSuffix | memberSuffix | optionalSuffix | nonNullSuffix)*
callSuffix    := typeArgs? '(' argumentList ')'
memberSuffix  := '.' IDENTIFIER | '[' expression ']'
optionalSuffix := '?.' IDENTIFIER | '?[' expression ']' | '?(' argumentList ')'
nonNullSuffix := '!.' IDENTIFIER | '![' expression ']' | '!(' argumentList ')'
argumentList  := (argument (',' argument)*)?
argument      := '展开'? expression

字符串与模板字面量#

Faber 采用分隔符语义:每种引号形式代表不同的源形态。它们并非可互换的同义词。

形式类型角色
'...'窄字串固定机器 token;无 §;无 (...)
"..."文本短 Unicode 单行字符串;(...) 渲染
«...»文本块/多行 Unicode;(...) 渲染
... 形式捕获的模板;(...) 捕获
{ ... }json编译期以对象为根的 JSON 文档(内部用 :
\|...\|字节编译期十六进制字节
"..." ↦ regexregex由文本转换编译出的模式
[ ... ]列表<T>Faber 列表(非 JSON 数组,也非字节)

§(U+00A7)在 Unicode 形式("« )中作为模板占位符。它不能出现在 窄字串` 字面量中。

渲染模板文本):"..."(...)«...»(...) 降级为 scriptum("...", args...)

捕获模板形式): ...(args) 捕获模板文本与参数而不渲染。适合绑定的 SQL/URL 载荷;不要为此使用 «...»(...)

文本 使用书名号 «...»。重型引号对已退役(在许多字体中与 " 视觉上太接近)。

实现状态(2026-06-30):

  • 已交付:"..."«...»文本'...'窄字串 ... 形式|...|字节{ ... }json,以及 text/ascii ↦ regex
  • 待 factory 交付:斜杠分隔的 /.../ 正则字面量。

内联块示例:

常量 _ tag ← «inline»

多行块示例(开 « 后换行):

常量 _ blob ← «
    select id, email
    from accounts
»

捕获模板示例:

常量 _ q ← `select * from accounts where id = §`(accountId)

字节十六进制字面量示例:

常量 _ sig ← |de ad be ef|
常量 _ hello ← |48 65 6c 6c 6f|

格式化模板应用#

字符串字面量调用语法是格式化模板应用的规范源形式:

"status: § (§)"(sample_status(), "ok")
"status: §1 (§0)"("ok", sample_status())

它降级为编译器的 scriptum("...", args...) 形式。在普通源码中使用字符串模板形式;将 scriptum(...) 留给显式脱糖示例和面向编译器的文档。

对于 文本,方括号索引以 Unicode 标量为单位:

"Salve, §!"[7]            # "§"
"hello world"[0‥5]        # "hello"
"hello world"[0 到 10]    # "hello world"
"abcdef"[0‥6 步 2]       # "ace"

文本切片接受完整的区间形式,包括

对于 列表<T>,方括号索引是单元素访问。索引必须是单个整数;不接受区间切片(拷贝区间请用 sectio(start, end)):

xs[i]        # 位置 i 处的元素
xs[i] ← v    # 写入位置 i 处的元素

列表方括号访问是普通的,而非可空:它返回裸元素 T,越界时陷阱。这与 tensor 不同——后者的方括号读取是 accipe 语法糖,返回 T ∪ 空。可空列表访问请用 xs.accipe(i) → T ∪ 空 配合 兜底

对于 tensor<T, Figura>,方括号索引是张量内建表面的语法糖:

vector[id]        # vector.accipe([id])
vector[id] ← v    # vector.ponde([id], v)
grid[[r, c]]      # grid.accipe([r, c])
grid[[r, c]] ← v  # grid.ponde([r, c], v)

读取返回 T ∪ 空,与 accipe 一致;请先使用 兜底 或其他普通选项处理形式,再参与算术。秩 1 张量接受符合张量 i64 运行边界的标量整数索引(u64 被拒绝)。秩 N 张量使用列表形态的索引表达式,如 [[r, c]],或一个绑定的 列表<integer> 值。grid[r, c] 不是合法语法;memberSuffix 在方括号之间仍只包含一个 expression

字节 是字节缓冲原语,不是数组,因此不接受(读或写)方括号索引。字节访问基于方法:

buf.accipe(i)      # → 整数<u8> ∪ 空(可空;越界安全)
buf.appende(b)     # 原地追加单字节
buf.longitudo      # 字节长度

这是有意为之。字节 是 HAL、加密与 |hex| 字面量所用的不透明边界字节缓冲;其读取默认可空,方括号语法保留给陷阱式访问模型。对于字节密集的索引,内部请使用 列表<整数<u8>>(方括号读/写、越界陷阱),并在边界处保留 字节

基本表达式#

无值 是上下文相关的空集合标记(标识符形态,不是保留关键字)。请与显式集合类型一起使用:常量 列表<整数> xs ← 无值,或 常量 tensor<小数<f32>, []> t ← 无值

primary := IDENTIFIER | NUMBER | STRING | ASCII_STRING | BACKTICK_STRING
         | '自身' | '真' | '假' | '空'
         | '无值' | arrayLiteral | jsonLiteral | typedConstructor
         | adExpr
         | '(' expression ')'
adExpr    := '调用' asciiLiteral adOpener?
arrayLiteral := '[' argumentList? ']'
# 裸 { ... } 是 JSON 文档字面量。键是用 `:` 分隔的带引号 JSON 字符串;
# 值是 JSON 常量。匿名 Faber 对象({ key = expr })已退役(字面量族 Stage 6)。
# 类构造使用 typedConstructor。
jsonLiteral := '{' (jsonMember (',' jsonMember)* ','?)? '}'
jsonMember  := STRING ':' jsonValue
typedConstructor := typeAnnotation '{' fieldList? '}'
fieldList := fieldInit (',' fieldInit)*
fieldInit := ('展开' expression) | (fieldKey '=' expression) | IDENTIFIER
fieldKey := IDENTIFIER | STRING | '[' expression ']'
# JSON 值:仅常量(无 Faber 表达式,无变量引用)。
jsonValue := jsonObject | jsonArray | jsonString | jsonNumber | 'true' | 'false' | 'null'
jsonObject := '{' (jsonMember (',' jsonMember)* ','?)? '}'
jsonArray  := '[' (jsonValue (',' jsonValue)* ','?)? ']'
jsonString := STRING
jsonNumber := NUMBER                       # 无 '.'/e/E 时为 整数,否则为 小数

STRING 既包括 " 分隔的短字符串,也包括 «» 分隔的块字符串。'...'窄字串)与反引号 ... 形式)是独立的字面量形式(见上文字符串与模板字面量)。

{ ... } 现在生成以对象为根、类型为 json 的 JSON 文档:{ "name": "Alice", "age": 30, "active": true }。键是用 : 分隔的带引号 JSON 字符串;值仅限 JSON 常量。重复键会报错(第二次出现即出错)。指派为 映射<K,V> 会降级为真正的常量 map。显式宽化到宽动态载体请用 ↦ 动态值。类/variant 构造 Type { field = expr } 仍使用 Faber 的 = 语法。

特殊表达式#

// verte(∷)是后缀——在上面的 cast 产生式中解析
fingeExpr     := '构造' IDENTIFIER ('{' fieldList '}')? ('∷' IDENTIFIER)?
praefixumExpr := '前缀' (blockStmt | '(' expression ')')
formatStringExpr := STRING '(' argumentList ')'                # 通过 scriptum 渲染 文本
formaTemplateExpr := BACKTICK_STRING '(' argumentList ')'      # 捕获 形式
scriptumExpr  := '格式化' '(' STRING (',' expression)* ')'   # 显式/脱糖形式
legeExpr      := '读取' '行'?
regexFromText   := (STRING | ASCII_STRING) '↦' 'regex'
# 斜杠分隔的正则字面量尚不是现行语法。`/` 词法为除法运算符,
# 而 `//` 与 `/* ... */` 作为非法注释被拒绝。
# 旧式 'sed' STRING [IDENT] 已移除;请使用 "..." ↦ regex。

---

模式#

objectPattern  := '{' patternProperty (',' patternProperty)* '}'
patternProperty := '其余'? IDENTIFIER ('作为' IDENTIFIER)?
arrayPattern   := '[' arrayPatternElement (',' arrayPatternElement)* ']'
arrayPatternElement := '_' | '其余'? IDENTIFIER

---

诊断输出#

outputStmt := ('显示' | '查看' | '警告' | '写入') expression (',' expression)*
  • 显示 = 中性诊断注记,查看 = debug/inspect,警告 = warn
  • 写入 是一种诊断通道拼写;真实输出请使用当前标准库方法

注释#

Faber 只接受行注释# 至行尾。# 必须是逻辑行上第一个非空白 token(仅允许可选的前导 ASCII 空格或制表符——其他 Unicode 空白分隔符不会被词法分析跳过)。若 # 在同一行上跟在任意其他 token 之后,则是词法错误,消息为 # comments must start a line; move this comment above the code

合法的行首注释会作为 leading_trivia 向前附着到下一条语句或声明上(见 comment-preservation)。位于字符串字面量、窄字串 字面量、形式 模板及其他分隔字面量内部的 # 不是注释。

---

入口点#

incipitStmt  := '入口' blockStmt
incipietStmt := '异步入口' blockStmt
  • 入口 = 同步入口,异步入口 = 异步入口

---

测试#

probandumDecl := '验题' STRING probaModifier* '{' probandumBody '}'
probandumBody := (praeparaBlock | probandumDecl | probaStmt)*
probaStmt     := '测试' STRING probaModifier* blockStmt
probaModifier := '跳过' STRING | '预期' STRING | '仅' | '标签' STRING
              | '时限' NUMBER | '计量' | '重复' NUMBER | '易碎' NUMBER
              | '需求' STRING | '仅于' STRING
praeparaBlock := ('备置' | '异步备置' | '收尾' | '异步收尾') '全部'? blockStmt

---

CLI 框架#

cliDecl       := annotation* (incipitStmt | funcDecl)
cliAnnotation := cliProgramAnnotation | imperiumAnnotation | optioAnnotation | operandusAnnotation

Faber 支持构建 CLI 应用,自动解析参数并生成帮助。

CLI 入口点#

@ cli "faber"
@ optio verbose longum "verbose" 类型 布尔
入口 参数 args {
    # CLI 框架自动解析参数
}

CLI 选项与参数#

@ imperium "deploy"
@ optio target brevis "t" longum "target" 类型 文本 descriptio "Deployment target"
@ optio verbose brevis "v" longum "verbose" 类型 布尔 descriptio "Enable verbose output"
@ operandus 文本 file descriptio "File to deploy"
函数 deploy() 参数 args {
    # 参数自动解析并传入
}

---

能力调用#

表达式形式的 调用 是唯一受支持的 调用 表面。旧式带类型的 调用 "route" (args) → T { } 与语句级流式块 ad 'route' { meus/tuus … } 在解析期即被拒绝。

adExpr        := '调用' asciiLiteral adOpener?
adOpener      := '(' expression ')'
conversioExpr := expr '↦' typeAnnotation
  • 路由:asciiLiteral'solum:lege'),而非双引号 STRING
  • Opener:可选的单个 expression → 作为 动态值 的 Request data
  • 表达式 `调用`:无块;求值为一个 sermo 会话句柄。请使用后缀 ↦ T(物化)、赋值给 sermo,或打开实时方向视图:s.meus<T>()(出站 da / fini)与 s.tuus<T>()(入站 accipe / cursor / exhauri / fini)。入站内容帧请用 s.tuus<T>().cursor() 迭代,而不是直接 遍历 取自 s.tuus<T>()
  • 已移除(解析错误):旧式带类型 调用 "route"、块 meus/tuus 分支,以及语句级 发送
  • 类型:编译器自有的 scriniumstatus;不透明的 sermo 会话句柄。
  • sermo ↦ T 使用面向 T 的类型导向收集器,把入站帧物化为单个 T 类型值。

docs/design/frame-stream-types.md

---

集合操作#

原先的 ab 集合管道 DSL 已退役。集合的过滤、切片与聚合通过普通的 文本/列表/映射/集合 方法与闭包来表达,而不是语法层的查询表达式。文本整数小数列表<T>映射<K,V>集合<T> 是编译器自有的核心类型;其规范方法表面记录在 docs/design/textus-intrinsics.mddocs/design/numerus-intrinsics.mddocs/design/fractus-intrinsics.mddocs/design/lista-intrinsics.mddocs/design/tabula-intrinsics.mddocs/design/copia-intrinsics.md 中,而非 Norma 声明。

primaultima 是普通方法名,不是变换关键字。ubi 不是现行集合语法。

取自 用于迭代(遍历 取自 items 常量 x)与导入(导入 取自 "path")。

---

Fac 块#

facBlockStmt := '执行' blockStmt catchClause? ('当' expression)?
  • 执行 { ... } 把作用域块执行一次。
  • 执行 { ... } 捕获 err { ... } 是规范的局部可恢复错误边界。
  • 执行 { ... } 当 condition 是后测试循环形式;后缀 仅附着于 执行,而非任意前置块。

---

目标支持#

目标支持不属于语法——本文件只定义语言本身。各编译目标降级到何种语法,以及相关运行期策略,见:

---

关键字参考#

类别Faber含义
声明判别标签联合
常量const
函数function
class
契约interface contract
维度size/index 泛型参数(在 <> 列表中)
枚举enum
推断型不可变局部量
可选可选声明槽位(名称之后)
类型type alias
无值上下文相关的空集合标记
变量let
控制流如果 / 否则如果 / 否则if / else-if / else
守护guard
匹配pattern match
while
选择 / 情况switch / case
执行作用域块 / 局部错误边界
遍历 取自...常量for-of(取值)
遍历 借自...常量for-in(取键)
遍历 ab...常量区间迭代
继续continue
返回return
中断break
静默no-op(静默)
紧凑单语句体连接符
仅紧凑闭包连接符
错误处理捕获结构化局部处理
断言assert
抛错throw
可抛throws 修饰符
崩溃panic
执行 / 捕获局部可恢复错误边界
异步@ futuraasync 注解
@ cursorgenerator 注解
等候按上下文 await/yield
端点调用能力调用表达式
发送已退役的语句级帧发射
布尔true
or
and
false
not
兜底局部可空默认
对象自身this/self
构造构造 variant
类型形状静态类型指派 / 编译期转型
类型转换↦ target运行期值转换
↦ T ⇥ exprconversio 带内联恢复,类型 T
↦ 整数解析为整数
↦ 小数解析为浮点
↦ 文本转换为字符串
↦ 布尔转换为布尔
位运算 / / / ¬and/or/xor/not
/ 左/右移
诊断显示中性注记
警告warn
写入诊断通道
查看debug/inspect

---

关键语法规则#

  1. 类型在前的参数函数 f(整数 x),而非 函数 f(x: 整数)
  2. 类型在前的声明常量 文本 name,而非 常量 name: 文本
  3. 迭代循环遍历 取自/借自 collection 常量/变量 item { }遍历 ab range 常量/变量 item { }(动词在前,之后是来源,再是绑定)
  4. 条件加括号合法但不地道:优先 如果 x > 0 { }如果 flag 是 真 { },而非 如果 (x > 0) { }
  5. 诊断关键字是语句,不是函数——显示 x 可用,显示(x) 也可用(括号只是组合表达式),但 显示 不是可调用值

---

Reader pack glossary (machine extract)#

Keywords#

LatinLocalized
discretio判别
fixum常量
functio函数
genus
implendum契约
importa导入
modulus模数
ordo枚举
sit
typus类型
varia变量
abstractus抽象
ceteri其余
curata委派
errata勘误
exitus退出
generis静态
iacit可抛
immutata不变
magnitudo维度
nexum属性
optiones选项
prae
privata私有
protecta保护
publica公开
sponte可选
casu情况
ceterum默认
custodi守护
discerne匹配
dum
elige选择
ergo
fac执行
itera遍历
secus否则
si如果
sic
sin否则如果
perge继续
redde返回
rumpe中断
tacet静默
adfirma断言
cape捕获
cede等候
iace抛错
mori崩溃
clausura闭包
falsum
nihil
verum
aut
est
et
non
vel兜底
ego自身
finge构造
implet实现
sub继承
mone警告
nota显示
scribe写入
vide查看
argumenta参数
cura管护
incipiet异步入口
incipit入口
ad调用
de借自
ex取自
in传入
lege读取
lineam
omnia全部
praefixum前缀
scriptum格式化
sparge展开
ut作为
ante
inter
intra
per
usque
fragilis易碎
futurum预期
metior计量
omitte跳过
postpara收尾
postparabit异步收尾
praepara备置
praeparabit异步备置
proba测试
probandum验题
requirit需求
repete重复
solum
solum_in仅于
tag标签
temporis时限
negativum
nonnihil有值
nonnulla诸值
nulla皆无
positivum

Types#

LatinLocalized
ascii窄字串
textus文本
numerus整数
modulus模数
fractus小数
bivalens布尔
nihil
vacuum无值
numquam永不
ignotum未知
octeti字节
regexregex
jsonjson
valor动态值
instans时刻
objectum对象
quidlibet任意
lista列表
tabula映射
copia集合
promissum期约
cursor游标

Glossary changes vs existing pack#

LatinOld packNew (this EBNF)Why
genus类型类型 改用于 typus(type alias 关键字);genus(class)改用 以消除歧义并匹配关键字参考表语义
modulus(缺)模数新增:modulus<W> 无符号模数字族需要独立词面,且与 numerus 区分
ordo(缺)枚举新增:enum 声明关键字
sit(缺)新增:推断型不可变局部量(fixum _ 的语法糖),取单字以匹配 fixum/varia 的单字密度
typus(缺)类型新增:type alias 声明关键字(释放自原 genus
abstractus(缺)抽象新增:抽象类前缀
ceteri(缺)其余新增:rest 参数/模式标记
curata(缺)委派新增:分配器要求修饰符
errata(缺)勘误新增:错误通道修饰符
exitus(缺)退出新增:退出码修饰符
generis(缺)静态新增:static 成员标记
iacit(缺)可抛新增:throws 修饰符(与 iace=抛错 区分)
immutata(缺)不变新增:不可变修饰符
magnitudo(缺)维度新增:泛型尺寸/索引参数
nexum(缺)属性新增:bound/property 成员标记
optiones(缺)选项新增:选项修饰符
prae(缺)新增:已移除的泛型前缀(保留以诊断)
protecta(缺)保护新增:保留的可见性注解名
sponte(缺)可选新增:可选声明槽位标记
custodi(缺)守护新增:guard 语句
ergo(缺)新增:紧凑单语句体连接符(与 区分;后者不本地化)
tacet(缺)静默新增:显式 no-op
adfirma(缺)断言新增:assert 语句
cede(缺)等候新增:await/yield 上下文关键字
iace(缺)抛错新增:throw(与 mori=崩溃、iacit=可抛 区分)
mori(缺)崩溃新增:panic(致命)
clausura(缺)闭包新增:旧式闭包字面量关键字
vel(缺)兜底新增:局部可空消除(与 aut=或 区分)
ego(缺)自身新增:this/self
finge(缺)构造新增:variant 构造
implet(缺)实现新增:implements
sub(缺)继承新增:extends
incipiet(缺)异步入口新增:异步入口(与 incipit=入口 区分)
ad(缺)调用新增:能力调用表达式
lege(缺)读取新增:读取输入
lineam(缺)新增:lege lineam 的行标记
omnia(缺)全部新增:通配/全部标记
praefixum(缺)前缀新增:前缀表达式
scriptum(缺)格式化新增:格式化模板的显式/脱糖形式
sparge(缺)展开新增:参数展开
ante(缺)新增:区间运算符的词形(与 usque 区分)
inter(缺)新增:成员关系“之间”(与 intra=内 区分)
intra(缺)新增:成员关系“之内”
per(缺)新增:区间步长
usque(缺)新增:区间运算符的词形
fragilis(缺)易碎新增:测试易碎性修饰符
futurum(缺)预期新增:测试的 future-target 标记(与注解 @ futura 区分,后者保留为注解名)
metior(缺)计量新增:基准测试修饰符
omitte(缺)跳过新增:跳过测试修饰符
postpara(缺)收尾新增:teardown 块
postparabit(缺)异步收尾新增:异步 teardown
praepara(缺)备置新增:setup 块
praeparabit(缺)异步备置新增:异步 setup
proba(缺)测试新增:测试用例语句
probandum(缺)验题新增:测试用例声明
repete(缺)重复新增:重复次数修饰符
requirit(缺)需求新增:requires 标记
solum(缺)新增:only 测试标记
solum_in(缺)仅于新增:only-in 测试标记
tag(缺)标签新增:tag 测试标记
temporis(缺)时限新增:timeout 测试修饰符
negativum(缺)新增:负数标记
nonnihil(缺)有值新增:some(单数)标记
nonnulla(缺)诸值新增:some(复数)标记
nulla(缺)皆无新增:none 标记
positivum(缺)新增:正数标记

新增类型(相对现有 pack 的 11 项):modulus→模数、numquam→永不、octeti→字节、ascii→窄字串、valor→动态值、instans→时刻、objectum→对象、quidlibet→任意、promissum→期约;regex/json 保持拉丁词面(与 body 中作为类型 token 的 regex/json 一致,且为通用计算术语,参照已通过质量门槛的 EBNF.hi.md)。既有 pack 类型(textus/numerus/fractus/bivalens/nihil/vacuum/ignotum/lista/tabula/copia/cursor)保持不变。