ARTICLE DETAIL

资讯详情

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

Carbon 语言数字字面量详解:整数、实数、进制前缀与数字分隔符的完整词法规范

Carbon 语言数字字面量详解:整数、实数、进制前缀与数字分隔符的完整词法规范 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导读本文以 Carbon Language 官方设计文档 docs/design/lexical_conventions/numeric_literals.md 为骨架系统讲解 Carbon 语言中数字字面量numeric literal的完整词法规范整数与实数两种形式的语法、进制前缀、指数标记、大小写敏感性、数字分隔符规则以及与 C 等语言的关键差异。同时结合仓库中词法分析器lexer的真实实现toolchain/lex/numeric_literal.cpp与测试用例从「设计规范」与「实现机制」两个层面交叉印证。读完本文你将能够准确写出合法、可读的 Carbon 数字字面量理解0x1.2p123这类十六进制浮点字面量的取值原理并掌握词法分析器在错误恢复、元组索引消歧等边界情况下的处理策略。一、总览Carbon 支持的字面量语法Carbon 的数字字面量分为两大类外加一种通用的分隔符机制全部列举如下整数字面量Integer literals12345十进制0x1FE十六进制0o755八进制0b1010二进制实数字面量Real-number literals123.456.两侧都必须有数字123.456e789可选在e后跟或-0x1.2p123可选在p后跟或-数字分隔符Digit separators_三条贯穿始终的总体规则值得先记住实数字面量必须包含.且两侧都有数字整数字面量绝不能包含.——这是两类字面量在形态上最根本的区分。字面量是大小写敏感的case-sensitive。与 C 不同Carbon 的数字字面量没有类型后缀如L、ULL、f字面量本身只表达数值类型由上下文决定。设计动机可追溯至 goals.md 中「代码易读、易理解、易书写」的目标_分隔符之外的语法刻意保持精简减少程序员不必要的选择简化语法规则提升 Carbon 代码的一致性。二、整数字面量进制与大小写规则2.1 十进制整数十进制整数写作「一个非零十进制数字后跟零个或多个十进制数字」或者单独一个0。也就是说0本身合法但0之后不能再跟其它数字。2.2 非十进制整数前缀 基数其它进制的整数写作「0 进制指示字符 一个或多个对应进制的数字」。可用进制与允许的数字字符如下进制指示符基数允许的数字b20和1o80...7x160...9、A...F上表大小写敏感0b1、0o7、0x1A合法而0B1、0O7、0X1A、0x1a全部非法十六进制数字只允许大写A~F。2.3 前导零规则0755式 C 风格八进制在 Carbon 中非法字面量开头的0之后永远不能再跟数字要么字面量就是0要么0是进制前缀的开头要么下一个字符是小数点见实数部分。八进制必须显式写作0o前缀0755这类 C 风格八进制在 Carbon 中是非法的。2.4 整数类型的多样性超出 64 位也没问题从实现看整数字面量的值存储为变宽的无符号整数llvm::APIntnumeric_literal.h 中的IntValue::value。因此超长整数是支持的词法测试 numeric_literal_test.cpp 中包含了12345678901234567890123456789012345678901234567890123456789这类 59 位十进制数的用例而 file test numeric_literals.carbon 也验证了它被正确 lex 为IntLiteral且保留完整 spelling 与 value。解析性能同样被实现层关注十进制数按每 19 位为一组uint64_t能容纳的最大十进制位数分批解析再通过乘10^19累加避免逐位跨存储单元的算术numeric_literal.cpp 中ParseDecimal的注释解释了这一优化。同时词法器拒绝过长的数字如 2 万个1会触发TooManyDigits错误numeric_literal_test.cpp。三、实数字面量.两侧必须有数字3.1 基本形式与「两侧必须有数字」实数写作「十进制或十六进制整数 . 一个或多个对应进制的数字」。.的每一侧都必须至少有一个数字0.和.3都不是完整的实数字面量。例如0.(Util.Abs)()与tuple.3中.都被当作成员访问或元素访问而不是小数点。3.2 为支持元组索引而设的关键规则为了支持元组索引tuple indexing当一个实数字面量紧跟在一个.token 之后中间无空白时绝不会形成实数字面量结果是一个整数字面量。这一点在词法器中有专门实现CanFormRealLiteral()检查字面量前一个 token 是否为Period.或MinusGreater-若是则不允许形成实数lex.cpp。对应的 file test 非常直观repeated_tuple_index.carbonx.0.1.2.3.foo.4.5被拆成Identifier x 一系列PeriodIntLiteral的组合每个数字都是独立整数 token1.2.3.4中只有第一个1.2是RealLiteral后续.3、.4均为「点 整数」tuple . 1.2.前有空白时1.2才能正常成为RealLiteral——因为.不再紧邻前文不会与成员访问混淆。3.3 指数exponent部分实数后面可以跟随一个指数标记其语法为指数标记 [ | - ] 十进制整数 _N_十进制实数用e效果是将数值乘以10±_N_。十六进制实数用p效果是将数值乘以2±_N_。/-均可选缺省视为。指数后缀对十进制、十六进制实数都是可选的。两个容易踩坑的细节十进制整数后跟e不构成实数。例如3e10不是合法字面量——实数必须有.。二进制/八进制实数不支持0b1.0、0o1.0会触发诊断「binary/octal real number literals are not supported」[InvalidRealLiteralRadix]但词法器仍会继续解析以做错误恢复见 numeric_literals.carbon 与 numeric_literal.cpp。3.4 十进制分数表达的完备性十进制实数的语法允许表达任意十进制分数即形如a× 10-b的数a为整数b为非负整数。由于十进制分数在实数中是稠密的而实数类型的值集合假定是离散的因此每一个实数类型的值都能用一个十进制实数字面量表达。不过对某些应用而言直接表达期望的实数表示可能比先写一个「已知能转换到目标值」的十进制等价形式更方便。十六进制实数字面量正是为此提供它允许二进制浮点或定点实数类型的值被直接表达。3.5 取值的舍入规则当一个实数字面量被解释为某个实数类型的值时其值取最接近字面量值的可表示实数若出现平局tie则选择尾数mantissa为偶数的那个最近值——即经典的 round-to-even银行家舍入。该行为由提案 p000866-allow-ties-in-floating-literals.md 决定。3.6 内部表示尾数 × 基数^指数实现层面实数被规范化为「尾数mantissa 指数exponent」二元组RealValue结构体包含指数基数Binary或Decimal、变宽无符号尾数llvm::APInt、变宽有符号指数llvm::APIntnumeric_literal.h。归一化时小数部分的每一位都会折算进指数十进制每位减 1十六进制每位减 4八进制每位减 3numeric_literal.cpp。测试数据可以清楚看到这种内部表示numeric_literal_test.cpp 与 numeric_literals.carbon 中的 value 输出字面量内部表示mantissa×10^exp0.99*10^-18.080*10^-11.0e710*10^61.0e-810*10^-912.345e612345*10^31_234.5e-212345*10^-30x1_2345_6789.CDEF尾数0x123456789CDEF指数-160x0.0001p-4尾数1指数-20其中12.345e6的换算值得演示先把字面量看成尾数12345而小数点后 3 位使初始指数为-3加上显式指数6得到12345×10^(6-3)12345×10^3与实现中GetExponent()的算法完全一致numeric_literal.cpp。极端的指数边界精确等于INT64_MIN、或先超出再因小数点折算回合法范围也有专门测试覆盖见 numeric_literal_test.cpp 与HandlesRealLiteralOverflow用例numeric_literal_test.cpp。四、数字分隔符_位置任意但受限一个数字分隔符_可以出现在一个数字字面量中任意两个数字之间。官方示例十进制整数1_23_456_7890十六进制整数0x7_F_FF_FFFF八进制整数0o7_55二进制整数0b1_000_101_11实数字面量2_147.48_3648e12_345、0x1_00CA.FE_F00Dp2_4注意分隔符可以出现在整数部分、小数部分甚至指数部分之中规则始终是「位于两个数字之间」。4.1 实现中的合法/非法判定实现层CheckDigitSequencenumeric_literal.cpp对分隔符做了精确校验分隔符不能出现在数字序列的开头、结尾也不能与另一个分隔符相邻__连续两个下划线非法。对于小数部分分隔符被明确禁用CheckFractionalPart传入allow_digit_separatorsfalsenumeric_literal.cpp。单元测试给出了完整矩阵numeric_literal_test.cpp合法1_234、123_456、1_234_567、12_34、123_4_6_789、0x1_0000、0b1_0_1_0_1_0、0o1_234等——分组并不强制按 3 位或 4 位位置相当自由。非法仍产生 token但伴随诊断12__345、1_、0x_1234、0x123_、0b_10101、0b1__01、0b1011_、0o_1234、0o123_、0o1_2__34。file test 进一步展示了诊断输出numeric_literals.carbon1__2→misplaced digit separator in numeric literal但错误恢复后仍产出值为12的IntLiteral1.0000_0000、1.000_→ 小数部分的分隔符报同样的错但实数 token 依旧生成100000000*10^-9、1000*10^-4。这体现了词法层的错误恢复策略非法分隔符位置会被诊断但字面量仍尽量按去除了_后的语义继续解析。历史背景早期提案 p000143-numeric-literals.md 对分隔符位置约束更严格后来 p001983-weaken-digit-separator-placement-rules.md 放宽了分组规则并讨论了「3 位十进制分组」「2 位或 4 位十六进制分组」「禁用小数部分分隔符」等备选方案。五、词法层面的整体流程与诊断把以上规则串起来看Carbon 词法器处理数字字面量的完整链路是贪婪收集NumericLiteral::Lexnumeric_literal.cpp从当前字符开始贪婪地吞掉所有可能属于数字字面量的字符字母数字、_、一个受条件约束的.、以及出现在潜在指数后的/-。刻意这样做是为了在非法字面量上产出更好的诊断。合法性检查Parser::Check依次检查前导零CheckLeadingZero、整数部分CheckIntPart、小数部分CheckFractionalPart、指数部分CheckExponentPart任何失败都会发射对应诊断numeric_literal.cpp。求值并生成 tokenComputeValue返回IntValue/RealValue/UnrecoverableError三态。词法器据此产出IntLiteral整数值存入整数值存储或RealLiteral规范化尾数/指数存入实数值存储无法恢复的错误则产出Errortokenlex.cpp。常见的词法诊断包括均来自 numeric_literal.cpp 与 testdata 验证诊断消息触发示例invalid digit a in hexadecimal numeric literal0x1a十六进制只允许大写 A–Fnumeric_literals.carbonunknown base specifier in numeric literal05前导零 未知基数misplaced digit separator in numeric literal1__2、1.000_binary real number literals are not supported0b1.0octal real number literals are not supported0o1.0expected e to introduce exponent1.0r3十进制却用了rnumeric_literals.carbonempty digit sequence in numeric literal如0x_这类前缀后无数字的情况更完整的非法用例矩阵如0x.0、0.e0、123A.4、0.0p0、0.0e_、0x0.0pA等集中在ValidatesRealLiterals测试中numeric_literal_test.cpp。5.1 大小写敏感性在实现中的落点大小写规则并非装饰CheckDigitSequence用0123456789ABCDEF按基数截取作为合法数字集合numeric_literal.cpp因此十六进制只接受大写A~F指数标记字符则按基数精确匹配e或pCheckExponentPartnumeric_literal.cpp。测试中0X123、0O123、0B1、0x123abc、0.0p0十进制用了p均被判为非法numeric_literal_test.cpp。六、与其它语言的差异DivergenceCarbon 刻意选择了一套与 C 及许多语言相近但更精简的语法子集让开发者感到熟悉同时践行「易读、易理解、易书写」的目标减少程序员面对的不必要选择简化语言的语法规则提升已写 Carbon 代码的一致性。尽管如此为满足「不给低级语言留下空间」的目标Carbon 仍提供了三类重要的变体十六进制、八进制、二进制整数字面量科学计数法浮点字面量十进制e指数十六进制科学计数法浮点字面量p指数。与 C 最显眼的两个差异总结如下无类型后缀C 的123u、1.0f、1.0L在 Carbon 中不存在字面量值的大小/类型交由上下文推导。八进制写法C 的0755在 Carbon 中非法必须写0o755。大小写规则十六进制数字只允许大写进制前缀只允许小写与 C 的大小写宽容截然不同。七、替代方案与设计决策回顾Alternatives considered设计过程中被讨论并否决/调整的方案详见 p000143-numeric-literals.md 及其后续提案整数进制八进制字面量最终采用0o前缀曾讨论是否支持、以及0前缀形式十进制字面量不允许前导零大小写敏感性最终决定字面量大小写敏感。实数语法是否允许平局tie时的舍入由 p000866-allow-ties-in-floating-literals.md 决定允许采用 round-to-even。数字分隔符语法3 位十进制分组、2 位/4 位十六进制分组、禁用小数部分分隔符这些选项在 p001983-weaken-digit-separator-placement-rules.md 中被逐一讨论最终放宽为「任意两个数字之间」的通用规则小数部分除外。八进制字面量存废「不支持八进制」的备选方案记录在 p006910-support-octal-literals.md最终决定支持并采用0o前缀。八、实操速查如何验证与继续探索如果你想在本地亲自验证上述行为可以使用仓库自带的 file test 框架。例如针对数字字面量的测试数据运行# 运行单个测试数据文件 bazel test //toolchain/testing:file_test \ --test_arg--file_teststoolchain/lex/testdata/numeric_literals.carbon # 或转储词法输出便于对照 bazel run //toolchain/testing:file_test -- \ --dump_output --file_teststoolchain/lex/testdata/numeric_literals.carbon以上命令来自 numeric_literals.carbon 文件头部的 TIP 注释。可继续深入阅读的仓库材料设计文档docs/design/lexical_conventions/numeric_literals.md核心实现toolchain/lex/numeric_literal.h、toolchain/lex/numeric_literal.cpp词法器集成toolchain/lex/lex.cppToken 定义toolchain/lex/token_kind.def单元测试toolchain/lex/numeric_literal_test.cpp文件级测试 toolchain/lex/testdata/numeric_literals.carbon、toolchain/lex/testdata/repeated_tuple_index.carbon、toolchain/lex/testdata/printing_real_literal.carbon相关提案proposals/p000143-numeric-literals.md、proposals/p000866-allow-ties-in-floating-literals.md、proposals/p001983-weaken-digit-separator-placement-rules.md、proposals/p006910-support-octal-literals.md【免费下载链接】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),仅供参考
返回列表