Repository: CrossGL/crosstl

Issue:
do-while loop

Discussion:

Patch:
diff --git a/crosstl/src/backend/DirectX/DirectxAst.py b/crosstl/src/backend/DirectX/DirectxAst.py
index 2484e0a..ac88d52 100644
--- a/crosstl/src/backend/DirectX/DirectxAst.py
+++ b/crosstl/src/backend/DirectX/DirectxAst.py
@@ -95,6 +95,15 @@ class WhileNode(ASTNode):
         return f"WhileNode(condition={self.condition}, body={self.body})"
 
 
+class DoWhileNode(ASTNode):
+    def __init__(self, condition, body):
+        self.condition = condition
+        self.body = body
+
+    def __repr__(self):
+        return f"DoWhileNode(condition={self.condition}, body={self.body})"
+
+
 class ReturnNode(ASTNode):
     def __init__(self, value):
         self.value = value
diff --git a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
index 57f34de..04a4b63 100644
--- a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
+++ b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
@@ -142,6 +142,8 @@ class HLSLToCrossGLConverter:
                 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)
         return code
@@ -164,6 +166,15 @@ class HLSLToCrossGLConverter:
         code += "    " * indent + "}\n"
         return code
 
+    def generate_do_while_loop(self, node, indent, is_main):
+        condition = self.generate_expression(node.condition, is_main)
+
+        code = "do {\n"
+        code += self.generate_function_body(node.body, indent + 1, is_main)
+        code += "    " * indent + "} "
+        code += f"while ({condition});\n"
+        return code
+
     def generate_if_statement(self, node, indent, is_main):
         condition = self.generate_expression(node.condition, is_main)
 
diff --git a/crosstl/src/backend/DirectX/DirectxLexer.py b/crosstl/src/backend/DirectX/DirectxLexer.py
index c5a1574..41c8a78 100644
--- a/crosstl/src/backend/DirectX/DirectxLexer.py
+++ b/crosstl/src/backend/DirectX/DirectxLexer.py
@@ -20,6 +20,7 @@ TOKENS = [
     ("ELSE", r"\belse\b"),
     ("FOR", r"\bfor\b"),
     ("WHILE", r"\b\while\b"),
+    ("DO", r"\b\do\b"),
     ("REGISTER", r"\bregister\b"),
     ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"),
     ("NUMBER", r"\d+(\.\d+)?"),
@@ -45,6 +46,7 @@ TOKENS = [
     ("DIVIDE_EQUALS", r"/="),
     ("ASSIGN_XOR", r"\^="),
     ("ASSIGN_OR", r"\|="),
+    ("ASSIGN_AND", r"\&="),
     ("AND", r"&&"),
     ("OR", r"\|\|"),
     ("DOT", r"\."),
@@ -74,6 +76,7 @@ KEYWORDS = {
     "else": "ELSE",
     "for": "FOR",
     "while": "WHILE",
+    "do": "DO",
     "register": "REGISTER",
 }
 
diff --git a/crosstl/src/backend/DirectX/DirectxParser.py b/crosstl/src/backend/DirectX/DirectxParser.py
index 3dd6014..1d70563 100644
--- a/crosstl/src/backend/DirectX/DirectxParser.py
+++ b/crosstl/src/backend/DirectX/DirectxParser.py
@@ -3,6 +3,7 @@ from .DirectxAst import (
     BinaryOpNode,
     ForNode,
     WhileNode,
+    DoWhileNode,
     FunctionCallNode,
     FunctionNode,
     IfNode,
@@ -198,6 +199,8 @@ class HLSLParser:
             return self.parse_return_statement()
         elif self.current_token[0] == "WHILE":
             return self.parse_while_statement()
+        elif self.current_token[0] == "DO":
+            return self.parse_do_while_statement()
         else:
             return self.parse_expression_statement()
 
@@ -234,6 +237,7 @@ class HLSLParser:
                     "DIVIDE_EQUALS",
                     "ASSIGN_XOR",
                     "ASSIGN_OR",
+                    "ASSIGN_AND",
                 ]:
                     # Handle assignment operators (e.g., =, +=, -=, ^=, etc.)
                     op = self.current_token[1]
@@ -250,6 +254,7 @@ class HLSLParser:
                 "DIVIDE_EQUALS",
                 "ASSIGN_XOR",
                 "ASSIGN_OR",
+                "ASSIGN_AND",
             ]:
                 # Handle assignment operators (e.g., =, +=, -=, ^=, etc.)
                 op = self.current_token[1]
@@ -269,6 +274,7 @@ class HLSLParser:
                     "DIVIDE_EQUALS",
                     "ASSIGN_XOR",
                     "ASSIGN_OR",
+                    "ASSIGN_AND",
                 ]:
                     op = self.current_token[1]
                     self.eat(self.current_token[0])
