Source code for crosstl.backend.slang.SlangCrossGLCodeGen

"""Reverse code generator that emits CrossGL from Slang AST nodes."""

from .SlangAst import *
from .SlangParser import *
from .SlangLexer import *


[docs] class SlangToCrossGLConverter: """Serialize Slang backend AST nodes back into CrossGL source.""" def __init__(self): """Initialize Slang-to-CrossGL type, semantic, and resource mappings.""" self.vertex_inputs = [] self.vertex_outputs = [] self.fragment_inputs = [] self.fragment_outputs = [] self.cbuffers = [] self.type_map = { "void": "void", "float2": "vec2", "float3": "vec3", "float4": "vec4", "float2x2": "mat2", "float3x3": "mat3", "float4x4": "mat4", "int": "int", "int2": "ivec2", "int3": "ivec3", "int4": "ivec4", "uint": "uint", "uint2": "uvec2", "uint3": "uvec3", "uint4": "uvec4", "bool": "bool", "bool2": "bvec2", "bool3": "bvec3", "bool4": "bvec4", "float": "float", "double": "double", "Texture2D": "sampler2D", "TextureCube": "samplerCube", } self.semantic_map = { # Vertex inputs position "POSITION": "in_Position", "POSITION0": "in_Position0", "POSITION1": "in_Position1", "POSITION2": "in_Position2", "POSITION3": "in_Position3", "POSITION4": "in_Position4", "POSITION5": "in_Position5", "POSITION6": "in_Position6", "POSITION7": "in_Position7", # Vertex inputs normal "NORMAL": "in_Normal", "NORMAL0": "in_Normal0", "NORMAL1": "in_Normal1", "NORMAL2": "in_Normal2", "NORMAL3": "in_Normal3", "NORMAL4": "in_Normal4", "NORMAL5": "in_Normal5", "NORMAL6": "in_Normal6", "NORMAL7": "in_Normal7", # Vertex inputs tangent "TANGENT": "in_Tangent", "TANGENT0": "in_Tangent0", "TANGENT1": "in_Tangent1", "TANGENT2": "in_Tangent2", "TANGENT3": "in_Tangent3", "TANGENT4": "in_Tangent4", "TANGENT5": "in_Tangent5", "TANGENT6": "in_Tangent6", "TANGENT7": "in_Tangent7", # Vertex inputs binormal "BINORMAL": "in_Binormal", "BINORMAL0": "in_Binormal0", "BINORMAL1": "in_Binormal1", "BINORMAL2": "in_Binormal2", "BINORMAL3": "in_Binormal3", "BINORMAL4": "in_Binormal4", "BINORMAL5": "in_Binormal5", "BINORMAL6": "in_Binormal6", "BINORMAL7": "in_Binormal7", # Vertex inputs color "COLOR": "Color", "COLOR0": "Color0", "COLOR1": "Color1", "COLOR2": "Color2", "COLOR3": "Color3", "COLOR4": "Color4", "COLOR5": "Color5", "COLOR6": "Color6", "COLOR7": "Color7", # Vertex inputs texcoord "TEXCOORD": "TexCoord", "TEXCOORD0": "TexCoord0", "TEXCOORD1": "TexCoord1", "TEXCOORD2": "TexCoord2", "TEXCOORD3": "TexCoord3", "TEXCOORD4": "TexCoord4", "TEXCOORD5": "TexCoord5", "TEXCOORD6": "TexCoord6", # Vertex inputs instance "FRONT_FACE": "gl_IsFrontFace", "PRIMITIVE_ID": "gl_PrimitiveID", "INSTANCE_ID": "gl_InstanceID", "VERTEX_ID": "gl_VertexID", # Vertex outputs "SV_Position": "Out_Position", "SV_Position0": "Out_Position0", "SV_Position1": "Out_Position1", "SV_Position2": "Out_Position2", "SV_Position3": "Out_Position3", "SV_Position4": "Out_Position4", "SV_Position5": "Out_Position5", "SV_Position6": "Out_Position6", "SV_Position7": "Out_Position7", # Fragment inputs "SV_Target": "Out_Color", "SV_Target0": "Out_Color0", "SV_Target1": "Out_Color1", "SV_Target2": "Out_Color2", "SV_Target3": "Out_Color3", "SV_Target4": "Out_Color4", "SV_Target5": "Out_Color5", "SV_Target6": "Out_Color6", "SV_Target7": "Out_Color7", "SV_Depth": "Out_Depth", "SV_Depth0": "Out_Depth0", "SV_Depth1": "Out_Depth1", "SV_Depth2": "Out_Depth2", "SV_Depth3": "Out_Depth3", "SV_Depth4": "Out_Depth4", "SV_Depth5": "Out_Depth5", "SV_Depth6": "Out_Depth6", "SV_Depth7": "Out_Depth7", }
[docs] def generate(self, ast): """Generate complete CrossGL source from a parsed Slang AST.""" code = "shader main {\n" if ast.imports: for imp in ast.imports: code += f" import {imp.module_name};\n" code += "\n" if ast.exports: for exp in ast.exports: code += f" export {exp.item};\n" code += "\n" for node in ast.typedefs: code += ( f" typedef {self.map_type(node.original_type)} {node.new_type};\n" ) for node in ast.structs: if isinstance(node, StructNode): code += f" struct {node.name} {{\n" for member in node.members: code += f" {self.map_type(member.vtype)} {member.name} {self.map_semantic(member.semantic)};\n" code += " }\n" for node in ast.global_vars: code += f" {self.map_type(node.vtype)} {node.name};\n" if ast.cbuffers: code += " // Constant Buffers\n" code += self.generate_cbuffers(ast) for func in ast.functions: if func.qualifier == "vertex": code += " vertex {\n" code += self.generate_function(func) code += " }\n\n" elif func.qualifier == "fragment": code += " fragment {\n" code += self.generate_function(func) code += " }\n\n" elif func.qualifier == "compute": code += " compute {\n" code += self.generate_function(func) code += " }\n\n" else: code += self.generate_function(func) code += "}\n" return code
def generate_cbuffers(self, ast): code = "" for node in ast.cbuffers: if isinstance(node, StructNode): code += f" cbuffer {node.name} {{\n" for member in node.members: code += f" {self.map_type(member.vtype)} {member.name};\n" code += " }\n" return code
[docs] def generate_function(self, func, indent=1): """Render one Slang function node as a CrossGL function.""" code = " " code += " " * indent params = ", ".join( f"{self.map_type(p.vtype)} {p.name} {self.map_semantic(p.semantic)}" for p in func.params ) code += f" {self.map_type(func.return_type)} {func.name}({params}) {self.map_semantic(func.semantic)} {{\n" code += self.generate_function_body(func.body, indent=indent + 1) code += " }\n\n" return code
def generate_function_body(self, body, indent=0, is_main=False): code = "" for stmt in body: code += " " * indent if isinstance(stmt, VariableNode): code += f"{self.map_type(stmt.vtype)} {stmt.name};\n" elif isinstance(stmt, AssignmentNode): code += self.generate_assignment(stmt, is_main) + ";\n" elif isinstance(stmt, BinaryOpNode): code += f"{self.generate_expression(stmt.left, is_main)} {stmt.op} {self.generate_expression(stmt.right, is_main)};\n" elif isinstance(stmt, ReturnNode): if not is_main: code += f"return {self.generate_expression(stmt.value, is_main)};\n" elif isinstance(stmt, ForNode): code += self.generate_for_loop(stmt, indent, is_main) elif isinstance(stmt, IfNode): code += self.generate_if_statement(stmt, indent, is_main) return code def generate_for_loop(self, node, indent, is_main): init = self.generate_expression(node.init, is_main) condition = self.generate_expression(node.condition, is_main) update = self.generate_expression(node.update, is_main) code = f"for ({init}; {condition}; {update}) {{\n" code += self.generate_function_body(node.body, indent + 1, is_main) code += " " * indent + "}\n" return code def generate_if_statement(self, node, indent, is_main): condition = self.generate_expression(node.condition, is_main) code = f"if ({condition}) {{\n" code += self.generate_function_body(node.if_body, indent + 1, is_main) code += " " * indent + "}" if node.else_body: if isinstance(node.else_body, IfNode): code += " else " code += self.generate_if_statement(node.else_body, indent, is_main) else: code += " else {\n" code += self.generate_function_body(node.else_body, indent + 1, is_main) code += " " * indent + "}" code += "\n" return code def generate_assignment(self, node, is_main): lhs = self.generate_expression(node.left, is_main) rhs = self.generate_expression(node.right, is_main) op = node.operator return f"{lhs} {op} {rhs}"
[docs] def generate_expression(self, expr, is_main=False): """Render a Slang backend expression node as CrossGL syntax.""" if isinstance(expr, str): return expr elif isinstance(expr, VariableNode): return f"{self.map_type(expr.vtype)} {expr.name}" elif isinstance(expr, BinaryOpNode): left = self.generate_expression(expr.left, is_main) right = self.generate_expression(expr.right, is_main) return f"{left} {expr.op} {right}" elif isinstance(expr, AssignmentNode): left = self.generate_expression(expr.left, is_main) right = self.generate_expression(expr.right, is_main) return f"{left} {expr.operator} {right}" elif isinstance(expr, UnaryOpNode): operand = self.generate_expression(expr.operand, is_main) return f"{expr.op}{operand}" elif isinstance(expr, FunctionCallNode): args = ", ".join( self.generate_expression(arg, is_main) for arg in expr.args ) return f"{expr.name}({args})" elif isinstance(expr, MemberAccessNode): obj = self.generate_expression(expr.object) return f"{obj}.{expr.member}" elif isinstance(expr, TernaryOpNode): return f"{self.generate_expression(expr.condition, is_main)} ? {self.generate_expression(expr.true_expr, is_main)} : {self.generate_expression(expr.false_expr, is_main)}" elif isinstance(expr, VectorConstructorNode): args = ", ".join( self.generate_expression(arg, is_main) for arg in expr.args ) return f"{self.map_type(expr.type_name)}({args})" else: return str(expr)
[docs] def map_type(self, slang_type): """Map a Slang type name to the closest CrossGL type name.""" if slang_type: return self.type_map.get(slang_type, slang_type) return slang_type
[docs] def map_semantic(self, semantic): """Map a Slang semantic to CrossGL semantic annotation syntax.""" if semantic is not None: return f"@ {self.semantic_map.get(semantic, semantic)}" else: return ""