Repository: CrossGL/crosstl

Issue:
Add translator support for `Bitwise AND` token

Hints from issue comments:
@anubhavray678: @samthakur587 
Hey, I would like to work on this. Can you assign it to me?

Solution patch:
diff --git a/crosstl/src/backend/DirectX/DirectxAst.py b/crosstl/src/backend/DirectX/DirectxAst.py
index b314f058..2484e0a4 100644
--- a/crosstl/src/backend/DirectX/DirectxAst.py
+++ b/crosstl/src/backend/DirectX/DirectxAst.py
@@ -86,6 +86,15 @@ def __repr__(self):
         return f"ForNode(init={self.init}, condition={self.condition}, update={self.update}, body={self.body})"
 
 
+class WhileNode(ASTNode):
+    def __init__(self, condition, body):
+        self.condition = condition
+        self.body = body
+
+    def __repr__(self):
+        return f"WhileNode(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 92f02b53..57f34dec 100644
--- a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
+++ b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
@@ -140,6 +140,8 @@ def generate_function_body(self, body, indent=0, is_main=False):
                     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, IfNode):
                 code += self.generate_if_statement(stmt, indent, is_main)
         return code
@@ -154,6 +156,14 @@ def generate_for_loop(self, node, indent, 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_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 c42565b8..c5a1574b 100644
--- a/crosstl/src/backend/DirectX/DirectxLexer.py
+++ b/crosstl/src/backend/DirectX/DirectxLexer.py
@@ -19,6 +19,7 @@
     ("ELSE_IF", r"\belse\sif\b"),
     ("ELSE", r"\belse\b"),
     ("FOR", r"\bfor\b"),
+    ("WHILE", r"\b\while\b"),
     ("REGISTER", r"\bregister\b"),
     ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"),
     ("NUMBER", r"\d+(\.\d+)?"),
@@ -72,6 +73,7 @@
     "if": "IF",
     "else": "ELSE",
     "for": "FOR",
+    "while": "WHILE",
     "register": "REGISTER",
 }
 
diff --git a/crosstl/src/backend/DirectX/DirectxParser.py b/crosstl/src/backend/DirectX/DirectxParser.py
index 61fd5447..3dd60149 100644
--- a/crosstl/src/backend/DirectX/DirectxParser.py
+++ b/crosstl/src/backend/DirectX/DirectxParser.py
@@ -2,6 +2,7 @@
     AssignmentNode,
     BinaryOpNode,
     ForNode,
+    WhileNode,
     FunctionCallNode,
     FunctionNode,
     IfNode,
@@ -195,6 +196,8 @@ def parse_statement(self):
             return self.parse_for_statement()
         elif self.current_token[0] == "RETURN":
             return self.parse_return_statement()
+        elif self.current_token[0] == "WHILE":
+            return self.parse_while_statement()
         else:
             return self.parse_expression_statement()
 
@@ -340,6 +343,19 @@ def parse_for_statement(self):
 
         return ForNode(init, condition, update, body)
 
+    def parse_while_statement(self):
+        self.eat("WHILE")
+        self.eat("LPAREN")
+
+        # Parse condition
+        condition = self.parse_expression()
+        self.eat("RPAREN")
+
+        # Parse body
+        body = self.parse_block()
+
+        return WhileNode(condition, body)
+
     def parse_return_statement(self):
         self.eat("RETURN")
         value = self.parse_expression()
diff --git a/crosstl/src/translator/codegen/directx_codegen.py b/crosstl/src/translator/codegen/directx_codegen.py
index c013e404..e9de6f9c 100644
--- a/crosstl/src/translator/codegen/directx_codegen.py
+++ b/crosstl/src/translator/codegen/directx_codegen.py
@@ -258,6 +258,7 @@ def map_operator(self, op):
             "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 50cc592e..11b59495 100644
--- a/crosstl/src/translator/codegen/metal_codegen.py
+++ b/crosstl/src/translator/codegen/metal_codegen.py
@@ -343,6 +343,7 @@ def map_operator(self, op):
             "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 4e4fb1c4..e4e148ec 100644
--- a/crosstl/src/translator/parser.py
+++ b/crosstl/src/translator/parser.py
@@ -439,6 +439,7 @@ def parse_assignment_or_function_call(self, update_condition=False):
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_LEFT",
             "ASSIGN_SHIFT_RIGHT",
         ]:
@@ -507,6 +508,7 @@ def parse_variable_declaration(self, type_name, update_condition=False):
             "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 @@ def parse_variable_declaration(self, type_name, update_condition=False):
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "EQUAL",
             "ASSIGN_AND",
             "ASSIGN_OR",
@@ -599,6 +602,7 @@ def parse_assignment(self, name):
             "BITWISE_SHIFT_RIGHT",
             "BITWISE_SHIFT_LEFT",
             "BITWISE_XOR",
+            "BITWISE_AND",
             "ASSIGN_SHIFT_RIGHT",
             "ASSIGN_SHIFT_LEFT",
         ]:
@@ -790,6 +794,7 @@ def parse_expression(self):
             "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 a7ca79d6..ed7112b7 100644
--- a/tests/test_backend/test_directx/test_codegen.py
+++ b/tests/test_backend/test_directx/test_codegen.py
@@ -160,6 +160,56 @@ def test_for_codegen():
         pytest.fail("For loop parsing or code generation not implemented.")
 
 
+def test_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;
+        while (i < 10) {
+            output.out_position = input.color;
+            i = i + 1;  // Increment the loop variable
+        }
+        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;
+        while (i < 10) {
+            output.out_color = float4(1.0, 1.0, 1.0, 1.0);
+            i = i + 1;  // Increment the loop variable
+        }
+        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 {
diff --git a/tests/test_backend/test_directx/test_parser.py b/tests/test_backend/test_directx/test_parser.py
index eed91e95..7d3cc2d1 100644
--- a/tests/test_backend/test_directx/test_parser.py
+++ b/tests/test_backend/test_directx/test_parser.py
@@ -75,6 +75,25 @@ def test_for_parsing():
         pytest.fail("for parsing not implemented.")
 
 
+def test_while_parsing():
+    code = """
+    VSOutput VSMain(VSInput input) {
+        VSOutput output;
+        int i = 0;
+        while (i < 10) {
+            output.out_position = input.position;
+            i = i + 1;
+        }
+        return output;
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("while parsing not implemented")
+
+
 def test_else_parsing():
     code = """
     PSOutput PSMain(PSInput input) {
diff --git a/tests/test_translator/test_codegen/test_directx_codegen.py b/tests/test_translator/test_codegen/test_directx_codegen.py
index 686dbf4b..ca816a23 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 2bcd5c72..737e1d66 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()