@@ -356,6 +362,22 @@ class HLSLParser:
 
         return WhileNode(condition, body)
 
+    def parse_do_while_statement(self):
+        # do token
+        self.eat("DO")
+
+        # parse do block
+        body = self.parse_block()
+
+        # parse while condition
+        self.eat("WHILE")
+        self.eat("LPAREN")
+        condition = self.parse_expression()
+        self.eat("RPAREN")
+        self.eat("SEMICOLON")
+
+        return DoWhileNode(condition, body)
+
     def parse_return_statement(self):
         self.eat("RETURN")
         value = self.parse_expression()
@@ -380,6 +402,7 @@ class HLSLParser:
             "DIVIDE_EQUALS",
             "ASSIGN_XOR",
             "ASSIGN_OR",
+            "ASSIGN_AND",
         ]:
             op = self.current_token[1]
             self.eat(self.current_token[0])
diff --git a/crosstl/src/translator/codegen/directx_codegen.py b/crosstl/src/translator/codegen/directx_codegen.py
index c013e40..e9de6f9 100644
--- a/crosstl/src/translator/codegen/directx_codegen.py
+++ b/crosstl/src/translator/codegen/directx_codegen.py
@@ -258,6 +258,7 @@ class HLSLCodeGen:
             "MULTIPLY": "*",
             "DIVIDE": "/",
             "BITWISE_XOR": "^",
+            "BITWISE_AND": "&",
             "LESS_THAN": "<",
             "GREATER_THAN": ">",
             "ASSIGN_ADD": "+=",
diff --git a/crosstl/src/translator/codegen/metal_codegen.py b/crosstl/src/translator/codegen/metal_codegen.py
index 50cc592..11b5949 100644
--- a/crosstl/src/translator/codegen/metal_codegen.py
+++ b/crosstl/src/translator/codegen/metal_codegen.py
@@ -343,6 +343,7 @@ class MetalCodeGen:
             "MULTIPLY": "*",
             "DIVIDE": "/",
             "BITWISE_XOR": "^",
+            "BITWISE_AND": "&",
             "LESS_THAN": "<",
             "GREATER_THAN": ">",
             "ASSIGN_ADD": "+=",
diff --git a/crosstl/src/translator/parser.py b/crosstl/src/translator/parser.py
index 4e4fb1c..e4e148e 100644
--- a/crosstl/src/translator/parser.py
+++ b/crosstl/src/translator/parser.py
@@ -439,6 +439,7 @@ class Parser:
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_LEFT",
             "ASSIGN_SHIFT_RIGHT",
         ]:
@@ -507,6 +508,7 @@ class Parser:
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_LEFT" "ASSIGN_SHIFT_RIGHT",
         ]:
             op = self.current_token[1]
@@ -538,6 +540,7 @@ class Parser:
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "EQUAL",
             "ASSIGN_AND",
             "ASSIGN_OR",
@@ -599,6 +602,7 @@ class Parser:
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_RIGHT",
             "ASSIGN_SHIFT_LEFT",
         ]:
@@ -790,6 +794,7 @@ class Parser:
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_RIGHT",
             "ASSIGN_SHIFT_LEFT",
         ]:


Test patch:
diff --git a/tests/test_backend/test_directx/test_codegen.py b/tests/test_backend/test_directx/test_codegen.py
index ed7112b..65e9f48 100644
--- a/tests/test_backend/test_directx/test_codegen.py
+++ b/tests/test_backend/test_directx/test_codegen.py
@@ -210,6 +210,51 @@ def test_while_codegen():
         pytest.fail("While loop parsing or code generation not implemented.")
 
 
+def test_do_while_codegen():
+    code = """
+    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;
+        int i = 0;
+        do {
+            output.out_position = input.color;
+            i = i + 1;  // Increment the loop variable
+        } while (i < 10);
+        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;
+        int i = 0;
+        do {
+            output.out_color = float4(1.0, 1.0, 1.0, 1.0);
+            i = i + 1;  // Increment the loop variable
+        } while (i < 10);
+        return output;
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("While loop parsing or code generation not implemented.")
+
+
 def test_else_codegen():
     code = """
     struct VSInput {
@@ -386,7 +431,10 @@ def test_assignment_ops_parsing():
             output.redValue ^= 0x1;
             output.out_color.r = asfloat(redValue);
 
-            outpu.redValue |= 0x2;
+            output.redValue |= 0x2;
+
+            output.redValue &= 0x3;
+
         }
 
         return output;
