ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Carbon 语言逻辑运算符设计解析:and / or / not 的语法、优先级与实现

Carbon 语言逻辑运算符设计解析:and / or / not 的语法、优先级与实现 Carbon 语言逻辑运算符设计解析and / or / not 的语法、优先级与实现【免费下载链接】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本篇文章基于 Carbon Language 官方提案 p000680-and-or-not系统梳理 Carbon 在布尔逻辑运算上的核心设计决策为何选择and、or、not三个关键字而非、||、!这三者的优先级、结合性、类型转换与重载规则如何定义以及这些规则最终如何在 Carbon 工具链的词法、语法与语义检查阶段落地。读完本文你将理解该设计背后的工程权衡并能据此正确编写 Carbon 条件表达式避免常见的优先级与短路陷阱。问题背景Carbon 需要布尔逻辑运算布尔逻辑的与、或、非AND、OR、NOT是几乎所有编程语言处理条件判断的基础构件。Carbon 作为一门面向 C 迁移与互操作的新语言同样必须提供这三个运算符。提案在 Problem 一节明确指出逻辑 AND、OR、NOT 是使用 Boolean 值工作的重要积木Carbon 应当支持它们。不过支持并不等于照搬 C。围绕用什么拼写、优先级怎么定、短路语义如何实现提案展开了详细的背景调查与方案对比这正是本文要展开的核心内容。业界现状两种主流的写法流派提案在 Background 中梳理了主流编程语言的两种做法流派一、||、!源自 C与||最早由 C 语言引入其目的正是为了与普通位运算运算符区分开明确表达短路求值short-circuiting行为。如今 C、C、C#、Swift、Rust、Haskell 等大量语言都采用这套写法且、||一律短路求值。流派二and、or、not偏重可读性与脚本风格Python、Pascal、Nim、SQL 以及各种 BASIC 变体常使用关键字拼写。短路行为因语言而异Python 的and/or短路Pascal 与 Visual Basic 本身不短路但通过and then/or elsePascal与AndAlso/OrElseVisual Basic提供短路版本。C 直接把and、or、not识别为关键字作为、||、!的词法同义词C 语言则通过标准头文件iso646.h以宏形式提供同样的拼写。Perl、Ruby、Raku 两种写法都支持其中标点形式优先级更高、关键字形式优先级更低两者都短路Raku 还提供andthen/orelse区别在于短路时产出的值不同而非是否短路。Zig 提供and、or与!的混合组合。标点运算符的已知痛点提案特别列举了标点写法带来的真实风险这也是 Carbon 最终转向关键字拼写的重要动因与、||与|极易混淆当逻辑运算符与位运算符并存时手误将写成是常见错误来源。业界有明确的安全规则如 CERT 规则 EXP46-C 建议不要在布尔型操作数上使用位运算符提案还引用了 2021 年一起真实事故ChromiumOS 因/手误导致登录功能故障。!难以辨认有轶事证据表明在某些上下文里尤其紧邻(、I、l、1等形状相近字符时部分读者很难看清!这个字符。提案核心三个关键字形式的逻辑运算符提案 Proposal 给出的方案非常简洁Carbon 提供三个操作符用于 Boolean 值的逻辑运算and提供短路的逻辑与logical AND运算。or提供短路的逻辑或logical OR运算。not提供逻辑非logical NOT运算。其中and与or是中缀二元运算符not是前缀一元运算符。值得注意的一个细节是这三个拼写在 C 中本就是含义相同的关键字Carbon 采用它们不会与合法 C 标识符发生冲突因而允许开发者在既有 C 代码库中提前采用 Carbon 风格语法提案当时还配套提供了将该写法应用到 Carbon 项目 C 代码中的演示性 PR。细节设计优先级、结合性、转换与重载提案的 Details 一节给出了四个维度的精确规则。优先级极低且 and / or 之间没有优先级关系and、or、not的优先级非常低。当一个表达式作为if条件且不加括号地使用这些运算符时它们总是该表达式中优先级最低的运算符。任何可能用于构造布尔值的合理运算符都可作为其子表达式特别是比较运算符如、优先级高于所有逻辑运算符。not可以出现在and/or内部但and与or不能在没有括号的情况下互相直接嵌套。官方给出的示例与等价加括号形式if (n m 3 and not n m) {等价于if (((n m) 3) and (not (n m))) {而下述两种写法都是错误的必须加括号if (cond1 not cond2) { // ... if (cond1 and cond2 or cond3) {这里的关键设计点是and与or之间不建立任何优先级关系。主流语言普遍规定高于||见下文备选方案分析但提案认为该规则在几十年间跨越多种语言仍未被相当比例的开发者可靠掌握因此决定干脆不定义这条优先级边强制开发者用括号表达意图。结合性左结合not不可重复and与or都是左结合的。not表达式不能作为另一个not表达式的操作数——not not b不加括号即为错误。官方示例// OK if (not a and not b and not c) { ... } if (not (not a)) { ... } // Error if (not a or not b and not c) { ... } if (not not a) { ... }注意not a or not b and not c报错正是因为它同时混用了and与or没有括号而触发无优先级关系必须加括号的规则。类型转换与 if 条件完全一致and、or、not的操作数会按照与if条件相同的方式转换为 Boolean 值。具体含义是如果某些值如指针、整数不能直接作为if条件使用必须显式与 null 或零比较那么它们同样不能直接作为and、or、not的操作数。如果未来提供了决定如何对某个值进行真值分支的扩展点例如提供到布尔类型的转换该扩展点对and、or、not同样生效。换言之这三者的真值判定规则与if严格绑定杜绝了条件判断一套规则、逻辑运算另一套规则的分裂。重载不可重载逻辑运算符and、or、not不可重载。正如上文所述任何允许类型定制if行为如operator bool转换的机制都会同样作用于and、or、not因此无需也不应提供独立的运算符重载入口。设计论证基于 Carbon 项目目标提案在 Rationale based on Carbons goals 一节把上述每条设计决定都映射到了 Carbon 的两个核心目标上。代码易读、易理解、易编写用and/or替代/||规避了与的视觉混淆。用not替代!规避了小号标点字符难以察觉的问题。and与or之间不设优先级强制用括号表达组合避免了两者混用时的可读性灾难。关键字而非标点的形式强调了这些运算符不是普通运算而是带有控制流语义短路的构造。not与and、or保持相同优先级便于视觉上快速扫描条件整体结构、识别嵌套层级。与既有 C 代码的互操作与迁移and、or、not在 C 中本就是含义相同的关键字因此 Carbon 关键字不会与合法 C 标识符冲突还允许在 C 代码库中提前采用 Carbon 语法。虽然这三个运算符在 C 中可重载但、||几乎是重载率最低的运算符!也通常只是被当作转bool的迂回手段。目前没有已知需求要求 Carbon 代码调用 C 重载的operator/operator||/operator!。至于if、and、or、not调用 C 中可能为explicit的operator bool的机制提案明确表示属于范围之外的工作。备选方案与取舍分析提案记录了六个被认真考虑过的备选方案理解它们能更深入地把握最终设计的边界。备选一三个运算符全部使用标点拼写即沿用 C 传统使用、||、!。优点对熟悉这套写法的开发者更亲切。缺点① 关键字能提示and/or除计算外还影响控制流②!对部分读者难以看清③ 与、|并存时有混淆风险④ 若未来要改优先级规则不同拼写可提醒行为与不同⑤ 在多数英文键盘布局上/||/!需要按 Shift 键并离开字母键区比字母拼写更难敲。备选二为 AND 与 OR 建立优先级多数语言规定 AND 高于 ORif (a b || c d) { ... } // ... 等价于 ... if ((a b) || (c d)) { ... }这一规则可从布尔代数角度解释相当于乘法、||相当于加法乘法通常结合得更紧。但提案引用 运算符优先级提案 #555 中何时添加优先级边的判定标准指出尽管这条规则在多种语言中存在数十年仍有相当比例开发者不能可靠掌握业界甚至普遍建议开启编译器警告要求改写为显式括号形式——因此 Carbon 选择不添加这条优先级边。备选三给 NOT 高优先级可模仿 C 让not高优先级但会带来不便var x: Bool cond1 not cond2; // 本提案中非法需加括号以及一个隐蔽的等价关系var y: Bool not cond1 cond2; // 等价于 var y: Bool cond1 ! cond2; // 可能并非开发者本意不过给not高优先级会得到更复杂的规则并破坏and、or、not三者间的对称性。备选四NOT 使用标点!not在操作数含括号嵌套内部又有and/or时可能不如!易读且没有!运算符会让!的拼写缺少一致性论据。但反方理由更充分破坏三者对称Python 就用这套关键字且同样有!实践上未见混淆not在部分场景更易读、更易从上下文跳出!已在泛型领域被用于表示早期替换避免同一语法承担多重职责函数式的not (...)比!(...)更符合读者预期。备选五两种 NOT 并存同时提供低优先级not和高优先级!似乎两头都占。但代价是两种形式必有一种被闲置或需要额外风格规则指导何时用哪个而且只提供!却不提供、||会显得突兀。备选六允许重复 NOT允许not not x作为把x转成 Boolean 的惯用法。但提案认为将来会有更清晰的转换语法如x as Bool届时not not x反而会成为反模式甚至可能暗示多敲了一个 not的 bug。依据 运算符优先级提案 #555 的存疑时不加规则、等待真实世界经验原则Carbon 最终让not not b成为必须加括号的错误。备选七AND/OR 产出决定性值即让and/or返回未转换的决定结果的那个值而非 Boolean。例如对可空指针p、qp or q在p非空时产出p否则产出q。这是 Python、Perl、Raku 的做法也是 C 的std::conjunction/std::disjunction的做法。优点是可语法化地求列表中的第一个 truthy 值或最后一个 falsy 值缺点同样明显结果传入函数调用时更难读懂、要求链式条件各分支有公共类型否则要兜底规则、与类型推导结合时容易推出意外类型如var x: auto p and q;推导出指针类型而非 Boolean。Carbon 因此坚持产出 Boolean 值。在 Carbon 工具链中的落地实现回到当前仓库源码可以看到上述设计已经在工具链中完整落地这为文档中的规则提供了实现级佐证。词法层三个关键字 tokenand、or、not在词法阶段被注册为关键字。在 token_kind.def 中可以看到CARBON_KEYWORD_TOKEN(And, and)第 177 行、CARBON_KEYWORD_TOKEN(Not, not)第 208 行与CARBON_KEYWORD_TOKEN(Or, or)第 211 行。同时符号表中保留着!Exclaim与!ExclaimEqual等符号 token但!在 precedence.cpp 中被明确列为将来可能是运算符的符号而非当前逻辑非运算符——与提案避免!承载多重职责的意图一致。语法层优先级与结合性的精确建模优先级规则的核心实现在 precedence.cpp第 32 行MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr})明确声明比较运算符Relational与逻辑前缀LogicalPrefix即not优先级高于逻辑与/逻辑或正是提案中比较运算符高于所有逻辑运算符与not可用于and/or内部的代码化表达。not在前缀解析中映射为LogicalPrefix第 154-155 行and、or在中缀解析中分别映射为LogicalAnd、LogicalOr且is_binary true第 200-203 行。结合性方面第 120-123 行把LogicalAnd、LogicalOr的对角线填充为LeftFirst左结合而LogicalAnd与LogicalOr之间、以及LogicalPrefix自身的对角线没有填充对应第 126-127 行注释对于其他运算符要求显式括号。这一实现与提案规则一一对应a and b and c合法且左结合a and b or c报错要求加括号not not a报错要求加括号。语法测试文件也固化了这些行为例如fail_precedence_and_or.carbona and b or c;触发OperatorRequiresParentheses诊断并给出了完整语法树ShortCircuitOperatorAnd与ShortCircuitOperatorOr节点均带has_error。associative.carbona and b and c;被解析为左结合结构(a and b) and c。同目录下还有对称的fail_precedence_or_and.carbon等测试覆盖or/and反向混用、as转换、赋值等场景的优先级边界。语义层短路求值如何实现短路语义在语义检查阶段的 handle_operator.cpp 中实现HandleShortCircuitOperand第 407 行起为短路操作数建立基本块并通过AddDominatedBlockAndBranchIf生成条件分支这从实现上印证了and/or是带控制流语义的构造而非普通函数调用。HandleShortCircuitOperator第 459 行起负责合并分支结果对and与or共用同一套处理逻辑仅通过is_or参数区分。not则生成SemIR::UnaryOperatorNot指令第 331 行作为一元运算符直接求值。从源码结构可以推断and/or的短路通过显式支配块dominated block与分支指令完成左操作数先求值据此决定是否继续求值右操作数最终汇合点产出 Boolean 值。这与提案操作数按if条件方式转换为 Boolean的规定共同保证了语义一致性。结语Carbon 对逻辑运算的设计是一份少即是多的范本用and/or/not三个关键字换取可读性与 C 互操作的便利用不定义and/or之间的优先级换取无歧义的表达式用与if共享转换规则、不可重载换取统一的真值语义。如果你想亲手验证这些规则可以直接阅读并运行 toolchain/parse/testdata/operators/ 目录下的语法测试例如bazel test //toolchain/testing:file_test --test_arg--file_teststoolchain/parse/testdata/operators/fail_precedence_and_or.carbon或在语义检查源码中追踪ShortCircuit相关处理逻辑体验一份提案从纸面规则到编译器实现的完整闭环。【免费下载链接】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),仅供参考
返回列表