Slang 关键字与内建语法完全解析:从词法、解析器到核心模块的实现指南

Slang 关键字与内建语法完全解析:从词法、解析器到核心模块的实现指南 编译器图形学编程语言【免费下载链接】slangMaking it easier to work with shaders项目地址https://gitcode.com/GitHub_Trending/sl/slang点击查看免费下载本篇文章面向希望在 Slang 编译器中新增关键字、理解某个标识符为何特殊、或探究语法扩展机制的开发者。文档将揭示一个反直觉的事实Slang 中绝大多数关键字并不是词法lexer层面的保留字它们以TokenType::Identifier进入解析器只因为在活动环境中某个SyntaxDecl把它们绑定到了一个解析回调上因此新增或重命名一个关键字改的是解析器的语法表或核心模块源码而不是词法器。阅读本文后你将掌握 Slang 关键字的三大来源、四类注册方式语句 / 声明 / 修饰符 / 表达式以及syntax与attribute_syntax扩展语法的底层原理。关键字的三大来源Slang 中的关键字由三个相互独立的来源共同贡献理解它们的分工是理解整篇文档的前提硬编码的语句关键字位于 source/slang/slang-parser.cpp。语句解析器通过LookAheadToken(if)、LookAheadToken(for)等直接按标识符文本匹配并分发到专门的解析函数。这类关键字是封闭语法closed grammar不能被用户代码重定义。解析器的SyntaxParseInfo语法表同样在 slang-parser.cpp 中即数组g_parseSyntaxEntries[]当前源码中定义于第 10831 行附近。getSyntaxParseInfos()把它作为只读视图暴露populateBaseLanguageModule()将其注册进默认环境。声明、修饰符与表达式关键字全部来自这里。核心模块*.meta.slang声明元模块core.meta.slang、hlsl.meta.slang、glsl.meta.slang、diff.meta.slang声明了内建类型、函数与运算符向默认环境贡献名字。它们在编译器启动时被处理但属于内建词汇built-in vocabulary而非关键字只有SyntaxDecl绑定才会驱动解析回调而已知的类型名仅参与少数有限的分歧消解disambiguation决策。词法器识别的关键字只有标点与运算符一个容易忽略的事实是Slang 的词法器不识别任何字母型关键字。词法器 / token 目录中拼写出来的 token 只有标点与运算符——分号Semicolon、作用域::Scope、右箭头-RightArrow、双右箭头DoubleRightArrow、以及所有Op*运算符等。完整的 token 列表参见 tokens.md。这一设计带来直接后果诸如if、func这样的词在词法阶段与普通标识符毫无区别它们是否特殊完全取决于解析器在后续环节如何解释。解析器注册的语法关键字Slang 关键字的绝大部分由解析器识别而非词法器。语句关键字由语句解析器直接匹配声明、修饰符与表达式关键字则注册在解析器的语法声明表g_parseSyntaxEntries[]中从而可以通过syntax/attribute_syntax声明被重定义或扩展。syntax与attribute_syntax扩展点是一个新拼写而不是新节点这两种声明的书写形式为syntax name [: SyntaxClass] [ existingKeyword]; attribute_syntax [Name(param : Type, ...)] : SyntaxClass;两者都指名一个 AST 节点类parseSyntaxDecl与parseAttributeSyntaxDecl通过ASTBuilder::findSyntaxClass解析该类源码中可见于 slang-parser.cpp 第 5349、5709、10786 行附近的调用。这带来一条关键约束两种形式都无法引入编译器尚未声明的节点——扩展点是为既有节点提供一个新的拼写而不是创造新节点。正因如此实践中这两种声明只出现在核心模块里代表性示例见 core.meta.slang 第 28 行与第 128 行为syntax constexpr : ConstExprModifier; attribute_syntax [ExperimentalModule] : ExperimentalModuleAttribute;其中 existingKeyword别名形式会复制被指名关键字的解析回调与语法类使新拼写成为原关键字的即插即用别名drop-in alias。在该文档对应的source_commit上仓库中没有任何.slang源码使用这一别名形式。语句关键字语句关键字在语句解析器中通过直接标识符比较识别。下表汇总了各关键字及其解析位置行号对应Parser::ParseStatement及其子函数所在的 slang-parser.cpp当前源码中语句分发链位于第 7018 行起关键字解析位置if语句入口通过LookAheadToken(if)匹配若往前再前瞻两个 token 发现let则路由到parseIfLetStatement即if let绑定形式否则进入parseIfStatementelse在该函数内部消费for语句入口匹配编译期形式由parseCompileTimeStmt到达——它先读取一个$再检查for并调用parseCompileTimeForStmt。其头部不是普通的(init; cond; update)三元组而是$for(i in Range(N))读取循环变量后依次读取字面 tokenin与Range再接一个或两个区间表达式——Range(end)或Range(begin, end)while语句入口匹配do语句入口匹配break语句入口匹配continue语句入口匹配return语句入口匹配switch语句入口匹配__target_switchparseTargetSwitchStmt编译器内部__stage_switchparseStageSwitchStmt编译器内部__intrinsic_asmparseIntrinsicAsmStmt编译器内部case语句入口匹配同时在 switch 体内匹配。自allowCaseDefault参数加入后语句解析器在任何位置都接受case再予以诊断ParseCaseStmt总是构建节点而未传AllowCaseDefaultStatements::Allow的调用方会报告CaseOutsideSwitchE39999default语句入口匹配同时在 switch 体内匹配。与case同构无条件解析除非调用方允许否则在语句级报告DefaultOutsideSwitchE39999__GPU_FOREACHParseGpuForeachStmt编译器内部discard语句入口匹配defer语句入口匹配throw语句入口匹配__requireCapabilityParser::ParseRequireCapabilityStatement编译器内部catchParseDoCatchStatement由ParseDoStatement到达。catch在语句层面不与try配对这些关键字不在语法声明表中因为 Slang 把控制流视为封闭语法用户代码无法重定义。注意try是表达式关键字见下文表达式关键字语句级的异常处理器是do { ... } catch ( ... ) { ... }解析于 slang-parser.cpp 第 7482-7513 行附近。错误参数是可选的——裸catch捕获所有错误类型ParseDoCatchStatement会循环跟随其后的每个catch把每个CatchStmt作为下一个的tryBody从而一串catch子句呈嵌套结构而非扁平列表。编译器内部语句的固定形式每个编译器内部行解析的是固定形式在核心模块源码中遇到时可以据此识别__stage_switch { case stage: ... default: ... }与__target_switch共用parseTargetSwitchStmtImplcase后的标签是能力capability名由findCapabilityName解析无法识别的标签会被诊断而非当作标识符处理。__intrinsic_asm text;分号前可选地跟一个逗号分隔的参数列表例如__intrinsic_asm (gl_SubgroupID);。__GPU_FOREACH(device, gridDims, LAMBDA(uint3 id) { ... });—— 字面 tokenLAMBDA是语法的一部分不是用户提供的名字。__requireCapability(capability, ...);—— 逗号分隔的能力名列表每个同样由findCapabilityName解析。声明关键字声明关键字通过_makeParseDecl(...)注册进g_parseSyntaxEntries[]当前源码中_makeParseDecl定义于 slang-parser.cpp 第 10802 行g_parseSyntaxEntries[]自第 10831 行起。以双下划线__开头的标识符被有意地命名空间化为编译器内部 / 不稳定。关键字解析目标typedefC 风格类型别名parseTypeDeftypealiasSlang 风格类型别名parseTypeAliasDeclassociatedtype接口关联类型parseAssocType__constraint接口级约束要求parseInterfaceConstraintDecl__associatedfunc接口关联函数parseAssocFunctype_param模块级泛型类型参数parseGlobalGenericTypeParamDecl__generic泛型参数列表头部parseGenericDecl__generic_value_param模块级泛型值参数parseGlobalGenericValueParamDeclextension、__extension类型扩展parseExtensionDecl__func_extension面向自定义导数的函数扩展简写parseFuncExtensionDecl实验特性需-experimental-feature门控interface接口parseInterfaceDecl__init构造函数parseConstructorDecl__subscript下标parseSubscriptDeclproperty属性parsePropertyDeclsemanticHLSL 风格语义声明parseSemanticDeclcbufferHLSL 常量缓冲声明parseHLSLCBufferDecltbufferHLSL 纹理缓冲声明parseHLSLTBufferDeclsyntax用户自定义语法parseSyntaxDeclattribute_syntax属性语法parseAttributeSyntaxDeclimport、__import模块导入parseImportDecl__include包含指令parseIncludeDeclmodule模块声明parseModuleDeclarationDeclimplementing模块实现声明parseImplementingDecllet不可变绑定parseLetDeclvar可变绑定parseVarDeclfunc函数声明parseFuncDeclnamespace命名空间块parseNamespaceDeclusingusing 指令parseUsingDecl__ignored_block编译器内部忽略块__transparent_block编译器内部透明块__file_decl编译器内部按文件分组的声明__require_capability能力要求parseRequireCapabilityDecl三种值得展开的__形式__constraint__constraint type type;声明类型相等要求__constraint type : type;声明子类型要求。无论哪种都会成为所在接口的一个GenericTypeConstraintDecl成员用于精化This或继承自基接口的关联类型——例如interface IDerived : IBase { __constraint DataType This; }要求每个实现者的This.DataType This。它只在接口体内有意义。__associatedfunc形式为__associatedfunc function-type name;——一个类型表达式后跟需求名。核心模块用它声明自动微分函数需求例如 core.meta.slang 中的static __associatedfunc FwdDiffFuncTypeFType fwd_diff;。__func_extension接受一个高阶目标表达式、参数列表、可选的throws子句、可选的- result type与函数体。目标通过语法声明查找而非硬编码的运算符列表解析因此使用微分表达式关键字书写例如 hlsl.meta.slang 中的__func_extensionT : __BuiltinFloatingPointType, let N : int fwd_diff(CoopVecT, N::__subscript::get)(DifferentialPairCoopVecT, N self, int index) - DifferentialPairT { ... }。struct / class / enum不走语法表的特例struct、class、enum同样是声明关键字但不通过g_parseSyntaxEntries[]/_makeParseDecl注册。解析器在类型说明符解析器中通过直接标识符前瞻分发slang-parser.cpp 第 3554-3568 行附近该路径由ParseDeclWithModifiers到达专用解析例程ParseStruct、ParseClass、parseEnumDecl直接构造对应 AST 节点。在解析层面class比struct窄得多ParseClass只读取必需的名字、可选的继承子句与函数体除此之外什么都不读。ParseStruct额外接受匿名形式无标识符时合成名字、通过parseOptGenericDecl的泛型参数列表、无函数体的前向声明struct S;、以及别名形式struct S T;——因此泛型聚合类型必须写成struct因为ParseClass中没有路径消费泛型参数列表。除此之外解析器不作区分它构建ClassDecl而非StructDecl把该选择的所有语义后果留给后续阶段处理参见 pipeline/03-semantic-check.md。expand / each / functype同样由前瞻识别的类型形式同一个类型说明符解析器还通过直接标识符前瞻识别变参包类型形式expand与eachslang-parser.cpp 第 3576 行附近以及__first/__last/__trimFirst/__trimLast/__shapeConcat/__shapePermute/__shapeSwap/__shapeReduce/__packBranch等形状工具列于下文表达式关键字这些同样不在g_parseSyntaxEntries[]中。紧随其后的链上接受functype交给parseFuncTypeExpr解析函数类型——同样通过前瞻匹配而非注册为语法。拼写为functype(parameter types) - result type零个或多个逗号分隔的参数类型表达式、一个必需的-、一个结果类型。核心模块用它声明高阶参数例如 hlsl.meta.slang 中的This MapElement(functype(uint32_t, uint32_t, T) - T mapOp)——此类参数接受一个函数名作为实参。泛型参数列表接受functype F形式以声明函数类型的泛型参数。修饰符关键字修饰符关键字通过_makeParseModifier注册当前源码中定义于 slang-parser.cpp 第 10810/10821 行附近。有些是简单修饰符单个关键字、单个 AST 节点类有些带参数如layout、__target_intrinsic。简单修饰符下表每一行都用_makeParseModifier(keyword, getSyntaxClass...())重载注册其回调是parseSimpleSyntax构造类的一个实例并不读取更多 token。因此该关键字在解析期的全部含义就是第二列的节点附加于声明之上的任何其他含义都不在此处检查——各节点类的用户可见语义请查阅 ast-reference/modifiers.md例如row_major、nointerpolation的效果在该页而非本文档说明。关键字AST 节点inInModifieroutOutModifierinoutInOutModifier__refRefModifier__constrefBorrowModifierconstConstModifier__builtinBuiltinModifierhighp、lowp、mediumpGLSLPrecisionModifier__globalActualGlobalModifierinlineInlineModifierpublic、private、internalPublicModifier、PrivateModifier、InternalModifierrequireRequireModifierparamParamModifierexternExternModifierdynDynModifierrow_major、column_majorHLSLRowMajorLayoutModifier、HLSLColumnMajorLayoutModifiernointerpolation、noperspective、linear、sample、centroid、precise插值修饰符groupsharedHLSLGroupSharedModifierstaticHLSLStaticModifieruniformHLSLUniformModifierexportHLSLExportModifierdynamic_uniformDynamicUniformModifieroverrideOverrideModifierpoint、line、triangle、lineadj、triangleadj几何着色器输入修饰符vertices、indices、primitives、payload网格着色器输出修饰符__prefix、__postfix一元运算符位置修饰符__exportedre-exportimport修饰符回调解析的修饰符部分带参数关键字解析目标sharedparseSharedModifier在上下文中设置 HLSL groupshared / sharedvolatileparseVolatileModifiercoherentparseCoherentModifierrestrictparseRestrictModifierreadonlyparseReadonlyModifierwriteonlyparseWriteonlyModifierlayoutparseLayoutModifierGLSL 风格布局块hitAttributeEXTparseHitAttributeEXTModifier光追__intrinsic_opparseIntrinsicOpModifier__target_intrinsicparseTargetIntrinsicModifier__specialized_for_targetparseSpecializedForTargetModifier__glsl_extensionparseGLSLExtensionModifier__glsl_versionparseGLSLVersionModifier__spirv_versionparseSPIRVVersionModifier__wgsl_extensionparseWGSLExtensionModifier__cuda_sm_versionparseCUDASMVersionModifier__builtin_typeparseBuiltinTypeModifier__builtin_requirementparseBuiltinRequirementModifier__magic_typeparseMagicTypeModifier__magic_enumparseMagicEnumModifier__intrinsic_typeparseIntrinsicTypeModifier__implicit_conversionparseImplicitConversionModifier__attributeTargetparseAttributeTargetModifier到底哪些带参数由回调决定而非由表头决定。其中有六个不读任何 tokenshared、volatile、coherent、restrict、readonly、writeonly裸写即可其回调存在的意义是选择或复制节点而非解析操作数——shared在解析器设置allowGLSLInput选项时构建HLSLGroupSharedModifier否则构建HLSLEffectSharedModifiervolatile同时构建 HLSL 与 GLSL 两个节点并从语言版本 2025 起把该关键字诊断为已弃用、2026 起移除。hitAttributeEXT同样不带参数。其余关键字的参数形态layout接受圆括号内逗号分隔的 GLSL 限定符列表每个是裸名或name expr例如layout(local_size_x 8, std430)。__前缀各行接受圆括号参数列表标识符型——__glsl_extension(GL_KHR_shader_subgroup_basic)、__wgsl_extension(subgroups)、__specialized_for_target(glsl)、__attributeTarget(SyntaxClass)整数型——__glsl_version(430)、__builtin_type(tag)、__builtin_requirement(kind)版本型——__spirv_version(1.3)、__cuda_sm_version(7.0)major.minor或带引号的版本名字加可选标签——__magic_type(Name[, tag])与__magic_enumIR 操作码加可选整数操作数——__intrinsic_type(op[, operand]...)目标名加可选定义——__target_intrinsic(hlsl, ...)。其中四个可选项性地省略括号且省略后仍具含义裸__intrinsic_op从函数名推导操作码而非接受整数或标识符__target_intrinsic、__specialized_for_target、__implicit_conversion省略括号时回退到默认行为。表达式关键字表达式关键字通过_makeParseExpr注册当前源码中定义于 slang-parser.cpp 第 10794 行附近。关键字解析目标this自引用parseThisExprtrue、false布尔字面量nullptr空指针字面量noneOptional的 none 字面量try错误处理表达式parseTryExprno_diff不可微分包装parseTreatAsDifferentiableExpr__fwd_diff、fwd_diff前向模式微分parseForwardDifferentiate__bwd_diff、bwd_diff反向模式微分parseBackwardDifferentiate__apply面向反向微分的高阶表达式parseApplyForBwd用于__func_extension内部向自定义bwd_diff暴露带上下文的主值伴生形式实验特性new堆风格分配表达式由parsePrefixExpr定义于第 9703 行附近中的AdvanceIf(parser, new)分支特殊解析并非经由_makeParseExpr__return_val编译器内部返回值引用__func_as_type函数即类型反射__dispatch_kernel内核分发原语sizeof、alignof、countof大小 / 对齐 / 元素个数查询__first、__last、__trimFirst、__trimLast、__shapeConcat、__shapePermute、__shapeSwap、__shapeReduce、__packBranch形状 / 打包工具表达式__getAddress编译器内部取地址__floatAsInt编译器内部位重解释每行的操作数形态由回调固定且大多数但非全部带圆括号try与no_diff后接一个叶子表达式自身无括号——try f(x)、no_diff f(x)。__return_val无操作数是对待返回值的裸引用。fwd_diff/__fwd_diff、bwd_diff/__bwd_diff、__apply、__func_as_type、__getAddress、__floatAsInt、countof恰好接受一个圆括号操作数——fwd_diff(f)、countof(pack)。sizeof与alignof接受一个操作数外加可选的数据布局操作数sizeof(T)或sizeof(T, Std140DataLayout)。__first、__last、__trimFirst、__trimLast接受一个包操作数__shapePermute、__shapeReduce接受两个__shapeConcat、__shapeSwap、__packBranch接受三个__packBranch(pack, empty type, non-empty type)。__dispatch_kernel(function, dispatch size, thread-group size)接受三个。new是作用于后缀表达式的前缀运算符写作new T(args)——先解析类型名与其参数列表再折叠进NewExpr。核心模块语法声明内建词汇而非关键字source/slang/ 下的四个*.meta.slang文件向默认环境贡献额外名字。它们不是解析器语法表意义上的关键字解析器的语法查找只接受SyntaxDecl因此这些名字是内建词汇——解析器在有限的类型名分歧消解时查阅它们而不是当作解析关键字。各文件的分工如下core.meta.slang声明内建标量 / 向量 / 矩阵类型、Optional与Tuple类型以及核心内建函数。hlsl.meta.slang叠加 HLSL 兼容名字Texture2D、RWTexture2D、StructuredBuffer以及mul、dot、length等内建函数与 wave 内建函数。其中存在两种不可互换的wave 内建WaveGetWaveIndex()是普通的核心模块函数携带[require(cuda_glsl_hlsl_metal_spirv_wgsl, subgroup_workgroup_index)]与一个逐目标的__target_switch函数体源码见该文件第 17596 行起其可用性在模块自身中声明而SV_WaveIndex与SV_GroupIndex不是函数而是写在隐藏in全局变量__builtinWaveIndex、__builtinGroupIndex文件第 15 行附近的:之后的系统值语义模块未对它们附加能力要求哪些目标接受由入口点 varying 参数合法化pipeline/05-ir-passes.md决定而非在此处决定。因此WaveGetWaveIndex()是可移植拼写。同一文件还声明了描述符堆词汇——UntypedResourceHandle与UntypedSamplerHandle连同__ResourceDescriptorHeapType/__SamplerDescriptorHeapType类型其__subscript把索引转成句柄各自以[require(glsl_hlsl_spirv_wgsl, descriptor_handle)]门控使不支持的目标在索引处即被诊断。这两个类型的用户级拼写是static const全局量ResourceDescriptorHeap与SamplerDescriptorHeap表面形式为ResourceDescriptorHeap[i]。它产出的无类型句柄不被写下来每个可堆转换的资源类型都声明了一个接受该句柄的__implicit_conversion构造函数因此具体类型从赋值目标恢复如RWStructuredBufferuint buf ResourceDescriptorHeap[0];相关__init见该文件第 27738 行附近。glsl.meta.slang提供 GLSL 风格名字vec3、mat4、gl_Position等。diff.meta.slang贡献可微对differentiable-pair类型与自动微分机制使用的辅助函数参见 pipeline/05-ir-passes.md。核心模块管线的整体描述见 cross-cutting/core-module.md。保留标识符前缀__、gl_与SV_按约定以__开头的名字如__intrinsic_op、__target_intrinsic、__init、__subscript、__import、__include、__constraint、__file_decl表示编译器内部词汇用户代码不应依赖。少数有去掉下划线的公开拼写extension、import__init、__subscript、__include在g_parseSyntaxEntries[]中没有无下划线拼写。以gl_开头的名字来自 GLSL 元模块表示着色器阶段内建量。它们是普通的全局声明——public out float4 gl_Position : SV_Position;——而非某个词法类别。以SV_开头的名字HLSL 系统值语义以语义字符串而非关键字出现在语义检查阶段被识别pipeline/03-semantic-check.md。需要强调的是被禁止 / 保留的集合并非词法强制而是由元模块编码的策略两个前缀都只是建议性的两者背后都没有诊断支撑因此用户声明一个以它们开头的名字就是普通声明。解析器只在一处检查gl_前缀——GLSL 接口块名字以gl_开头时被视为内建块的重声明并替换为EmptyDecl——且从不检查SV_。解析器唯一的保留名检查是isReservedKeywordNameslang-parser.cpp 第 2552 行当声明符命名为struct、class、enum、typealias、typedef这五个开启类型说明符的拼写时发出KeywordUsedAsName警告第 2571 行起的maybeDiagnoseKeywordUsedAsName负责发出。之所以是警告而非错误是因为这些名字在语句头部无法被引用如struct ...;会被解析为畸形的声明而非赋值但在某些表达式上下文中仍可用。其注释说明了其余词汇遵循的规则几乎每个关键字都是上下文相关的可被用户定义的名字遮蔽——包括func、let、var、interface、extension、import这样的声明关键字。__前缀是真正承重的一个且只对上述表格列出的确切拼写承重只有__init、__subscript、__include的下划线形式注册在g_parseSyntaxEntries[]中裸的init、subscript、include是普通标识符可以用作函数名并在语句头部调用。实践要点如何读懂或新增一个关键字综合全文可以把 Slang 关键字的生命周期归纳为一张决策图供新增关键字或排查问题使用先问它是否是字母型关键字——如果是词法器不认识它它必然以TokenType::Identifier流入解析器。再判断它属于哪一类语法位置——语句关键字由Parser::ParseStatement中的LookAheadToken链直接匹配不可扩展声明 / 修饰符 / 表达式关键字注册在g_parseSyntaxEntries[]可通过syntax/attribute_syntax扩展。对__前缀保持警惕——它是编译器内部词汇的标识只有本文表格列出的确切拼写是保留的裸init、subscript、include都是普通标识符。对gl_/SV_前缀保持预期——它们是元模块中的普通声明与语义字符串解析器几乎不特殊处理。修改落点新增一个关键字要么改slang-parser.cpp的语句前瞻链封闭语法要么改g_parseSyntaxEntries[]_makeParseDecl/_makeParseModifier/_makeParseExpr要么在*.meta.slang中通过syntax/attribute_syntax为既有 AST 节点提供新拼写——永远不需要动词法器。别名形式syntax name existingKeyword;是现成的drop-in 别名通道尽管当前source_commit上仓库内尚无.slang源码使用它。相关参考token 全集见 tokens.md语法整体结构见 grammar.md修饰符的用户可见语义见 ast-reference/modifiers.md。赞分享编译器图形学编程语言【免费下载链接】slangMaking it easier to work with shaders项目地址https://gitcode.com/GitHub_Trending/sl/slang点击查看免费下载相关推荐Slang 关键词与内置语法全解析语法声明模型、核心模块词汇与保留前缀Slang 关键词与内置语法全解析语法声明模型、核心模块词汇与保留前缀 本篇技术指南系统梳理 Slang 编译器中关键词的真实来源与注册机制绝大多数看似编译器图形学编程语言Slang 关键字与内置语法体系全解从语法表到 meta-module 的完整实现脉络Slang 关键字与内置语法体系全解从语法表到 meta module 的完整实现脉络 导读 本文以 Slang 仓库中 keywords and built编译器图形学编程语言Slang 关键字与内建词元识别机制从解析器语法表到测试验证体系Slang 关键字与内建词元识别机制从解析器语法表到测试验证体系 本篇技术指南围绕 Slang 编译器中一个反直觉的核心事实展开 绝大多数关键字并不是词编译器图形学编程语言创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考