Carbon 语言注释设计解析从 p000198 提案到//词法规则的落地实现【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-langCarbon Language实验性语言的注释语法设计提案p000198-comments.md定义了语言唯一的注释形式以//开头、一直延伸到行尾、且必须独占一行的整行注释。本文以此提案为骨架结合toolchain/lex/lex.cpp词法分析器源码与toolchain/lex/testdata/测试用例逐条解析注释的语法规则、设计动机、被否决的替代方案以及提案中保留注释语法空间这一前瞻性决策在实现中的真实体现。一、提案背景注释要解决什么问题在语言设计早期2020 年Pull Request #198Carbon 团队便提交了这份为注释提供具体词法语法建议的提案。它首先梳理了注释在现有编程语言中承担的四类核心用途这些用例构成了后续一切语法取舍的评判基准文档Documentation向 API 的使用者与未来维护者解释函数行为与用法通常附着在函数声明、类定义、公有成员声明与文件级作用域上。C 中常见///风格的 Doxygen 注释。实现注释Implementation comments向读者或维护者解释代码意图与机制或概括代码行为通常较短用于代码本身不易自明之处。语法消歧注释Syntactic disambiguation comments包含代码或伪代码帮助人类读者像编译器一样解析代码典型如 C 的f(/*repaint_all*/true)、/*static*/前缀、} // end namespace结尾标记。禁用代码Disabled code注释掉不完整、错误、调试中或作为进行中变更参考的代码区域通常被认为不应检入版本控制。提案的结论是先做减法仅保留一种注释且刻意不覆盖文档与语法消歧两类用例后者留待语言语法本身来解决。二、C 的三种注释实践作为对照背景提案以 C 的现实做法作为参照系梳理出三种事实上的注释表达方式并逐一分析其词法特性为后续决策提供背景1. 行注释// ...又称 BCPL comments可出现在任意位置行首或记号之后可包含除换行符外的任意文本在逻辑行末尾结束可用行尾\续行C14 及更早还支持??/三字符序列与普通语法无歧义//内部再出现//无效果天然嵌套但与其他注释种类不嵌套常被用于文档注释Doxygen 风格///与实现注释。2. 块注释/* ... */可出现在任意位置包含除*/外的任意文本在*/定界符处结束不嵌套——第一个*/即结束注释与普通语法存在歧义如ca/*b;这类除法与注释边界纠缠的经典案例实践中问题不大常用于语法消歧注释有时也用于禁用代码部分编码风格将其用于长文档Doxygen 风格/**。3.#if 0 ... #endif预处理禁用代码只能出现在逻辑行行首只能包含预处理记号序列允许等无效记号但不允许未终止的多行字符串字面量在匹配的#endif处结束与其他任何语法无歧义支持正确嵌套内部可再嵌其他注释基本只用于禁用代码。提案对这三种做法的态度是//行注释足够简单可靠应作为基础/* ... */与#if 0各自的问题不嵌套、依赖预处理器、对残缺代码不友好使其不适合作为 Carbon 的禁用代码方案。三、核心提案唯一一种注释//到行尾提案给出的注释语法规则极为克制可用如下要点概括注释以//开头并延伸到行尾实验性规则同一行内//之前不允许有任何非水平空白文本——一行要么全是注释要么全不是//之后必须紧跟空白字符换行符也是空白因此单独一行//是合法注释文件末尾视同空白没有物理行续行机制行尾\不会把注释延伸到下一行所有注释在形成记号token之前即被移除。提案中的示例// This is a comment and is ignored. \ This is not a comment. var Int: x; // error, trailing comments not allowed第一行中\之后的This is not a comment.是真正的代码而不是注释第二行的var Int: x;后的行尾注释则是语法错误。提案明确指出这套语法面向实现注释与实验性的禁用代码两类用例文档注释不被覆盖意图由未来某个独立的非注释机制解决语法消歧注释也不被覆盖意图是通过设计语言语法本身来避免这类用例的出现。四、深入实现toolchain/lex/lex.cpp中的注释词法分析提案在 Carbon 语言工具链中落地于词法分析器 toolchain/lex/lex.cpp核心函数为LexCommentOrSlash与LexCommentlex.cpp#L896-L1162。分派入口/的 max-munch 消歧LexCommentOrSlash是所有以/开头的输入的统一入口由于注释与除号都以/开始词法器采用**最长匹配max-munch**规则消歧——如果当前字符后的下一个字符也是/则按注释处理否则按斜杠符号除号等词法化。从源码看注释分支被显式优化为更热的路径注释在现实中远比除号常见// Both comments and slash symbols start with a /. We disambiguate with a // max-munch rule -- if the next character is another / then we lex it as // a comment start. If it isnt, then we lex as a slash. ... if (LLVM_LIKELY(position 1 static_castssize_t(source_text.size()) source_text[position 1] /)) { LexComment(source_text, position); return; }注释的两种形态整行注释与行尾注释实现中的LexComment首先用行首缩进信息判断注释是否为行尾注释trailingis_trailing position ! line_info.start line_info.indent即注释是否位于该行第一个非空白字符处。值得注意的是当前实现允许行尾注释只要//后紧跟空白例如var a: i32 1; // A trailing comment可以正常词法化提案中行内不允许其他代码是标记为experimental的实验性规则这正是从源码中可以观察到的设计决策与当前实现的差异。无论哪种形态注释都不产生 token而是通过buffer_.AddComment(...)记录到注释缓冲中记录缩进、起始位置、结束位置与is_trailing标志使得注释与记号共同构成完整的源码重建信息供格式化器等工具使用。//后必须跟空白NoWhitespaceAfterCommentIntroducer诊断实现严格兑现了//后必须为空白的规则当//后的字符不是空白时发出NoWhitespaceAfterCommentIntroducer错误提示whitespace is required after //。对应的测试用例位于 toolchain/lex/testdata/fail_bad_comment_introducers.carbon其中//abc、//indented等均被诊断为错误。性能优化整行注释块批量跳过由于整行注释尤其是同一缩进、连续多行的注释块是源码中最常见的模式实现对其做了专门的批量跳过优化缩进不超过 13MaxIndent时启用 SIMD 向量化前缀比较ARM NEON / x86 SSE逐行比对注释前缀以整块跳过非 SIMD 路径使用memcmp逐行比较prefix_size缩进 //后内容长度无效注释行//后无空白被合并为一个块、按行逐个处理使诊断噪音收敛为每个连续块一次。这些细节表明注释虽不产 token但词法分析器对其投入了与 token 同等量级的性能关注。//指令注释实现中扩展的保留语法空间提案提到//后不跟空白的注释为未来扩展保留当前实现已在这个保留空间内落地了工具指令//include-in-dumps、//dump-sem-ir-begin、//dump-sem-ir-end三个//...全行指令被词法器识别分别控制调试转储时是否包含注释行、以及 SemIR 转储区间的起止BeginDumpSemIRRange/EndDumpSemIRRange。它们同时作为注释被记录保证格式化等工具不会静默丢弃这些指令行。这是保留注释语法空间这一设计决策最直接的实现例证。行尾注释的完整词法行为包括var c: i32 3;// no space这种//紧贴记号、下一行首 token 仍保持has_leading_space语义的边界情况可在测试 toolchain/lex/testdata/trailing_comments.carbon 中逐条核对。五、为什么不支持块注释Block comments rationale提案明确不提供块注释注释掉大片文字或代码的方式是逐行加//。其理由分三层对实现注释用例价值不大预期此类注释通常较短且 C 代码库中长实现注释也习惯用行注释而非块注释现有块注释语法不适合禁用代码场景/* ... */不嵌套且会被//注释或字符串字面量里的*/提前终止无法可靠注释掉任意代码块#if 0 ... #endif依赖预处理器Carbon 总体上不打算引入预处理器且要求其间文本基本是合法记号序列无法容纳不完整代码不应发明新语法为禁用代码这种临时、罕见的需求引入新词法成本高昂也不宜用既有语法承载新语义如会词法化内容的/* ... */以免让 C 开发者感到意外。结论是禁用代码场景用逐行//实现行内禁用时重排或复制该行虽然繁琐但足以作为试验——如果由此产生的摩擦确实需要一种新注释形式再考虑引入。这是一种以摩擦探测需求的刻意设计。六、保留注释空间Reserved comments 的远见规则的另一半是保留注释Reserved comments//后不跟空白的注释形式为未来扩展保留预期可能的扩展包括块注释、文档注释、代码折叠区域标记。理由是在注释语法中预留一段程序易于避免的语法空间未来新增注释种类时可以作为非破坏性变更non-breaking change引入。这在实现中体现为两部分其一//后非空白直接报错NoWhitespaceAfterCommentIntroducer这正是保留空间的强制护栏其二//指令已在保留空间内先行使用见上文第四节。也正因如此C 开发者熟知的///Doxygen 文档注释、//!等风格在 Carbon 中目前都是非法注释引入符。七、被否决的替代方案每个决定背后的权衡提案用大量篇幅记录了讨论中被否决的备选方案这是理解该设计的关键材料。1. 行内注释Intra-line comments包括两类能力一是类似 C 的render(/*use_world_coords*/true)式参数注解——提案认为这类需求应由语言语法扩展命名参数、注解语法解决让注解成为对程序员和工具都有意义的代码而非注释二是行尾注释int n; // number of hats、} // end namespace N。提案分析道除 end namespace 外行尾注释大多可以移动到声明之前而 end namespace 注释本质是语法消歧用例应通过语法设计解决——例如不提供描述大型作用域命名空间、包内容的定界作用域语法示例给出了声明式命名空间的未来方向// This declares the namespace N but does not open a scope. namespace N; // This declares a member of namespace N. Number of hats. var Int: N.n; enum N.Mode { First mode. mode1; Second mode. mode2; }此外行内注释对代码格式化工具构成巨大挑战工具必须理解注释附着于语法树的哪一部分才能正确重排。提案讨论了方向标记//...//等设想方案并展示了对齐行尾注释在重命名、折行时的复杂连锁反应var Int: quality 3; // The quality of the widget. It should always // be between 1 and 9. var Int: blueness 72; // The blueness of the widget, as a percentage.不支持行尾与行内注释被标记为experimental并明确写出如果完整语言设计中确有此需求应当重新审视。2. 多行文本注释Multi-line text comments不支持意图是每行都重复//标记。理由消除非局部状态读者无需回看注释起点、消除风格变体来源且这种风格在其他语言与编辑器中普遍受支持即便 C/C 用/* ... */注释文本块续行也习惯以*开头。3. 块注释Block comments的多种设想围绕注释掉大段可能不规范的 Carbon 代码且需要嵌套这一目标提案列举了四种候选方案及各自缺陷完全行导向的块注释无差别移除整行甚至允许注释掉块字符串字面量的一部分——缺点是在含 Carbon 代码的字符串字面量内行为出人意料完全词法化的块注释类似#if 0 ... #endif产生并丢弃其间记号序列、放宽词法规则——缺点是无法处理未终止的块字符串字面量等不完整片段处理效率也略低混合方案//\{与//\}定界符——缺点是词法规则需区分字符串字面量种类增加复杂度复用/*与*/与 C/C 语法相似但语义分叉易引发混淆。最终基于用例有限 减少独创性的原则上述方案均未采纳。4. 文档注释Documentation comments讨论中多数意见支持采用不像注释的语法表达文档例如属性语法 expression前置到声明上用字符串字面量属性承载文档Get the size of the thing. fn GetThingSize() - Int; Rate the quality of the widget. Returns a quality factor between 0.0 and 1.0. fn RateQuality( The widget to rate. Widget: w, A widget quality database. QualityDB: db) - Float;该方向留给未来提案继续探索该设想后来在docs/spec/lang/lex.md所涵盖的词法与属性设计中继续演进。5. 代码折叠注释Code folding commentsVS Code 通过含#region/#endregion的注释行支持自定义折叠区域// #region Functions F and G fn f() { ... } fn g() { ... } // #endregion此类标记作为普通文本放入行注释无需额外工作是否引入 Carbon 专属的折叠区域语法以统一各编辑器表现提案明确不在本提案范围内可交由新注释形式处理。八、Rationale设计动机总览提案以五条理由收束直接呼应 Carbon 的项目目标详见 docs/project/goals.md软件演化某种注释语法是代码可读可维护、支持演化的必要条件易读易写、工具友好单一、简单、一致的注释风格支持 Carbon 易于阅读理解、开发工具快速的目标软件演化实验性规则把注释限制为行内唯一非空白文本正是为演化服务的实验语言演化刻意留出的开放词法空间支持语言演进C 互操作沿用//作为主要注释标记避免与 C 背景的程序员和代码库产生不必要、无益的语法变动。九、结语从提案到工具链的闭环p000198提案为 Carbon 的注释设计确立了清晰的取舍哲学少即是多——只保留一种注释形式把文档交给独立机制、把消歧交给语法、把禁用代码交给逐行注释同时用保留空间为未来留出非破坏性扩展通道。而 toolchain/lex/lex.cpp 的实现忠实承载了这套设计max-munch 分派、//后强制空白、注释不产 token、整行注释块的 SIMD 批量跳过以及//指令对保留空间的实际占用都让这份提案从纸上设计变成了可运行、可测试、可性能优化的语言事实。对读者而言理解这份提案的意义在于注释看似是语言里最不起眼的细节但正是这类细节的选择决定了一门语言在可读性、工具链复杂度与长期演化空间上的走向。延伸阅读本提案位于 proposals/p000198-comments.md更多早期语言设计决策见 proposals 目录如 p000162-basic-syntax.md基础语法与 p000601-operator-tokens.md运算符记号词法与记号体系的最新规范见 docs/spec/lang/lex.md。当前实现的行为细节可在 toolchain/lex/testdata 的 file_test 用例中验证例如运行bazel test //toolchain/testing:file_test --test_arg--file_teststoolchain/lex/testdata/fail_bad_comment_introducers.carbon。【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考