Code · .notc
Code 是用于计算数据与生成内容的函数语言。每份 .notc 是独立模块,内容字面量使用所选 Markup 前端的语法。类型与求值规则见 Code 函数系统。
声明
Module = (ImportDecl | FunctionDecl)*
ImportDecl = "import" String (("as" name) | (":" ("*" | ImportMember ("," ImportMember)* ","?)))? ";"
ImportMember = name ("as" name)?
FunctionDecl = "fn" name ComptimeParams? Params? Children? "->" TypeRef Where? (Block | ";")
Where = "where" Expr
ComptimeParams = "<" (name ":" TypeRef ("=" Literal)? ",")* ">"
Params = "(" (name "?"? ":" TypeRef ("=" Literal)? ",")* ")"
Children = "[" "children" ":" TypeRef "]"
TypeRef = Path ("<" TypeRef ("," TypeRef)* ">")?
Block = "{" ("let" name "=" Expr ";")* Expr? "}"
列表允许尾逗号。? 声明可选参数,与默认值互斥;children 是固定的内容槽名。泛型类型包括 Array<E>、Option<T> 和 Result<T, E>,详见 类型。
fn math<C: Type = InlineContent>(text: String) -> C;
fn mark<C: Type>[children: C] -> C {
if C == InlineContent { [#emph[#children]] }
else { [ #children ] }
}
import syntax Trivia from `../markup/syntax.not`;
import syntax Path from `../markup/syntax.not`;
import syntax Value from `types.not`;
syntax Module -> Trivia* ((ImportDecl | FunctionDecl) Trivia*)* !ANY;
syntax ImportDecl -> `import` STR ((`as` IDENT) | (`:` (`*` | ImportMember (`,` ImportMember)* `,`?)))? `;`;
syntax ImportMember -> IDENT (`as` IDENT)?;
syntax FunctionDecl -> `fn` IDENT ComptimeParams? Params? Children? `->` TypeRef Where? (Block | `;`);
syntax Where -> `where` Expr;
syntax ComptimeParams -> `<` ComptimeParam (`,` ComptimeParam)* `,`? `>`;
syntax ComptimeParam -> IDENT `:` TypeRef (`=` Value)?;
syntax Params -> `(` (Parameter (`,` Parameter)* `,`?)? `)`;
syntax Parameter -> IDENT `?`? `:` TypeRef (`=` Value)?;
syntax Children -> `[` `children` `:` TypeRef `]`;
syntax TypeRef -> Path (`<` TypeRef (`,` TypeRef)* `,`? `>`)?;
syntax Block -> `{` Binding* Expr? `}`;
syntax Binding -> `let` IDENT `=` Expr `;`;
syntax Expr -> If | Match | For | Comptime | Or;
syntax If -> `if` Expr Block `else` (If | Block);
syntax Match -> `match` Expr `{` Arm (`,` Arm)* `,`? `}`;
syntax For -> `for` IDENT `in` Expr (ContentLiteral | Block);
syntax Arm -> Expr `=>` Expr;
syntax Comptime -> `comptime` Expr;
syntax Or -> And (`||` And)*;
syntax And -> Equality (`&&` Equality)*;
syntax Equality -> Compare ((`==` | `!=`) Compare)*;
syntax Compare -> Sum ((`<` | `<=` | `>` | `>=`) Sum)*;
syntax Sum -> Product ((`+` | `-`) Product)*;
syntax Product -> Prefix ((`*` | `/` | `%`) Prefix)*;
syntax Prefix -> (`!` | `-`)* Postfix;
syntax Postfix -> Atom (CallSuffix | (`.` IDENT))*;
syntax Atom -> Path | Value | Block | ContentLiteral;
syntax CallSuffix -> ComptimeArgs Args? ContentLiteral? | Args ContentLiteral? | ContentLiteral;
syntax ComptimeArgs -> `<` Argument (`,` Argument)* `,`? `>`;
syntax Args -> `(` (Argument (`,` Argument)* `,`?)? `)`;
syntax Argument -> IDENT `:` Expr | Expr;
syntax ContentLiteral -> `[` body:scan FrontendContent `]`?;上述产生式省略词法项间的 Trivia;表达式中 ComptimeArgs 的开括号必须紧贴调用目标。FrontendContent 委托当前前端识别结束范围,不能由 Code 规则递归扫描正文。
import 只在模块顶层出现,路径为相对字符串字面量。省略选择部分时绑定文件名去掉扩展名,as 指定命名空间别名;: name as local, other 直接导入公开成员,: * 导入所有公开绑定。导出、模块身份与宿主装配见 本地模块。
表达式
原子表达式为共享数据字面量、名称、内容字面量和块。module.member 按嵌套 MemberExpr 保存静态模块成员访问,成员之间不插入空白。调用写作 foo<ct>(args)[body];三个实参区均可省略,但调用必须保留至少一个实参区;foo<ct> 已表示调用,全部省略时名称只表示引用。编译期调用的 < 必须紧贴目标;比较运算 < 与左操作数之间使用空白或注释,避免二者歧义。实参支持位置或 name: expression 绑定。Code 字面量中的调用加 #,值插值写 #name。
优先级,从低到高 | 运算 |
|---|---|
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | 前缀 |
8 | 调用与括号 |
if 写作 if condition { yes } else { no };match 写作 match subject { pattern => expr, _ => expr, }。comptime expression 要求编译期结果。<...> 内的比较表达式使用括号,例如 <enabled: (n > 0)>,以区分角括号与比较运算。普通数据参数与集合字面量共用 Types & Literals 的值转换规则。
词法、CST 与内容嵌入
for 写作 for element in array [内容体] 或 for element in array {代码体};两种正文前端使用 #for 入口。主体只接受静态 Array<E>,结果必须为同一类别的内容连接;详情见 标量显示与有限遍历。集合表达式顶层紧随的方括号属于循环体;若集合计算自身需要内容实参,将整个调用括起来。代码块使用 let 加尾表达式,不隐式连接其他表达式或转换 Unit。
接收者方法使用 expression.method(),例如 records.len()、make_records().len()。模块成员仍按词法名称解析;方法按接收者静态类型选择标准库定义,不提供函数值、动态派发或自定义 extension method。
关键字为 fn、import、as、let、if、else、match、comptime、for、in。标识符支持 Unicode 字母、数字、下划线及连字符;减法与名称以空白区分,箭头 -> 总是结束标识符。字符串、注释对括号平衡不透明。
notist-syntax 共用一套 Rowan 类型与 SyntaxKind,Document 与 Module 是独立根。Module 包含 ImportDecl、ImportMember、FunctionDecl、编译期与普通 Parameter、ChildrenDecl、TypeRef、BlockExpr 及表达式节点。AST 访问器只返回语法与范围,语义检查属于 pipeline。
let parsed = parse_module;
parse_module 显式接收 ContentParser。遇到内容字面量时,回调确定范围并生成前端自己的树;Code CST 只保存方括号、ContentText 和关联范围。token 游标越过整个字面量后恢复 Code 扫描,不以 Code 字符串规则扫描正文。
调用头、注解和内容字面量另有片段入口,供 Markup 前端组合。两侧插值使用同一语法;独立 Markup 对值引用的限制在语义检查阶段实施。坐标与接口见 文档前端。
恢复
解析保留全部已消费文本及诊断,不合成缺失 token。损坏的声明同步到分号、下一个 fn / import 或 EOF,不吞掉后续声明。类型嵌套与表达式嵌套有界。字符串及块注释未闭合时,按其词法范围恢复。
公开签名错误阻止模块安装;函数体的解析或 lowering 错误保留在实现模板上,使用该实例时诊断并恢复,不妨碍无关定义。