编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载本篇文章围绕 Unison 代码仓库中的回归测试转录文件 fix-4536.md 展开系统剖析 Unison 语言中点号.的合法使用边界以及 UCM 在遇到非法点号时如何生成可读的解析错误信息。读者在读完本文后将理解 Unison 中use指令、绝对命名空间、unique 类型修饰与 watch 表达式对点号的不同语法要求并能读懂 UCM 输出中“I got confused here”“I was surprised to find a … here”等错误消息背后的词法/语法层成因。一、转录测试Transcript与 fix-4536 的背景Unison 使用“转录测试”transcript体系来验证 UCMUnison Codebase Manager命令与语言解析的实际行为。一份.md转录文件由多个代码块构成以unison :error标记的代码块表示预期会解析失败的 Unison 源码紧跟着的ucm :added-by-ucm块则是在转录运行过程中由 UCM 自动补写的实际输出用于固化错误消息的“黄金快照”。因此fix-4536.md 实际上是一份解析错误消息的回归测试它锁定了若干种点号误用场景下 UCM 应当呈现的错误文本。与 fix-4536 同属这一错误消息测试家族的转录文件还包括 generic-parse-errors.md、error-messages.md、fix5337.md、fix5349.md 等它们共同保证了错误提示的措辞不会在重构词法/语法器时悄然退化。关于 issue 4536 本身该转录文件以fix-4536命名表明其目标是固定某次针对 4536 号问题的修复行为。仓库中的源码与测试可确认这些错误消息的产生路径但原始 issue 的具体讨论内容不在本仓库内本文不加以臆测。二、五种点号误用场景逐一还原场景一use Nat .—— 运算符使用指令中的点号转录文件首先执行builtins.merge lib.builtin以准备标准库环境随后提交如下源码foo : Nat foo use Nat . 1 2UCM 的回应是Loading changes detected in scratch.u. I got confused here: 3 | use Nat . I was surprised to find a . here. I was expecting one of these instead: * bang * binding * do * false * force * handle * if * lambda * let * newline or semicolon * pattern * quote * termLink * true * tuple * typeLink关键教训use指令用于在当前作用域内引入某个命名空间中的名字其语法是use namespace name其中name应当是不带点号前缀的名字。Nat .在 Unison 词法层面被切分成Nat、.、三个 token——.是一个独立的符号 token而会被词法器当作一元符号数字的一部分处理见下文“tweak 后处理”。use之后紧跟着一个独立.token 完全超出了语法器的预期集合于是触发了TrivialError分支打印出上述完整的“意外 token 期望集合”清单。场景二namespace .foo—— 绝对路径写法出现在 namespace 指令后namespace .fooUCM 输出Loading changes detected in scratch.u. I got confused here: 1 | namespace .foo I was surprised to find a .foo here.关键教训namespace指令期望一个普通名称。.foo以点号开头词法器会将其识别为带“绝对路径”含义的 token.起头的名字在 Unison 中用于引用从根命名空间开始的绝对路径。但namespace指令的参数位置并不接受这种形式的绝对路径引用语法器对此 token 无任何预期直接给出“I was surprised to find a .foo here.”场景三unique[.foo] type Foo Foo—— unique 修饰符中的非法参数unique[.foo] type Foo FooUCM 输出Loading changes detected in scratch.u. I got confused here: 1 | unique[.foo] type Foo Foo I was surprised to find a .foo here.关键教训unique[ ... ]是 Unison 类型声明的修饰语法方括号内应填写唯一的类型标识名通常是一个带点的全限定名或普通的唯一标签用于保证类型身份的独特性而不是以.foo形式给出的绝对路径引用。语法器在[与]之间对 token 的种类有严格约束.foo不在可接受集合内。场景四.foo 17—— 顶层 watch 表达式写成绝对路径.foo 17UCM 输出Loading changes detected in scratch.u. This looks like the start of an expression here 1 | .foo 17 but at the file top-level, I expect one of the following: - A binding, like .foo 42 OR .foo : Nat .foo 42 - A watch expression, like .foo 1 - An ability declaration, like unique ability Foo where ... - A type declaration, like structural type Optional a None | Some a这是五种场景中最特殊的一种它不是词法层的TrivialError而是由语法层判定“这里看起来是表达式的开始”.foo的令语法器认为进入了 watch 表达式但该位置处于文件顶层。顶层只允许绑定、watch、ability、type 四类声明因此语法器抛出DidntExpectExpression错误并给出了非常具体的纠错建议列表绑定binding.foo 42或带类型签名的.foo : Nat/.foo 42watch 表达式 .foo 1注意 watch 的应写在表达式之前而不是跟在名字之后ability声明unique ability Foo where ...type声明structural type Optional a None | Some a。场景五foo. 17—— 名字后紧跟的歧义写法foo. 17UCM 输出Loading changes detected in scratch.u. I got confused here: 1 | foo. 17 I was surprised to find a foo. here. I was expecting one of these instead: * ability * bang * binding * do * false * force * handle * if * lambda * let * namespace * newline or semicolon * quote * termLink * true * tuple * type * typeLink * use关键教训foo.会被词法器作为一个整体 token 读取名字foo加后缀.而顶层并不接受这样的 token。注意此场景的期望集合与场景一不同这里额外包含ability、namespace、type、use等——因为该 token 位于语句的起始位置语法器在此位置能接受“声明类”开头而场景一中.出现在use指令内部期望集合则变成了表达式/模式类的起始 token。这种差异正是错误消息定位精度的体现。三、错误消息的来源词法层与语法层的分工fix-4536 展示的两类错误文本分别来自 Unison 解析管线的两个阶段1. “I got confused here: … I was surprised to find a X here.”词法/简单错误在 PrintError.hs 中UnexpectedTokens msg分支负责渲染“I got confused here:”加源码摘录加错误消息而 Lexer/Unison.hs 中的formatTrivialError则根据意外 token 集合生成两段文案意外 token 恰好一个时I was surprised to find article x here.其中article会依据 token 首字母是否为元音自动选择a/an所以既会出现 “a . here.”也会出现 “an end of input here.”意外 token 多个时改为I was surprised to find these:\n\n* ...列表期望 token 集合非空时追加I was expecting one of these instead:\n\n* ...列表。同一文件中的displayLexemeLexer/Unison.hs负责把内部 token 表示成人类可读文本例如Semi False显示为semicolon、Semi True显示为end of stanza、Close显示为end of section、Reserved r显示为r。这解释了错误列表中bang、do、handle、if、let、quote、true、false、tuple等关键字为何以不带引号的裸词出现——它们都是Reserved保留字 token。2. “This looks like the start of an expression here … but at the file top-level, I expect one of the following:”语法层专用错误PrintError.hs 中的DidntExpectExpression分支专门处理“顶层出现表达式”的情形它渲染“This looks like the start of an expression here”、用tokenAsErrorSite标注出错的 token然后固定列出绑定、watch、ability、type 四类顶层允许的结构含.foo 42、 .foo 1、unique ability Foo where ...、structural type Optional a None | Some a等示例。同时该分支还有一个特化版本PrintError.hs当意外 token 是::时会额外提示“Did you mean to use a single:here for a type signature?”可见错误消息是经过逐 token 打磨的。3. 点号与符号 token 的词法后处理Lexer/Unison.hs 中的tweak后处理函数说明了符号表达式为何容易被误解析11会被词法化为[1, 1]被并入符号数字tweak负责将其改写为[1, , 1]三个 token。这解释了场景一中Nat .若允许点号将造成何种歧义——.与的组合极易被词法器吞并成带符号的 token因此在use指令后直接写. 是不被允许的必须写成例如use Nat.这样的形式不带分隔空格或改用其它导入方式。四、错误消息的实战价值如何读懂与规避综合以上五种场景可以提炼出 Unison 中关于点号的一套“语法速查”上下文合法写法示例非法写法本文场景错误特征use导入指令use Nat.名字与运算符间无空格点号use Nat .词法错误意外.token期望集合为表达式起始 tokennamespace指令namespace foonamespace .foo词法错误意外.foounique[...]类型修饰unique[foo] type Foo Foounique[.foo] type Foo Foo词法错误意外.foowatch 表达式 .foo 1.foo 17语法错误顶层出现表达式文件顶层声明.foo 42或foo 42foo. 17词法错误意外foo.从这份对照表可以看出Unison 的词法设计让点号承担了“绝对路径引用”的语义因此名字内部的点号位置极其敏感它只能出现在名字的起始处如.foo表示根命名空间下的foo或作为合法的连接符出现不能随意出现在use参数、namespace参数、unique修饰符参数或名字与之间。当你在 UCM 中看到 “I was surprised to find a … here.” 时先检查目标 token 前后是否多写了点号或是否把 watch 的误放到了名字之后——这两类问题几乎覆盖了 fix-4536 中 80% 的场景。五、如何复现与验证fix-4536.md 是一份可自动运行的转录测试。要复现其中的全部错误消息只需在 UCM 环境中执行转录启动 UCM进入一个临时代码库执行builtins.merge lib.builtin对应转录文件第一段用于引入标准库使Nat可用依次在 scratch 文件中输入五种 Unison 源码或以unison命令提交观察 UCM 输出是否与转录文件中的:added-by-ucm块一致。转录文件的“黄金输出”保证了这些错误消息在后续版本迭代中保持稳定。若你有兴趣继续研究可在 unison-syntax/src/Unison/Syntax/Lexer/Unison.hs 中追踪 token 的产生与formatTrivialError的组装逻辑在 parser-typechecker/src/Unison/PrintError.hs 中对比UnexpectedTokens、TrivialError与DidntExpectExpression三种渲染分支并将结果与 generic-parse-errors.md、error-messages.md 等其他错误消息转录对照即可获得对 Unison 解析错误体系的全景认识。赞分享编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载相关推荐Unison 转录测试中的 ucm :hide 与 :error 标记解析器如何报告隐藏错误Unison 转录测试中的 ucm :hide 与 :error 标记解析器如何报告隐藏错误 导读 Unison 仓库用可执行的 Markdowntra编程语言编译器语言运行时开发工具Unison 转写测试中 :hide-all :error 隐藏错误机制源码解析从 ucm-hide-all-error.output.md 说起Unison 转写测试中 :hide all :error 隐藏错误机制源码解析从 ucm hide all error.output.md 说起 导读 本文编程语言编译器语言运行时开发工具Unison UCM 分支相对路径机制详解从 no-abspath-in-ucm 错误理解现代 UCM 路径解析Unison UCM 分支相对路径机制详解从 no abspath in ucm 错误理解现代 UCM 路径解析 本指南以仓库内转录测试 unison src编程语言编译器语言运行时开发工具上一篇如何快速使用Squirrel-RIFE免费AI视频补帧完整教程下一篇Python-100-DaysDjango 项目接入三方平台的完整实战指南短信网关与七牛云存储创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考