新闻详情

新闻详情

首页 / 资讯中心 / 详情

Slang 编译器词法分析与预处理阶段深度解析:从源码字节流到扁平 Token 列表

发布时间:2026/9/18 15:59:44来源:尧图网络
Slang 编译器词法分析与预处理阶段深度解析:从源码字节流到扁平 Token 列表
Slang 编译器词法分析与预处理阶段深度解析从源码字节流到扁平 Token 列表【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang本篇技术指南聚焦 Slang 着色器编译器编译流水线的第一阶段——词法分析Lexing与预处理Preprocessing。该阶段负责把源码缓冲区转换为解析器可直接消费的扁平Token数组涵盖字节编码解码、字面量扫描与取值分离、宏展开、条件编译指令、#include解析以及宏展开后的源码位置保留策略。读完本文你将掌握 Slang 编译器第一阶段的完整实现脉络、每条核心指令与诊断的具体行为边界以及如何在 docs/generated/design/pipeline/01-lex-preprocess.md 对应的源码与测试资产中定位并验证这些行为。第一阶段全景从源码字节流到扁平 Token 列表整个编译流水线以 Lex / Preprocess → Parse → Semantic check → AST to IR → IR passes → Emit 的顺序推进参见 docs/generated/design/pipeline/overview.md而词法与预处理是这条流水线的起点。输入SourceFile::decodeContentBlob与编码归一化该阶段的输入是一个源码缓冲区通常由 include 系统从磁盘加载外加当前激活的Linkage配置预定义宏、include 搜索路径。原始文件字节通过SourceFile::decodeContentBlob转换为源码文本其核心行为实现在 source/compiler-core/slang-source-loc.cpp剥离开头的 Unicode 字节序标记BOM将非 UTF-8 编码解码到一块全新缓冲区无 BOM 的 UTF-8 文件则原样透传。SourceFile::setContents也经由同一辅助函数路由因此词法器看到的永远是无 BOM 的 UTF-8文本。从源码结构看CharEncoding::determineEncoding/getEncoding(type)-decode见 source/compiler-core/slang-source-loc.cpp负责编码判定具体支持的编码集合定义在 source/core/slang-char-encode.cpp这也正是文档维护过程中被标记为待补充deferred的一个缺口文档层面尚未逐一列出decodeContentBlob识别的编码名。输出完全展开的TokenList不做流式输送输出是一个完全展开的TokenList——即所有#include、宏展开、条件预处理指令都已解析完毕的Token序列TokenList类型见 source/compiler-core/slang-lexer.h。需要特别强调的是预处理器不会向解析器流式推送 token。第一阶段必须完整跑完并产出扁平 token 列表第二阶段解析才开始。正是这种解耦让解析器可以使用任意长度的前瞻lookahead而无需与预处理逻辑交错。相关测试资产见 docs/generated/tests/design/pipeline/01-lex-preprocess/README.md。词法分析器Lexer实现与数据模型源码文件布局词法器实现位于 source/compiler-core 目录下核心文件包括slang-lexer.h / slang-lexer.cpp ——Lexer结构体及其initialize/ 词法驱动循环slang-token.h / slang-token.cpp ——Token、TokenType、TokenFlags、TokenList、TokenSpan、TokenReader等数据类型slang-token-defs.h —— 用 X-macro 列出全部TokenType值的目录文件。完整的 token 种类目录含分类位于 docs/generated/design/syntax-reference/tokens.md本文不重复罗列。Token 数据模型每个 token 携带四类信息字段说明TokenType一个字节通过 slang-token-defs.h 中的 X-macro 声明原始文本charsCount加上一个联合体要么指向原始字符要么指向驻留interned的Name*SourceLoc单个 32 位整数由 slang-source-loc.h 解码TokenFlags一个小字节含AtStartOfLine、AfterWhitespace、ScrubbingNeeded、Name后者用于区分联合体源码位置编码源码位置被压缩为单个uint32_t由SourceManagerslang-source-loc.h、slang-source-loc.cpp在需要时按需解码回文件/行/列。这样每个 token 只需携带一个廉价字段而不是三个。SourceLoc的拷贝构造函数被显式声明为 default而非手写从而保证该类型在嵌入联合体时仍保持平凡可拷贝trivially copyable。LexerFlags与特殊规则LexerFlags声明于 slang-lexer.h当前包含kLexerFlag_SuppressDiagnostics用于在遇到非法或不支持的字符时不产生诊断——典型场景是在非激活inactive的#if块内部进行分词。该标志通过Lexer::getDiagnosticSink生效当标志被设置时该访问器返回 null 而非真实 sink于是所有经由该访问器的扫描期诊断点都会被静默。例外情况畸形 UTF-8 诊断绕过这一抑制机制——它直接通过lexer-m_sink上报因此在非激活分支内依然会触发。词法器还处理若干 C 语言遗留特性反斜杠续行\newline被折叠但结果 token 的源码位置仍映射回原始的物理行标识符与关键字不做区分每个关键字 token 都以TokenType::Identifier到达解析器见 slang-token-defs.h 的 X-macro 列表关键字状态由解析器通过查找解析详见 docs/generated/design/pipeline/02-parse-ast.md数值、字符串、字符字面量的文本原样保留在 token 的原始文本中词法器只扫描字面量的边界不解码其值取值延迟到下述辅助函数非 ASCII 输入按 UTF-8 解码字节级_advance辅助函数slang-lexer.cpp把一个合法的多字节序列折叠成单个码点供标识符规则接受。畸形序列会被诊断为invalidUtf8ByteSequence以十六进制格式化出错字节然后替换为一个空格——坏字节终结当前 token而不会破坏后续扫描。_advance停在第一个非法的续接字节之前而非消费它使恢复点保持在下一个字符的起始处。字面量扫描与取值分离扫描期只做纯语法检查严重级别警告 vs 错误词法器把字面量的扫描记录其边界为原始 token 文本与解码求值分离且这种分离几乎是干净的扫描只做找到字面量结尾所需的最小工作外加少量纯语法检查——invalidDigitForBase、octalLiteral、quoteCannotBeDelimiter。三者严重级别并不相同这一细节由 gap-intake 质量审计明确补全检查严重级别行为octalLiteral如017的0前缀警告W10002扫描继续进入_lexNumber(lexer, 8)017依然产出可用的八进制字面量 tokenslang-lexer.cpp 第 2097 行invalidDigitForBase超出进制的数字错误E10003第 551 行quoteCannotBeDelimiter原始字符串定界符中出现引号错误E10010第 1797 行对应测试 octal-literal-scan-warning.slang 用DIAGNOSTIC_TEST:SIMPLE将警告钉死在 W10002并断言 caret 覆盖整个字面量 tokenstatic const int legacy 017; //CHECK: ^^^ 0 prefix indicates octal literal // CHECK: W10002字符串 / 字符 / 原始字符串的扫描扫描发生在词法驱动循环中。_lexStringLiteralBody(lexer, char quote)slang-lexer.cpp 第 1731 行同时处理...和...——闭合引号字符是参数因此不存在独立的字符字面量模式。它的唯一转义处理是跳过\、\、\\使被转义的引号不会误终结 token它不尝试识别八进制、十六进制或 Unicode 转义形式。因此它只会抛出两种根本找不到结尾的诊断endOfFileInLiteral和newlineInLiteral。原始字符串字面量由_lexRawStringLiteralBody第 1782 行单独扫描它寻找匹配的)delimiter且完全不进行转义处理——测试 raw-string-no-escape-processing.slang 验证了这一点。解码按需取值辅助函数解码为值由 slang-lexer.h 中声明的辅助函数按需完成getIntegerLiteralValue——解析整数可选返回后缀、十进制基数标志和溢出标志getFloatingPointLiteralValue——解析浮点值同时负责后缀分类把 token 拆成数值部分与后缀部分将后缀映射为FloatingPointLiteralTypeh/hf/fh→Half空后缀或f→Floatl/lf/fl→Double大小写均可并把解析值按该类型精度与范围取整。同一枚举还上报两种畸形情形BadSignificand与BadSuffixoutErrorContent携带出错的文本。全部四个出参outLiteralType、outIsOutOfRange、outPrecisionLost、outErrorContent都是不可省略的引用。其中outIsOutOfRange表示结果为0下溢或INFINITY超过字面量类型最大值的上溢而非超过doubleoutPrecisionLost仅对十六进制浮点且值在范围内时上报getStringLiteralTokenValue(token, sink)——把转义解码为结果字节getFileNameTokenValue是用于#include风格文件名的变体不处理转义对应测试 include-filename-escapes-not-processed.slanggetCharLiteralValue(token, sink)——返回 32 位码点失败时返回-1同时给出诊断。该辅助函数独占单字符规则对太短无法容纳字符的报illegalCharacterLiteral当解码后仍有未消费输入时再次上报——这正是多字符字面量被捕获的方式slang-lexer.cpp 第 1660-1725 行。空后缀分类为Float而非Double因此后缀单独即可决定一个字面量是否可表示。设计文档给出了这个最反直觉的例子double a 1e300lf; // Double: stays finite float b 1e300; // Float: out of range, decodes to INFINITY1e300能放进double但远高于float最大值所以后缀单独改变了解码值slang-lexer.cpp 第 1273-1284 行float分支解析见第 1331 行。边界测试 float-double-suffix-preserves-double-range.slang 用有限值减半会改变自身、而 INFINITY 减半不变的判定谓词同时验证两条路径double withSuffix 1e300lf; printf(double-finite%d\n, (withSuffix 0.0 withSuffix * 0.5 ! withSuffix) ? 1 : 0); // 1 float noSuffix 1e300; printf(float-inf%d\n, (noSuffix 0.0 noSuffix * 0.5 noSuffix) ? 1 : 0); // 1转义语法_decodeStringEscape字符串/字符辅助函数接收DiagnosticSink*因为所有字面量取值错误都在解码时上报越界的码元与码点、畸形转义语法、字符计数规则。转义处理集中在_decodeStringEscapeslang-lexer.cpp 第 1440 行它接收 sink 与 token 的SourceLoc自行诊断失败情形未知转义字母、无数字或未终止的\x{...}/\u{...}形式、溢出值对应invalidStringEscape\u不是恰好 4 个十六进制数字或\U不是恰好 8 个对应invalidUnicodeStringEscape然后返回-1。其文法遵循 docs/language-reference/expressions-literal.md八进制\NNN、十六进制\xNN任意位数、Unicode 转义\uNNNN4 位十六进制、\UNNNNNNNN8 位十六进制以及带花括号的\u{...}最长 32 位。在字符串字面量中\xNN映射为单个字节而 Unicode 码点转义被编码为 UTF-8 字节序列经encodeUnicodePointToUTF8。getStringLiteralTokenValue对裸非转义字节直接逐字节复制、不复查 UTF-8 良构性因为词法器的_advance已在扫描字面量体时诊断过畸形序列。在字符字面量中主体字节会经getUnicodePointFromUTF8二次重新解码畸形 UTF-8 会被再次拒绝因此在字符字面量中\x与\u没有实际差别。对应诊断invalidUtf8ByteSequence、invalidStringEscape、invalidUnicodeStringEscape、outOfRangeCodeUnit、outOfRangeCodePointForUtf8定义在 slang-lexer-diagnostic-defs.h。解码期错误上限测试资产覆盖了\x无数字E10007、\u位数不足E10008、越界码元E10009、多字符字面量E10001等边界。词法器不做什么不分类关键字推迟到查找阶段扫描期间不求值数值字面量取值是独立、按需的步骤默认不跳过空白与注释lexToken会把它们作为独立 token 类型发出由lexAllSemanticTokens与预处理器的ReadAllTokens过滤因此解析器永远不会看到它们测试 comments-not-in-parser-token-list.slang。预处理器Preprocessor预处理器实现在 source/slang/slang-preprocessor.cpp公共接口在 slang-preprocessor.h。PreprocessorDesc配置Preprocessor通过PreprocessorDesc配置其字段包括DiagnosticSink*——消息输出NamePool*——标识符文本驻留ISlangFileSystemExt*与SourceManager*——I/O可选IncludeSystem*——#include解析slang-include-system.h可选的DictionaryString, String预定义宏可选PreprocessorHandler*——事件回调翻译单元结束、文件依赖等构建系统用它记录 include 依赖可选PreprocessorContentAssistInfo*——语言服务器在预处理时收集代码辅助信息用。输入流栈预处理器维护一个输入流栈原始源文件位于栈底#include的文件与宏展开压入其上。token 向上流动时经过指令识别每个被#include的文件拥有自己的InputFile、自己的词法器、以及自己独立的Conditional状态栈因此条件跳过与指令诊断都是按文件隔离的不会扰动父文件。测试 include-conditional-stack-is-per-file.slang 验证头文件里开启的#if在头文件文件尾被诊断而父文件中的#endif没有可闭合的条件。宏展开Op 编译与参数回放宏定义保存一份已经词法化的 token 序列预先切分为MacroDefinition::Op条目展开即回放这些 op。参数引用被编译为携带参数索引的参数 opExpandedParam、UnexpandedParam或StringizedParam。调用时MacroInvocation按索引回放匹配实参的 token 范围_getArgTokens见 slang-preprocessor.cppExpandedParam情形下用ExpansionInputStream包裹使实参 token 自身也参与宏展开——不存在按调用建立的伪宏环境。相关功能测试覆盖对象宏展开object-macro-expansion.slang、函数宏参数替换function-macro-argument-substitution.slang、实参 token 再次展开function-macro-argument-tokens-are-expanded.slang、零参数宏与空实参等边界。参数数量不匹配E15501实参数量与形参列表不匹配的调用会被WrongNumberOfArgumentsToMacro诊断并跳过因此不贡献任何 tokenslang-preprocessor.cpp 第 1870 行非变参、第 1892 行变参供给不足。注意细节变参宏只要求其非变参形参被满足尾部实参数量任意。测试 macro-wrong-argument-count-diagnostic.slang 把 2 形参 1 实参的下供边界钉死在 E15501并断言消息中的期望/实际数量#define BIN(a, b) ((a) (b)) static const int q BIN(1); //CHECK: ^ wrong number of arguments to macro (expected 2, got 1) // CHECK: E15501busy-list 自抑制递归展开是自抑制self-suppressing而非深度受限的每个进行中的调用都在一张忙碌列表上当MacroInvocation::isBusy发现自己的宏在该列表上时_maybeBeginMacroInvocation就不再展开而是直接返回把m_lookaheadToken当作标识符交给上层第 1462 与 1728 行。因此在自身宏体里引用自己的宏到达解析器时是一个普通标识符——既不会重新展开也不会被诊断同一张列表也切断了嵌套展开间的相互递归。非激活的#if分支仍然流经词法器保证列/行计数正确但其内容不会被展开只有可能切换激活/非激活状态的指令#if、#ifdef、#ifndef、#else、#elif、#endif才会在非激活块内被真正求值测试 inactive-block-skips-non-conditional-directives.slang、ifndef-nesting-tracked-inside-inactive-block.slang。预处理指令指令按名字在预处理器状态上的回调表中查找因此新增指令#pragma、自定义扩展就是向表中注册新回调。kDirectives表slang-preprocessor.cpp持有 C / HLSL 风格指令集#if一族、#include、#define、#undef、#warning、#error、#line、#pragma外加 Slang 语言选择指令#language/#lang与 GLSL 指令#version/#extension。每条指令消费自己的整行因此不向输出列表贡献任何 token。#language/#lang版本按名字解析而非按算术#language/#langHandleLanguageDirective第 4536 行接受可选的slang名字后跟版本#lang [slang] version #language [slang] version它们设置源语言与版本preprocessSource通过outDetectedLanguage/outLanguageVersion返回slang-preprocessor.h。版本操作数按名字解析而非按算术无论它是以标识符还是整数字面量到达其文本都被交给TypeTextUtil::findLanguageVersion在单一表中查找——每个版本携带多个被接受的拼写版本接受的拼写2018legacy/default/201820252025/202a20262026/202b/latest2027202c/next因此#lang 2026、#lang 202b与#lang latest是同一条指令而编译器不认识的版本只是表中不存在而不是数字越界。测试 lang-directive-version-spellings.slang 用#lang 202b编译成功来钉住202b与2026共享同一表行的别名关系。注意只有slang被接受为可选语言名该指令已不再识别glsl。不可用操作数产生哪种诊断取决于解析器还能从中推断出什么因此共有三种操作数形态诊断既非标识符也非整数字面量ExpectedIntegralVersionNumber未识别的标识符且未给slang名UnknownLanguage——语言未指定时孤立标识符被读作试图命名语言未识别的整数字面量或显式slang之后出现未识别标识符UnknownLanguageVersion——语言已确定该 token 只可能是版本测试 lang-directive-unknown-version-after-language.slang 验证显式slang名之后的不识别标识符报 E15207未知语言版本而非 E15208未知语言。#version与#extension#version第 4501 行接受一个整数当它命名有效的 GLSL 版本时把检测到的语言切换到 GLSL#extension第 4496 行被接受然后丢弃。#pragma once与未知子指令策略#pragma通过kPragmaDirectives第 4445 行按第二个名字分发目前只有两个条目once与warning。#pragma once把所在文件的唯一身份记录进pragmaOnceUniqueIdentities第 4272 行之后某次#include解析到同一身份时HandleIncludeDirective提前返回第 3680 行从而防止重复包含。测试 pragma-once-prevents-redefinition.slang 两次包含同一#pragma once头文件并成功编译验证。未知子指令不是错误findPragmaDirective回退到handleUnknownPragmaDirective第 4242 行以UnknownPragmaDirectiveIgnored警告并跳过该行警告严重级别见 source/slang/slang-diagnostics.lua——这与未知#指令形成鲜明对比后者由HandleInvalidDirective第 4614 行作为错误上报。测试 pragma-unknown-emits-warning.slang 与 unknown-directive-diagnostic.slang 分别钉住这两种失败形态。#if表达式求值模型#if与#elif条件经递归下降求值器求值其值类型是普通的带符号intPreprocessorExpressionValueslang-preprocessor.cpp 第 2984 行没有无符号或 64 位模式任何非零结果即选中该分支。关键规则均由 gap-intake 审计确认并补进文档命名了某个非对象宏的标识符在发出UndefinedIdentifierInPreprocessorExpression警告后求值为0第 3117 行——测试 if-undefined-macro-is-zero.slangEvaluateInfixOp第 3194 行直接套用 C 运算符只有/和%检查操作数除数为零会被诊断并产生0因此、-、*、的溢出既不被检测也不被诊断——测试 if-expression-signed-overflow-wraps.slang 验证(INT_MAX 1)回绕为负值if-expression-uint32-max-is-true.slang 验证0xFFFFFFFF作为带符号值求值仍非零并选中激活分支。#include解析#include字符串由IncludeSystemslang-include-system.cpp解析它查询Linkage的搜索路径。两种形态尖括号形态HandleIncludeDirective把OpLess与OpGreater之间的原始 token 拼接为路径字符串并选择IncludeSystem::Mode::System测试 include-angle-bracket-mode.slang引号形态路径来自单个StringLiteraltoken。解析返回一个SourceFile预处理器随后为该文件压入全新的输入流。处理器还会收到handleFileDependency回调使前端能为构建系统建立依赖记录。测试资产还包括 5 层嵌套 include 的压测include-nested-5deep.slang与include 压入全新流的行为验证include-pushes-fresh-stream.slang。#pragma warning跨文件状态跟踪#pragma warning(push/pop/disable/...)的状态由WarningStateTrackerslang-preprocessor.cpp跟踪它按诊断 id 记录一条以绝对源码位置轴为键的时间线。由于每个__include的文件都在一次独立的preprocessSource流程携带全新的Preprocessor中预处理tracker 携带一个persistedAbsoluteSourceLocCounterpreprocessSource在进入时用持久化的值种子化自己的absoluteSourceLocCounter退出时把推进后的值交还从而保证时间线的绝对轴在文件间全局单调而不是每次流程从 0 重启导致碰撞。交还计数器上的SLANG_RELEASE_ASSERT守卫单调性例如防止超大型翻译单元上uint32_t回绕因为一旦违反发布构建中#pragma warning状态会被静默地错误解析。测试 pragma-warning-disable-timeline.slang 验证同一构造在指令之前警告、之后静默pragma-warning-push-pop-restores.slang 验证 push/pop 恢复。源码位置保留宏展开 token 的三类位置策略宏展开产出的 token 按源码位置的选取方式分为三类这也是诊断定位质量的关键诊断系统详见 docs/generated/design/cross-cutting/diagnostics.md1. 宏体原始 token从宏定义逐字复制的 token其SourceLoc是宏定义中对应 token 的位置由MacroInvocation::readTokenslang-preprocessor.cpp回放。不会为调用创建新的SourceView因此这类诊断指向宏体内部而非调用点。测试 macro-body-diagnostic-points-into-macro-body.slang 验证之。2. 实参 token取自调用点实参列表的 token。_getArgTokens通过PretokenizedInputStream回放记录的 token 范围且不做任何重写第 2351 与 2494 行因此每个 token 保留其在调用点被物理词法化时的SourceLoc。参数被多次使用时每次都回放同一个位置。可证伪的经典示例#define TWICE(x) ((x) (x)) TWICE(undeclared)对实参undeclared的诊断指向调用点的undeclared而不是宏体中任何一个(x)——后者正是第 1 类规则会放置的位置。3. 构造 token全新合成的 token有三种不同的位置规则内建宏__LINE__、__FILE__由_pushStreamForSourceLocBuiltin压入合成的 token 获得m_macroInvocationLoc归属于调用点但报告的行/文件值来自发起的最顶层位置。测试 line-macro-through-nested-expansion.slang 验证嵌套展开中__LINE__报告的是发起调用的最顶层行字符串化参数#x取宏定义中#token 的位置m_macro-tokens.m_tokens[tokenIndex].loc粘贴 tokenx##y从全新的PathInfo::makeTokenPaste()源码视图重新词法化其来源是##token 的位置tokenPasteLoc。只有粘贴 token 情形会构建发起位置链格式化诊断时DiagnosticSink只要视图的PathInfo::Type是TokenPaste就沿getInitiatingSourceLoc行走每跳发出一个seeTokenPasteLocation附注测试 token-paste-diagnostic-notes-paste-location.slang。粘贴后的 token 会被重新词法化因此粘贴两个整数字面量能形成一个新的整数字面量token-paste-relexed-numeric.slang。这一三分法让宏自身有问题的诊断能指向宏体内部而实参 token 仍指向调用点。失败模式与恢复行为扫描期 vs 解码期错误非法字符与畸形数值/字符串/字符字面量经DiagnosticSink上报且跨两个阶段分离扫描期错误走传入词法器initialize的 sink覆盖扫描器无需解释字面量即可看到的问题非法字符、畸形 UTF-8 序列、字面量内的文件尾或换行、超出进制的数字、遗留八进制字面量、作为原始字符串定界符的引号解码期错误通过传给取值辅助函数的 sink 上报getStringLiteralTokenValue与getCharLiteralValue报告畸形转义语法、越界码元与码点、字符字面量的空或多字符主体getIntegerLiteralValue报告integerLiteralTooLargeForAnyType测试 integer-literal-too-large-diagnostic.slang。getFloatingPointLiteralValue是例外它完全不接收 sink而是以BadSignificand或BadSuffix的FloatingPointLiteralType外加出错文本返回错误由调用方选择诊断。从源码看其唯一调用方parseFloatingPointLiteralExprsource/slang/slang-parser.cpp把BadSignificand映射为InvalidFloatingPointLiteralNumber、把BadSuffix映射为InvalidFloatingPointLiteralSuffix。两个阶段都经由 slang-lexer.cpp 中文件局部的diagnose辅助函数汇入它丢弃 null sink并且一旦 sink 的错误计数超过kMaxLexErrorCount100就停止转发从而防止病态畸形文件淹没 sink。抑制诊断下的行为设置kLexerFlag_SuppressDiagnostics后词法器对畸形输入仍会产出TokenType::Invalidtoken但抑制诊断——用于被跳过的预处理块内部那里的 token 会被非激活分支过滤器丢弃。由于抑制机制是让Lexer::getDiagnosticSink交出 null sink它只覆盖使用该访问器的扫描期位置畸形 UTF-8 绕过它如果调用方之后向取值辅助函数请求字面量值传入自己的 sink仍会看到解码期诊断。预处理错误与继续策略预处理错误不配对的#if、未知指令、缺失 include、循环 include、没有打开条件的#else/#elif/#endif同样经 sink 上报且预处理器会继续运行畸形指令被跳到其行尾失败的#include从其处理器返回坏的宏调用可能展开为无 token未闭合的条件在文件尾对每个仍打开的条件各诊断一次即EndOfFileInPreprocessorConditionalslang-preprocessor.cpp 第 4773 行测试 unbalanced-if-diagnostic.slang游离的闭合器是独立的诊断DirectiveWithoutIf第 3530 行循环 include当解析出的 include 已经在 include 栈上打开时CyclicInclude触发第 3710 行该#include被跳过。测试 include-self-cycle-diagnostic.slang 让文件包含自身并钉住 E15302#include include-self-cycle-diagnostic.slang //CHECK: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic #include of file include-self-cycle-diagnostic.slang // CHECK: E15302输出中唯一的EndOfFiletoken 由ReadAllTokens在整个输入栈弹出之后才追加。文档质量保障gap-intake 审计与测试资产本主题对应的设计文档 docs/generated/design/pipeline/01-lex-preprocess.md 经过一轮系统性质量审计见 docs/generated/design/_meta/gap-intake/pipeline/01-lex-preprocess.md.gap-intake.md共识别 11 处缺口9 处已修复、2 处推迟、0 处驳回、0 处升级为缺陷——没有任何观察与受监视源码矛盾。修复分布在五个章节扫描检查严重级别与1e300/1e300lf后缀示例、宏展开参数数量不匹配与 busy-list 递归规则、预处理指令#language/#lang/#version/#extension的效果、#pragma once与未知子指令策略、#if算术模型、源码位置保留第 2 类改写为围绕_getArgTokens并附可证伪示例、失败模式循环 include 与游离条件闭合器。两处推迟项均因佐证源码位于本文档监视路径之外编码集合命名需 source/core/slang-char-encode.cppBadSignificand/BadSuffix的用户级诊断命名需 source/slang/slang-parser.cpp。围绕该文档生成的测试套件docs/generated/tests/design/pipeline/01-lex-preprocess/README.md采用一条正向主张一个功能测试、数值/转义/后缀/嵌套轴上的边界探测、每条拒绝主张一个钉死诊断码的诊断测试的覆盖策略全部测试与目标无关可通过INTERPRET、-target cpp文本发射与DIAGNOSTIC_TEST运行。这也是阅读上述每一处行为细节最直接的验证入口先读设计文档定位语义再打开对应.slang测试查看可观察断言最后在 source/compiler-core 与 source/slang 的源码中确认实现。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

