资讯动态

Slang 字面量表达式规范与一致性测试:从语法、类型推导到 Lexer 实现

发布时间:2026/9/19 19:57:48 来源:尧图企业网站定制
Slang 字面量表达式规范与一致性测试从语法、类型推导到 Lexer 实现【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang本文以 Slang 语言参考手册 docs/language-reference/expressions-literal.md 为权威规范主体结合其一致性测试包 docs/generated/tests/conformance/expressions-literal/ 与编译器词法分析器实现 source/compiler-core/slang-lexer.cpp系统梳理布尔、整数、浮点、字符串、字符五类字面量表达式的文法、求值语义与后缀→类型推导规则并剖析这些规则如何被翻译成可被slangc观察的一致性测试。读完本文你将掌握 Slang 字面量的完整类型推导规则含最小负整数特殊豁免、已弃用的八进制、指针宽度后缀等边界情形理解规范驱动测试的锚定与观察方法并能对照词法分析器源码定位字面量的实际解析路径。一、背景语言参考手册是字面量语义的权威来源Slang 仓库维护着一套自动生成的一致性测试体系位于 docs/generated/tests/其核心方法论记录在 docs/generated/tests/_meta/prompts/_common.md 中遵循严格的Source-of-truth 层级语言参考手册docs/language-reference/*.md——人工编写的规范描述 Slang 行为应该是什么是权威规范生成的设计文档docs/generated/design/*.md——从编译器源码反推生成可能把 bug 当作有意行为固化仅在语言参考未描述时才作为兜底锚点编译器源码——永远不作为测试的首要引用。本主题的一致性测试包位于 docs/generated/tests/conformance/expressions-literal/其生成提示 docs/generated/tests/conformance/expressions-literal/_prompt.md 明确规定该包中的每个测试都必须锚定到语言参考手册本身提出的论断//META: doc_refdocs/language-reference/expressions-literal.md#anchor而不是设计文档。测试受众是夜间 CInightly CI测试仅驱动词法分析器lexer与语义检查器checker无需 GPU。二、布尔字面量仅true与false两种拼写语言参考手册的布尔字面量文法极简BooleanLiteral (true | false)布尔字面量表示 布尔类型 的true/false值并且与所有字面量表达式一样是右值r-value。测试包中的 bool-true-false.slang 通过重载决议验证两个拼写都是bool类型并在slangi解释器下打印出文档声称的真值int probe(bool x) { return x ? 1 : 0; } int probe(int x) { return -1; } void main() { int t probe(true); int f probe(false); //CHECK: t1 f0 printf(t%d f%d\n, t, f); }值得注意该测试在解释器slangi下曾暴露过 VM 层面的 bugfalse作为bool参数传入时在 callee 内被求值为true而前端在-target hlsl与-target cpu下生成的调用点是正确的说明问题在 VM 而非规范——详见测试包 README 的## Doc gaps observed一节。三、整数字面量四种进制、后缀与类型推导3.1 文法骨架语言参考手册给出了完整的整数文法IntegerLiteral核心结构为body主体可选 suffix后缀。IntegerLiteral ( DecIntegerLiteralBody | HexIntegerLiteralBody | BinIntegerLiteralBody | OctIntegerLiteralBody ) IntegerSuffix? DecIntegerLiteralBody 0 | DecDigitNonZero DecDigit* DecDigit [0-9] DecDigitNonZero [1-9] HexIntegerLiteralBody (0x | 0X) HexDigit HexDigit [0-9A-Fa-f] BinIntegerLiteralBody (0b | 0B) BinDigit BinDigit 0 | 1 OctIntegerLiteralBody 0 OctDigit OctDigit [0-7]四种 body 形态的要点十进制单独一个0或非零首数字后跟零个或多个十进制数字十六进制0x/0X大小写不敏感前缀后跟一个或多个0-9A-Fa-f二进制0b/0B后跟一个或多个0/1八进制0后跟一个或多个0-7数字。八进制字面量已被弃用仅为向后兼容而支持使用会触发警告。特别注意单独的0是十进制而00、01等是已弃用的八进制。词法分析器源码 source/compiler-core/slang-lexer.cpp 印证了这一点在_lexNumber的0分支中0x/0X以基数 16 进入_lexNumber(lexer, 16)0b/0B以基数 2 进入_lexNumber(lexer, 2)而0后紧跟0-9时先触发LexerDiagnostics::octalLiteral诊断、再以基数 8 解析。此外裸0后若跟e/E指数或#INF会被识别为浮点字面量对应0e10、0#INF的用例。3.2 后缀文法与后缀→类型表后缀由无符号说明符u/U与宽度说明符l/L、ll/LL、z/Z以任意顺序组合而成IntegerSuffix (IntegerSuffixUnsigned IntegerSuffixWidth?) | (IntegerSuffixWidth IntegerSuffixUnsigned?) IntegerSuffixUnsigned u | U IntegerSuffixWidth l | L | ll | LL | z | Z字面量的类型是表中第一个能容纳该值的类型后缀十进制基十六进制/二进制/八进制基(none)/Lint、int64_t、uint64_t(*)int、uint、int64_t、uint64_tLLint64_t、uint64_t(*)int64_t、uint64_tU/UL/LUuint、uint64_tuint、uint64_tULL/LLUuint64_tuint64_tZintptr_t、uintptr_t(*)intptr_tUZ/ZUuintptr_tuintptr_t标 (*) 的类型会触发警告它们仅作为静默接受本会溢出有符号整数类型的值的回退。再结合值域给出更细的类型判定节选关键行后缀基值域字面量类型(none)/Ldec[0, 2147483647] (**)int(none)/Ldec[2147483648, 9223372036854775807] (**)int64_t(none)/Ldec[9223372036854775808, 18446744073709551615]uint64_t(*)(none)/Lhex/bin/oct[0x0, 0x7FFFFFFF]int(none)/Lhex/bin/oct[0x80000000, 0xFFFFFFFF]uintLLdec[0, 9223372036854775807] (**)int64_tU/UL/LUany[0, 4294967295]uintU/UL/LUany[4294967296, 18446744073709551615]uint64_tULL/LLUany[0, 18446744073709551615]uint64_tZ(32-bit)dec[0, 2147483647] (**)intptr_tZ(64-bit)dec[0, 9223372036854775807] (**)intptr_t其中 (**) 表示当字面量前带一元负号时该值域会按下方最小负整数异常扩展一个值。例如int行在取负时也接受2147483648因为-2147483648可被int表示。3.3 最小负整数特殊豁免为了让-2147483648与-9223372036854775808无需立即溢出提升即可拼写语言参考明确给出三条例外2147483648或2147483648L前带一元减号 → 值为-2147483648、类型int9223372036854775808、9223372036854775808L或9223372036854775808LL前带一元减号 → 值为-9223372036854775808、类型int64_t2147483648Z32 位指针或9223372036854775808Z64 位指针前带一元减号 → 值为对应最小值、类型intptr_t。测试包中的 smallest-negative-int32.slang 与 smallest-negative-int64.slang 分别验证这两条豁免//TEST:INTERPRET(filecheckCHECK): void main() { int v -2147483648; //CHECK: v-2147483648 printf(v%d\n, v); }测试注释同时记录了一个上下文敏感的观察无约束的重载决议场景下字面量-2147483648会解析为int64_t而非int说明文档中类型是int的论断只在int类型上下文中成立——该歧义已作为ambiguous-claim型 doc-gap 记录在包 README 中。3.4 语言参考给出的完整示例0 // decimal literal, type int 2147483647 // decimal literal, type int 2147483648 // decimal literal, type int64_t 18446744073709551615 // decimal literal, type uint64_t (warning) 18446744073709551616 // decimal literal, overflow error -2147483648 // expression, type int -2147483648LL // expression, type int64_t -9223372036854775808 // expression, type int64_t (no warning per special case) 0U // decimal literal, type uint 5000000000U // decimal literal, type uint64_t 1000L // decimal literal, type int 1000LL // decimal literal, type int64_t 1000UL // decimal literal, type uint 1000ULL // decimal literal, type uint64_t 0x12345678 // hexadecimal literal, type int 0xDEADBEEF // hexadecimal literal, type uint 0x1234567890ABCDEF // hexadecimal literal, type int64_t 0xFEDCBA0987654321 // hexadecimal literal, type uint64_t 0b11010111 // binary literal, type int 0b11010111U // binary literal, type uint 0377 // octal literal, type int (warning) 0x10494810000UZ // hexadecimal literal, type uintptr_t (64-bit pointers only; // overflows on 32-bit)两条值得注意的备注语言参考原文Remark 1十六进制/二进制/八进制字面量若推导类型为无符号uint/uint64_t且无U/Z后缀可隐式转换为对应有符号整数类型而不触发收窄转换警告因此int x 0xFFFFFFFF;合法且二进制表示不变。Remark 2整数字面量类型遵循 C11 规则外加对一元减号前的最小整数值的特殊处理。3.5 测试包如何验证类型选择测试包采用重载决议探针模式为int/uint/int64_t/uint64_t各写一个同名probe重载将字面量作为实参传入根据被选中的重载编号推断字面量类型。例如 dec-int64-promotion.slang 验证无后缀十进制2147483648落在[2147483648, 9223372036854775807]应落在int64_t重载上int probe(int x) { return 1; } int probe(uint x) { return 2; } int probe(int64_t x) { return 3; } int probe(uint64_t x) { return 4; } void main() { int picked probe(2147483648); //CHECK: picked3 printf(picked%d\n, picked); }同类测试还包括0单独成字面量是十进制dec-zero-is-decimal.slang、十六进制前缀大小写不敏感hex-prefix-lowercase.slang 与 hex-prefix-uppercase.slang、十六进制数字大小写不敏感hex-digits-case-insensitive.slang、二进制前缀bin-prefix-lowercase.slang、LL优先选中int64_tsuffix-ll-int64.slang、U强制无符号suffix-u-unsigned.slang、ULL选中uint64_tsuffix-ull-uint64.slang。四、浮点字面量十进制/十六进制双形态与求值规则4.1 文法浮点字面量由 body十进制或十六进制与可选后缀组成无后缀时类型为floatFloatLiteral ( DecFloatLiteralBody | HexFloatLiteralBody ) FloatSuffix? DecFloatLiteralBody DecDigit DecExponent (* Form1 *) | DecDigit . DecExponent? (* Form2 *) | DecDigit* . DecDigit DecExponent? (* Form3 *) DecExponent (e | E) ( | -)? DecDigit | #INF HexFloatLiteralBody (0x|0X) ( HexDigit HexExponent | HexDigit . HexExponent | HexDigit* . HexDigit HexExponent ) HexExponent (p | P) ( | -)? DecDigit | #INF FloatSuffixF16 h|H|hf|HF|fh|FH FloatSuffixF32 f|F FloatSuffixF64 l|L|lf|LF|fl|FL4.2 语义要点十进制形式中有效数字与指数构成值significand × 10^x指数在含小数点时可省略十六进制形式强制要求十六进制指数p/P值 significand × 2^y指数本身始终写成十进制无论哪种形式用#INF作为指数表示正无穷其前的数字被忽略负无穷通过一元负号表达如-1#INFf这是继承自 MSVC 风格的遗留形态词法分析器在_lexNumber的裸0分支中专门兼容了0#INF求值规则四条值过大 → 无穷并告警非零且过小 → 0 并告警十进制按最接近可表示值舍入平局取偶舍入不告警十六进制截断向零舍入截断改变值时会告警。源码侧source/compiler-core/slang-lexer.cpp 的_hexFloatLiteralToDouble展示了十六进制浮点的转换实现逐位累加 significand超过 64 位余量时只更新指数偏置并标记precisionLost随后解析p/P指数最后按ldexp合成值——与规范十六进制截断的描述一致。4.3 官方示例123.0 // 32-bit float, value 123.0 123. // 32-bit float, value 123.0 .5 // 32-bit float, value 0.5 123e3 // 32-bit float, value 123000.0 123e-3 // 32-bit float, value 0.123 (not exact) 0x123p4 // 32-bit float, value 4656.0 ( 291 * 2^4) 0xC8p-4 // 32-bit float, value 12.5 ( 200 * 2^-4) 0xC.8p0 // 32-bit float, value 12.5 ( 12 8 * 2^-4) 123.0lf // 64-bit float, value 123.0 123.0hf // 16-bit float, value 123.0 1#INFhf // 16-bit float, positive infinity 1#INFf // 32-bit float, positive infinity 123f // error: 123 is an integer literal and f is not a valid // integer suffix. Write 123.f or 123e0f for a float.最后一个反例非常实用123f是错误而不是浮点字面量因为123已被词法化为整数字面量、f不是合法整数后缀正确写法是123.f或123e0f。五、字符串字面量双引号串、转义序列与原始字符串5.1 基本文法字符串字面量由一个或多个连续字符串 token组成其值为各 token 值的拼接token 间可用空白分隔与多数文法不同token 内部的空白是有效内容。类型为String底层数据格式未规定推荐 UTF-8 编码不强制。StringLiteral StringLiteralToken StringLiteralToken DQuotedString | RawString DQuotedString DStringChar* 5.2 转义序列表双引号串中的DStringChar有未转义与转义两类除新行、回车、反斜杠与双引号外大多数字符原样编码。完整转义表如下转义序列编码字符值\字符\字符\\字符\\?字符?\aASCII 7响铃\bASCII 8退格\fASCII 12换页\nASCII 10换行\rASCII 13回车\tASCII 9水平制表\vASCII 11垂直制表\nnn八进制字节码1–3 位数字\xnnn十六进制字符码一位及以上\x{nnn}十六进制字符码花括号包裹\unnnn4 位十六进制 Unicode 码点\u{nnn}花括号包裹的十六进制 Unicode 码点\Unnnnnnnn8 位十六进制 Unicode 码点字符串中八进制/十六进制字符码表示单个字节必须落在 0–255Unicode 码点按 UTF-8 展开为字节序列。5.3 原始字符串原始字符串以R开头后跟用户自定义分隔符与(内容逐字取用、不做任何转义处理直到) 相同分隔符 终止R(Raw string) // value: Raw string Rxz(Raw string)xz // value: Raw string Ra(Raw )a string)a // 解析为 Ra(Raw )a内容 Raw 注意尾部空格 // 剩余 string)a 是语法错误 R(ABC DEF) // 内容含换行 123 456 // 拼接为 123456 // UTF-8 字节 0xF0 0x9F 0x98 0x8A \u{1F60A} // \U0001F60A // \xF0\x9F\x98\x8A // 语言参考手册声明当前实现对字符串的符合性并不完整跟踪问题记录于 #11291 与 #11306阅读时需留意这一 caveat。六、字符字面量单引号包围的单个字符值字符字面量求值为单个字符值类型为uint单引号内的SChar与双引号串的DStringChar规则一致区别在于未转义字符可以是双引号但不能是单引号单引号必须写成\。十六进制转义的值域为 0–4294967295须能用uint表示八进制转义限于 0–255。\0 // 字符 0空字符 A // 字符 65A \t // 字符 9水平制表 \x53 // 字符 83S // 字符 34——双引号无需转义 \ // 字符 39——单引号必须转义 \\ // 字符 92\ \110 // 字符 72H——八进制转义 \x{75bcd15} // 字符 123456789——十六进制转义 \u{75bcd15} // 字符 123456789——Unicode 码点转义 // 字符 128522——UTF-8 编码语言参考特别注明与字符串字面量不同字符字面量中\x{nnn}与\u{nnn}两种相似转义没有实际区别。七、一致性测试方法论如何把规范翻译成可观察的测试测试生成规则 docs/generated/tests/_meta/prompts/_common.md 定义了可测试性判据若文档论断为假slangc产生的程序文本行为会以//TEST或//DIAGNOSTIC_TEST指令可观察到的方式改变则该论断可测试反之若论断只涉及词法分析器内部 token 化、分配的 AST 节点类或解析器辅助函数均不在 CLI 表面暴露则不可测试应记为 doc-gap 而非强行写测试。7.1 观察工具选择值级论断最小负整数、后缀选型结果用//TEST:INTERPRET(filecheckCHECK):printf观察解析后的值。注意slangiVM 存在常量越界constants-OOB已知问题若测试疑似命中该 bug 而非字面量本身应先简化测试去掉inout链、拆分 printf再考虑加入expected-failures.txt类型级论断字面量X的类型是T把字面量赋值给期望类型的变量且不做转换再检查 SPIR-V / HLSL 发射结果中TypeInt/TypeFloat的宽度或使用__check_typeT(value)惯用法弃用警告八进制用//DIAGNOSTIC_TEST 诊断码 CHECK 或定位到字面量处的 CHECK错误如08——非法八进制数字用//DIAGNOSTIC_TEST钉住诊断。7.2 每个测试的 META 块每个.slang测试文件以//META块开头声明其锚定关系//META: generatedtrue //META: modelclaude-opus-4-7 //META: generated_at2026-06-01T13:00:0000:00 //META: source_commitd25453d7f0b4867db4cb5eabf34fb6cd088cf596 //META: doc_refdocs/language-reference/expressions-literal.md#integer-literal-expressions //META: doc_section_digest769b627a9edfb98d3dcd82a049d5720ec282bc1a720cf4d1da09d5420919dd11 //META: purposeSmallest-negative-int32 exception: the literal 2147483648 preceded by unary minus yields -2147483648 of type int per the docs explicit rule. //META: intentboundary //META: pipeline_stageparse //META: warningAuto-generated. May drift from source. Do not edit by hand.pipeline_stage取值如lex/parse/check等intent采用受控词汇functional | boundary | negative | stress | expansion | regression。锚点必须指向语言参考手册中的真实标题锚点生成规则小写化、非单词字符删除、空格转连字符在_common.md中有明确算法。7.3 跨目标覆盖策略大多数字面量论断在解析/检查期成立、与目标无关因此每个论断用一条//TEST:INTERPRET或//TEST:SIMPLE(filecheckCHECK):-target hlsl即可。对于涉及发射的论断如int64_t与uint64_t的发射差异额外增加-target spirv-asm让 SPIR-V 的TypeInt宽度出现在 CHECK 中。7.4 什么不该测AST 层论断如IntegerLiteralExpr是LiteralExpr的子类——AST 形态不是 CLI 表面超出范围词法分析器内部正则回溯细节——超出范围语言参考未描述的行为——记录 doc-gap绝不凭直觉写测试。7.5 包规模与边界覆盖生成提示要求目标约25–35 个测试覆盖后缀→类型表的每一行、每一条最小负整数规则、每一种整数文法生成式若语言参考的浮点小节不完整则跳过浮点并记为 doc-gap。当前包README.md已含 12 个测试覆盖0为十进制、十六进制前缀/数字大小写、二进制前缀、两条最小负整数豁免、U/LL/ULL后缀选型与int64_t提升等论断并在## Untested claims中记录了浮点文法、字符串文法、nullptr字面量与Z/UZ/ZU指针宽度后缀目标相关需能力门控基础设施等未测声明。八、源码级佐证Lexer 的字面量解析路径source/compiler-core/slang-lexer.cpp 是字面量规范在编译器中的落地位置可与规范逐条对照_lexNumber(lexer, base)统一数字扫描入口由调用方传入基数10/16/2/8实现body 长度决定值的规则0分支第 2036–2101 行裸0后跟x/X→ 基数 16b/B→ 基数 20-7→ 先发出octalLiteral弃用诊断再按基数 8 解析其余 → 尝试浮点指数/后缀后按整数字面量返回_maybeLexNumberExponent/_maybeLexNumberSuffix负责e/E指数与u/U/l/L/ll/LL/z/Z后缀的收尾识别是123f报错与1000L类型仍为int这类行为的实现源头_hexFloatLiteralToDouble第 963 行起十六进制浮点转换significand 截断与precisionLost标记对应规范十六进制截断并告警的规则。九、已知的规范-实现漂移Doc Gaps测试包 README 的## Doc gaps observed是向文档再生成流程反馈问题的通道当前记录了三条值得使用者警惕的漂移八进制弃用警告未完全落地规范称八进制字面量弃用并触发警告但实践中08被词法分析器当作unexpected token拒绝、01未触发文档所述警告——要么词法分析器实现文档宣称的弃用路径要么文档改写为不支持八进制uint64_t回退警告缺失规范表中(none)/L十进制值域[9223372036854775808, …]→uint64_t*警告一行实践中超过INT64_MAX的值直接以E10012拒绝不存在警告后回退路径最小负整数类型声明的上下文敏感性规范称-2147483648类型为int实践中该论断只在int类型上下文成立无约束重载决议下解析为int64_t详见 smallest-negative-int32.slang 注释。这些漂移恰恰体现了一致性测试体系的设计初衷conformance/树中失败的测试就是规范 vs 编译器的信号由人工分诊决定修改规范还是修复编译器。十、如何查看与运行这套测试测试包位于 docs/generated/tests/conformance/expressions-literal/全部为无 GPU 依赖的 lexer/checker 级测试。通过slang-test框架运行常用指令形态见下表指令用途//TEST:INTERPRET(filecheckCHECK):在slangi字节码解释器下运行并检查 printf 输出//TEST:SIMPLE(filecheckCHECK):-target hlsl编译到 HLSL 文本并 FileCheck 发射代码//TEST:SIMPLE(filecheckCHECK):-target spirv-asm编译到 SPIR-V 汇编并检查TypeInt宽度等//DIAGNOSTIC_TEST:SIMPLE(diagCHECK):验证诊断的文本/严重度/代码每个测试文件都必须包含至少一条// CHECK:模式无 CHECK 的测试会被 lint 拒绝且 CHECK 应尽量松散、结构化用{{...}}、CHECK-DAG、错误码避免钉死易变的 SSA id 或名称修饰。生成规则要求每个 bundle 在提交前通过regenerate.py verify bundle本地验证CI 夜间运行则作为 GPU/DXC/nvrtc 等受限能力的兜底安全网。总结Slang 的字面量表达式在语言参考手册 docs/language-reference/expressions-literal.md 中被完整定义为五类布尔、整数、浮点、字符串、字符其中整数的后缀→类型表、最小负整数豁免与已弃用的八进制最易踩坑一致性测试包 docs/generated/tests/conformance/expressions-literal/ 通过//META: doc_ref锚定规范、用重载决议探针与printf观察值/类型使每一条规范论断都成为可在夜间 CI 上复现的可测试声明词法分析器 source/compiler-core/slang-lexer.cpp 则是这些规则在编译器中的最终落点。理解这三者之间的映射关系既是编写 Slang 着色器的实用指南也是参与规范驱动测试与文档维护工作的入门路径。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价