Source code for crosstl.backend.DirectX.DirectxCrossGLCodeGen

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

from .DirectxAst import *
from .DirectxParser import *
from .DirectxLexer import *
from ..common_ast import (
    ArrayAccessNode,
    BreakNode,
    CastNode,
    ContinueNode,
    TextureSampleNode,
)


[docs] class HLSLToCrossGLConverter: """Serialize DirectX backend AST nodes back into CrossGL source.""" def __init__(self): """Initialize HLSL-to-CrossGL type, function, and semantic mappings.""" self.type_map = { # Scalar Types "void": "void", "bool": "bool", "int": "int", "uint": "uint", "dword": "uint", "float": "float", "half": "float16", "double": "double", "min16float": "float16", "min10float": "float16", "min16int": "int16", "min12int": "int16", "min16uint": "uint16", "int64_t": "int64", "uint64_t": "uint64", # Vector Types - float "float2": "vec2", "float3": "vec3", "float4": "vec4", # Vector Types - half "half2": "f16vec2", "half3": "f16vec3", "half4": "f16vec4", # Vector Types - double "double2": "dvec2", "double3": "dvec3", "double4": "dvec4", # Vector Types - int "int2": "ivec2", "int3": "ivec3", "int4": "ivec4", # Vector Types - uint "uint2": "uvec2", "uint3": "uvec3", "uint4": "uvec4", # Vector Types - bool "bool2": "bvec2", "bool3": "bvec3", "bool4": "bvec4", # Matrix Types - float "float2x2": "mat2", "float2x3": "mat2x3", "float2x4": "mat2x4", "float3x2": "mat3x2", "float3x3": "mat3", "float3x4": "mat3x4", "float4x2": "mat4x2", "float4x3": "mat4x3", "float4x4": "mat4", # Matrix Types - half "half2x2": "f16mat2", "half2x3": "f16mat2x3", "half2x4": "f16mat2x4", "half3x2": "f16mat3x2", "half3x3": "f16mat3", "half3x4": "f16mat3x4", "half4x2": "f16mat4x2", "half4x3": "f16mat4x3", "half4x4": "f16mat4", # Matrix Types - double "double2x2": "dmat2", "double2x3": "dmat2x3", "double2x4": "dmat2x4", "double3x2": "dmat3x2", "double3x3": "dmat3", "double3x4": "dmat3x4", "double4x2": "dmat4x2", "double4x3": "dmat4x3", "double4x4": "dmat4", # Texture Types "Texture1D": "sampler1D", "Texture1DArray": "sampler1DArray", "Texture2D": "sampler2D", "Texture3D": "sampler3D", "TextureCube": "samplerCube", "Texture2DArray": "sampler2DArray", "TextureCubeArray": "samplerCubeArray", "Texture2DMS": "sampler2DMS", "Texture2DMSArray": "sampler2DMSArray", "FeedbackTexture2D": "feedbackTexture2D", "FeedbackTexture2DArray": "feedbackTexture2DArray", # RW Texture Types (for compute shaders) "RWTexture1D": "image1D", "RWTexture1DArray": "image1DArray", "RWTexture2D": "image2D", "RWTexture2DArray": "image2DArray", "RWTexture2DMS": "image2DMS", "RWTexture2DMSArray": "image2DMSArray", "RWTexture3D": "image3D", "RWTextureCube": "imageCube", "RWTextureCubeArray": "imageCubeArray", # Buffer Types "Buffer": "samplerBuffer", "RWBuffer": "imageBuffer", "StructuredBuffer": "buffer", "RWStructuredBuffer": "buffer", "ByteAddressBuffer": "buffer", "RWByteAddressBuffer": "buffer", "AppendStructuredBuffer": "buffer", "ConsumeStructuredBuffer": "buffer", "RaytracingAccelerationStructure": "accelerationStructure", "RayQuery": "rayQuery", "InputPatch": "inputPatch", "OutputPatch": "outputPatch", "PointStream": "pointStream", "LineStream": "lineStream", "TriangleStream": "triangleStream", # Sampler Types "SamplerState": "sampler", "SamplerComparisonState": "samplerShadow", } self.function_map = { "lerp": "mix", "rsqrt": "inverseSqrt", } self.interlocked_map = { "InterlockedAdd": "atomicAdd", "InterlockedAnd": "atomicAnd", "InterlockedOr": "atomicOr", "InterlockedXor": "atomicXor", "InterlockedMin": "atomicMin", "InterlockedMax": "atomicMax", "InterlockedExchange": "atomicExchange", "InterlockedCompareExchange": "atomicCompareExchange", } self.texture_method_map = { "Sample": "texture_sample", "SampleLevel": "texture_sample_level", "SampleGrad": "texture_sample_grad", "SampleBias": "texture_sample_bias", "SampleCmp": "texture_sample_cmp", "SampleCmpLevelZero": "texture_sample_cmp_level_zero", "Load": "texture_load", "Gather": "texture_gather", "GatherRed": "texture_gather_red", "GatherGreen": "texture_gather_green", "GatherBlue": "texture_gather_blue", "GatherAlpha": "texture_gather_alpha", "GetDimensions": "texture_dimensions", } self.buffer_method_map = { "Load": "buffer_load", "Store": "buffer_store", "Append": "buffer_append", "Consume": "buffer_consume", "GetDimensions": "buffer_dimensions", } self.semantic_map = { # System-value semantics - Vertex inputs "SV_VertexID": "gl_VertexID", "SV_InstanceID": "gl_InstanceID", "SV_PrimitiveID": "gl_PrimitiveID", # System-value semantics - Vertex outputs "SV_POSITION": "gl_Position", "SV_Position": "gl_Position", "SV_ClipDistance": "gl_ClipDistance", "SV_CullDistance": "gl_CullDistance", # System-value semantics - Fragment inputs "SV_IsFrontFace": "gl_FrontFacing", "SV_SampleIndex": "gl_SampleID", "SV_Coverage": "gl_SampleMask", # System-value semantics - Fragment outputs "SV_TARGET": "gl_FragColor", "SV_Target": "gl_FragColor", "SV_TARGET0": "gl_FragData[0]", "SV_Target0": "gl_FragData[0]", "SV_TARGET1": "gl_FragData[1]", "SV_Target1": "gl_FragData[1]", "SV_TARGET2": "gl_FragData[2]", "SV_Target2": "gl_FragData[2]", "SV_TARGET3": "gl_FragData[3]", "SV_Target3": "gl_FragData[3]", "SV_TARGET4": "gl_FragData[4]", "SV_Target4": "gl_FragData[4]", "SV_TARGET5": "gl_FragData[5]", "SV_Target5": "gl_FragData[5]", "SV_TARGET6": "gl_FragData[6]", "SV_Target6": "gl_FragData[6]", "SV_TARGET7": "gl_FragData[7]", "SV_Target7": "gl_FragData[7]", "SV_DEPTH": "gl_FragDepth", "SV_Depth": "gl_FragDepth", "SV_DepthGreaterEqual": "gl_FragDepth", "SV_DepthLessEqual": "gl_FragDepth", # System-value semantics - Compute shader "SV_GroupID": "gl_WorkGroupID", "SV_GroupThreadID": "gl_LocalInvocationID", "SV_DispatchThreadID": "gl_GlobalInvocationID", "SV_GroupIndex": "gl_LocalInvocationIndex", # Geometry shader semantics "SV_GSInstanceID": "gl_InvocationID", "SV_RenderTargetArrayIndex": "gl_Layer", "SV_ViewportArrayIndex": "gl_ViewportIndex", # Tessellation semantics "SV_OutputControlPointID": "gl_InvocationID", "SV_TessFactor": "gl_TessLevelOuter", "SV_InsideTessFactor": "gl_TessLevelInner", "SV_DomainLocation": "gl_TessCoord", # Mesh/Task semantics "SV_ViewID": "gl_ViewID", "SV_DispatchMeshID": "mesh_DispatchMeshID", # Raytracing semantics "SV_RayFlags": "rt_RayFlags", "SV_CullMask": "rt_CullMask", "SV_ObjectRayOrigin": "rt_ObjectRayOrigin", "SV_ObjectRayDirection": "rt_ObjectRayDirection", "SV_WorldRayOrigin": "rt_WorldRayOrigin", "SV_WorldRayDirection": "rt_WorldRayDirection", "SV_RayTMin": "rt_RayTMin", "SV_RayTCurrent": "rt_RayTCurrent", "SV_HitKind": "rt_HitKind", "SV_InstanceIndex": "rt_InstanceIndex", "SV_PrimitiveIndex": "rt_PrimitiveIndex", "SV_GeometryIndex": "rt_GeometryIndex", "SV_RayContributionToHitGroupIndex": "rt_RayContributionToHitGroupIndex", "SV_ShaderIndex": "rt_ShaderIndex", # Legacy semantics "FRONT_FACE": "gl_FrontFacing", "PRIMITIVE_ID": "gl_PrimitiveID", "INSTANCE_ID": "gl_InstanceID", "VERTEX_ID": "gl_VertexID", # User-defined semantics "POSITION": "Position", "POSITION0": "Position", "NORMAL": "Normal", "NORMAL0": "Normal", "TANGENT": "Tangent", "TANGENT0": "Tangent", "BINORMAL": "Binormal", "BINORMAL0": "Binormal", "TEXCOORD": "TexCoord", "TEXCOORD0": "TexCoord0", "TEXCOORD1": "TexCoord1", "TEXCOORD2": "TexCoord2", "TEXCOORD3": "TexCoord3", "TEXCOORD4": "TexCoord4", "TEXCOORD5": "TexCoord5", "TEXCOORD6": "TexCoord6", "TEXCOORD7": "TexCoord7", "COLOR": "Color", "COLOR0": "Color0", "COLOR1": "Color1", "BLENDWEIGHT": "BlendWeight", "BLENDINDICES": "BlendIndices", "PSIZE": "PointSize", "FOG": "Fog", } self.bitwise_op_map = { "&": "bitAnd", "|": "bitOr", "^": "bitXor", "~": "bitNot", "<<": "bitShiftLeft", ">>": "bitShiftRight", } self.indentation = 0 self.code = [] def get_indent(self): return " " * self.indentation def format_array_suffixes(self, node, is_main=False): sizes = getattr(node, "array_sizes", None) if not sizes: return "" parts = [] for size in sizes: if size is None: parts.append("[]") else: parts.append(f"[{self.generate_expression(size, is_main)}]") return "".join(parts) def format_attributes(self, attributes, indent): if not attributes: return "" lines = "" for attr in attributes: args = getattr(attr, "args", getattr(attr, "arguments", [])) if args: rendered_args = ", ".join(self.generate_expression(arg) for arg in args) lines += " " * indent + f"@ {attr.name}({rendered_args})\n" else: lines += " " * indent + f"@ {attr.name}\n" return lines def format_binding_attributes(self, node, indent): lines = "" register = getattr(node, "register", None) packoffset = getattr(node, "packoffset", None) if register: parts = [part.strip() for part in str(register).split(",") if part.strip()] rendered = ", ".join(parts) lines += " " * indent + f"@ register({rendered})\n" if packoffset: lines += " " * indent + f"@ packoffset({packoffset})\n" return lines def visit(self, node): if isinstance(node, SwitchStatementNode): return self.visit_SwitchStatementNode(node) elif isinstance(node, SwitchCaseNode): return self.visit_SwitchCaseNode(node) elif isinstance(node, StructNode): return self.visit_StructNode(node) elif isinstance(node, BinaryOpNode): return self.visit_BinaryOpNode(node) elif isinstance(node, UnaryOpNode): return self.visit_UnaryOpNode(node) if hasattr(self, f"generate_{type(node).__name__}"): method = getattr(self, f"generate_{type(node).__name__}") return method(node) return self.generate_expression(node)
[docs] def generate(self, ast): """Generate a complete CrossGL shader from a parsed HLSL AST.""" code = "shader main {\n" typedefs = getattr(ast, "typedefs", []) or [] enums = getattr(ast, "enums", []) or [] if typedefs: for alias in typedefs: alias_type = getattr(alias, "alias_type", None) or getattr( alias, "original_type", None ) if alias_type is not None: code += f" typedef {self.map_type(alias_type)} {alias.name};\n" if enums: for enum in enums: if isinstance(enum, EnumNode): code += f" enum {enum.name} {{\n" for member_name, member_value in enum.members: if member_value is None: code += f" {member_name},\n" else: code += ( f" {member_name} = " f"{self.generate_expression(member_value)},\n" ) code += " }\n" # Generate structs for node in ast.structs: if isinstance(node, StructNode): code += f" struct {node.name} {{\n" for member in node.members: array_suffix = self.format_array_suffixes(member) semantic = self.map_semantic(member.semantic) semantic = f" {semantic}" if semantic else "" code += ( f" {self.map_type(member.vtype)} " f"{member.name}{array_suffix}{semantic};\n" ) code += " }\n" elif isinstance(node, PragmaNode): code += f" #pragma {node.directive} {node.value};\n" elif isinstance(node, IncludeNode): code += f" #include {node.path}\n" for node in ast.global_variables: code += self.format_attributes(getattr(node, "attributes", []), 1) code += self.format_binding_attributes(node, 1) array_suffix = self.format_array_suffixes(node) code += f" {self.map_type(node.vtype)} {node.name}{array_suffix};\n" if ast.cbuffers: code += " // Constant Buffers\n" code += self.generate_cbuffers(ast) stage_map = { "vertex": "vertex", "fragment": "fragment", "compute": "compute", "geometry": "geometry", "tessellation_control": "tessellation_control", "tessellation_evaluation": "tessellation_evaluation", "mesh": "mesh", "task": "task", "ray_generation": "ray_generation", "ray_intersection": "ray_intersection", "ray_closest_hit": "ray_closest_hit", "ray_miss": "ray_miss", "ray_any_hit": "ray_any_hit", "ray_callable": "ray_callable", } for func in ast.functions: stage_name = stage_map.get(func.qualifier) if stage_name: code += f" // {stage_name} Shader\n" code += f" {stage_name} {{\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 += self.format_binding_attributes(node, 1) code += f" cbuffer {node.name} {{\n" for member in node.members: array_suffix = self.format_array_suffixes(member) code += ( f" {self.map_type(member.vtype)} " f"{member.name}{array_suffix};\n" ) code += " }\n" return code
[docs] def generate_function(self, func, indent=1): """Render one HLSL function node as a CrossGL function block.""" code = self.format_attributes(getattr(func, "attributes", []), indent) code += " " * indent params = ", ".join( f"{self.map_type(p.vtype)} {p.name}{self.format_array_suffixes(p)}" f"{(' ' + self.map_semantic(p.semantic)) if self.map_semantic(p.semantic) else ''}" for p in func.params ) semantic = self.map_semantic(func.semantic) semantic = f" {semantic}" if semantic else "" code += ( f"{self.map_type(func.return_type)} {func.name}({params}){semantic} {{\n" ) code += self.generate_function_body(func.body, indent=indent + 1) code += " " * indent + "}\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): array_suffix = self.format_array_suffixes(stmt, is_main) if stmt.value is not None: value = self.generate_expression(stmt.value, is_main) code += ( f"{self.map_type(stmt.vtype)} {stmt.name}{array_suffix} = " f"{value};\n" ) else: code += f"{self.map_type(stmt.vtype)} {stmt.name}{array_suffix};\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, UnaryOpNode): code += f"{self.generate_expression(stmt, is_main)};\n" elif isinstance(stmt, ReturnNode): if stmt.value is None: code += "return;\n" elif 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, WhileNode): code += self.generate_while_loop(stmt, indent, is_main) elif isinstance(stmt, DoWhileNode): code += self.generate_do_while_loop(stmt, indent, is_main) elif isinstance(stmt, IfNode): code += self.generate_if_statement(stmt, indent, is_main) elif isinstance(stmt, SwitchNode): code += self.generate_switch_statement(stmt, indent, is_main) elif isinstance(stmt, BreakNode): code += "break;\n" elif isinstance(stmt, ContinueNode): code += "continue;\n" elif isinstance(stmt, FunctionCallNode): code += f"{self.generate_expression(stmt, is_main)};\n" elif isinstance(stmt, str): code += f"{stmt};\n" else: code += f"// Unhandled statement type: {type(stmt).__name__}\n" return code def format_float(self, value: float) -> str: text = format(value, ".10f") text = text.rstrip("0").rstrip(".") if text in ("", "-0"): text = "0" if "." not in text and "e" not in text.lower(): text += ".0" return text def maybe_parenthesize(self, expr, rendered: str) -> str: if isinstance(expr, (BinaryOpNode, TernaryOpNode, AssignmentNode)): return f"({rendered})" return rendered def generate_for_loop(self, node, indent, is_main): if isinstance(node.init, VariableNode): array_suffix = self.format_array_suffixes(node.init, is_main) init = f"{self.map_type(node.init.vtype)} {node.init.name}{array_suffix}" if node.init.value is not None: init += f" = {self.generate_expression(node.init.value, is_main)}" elif node.init is None: init = "" else: init = self.generate_expression(node.init, is_main) condition = ( self.generate_expression(node.condition, is_main) if node.condition is not None else "" ) update = ( self.generate_expression(node.update, is_main) if node.update is not None else "" ) 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_while_loop(self, node, indent, is_main): condition = self.generate_expression(node.condition, is_main) code = f"while ({condition}) {{\n" code += self.generate_function_body(node.body, indent + 1, is_main) code += " " * indent + "}\n" return code def generate_do_while_loop(self, node, indent, is_main): condition = self.generate_expression(node.condition, is_main) code = "while (true) {\n" code += self.generate_function_body(node.body, indent + 1, is_main) code += " " * (indent + 1) + f"if (!({condition})) {{\n" code += " " * (indent + 2) + "break;\n" code += " " * (indent + 1) + "}\n" 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 DirectX backend expression node as CrossGL syntax.""" if isinstance(expr, str): return expr elif isinstance(expr, VariableNode): return expr.name elif isinstance(expr, BinaryOpNode): left = self.generate_expression(expr.left, is_main) right = self.generate_expression(expr.right, is_main) left = self.maybe_parenthesize(expr.left, left) right = self.maybe_parenthesize(expr.right, right) 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) op = expr.operator return f"{left} {op} {right}" elif isinstance(expr, UnaryOpNode): operand = self.generate_expression(expr.operand, is_main) operand = self.maybe_parenthesize(expr.operand, operand) if getattr(expr, "is_postfix", False): return f"{operand}{expr.op}" return f"{expr.op}{operand}" elif isinstance(expr, FunctionCallNode): args = ", ".join( self.generate_expression(arg, is_main) for arg in expr.args ) if isinstance(expr.name, MemberAccessNode): obj = self.generate_expression(expr.name.object, is_main) member = expr.name.member if member == "Load": if len(expr.args) <= 1: return f"{self.buffer_method_map['Load']}({obj}, {args})" return f"{self.texture_method_map['Load']}({obj}, {args})" if member == "GetDimensions": if len(expr.args) <= 1: return ( f"{self.buffer_method_map['GetDimensions']}({obj}, {args})" ) return f"{self.texture_method_map['GetDimensions']}({obj}, {args})" if member in self.texture_method_map: return f"{self.texture_method_map[member]}({obj}, {args})" if member in self.buffer_method_map: return f"{self.buffer_method_map[member]}({obj}, {args})" return f"{obj}.{member}({args})" func_name = ( expr.name if isinstance(expr.name, str) else self.generate_expression(expr.name, is_main) ) if func_name == "saturate": if expr.args: return f"clamp({self.generate_expression(expr.args[0], is_main)}, 0.0, 1.0)" return "clamp(0.0, 0.0, 1.0)" func_name = self.function_map.get(func_name, func_name) func_name = self.interlocked_map.get(func_name, func_name) return f"{func_name}({args})" elif isinstance(expr, MemberAccessNode): obj = self.generate_expression(expr.object, is_main) return f"{obj}.{expr.member}" elif isinstance(expr, ArrayAccessNode): array = self.generate_expression(expr.array, is_main) index = self.generate_expression(expr.index, is_main) return f"{array}[{index}]" elif isinstance(expr, CastNode): target_type = self.map_type(expr.target_type) expression = self.generate_expression(expr.expression, is_main) return f"{target_type}({expression})" elif isinstance(expr, TextureSampleNode): texture = self.generate_expression(expr.texture, is_main) sampler = self.generate_expression(expr.sampler, is_main) coords = self.generate_expression(expr.coordinates, is_main) if getattr(expr, "lod", None) is not None: lod = self.generate_expression(expr.lod, is_main) return f"texture_sample_level({texture}, {sampler}, {coords}, {lod})" return f"texture_sample({texture}, {sampler}, {coords})" 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})" elif isinstance(expr, bool): return "true" if expr else "false" elif isinstance(expr, float): return self.format_float(expr) elif isinstance(expr, int): return str(expr) else: return str(expr)
[docs] def map_type(self, hlsl_type): """Map an HLSL type name to the closest CrossGL type name.""" if not hlsl_type: return hlsl_type type_name = hlsl_type if "<" in type_name and type_name.endswith(">"): base, _ = type_name.split("<", 1) type_name = base return self.type_map.get(type_name, type_name)
[docs] def map_semantic(self, semantic): """Map an HLSL semantic to CrossGL semantic annotation syntax.""" if not semantic: return "" mapped = self.semantic_map.get(semantic) if mapped is None and isinstance(semantic, str): mapped = self.semantic_map.get(semantic.upper()) mapped = mapped or semantic return f"@ {mapped}"
def generate_switch_statement(self, node, indent=1, is_main=False): expression = getattr(node, "expression", None) or getattr( node, "condition", None ) code = ( " " * indent + f"switch ({self.generate_expression(expression, is_main)}) {{\n" ) for case in node.cases: code += ( " " * (indent + 1) + f"case {self.generate_expression(case.value, is_main)}:\n" ) case_body = getattr(case, "body", None) or getattr(case, "statements", []) code += self.generate_function_body(case_body, indent + 2, is_main) default_body = ( getattr(node, "default_body", None) or getattr(node, "default_case", None) or getattr(node, "default", None) ) if default_body: code += " " * (indent + 1) + "default:\n" code += self.generate_function_body(default_body, indent + 2, is_main) code += " " * indent + "}\n" return code def visit_BinaryOpNode(self, node): if hasattr(node.left, "visit"): left = node.visit_child(self, node.left) else: left = self.generate_expression(node.left) if hasattr(node.right, "visit"): right = node.visit_child(self, node.right) else: right = self.generate_expression(node.right) if hasattr(node.op, "token_type"): if node.op.token_type in ("BITWISE_AND", "AMPERSAND", "&"): return f"({left} & {right})" elif node.op.token_type in ("BITWISE_OR", "PIPE", "|"): return f"({left} | {right})" elif node.op.token_type in ("BITWISE_XOR", "CARET", "^"): return f"({left} ^ {right})" elif hasattr(node.op, "value"): if node.op.value in ("&", "BITWISE_AND", "AMPERSAND"): return f"({left} & {right})" elif node.op.value in ("|", "BITWISE_OR", "PIPE"): return f"({left} | {right})" elif node.op.value in ("^", "BITWISE_XOR", "CARET"): return f"({left} ^ {right})" elif isinstance(node.op, str): if node.op in ("&", "BITWISE_AND", "AMPERSAND"): return f"({left} & {right})" elif node.op in ("|", "BITWISE_OR", "PIPE"): return f"({left} | {right})" elif node.op in ("^", "BITWISE_XOR", "CARET"): return f"({left} ^ {right})" op_str = node.op.value if hasattr(node.op, "value") else str(node.op) return f"{left} {op_str} {right}" def visit_UnaryOpNode(self, node): if hasattr(node, "expr"): expr_node = node.expr else: expr_node = node.operand if hasattr(expr_node, "visit"): expr = node.visit_child(self, expr_node) else: expr = self.generate_expression(expr_node) if hasattr(node.op, "token_type") and node.op.token_type in ( "BITWISE_NOT", "TILDE", "~", ): return f"(~{expr})" elif hasattr(node.op, "value") and node.op.value in ( "~", "BITWISE_NOT", "TILDE", ): return f"(~{expr})" elif isinstance(node.op, str) and node.op in ("~", "BITWISE_NOT", "TILDE"): return f"(~{expr})" op_str = node.op.value if hasattr(node.op, "value") else str(node.op) if getattr(node, "is_postfix", False): return f"{expr}{op_str}" return f"{op_str}{expr}" def visit_SwitchStatementNode(self, node): return self.visit_SwitchNode(node) def visit_SwitchCaseNode(self, node): return self.visit_CaseNode(node) def visit_StructNode(self, node): code = f"struct {node.name} {{\n" self.indentation += 1 for member in node.members: semantic = "" if member.semantic: semantic = f" {self.map_semantic(member.semantic)}" array_suffix = self.format_array_suffixes(member) code += ( self.get_indent() + f"{self.map_type(member.vtype)} {member.name}{array_suffix}{semantic};\n" ) self.indentation -= 1 code += self.get_indent() + "}\n" return code def visit_SwitchNode(self, node): condition = self.generate_expression(node.condition) code = f"switch ({condition}) {{\n" for case in node.cases: code += self.visit_CaseNode(case) if node.default_body: code += self.get_indent() + "default:\n" self.indentation += 1 for stmt in node.default_body: code += self.get_indent() + self.generate_statement(stmt) + "\n" self.indentation -= 1 code += self.get_indent() + "}\n" return code def visit_CaseNode(self, node): value = self.generate_expression(node.value) code = self.get_indent() + f"case {value}:\n" self.indentation += 1 for stmt in node.body: code += self.get_indent() + self.generate_statement(stmt) + "\n" self.indentation -= 1 return code
[docs] def generate_statement(self, node): """Render one DirectX backend statement node as CrossGL source.""" if isinstance(node, str): return node if isinstance(node, BreakNode): return "break;" if isinstance(node, ContinueNode): return "continue;" if isinstance(node, ReturnNode): if node.value is None: return "return;" return f"return {self.generate_expression(node.value)};" elif hasattr(self, f"visit_{type(node).__name__}"): method = getattr(self, f"visit_{type(node).__name__}") return method(node) else: return self.generate_expression(node)