diff --git a/tests/test_backend/test_directx/test_lexer.py b/tests/test_backend/test_directx/test_lexer.py
index 4cb364d..76ba0bc 100644
--- a/tests/test_backend/test_directx/test_lexer.py
+++ b/tests/test_backend/test_directx/test_lexer.py
@@ -137,6 +137,10 @@ def test_assignment_ops_tokenization():
             uint redValue = asuint(output.out_color.r);
             redValue ^= 0x1;
             output.out_color.r = asfloat(redValue);
+
+            redValue |= 0x2;
+
+            redValue &= 0x3;
         }
 
         return output;
diff --git a/tests/test_backend/test_directx/test_parser.py b/tests/test_backend/test_directx/test_parser.py
index 7d3cc2d..d1c2b7a 100644
--- a/tests/test_backend/test_directx/test_parser.py
+++ b/tests/test_backend/test_directx/test_parser.py
@@ -94,6 +94,25 @@ def test_while_parsing():
         pytest.fail("while parsing not implemented")
 
 
+def test_do_while_parsing():
+    code = """
+    VSOutput VSMain(VSInput input) {
+        VSOutput output;
+        int i = 0;
+        do {
+            output.out_position = input.position;
+            i = i + 1;
+        } while (i < 10);
+        return output;
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("do while parsing not implemented")
+
+
 def test_else_parsing():
     code = """
     PSOutput PSMain(PSInput input) {
@@ -177,6 +196,8 @@ def test_assignment_ops_parsing():
             output.out_color.r = asfloat(redValue);
 
             output.redValue |= 0x2;
+
+            output.redValue &= 0x3;
         }
 
         return output;
diff --git a/tests/test_translator/test_codegen/test_directx_codegen.py b/tests/test_translator/test_codegen/test_directx_codegen.py
index 686dbf4..ca816a2 100644
--- a/tests/test_translator/test_codegen/test_directx_codegen.py
+++ b/tests/test_translator/test_codegen/test_directx_codegen.py
@@ -559,5 +559,42 @@ def test_bitwise_operators():
         pytest.fail("Bitwise Shift codegen not implemented")
 
 
+def test_bitwise_and_operator():
+    code = """
+    shader main {
+    struct VSInput {
+        vec2 texCoord @ TEXCOORD0;
+    };
+    struct VSOutput {
+        vec4 color @ COLOR;
+    };
+    sampler2D iChannel0;
+    vertex {
+        VSOutput main(VSInput input) {
+            VSOutput output;
+            // Use bitwise AND on texture coordinates (for testing purposes)
+            output.color = vec4(float(int(input.texCoord.x * 100.0) & 15), 
+                                float(int(input.texCoord.y * 100.0) & 15), 
+                                0.0, 1.0);
+            return output;
+        }
+    }
+    fragment {
+        vec4 main(VSOutput input) @ gl_FragColor {
+            // Simple fragment shader to display the result of the AND operation
+            return vec4(input.color.rgb, 1.0);
+        }
+    }
+}
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("Bitwise AND codegen not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_translator/test_parser.py b/tests/test_translator/test_parser.py
index 2bcd5c7..737e1d6 100644
--- a/tests/test_translator/test_parser.py
+++ b/tests/test_translator/test_parser.py
@@ -551,5 +551,40 @@ def test_xor_operator():
         pytest.fail("Bitwise XOR not working")
 
 
+def test_and_operator():
+    code = """
+    shader main {
+    struct VSInput {
+        vec2 texCoord @ TEXCOORD0;
+    };
+    struct VSOutput {
+        vec4 color @ COLOR;
+    };
+    sampler2D iChannel0;
+    vertex {
+        VSOutput main(VSInput input) {
+            VSOutput output;
+            // Use bitwise AND on texture coordinates (for testing purposes)
+            output.color = vec4(float(int(input.texCoord.x * 100.0) & 15), 
+                                float(int(input.texCoord.y * 100.0) & 15), 
+                                0.0, 1.0);
+            return output;
+        }
+    }
+    fragment {
+        vec4 main(VSOutput input) @ gl_FragColor {
+            // Simple fragment shader to display the result of the AND operation
+            return vec4(input.color.rgb, 1.0);
+        }
+    }
+}
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("Bitwise AND not working")
+
+
 if __name__ == "__main__":
     pytest.main()