Repository: CrossGL/crosstl

Issue:
#pragma
<!-- BOT_STATE: {"assignee": "plon-Susk7", "assigned_at": "2024-12-26T13:56:47.762956+00:00", "reminder_sent": false, "unassigned": false} -->

Hints from issue comments:
@plon-Susk7: @CrossGL-issue-bot assign me

Solution patch:
diff --git a/crosstl/backend/DirectX/DirectxAst.py b/crosstl/backend/DirectX/DirectxAst.py
index 829894f..30f69aa 100644
--- a/crosstl/backend/DirectX/DirectxAst.py
+++ b/crosstl/backend/DirectX/DirectxAst.py
@@ -161,6 +161,18 @@ class UnaryOpNode(ASTNode):
         return f"({self.op}{self.operand})"
 
 
+class PragmaNode(ASTNode):
+    def __init__(self, directive, value):
+        self.directive = directive
+        self.value = value
+
+    def __repr__(self):
+        return f"PragmaNode(directive={self.directive}, value={self.value})"
+
+    def __str__(self):
+        return f"#pragma {self.directive} {self.value}"
+
+
 class IncludeNode(ASTNode):
     def __init__(self, path):
         self.path = path
diff --git a/crosstl/backend/DirectX/DirectxCrossGLCodeGen.py b/crosstl/backend/DirectX/DirectxCrossGLCodeGen.py
index b77b448..790415c 100644
--- a/crosstl/backend/DirectX/DirectxCrossGLCodeGen.py
+++ b/crosstl/backend/DirectX/DirectxCrossGLCodeGen.py
@@ -70,6 +70,8 @@ class HLSLToCrossGLConverter:
                 for member in node.members:
                     code += f"        {self.map_type(member.vtype)} {member.name} {self.map_semantic(member.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"
         # Generate global variables
diff --git a/crosstl/backend/DirectX/DirectxLexer.py b/crosstl/backend/DirectX/DirectxLexer.py
index cf6835e..22c12f8 100644
--- a/crosstl/backend/DirectX/DirectxLexer.py
+++ b/crosstl/backend/DirectX/DirectxLexer.py
@@ -1,7 +1,6 @@
 import re
 from typing import Iterator, Tuple, List
 
-
 # using sets for faster lookup
 SKIP_TOKENS = {"WHITESPACE", "COMMENT_SINGLE", "COMMENT_MULTI"}
 
@@ -77,6 +76,7 @@ TOKENS = tuple(
         ("MOD", r"%"),
         ("HALF", r"\bhalf\b"),
         ("BITWISE_AND", r"&"),
+        ("PRAGMA", r"#\s*\bpragma\b"),
     ]
 )
 
diff --git a/crosstl/backend/DirectX/DirectxParser.py b/crosstl/backend/DirectX/DirectxParser.py
index 15c5aa7..79de030 100644
--- a/crosstl/backend/DirectX/DirectxParser.py
+++ b/crosstl/backend/DirectX/DirectxParser.py
@@ -14,6 +14,7 @@ from .DirectxAst import (
     UnaryOpNode,
     VariableNode,
     VectorConstructorNode,
+    PragmaNode,
     IncludeNode,
     SwitchNode,
     CaseNode,
@@ -53,6 +54,7 @@ class HLSLParser:
         cbuffers = []
         global_variables = []
         while self.current_token[0] != "EOF":
+
             if self.current_token[0] == "STRUCT":
                 structs.append(self.parse_struct())
             elif self.current_token[0] == "CBUFFER":
@@ -71,6 +73,9 @@ class HLSLParser:
                     functions.append(self.parse_function())
                 else:
                     global_variables.append(self.parse_global_variable())
+            elif self.current_token[0] == "PRAGMA":
+                structs.append(self.parse_pragma())
+
             elif self.current_token[0] == "INCLUDE":
                 structs.append(self.parse_include())
 
@@ -222,6 +227,15 @@ class HLSLParser:
         else:
             return self.parse_expression_statement()
 
+    def parse_pragma(self):
+        self.eat("PRAGMA")
+        name = self.current_token[1]
+        self.eat("IDENTIFIER")
+        value = self.current_token[1]
+        self.eat("IDENTIFIER")
+        self.eat("SEMICOLON")
+        return PragmaNode(name, value)
+
     def parse_variable_declaration_or_assignment(self):
         if self.current_token[0] in [
             "FLOAT",


Test patch:
diff --git a/tests/test_backend/test_directx/test_codegen.py b/tests/test_backend/test_directx/test_codegen.py
index e2d5cc8..292a824 100644
--- a/tests/test_backend/test_directx/test_codegen.py
+++ b/tests/test_backend/test_directx/test_codegen.py
@@ -116,6 +116,7 @@ def test_if_codegen():
 
 def test_for_codegen():
     code = """
+    #pragma exclude_renderers vulkan;
     struct VSInput {
     float4 position : POSITION;
     float4 color : TEXCOORD0;
@@ -498,6 +499,59 @@ def test_bitwise_ops_codgen():
         pytest.fail("bitwise_op parsing or codegen not implemented.")
 
 
+def test_pragma_codegen():
+    code = """
+    #pragma exclude_renderers vulkan;
+    struct VSInput {
+    float4 position : POSITION;
+    float4 color : TEXCOORD0;
+    };
+
+    struct VSOutput {
+        float4 out_position : TEXCOORD0;
+    };
+
+    VSOutput VSMain(VSInput input) {
+        VSOutput output;
+        output.out_position =  input.position;
+
+        for (int i = 0; i < 10; i=i+1) {
+            output.out_position = input.color;
+        }
+
+        return output;
+    }
+
+    struct PSInput {
+        float4 in_position : TEXCOORD0;
+    };
+
+    struct PSOutput {
+        float4 out_color : SV_TARGET0;
+    };
+
+    PSOutput PSMain(PSInput input) {
+        PSOutput output;
+        output.out_color =  input.in_position;
+
+        for (int i = 0; i < 10; i=i+1) {
+            output.out_color = float4(1.0, 1.0, 1.0, 1.0);
+        }
+
+        return output;
+    }
+    """
+
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("For loop parsing or code generation not implemented.")
+        pytest.fail("Include statement failed to parse or generate code.")
+
+
 def test_include_codegen():
     code = """
     #include "common.hlsl"
@@ -513,6 +567,11 @@ def test_include_codegen():
     VSOutput VSMain(VSInput input) {
         VSOutput output;
         output.out_position =  input.position;
+
+        for (int i = 0; i < 10; i=i+1) {
+            output.out_position = input.color;
+        }
+
         return output;
     }
 
@@ -527,15 +586,22 @@ def test_include_codegen():
     PSOutput PSMain(PSInput input) {
         PSOutput output;
         output.out_color =  input.in_position;
+
+        for (int i = 0; i < 10; i=i+1) {
+            output.out_color = float4(1.0, 1.0, 1.0, 1.0);
+        }
+
         return output;
     }
     """
+
     try:
         tokens = tokenize_code(code)
         ast = parse_code(tokens)
         generated_code = generate_code(ast)
         print(generated_code)
     except SyntaxError:
+        pytest.fail("For loop parsing or code generation not implemented.")
         pytest.fail("Include statement failed to parse or generate code.")