ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Unison 语言中 Term 声明禁止携带哈希限定名:语法规则、解析器实现与转写测试验证

Unison 语言中 Term 声明禁止携带哈希限定名:语法规则、解析器实现与转写测试验证 编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载本文以 Unison 开源仓库中的转写transcript测试文档no-hash-in-term-declaration.output.md为线索深入剖析 Unison 语言的一条核心语法约束Term项声明的名称——无论是类型签名还是类型定义中的名称——均不允许包含哈希限定符hash qualifier如x##Nat。读完本文你将理解这一规则的作用范围、它在词法/语法解析层的落地方式、违反规则时产生的错误行为以及如何通过仓库中的转写测试机制验证这条约束。一、规则速览Term 声明中不允许出现哈希unison-src/transcripts/no-hash-in-term-declaration.md其转写输出即本文关联文档 no-hash-in-term-declaration.output.md用一句话定义了该规则There should not be hashes in the names used in term declarations, either in the type signature or the type definition. Term 声明中所使用的名称不应包含哈希无论是在类型签名中还是在类型定义中。也就是说规则覆盖两种位置位置说明示例类型签名type signaturex : SomeType声明中的被声明名称x##Nat : Int - Int - Boolean类型定义/函数体type definition函数定义x ...左部head中的绑定名称x##Nat 5作为对照哈希限定名hash-qualified name在 Unison 的其他语法位置上是被正常支持的例如在表达式、类型引用中可以通过Name#hash的形式精确定位某个名称对应的特定 hash如引用base##Nat、.foo.bar#xyz。被禁止的只是在声明的位置上把名称写成带哈希的形式。二、转写测试如何验证这条规则仓库中的unison-src/transcripts/目录存放 Unison 的转写测试每个.md文件包含一段指令 Unison 代码的交互式脚本由转写框架执行后生成对应的.output.md期望输出文件。no-hash-in-term-declaration.md的完整内容为# No Hashes in Term Declarations There should not be hashes in the names used in term declarations, either in the type signature or the type definition. unison :hide-all :error x##Nat : Int - Int - Boolean x##Nat 5 这里的关键是指令标记:hide-all :error:hide-all表示该代码块中的所有输出包括错误信息都不写入转写输出文件:error则表明该代码块预期以出错的方式结束——即这段代码应当解析失败。因此对应的期望输出文件 no-hash-in-term-declaration.output.md 只保留了标题与规则说明两行没有任何代码或错误文本这正是代码因含哈希声明名而失败、且错误被隐藏的预期结果。如果未来某天 Unison 改变了该语法规则例如允许哈希声明名这段转写测试的.output.md将不再匹配实际输出测试即可立刻暴露行为变化。三、源码实现解析器为何拒绝哈希要理解x##Nat 5与x##Nat : Int - Int - Boolean为何失败需要沿 Unison 的词法 → 语法两层实现追查。3.1 词法层x##Nat可以被正常切分为哈希限定名首先需要说明x##Nat这样的写法在词法层面是合法的。在unison-syntax/src/Unison/Syntax/Lexer/Unison.hs中identifierP解析一个标识符后会通过P.optional shortHashP尝试读取可选的短哈希部分identifierP :: (Monad m) P.ParsecT (Token Err) String m (HQ.HashQualified Name) identifierP do P.label identifier (ex: abba1, snake_case, .foo.bar#xyz, .foo.#xyz, or ) do name - PI.withParsecT (fmap nameSegmentParseErrToErr) Name.nameP P.optional shortHashP \case Nothing - HQ.fromName name Just shorthash - HQ.HashQualified name shorthash即若标识符后面跟了#...短哈希就构造HQ.HashQualified name shorthash否则是纯名称HQ.fromName name。所以x##Nat会被词法器产出一个携带 hash 的HashQualified Name词元lexeme对应文档注释中的合法示例.foo.bar#xyz、.foo.#xyz。3.2 语法层prefixTermName明确拒绝带哈希的名称问题出在语法解析层。在unison-syntax/src/Unison/Syntax/Parser.hs中定义了两个行为不同的前缀标识符解析器-- | Parse a prefix identifier e.g. Foo or (), discarding any hash prefixDefinitionName :: (Var v) P v m (L.Token v) prefixDefinitionName wordyDefinitionName | parenthesize symbolyDefinitionName -- | Parse a prefix identifier e.g. Foo or (), rejecting any hash -- This is useful for term declarations, where type signatures and term names should not have hashes. prefixTermName :: (Var v) P v m (L.Token v) prefixTermName wordyTermName | parenthesize symbolyTermName where wordyTermName queryToken \case L.WordyId (HQ.NameOnly n) - Just $ Name.toVar n _ - Nothing symbolyTermName queryToken \case L.SymbolyId (HQ.NameOnly n) - Just $ Name.toVar n _ - Nothing源码注释直接点明了设计意图This is useful for term declarations, where type signatures and term names should not have hashes该解析器用于 term 声明其中类型签名与 term 名称不应含有哈希。其实现核心是模式匹配只有词元是HQ.NameOnly n即未携带哈希的纯名称时才接受一旦词元是HQ.HashQualified ...携带哈希两个分支都会落入_ - Nothing解析即失败。与之形成对照的是prefixDefinitionName/wordyDefinitionName后者通过Name.toVar (HQ.toName n)丢弃哈希而只取名称因此foo#abc在类型/构造器定义等位置是允许出现的-- | Parse a wordy identifier e.g. Foo, discarding any hash wordyDefinitionName :: (Var v) P v m (L.Token v) wordyDefinitionName queryToken \case L.WordyId n - Just $ Name.toVar (HQ.toName n) _ - Nothing3.3 两个声明位置的实际调用点prefixTermName在parser-typechecker/src/Unison/Syntax/TermParser.hs中被用于 term 声明的两类关键位置1类型签名typedecl——x : T形式的声明头部对应文档中x##Nat : Int - Int - Boolean的失败场景typedecl :: (Monad m, Var v) P v m (L.Token v, Type v Ann) typedecl (,) $ P.try (prefixTermName * reserved :) * TypeParser.valueType * semiprefixTermName * reserved :意味着签名左侧的名称必须是纯名称其后紧跟冒号。x##Nat因携带哈希而无法通过prefixTermName该声明解析失败。2函数定义左部definition head——对应文档中x##Nat 5的失败场景。在 term 定义解析时左部lhs由中缀形式与前缀形式组成let infixLhs do (arg1, op) - P.try $ (,) $ prefixDefinitionName * symbolyDefinitionName arg2 - prefixDefinitionName pure (ann arg1, op, [arg1, arg2]) let prefixLhs do v - prefixTermName vs - many prefixTermName pure (ann v, v, vs) let lhs :: P v m (Ann, L.Token v, [L.Token v]) lhs infixLhs | prefixLhs注意prefixLhs中绑定名v与全部函数参数vs都使用prefixTermName解析infixLhs中用于中缀操作符两侧操作数的prefixDefinitionName虽然会丢弃哈希但中缀形式要求第一个 token 为符号型标识符symbolyDefinitionName与x##Nat的词形也不匹配。因此x##Nat 5中定义头x##Nat无法作为合法的绑定名被接受。四、设计动机为什么声明名要禁止哈希从源码注释与周边设计可以推断这条约束服务于两个目的声明的确定性一个 term 声明是在引入一个新名称而哈希限定名表示指向某个已存在、特定 hash 的实体。允许在声明位置写x##Nat会造成语义混淆——究竟是定义新名称x还是断言x对应某个既有 hash与声明名称的其他约束一致声明名还有额外的合法性要求。例如parser-typechecker/src/Unison/Syntax/TermParser.hs中的verifyRelativeName会拒绝以.开头的绝对名称DisallowedAbsoluteName声明名称必须是相对名称且无哈希保证绑定名始终是干净、可被命名空间解析的名称。五、给使用者的实践要点在.u源文件中声明 term 时类型签名x : T与函数定义x ...中的x必须是纯名称可含命名空间段如foo.bar不要加#hash后缀。若确实需要引用某个特定 hash 的既有定义例如跨库重名消歧请把哈希限定名用在引用位置表达式中而不是声明位置。符号型标识符如()同样受此约束()##a : ...这类写法会被symbolyTermName的HQ.NameOnly匹配拒绝。想要回归验证该行为可直接运行仓库的转写测试no-hash-in-term-declaration.md使用:hide-all :error断言带哈希的 term 声明必然解析失败其期望输出保存在 no-hash-in-term-declaration.output.md与unison-src/transcripts/目录下的其他转写用例共同构成对 Unison 语法规则的自动化守护。六、小结Term 声明名称禁止携带哈希是 Unison 语法设计中的一条小而关键的铁律它由词法器identifierP支持产出哈希限定词元与语法器prefixTermName只接受NameOnly的配合实现覆盖类型签名typedecl与函数定义头prefixLhs/infixLhs两类位置并有对应的转写测试持续验证。对使用者而言记住一句话即可声明要干净引用可带哈希。赞分享编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载相关推荐Unison 语言中的哈希与 HMAC 内置函数crypto 模块 API 详解与测试实践Unison 语言中的哈希与 HMAC 内置函数crypto 模块 API 详解与测试实践 导读 Unison 是一门「来自未来」的编程语言其标准库在 cr编程语言编译器语言运行时开发工具Unison 字节字面量下划线分隔符完全指南语法规则、词法实现与测试验证Unison 字节字面量下划线分隔符完全指南语法规则、词法实现与测试验证 字节字面量bytes literal是 Unison 语言中表示 Bytes 类编程语言编译器语言运行时开发工具Action! 语言 ANTLR4 语法实现解析从语法规则、类型系统到示例验证Action! 语言 ANTLR4 语法实现解析从语法规则、类型系统到示例验证 导读 本文围绕 action/ https://link.gitcode.co编程语言编译器开发工具上一篇终极Citra模拟器使用指南如何完美运行3DS游戏的完整解决方案下一篇黑苹果配置革命OpCore-Simplify让OpenCore配置从8小时缩短到30分钟创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进