PS5 Pro + OLED电视玩赛博朋克2077:光追与HDR的极致体验 2026/9/18 16:41:53

PS5 Pro + OLED电视玩赛博朋克2077:光追与HDR的极致体验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
北师大版小学语文课文总目录:从Word到结构化数据的Python实践 2026/9/18 16:41:53

北师大版小学语文课文总目录:从Word到结构化数据的Python实践

简介:北师大版小学语文课文总目录.doc 是一份系统整理的小学语文教材课文目录文档,面向小学语文教师、家长及自学者,可用于快速查询各年级课文分布、单元主题及语文天地活动内容,为备课、辅导和制定学习计划提供清晰索引。文档共1…

阅读更多 →
嵌入式工控机采购:宽温、防护、EMC等工业指标解析 2026/9/18 16:41:53

嵌入式工控机采购:宽温、防护、EMC等工业指标解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Android Studio 找不到手机?adb devices 全链路排查 2026/9/18 16:41:53

Android Studio 找不到手机?adb devices 全链路排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
CANN 昇腾 3DGS 视锥剔除优化:GaussianFilter 融合算子设计与实现解析 2026/9/18 16:41:53

CANN 昇腾 3DGS 视锥剔除优化:GaussianFilter 融合算子设计与实现解析

CANN 昇腾 3DGS 视锥剔除优化:GaussianFilter 融合算子设计与实现解析 【免费下载链接】cann-recipes-spatial-intelligence 本项目针对空间智能业务中的典型模型、加速算法,提供基于CANN平台的优化样例 项目地址: https://gitcode.com/cann/cann-reci…

阅读更多 →
ESP32S3快速入门:硬件真相、IDF框架与静默陷阱避坑指南 2026/9/18 16:38:53

ESP32S3快速入门:硬件真相、IDF框架与静默陷阱避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