Repository: CrossGL/crosstl

Issue:
Add translator support for `Bitwise Shift Left` token

Discussion:

Patch:
diff --git a/crosstl/src/backend/DirectX/DirectxAst.py b/crosstl/src/backend/DirectX/DirectxAst.py
index b5d3ac2..b314f05 100644
--- a/crosstl/src/backend/DirectX/DirectxAst.py
+++ b/crosstl/src/backend/DirectX/DirectxAst.py
@@ -23,27 +23,6 @@ class ShaderNode:
         return f"ShaderNode(structs={self.structs}, functions={self.functions}, global_variables={self.global_variables}, cbuffers={self.cbuffers})"
 
 
-class shaderTypeNode:
-    """
-    Represents a shader type node in the AST.
-
-    Attributes:
-        vertex (bool): The vertex shader type
-        fragment (bool): The fragment shader type
-        compute (bool): The compute shader type
-
-    """
-
-    def __init__(self, vertex=False, fragment=False, compute=False, custom=False):
-        self.vertex = vertex
-        self.fragment = fragment
-        self.compute = compute
-        self.custom = custom
-
-    def __repr__(self):
-        return f"shaderTypeNode(vertex={self.vertex}, fragment={self.fragment}, compute={self.compute}, custom={self.custom})"
-
-
 class StructNode:
     def __init__(self, name, members):
         self.name = name
@@ -54,15 +33,16 @@ class StructNode:
 
 
 class FunctionNode(ASTNode):
-    def __init__(self, return_type, name, params, body, type_function="custom"):
+    def __init__(self, return_type, name, params, body, qualifier=None, semantic=None):
         self.return_type = return_type
         self.name = name
         self.params = params
         self.body = body
-        self.type_function = type_function
+        self.qualifier = qualifier
+        self.semantic = semantic
 
     def __repr__(self):
-        return f"FunctionNode(return_type={self.return_type}, name={self.name}, params={self.params}, body={self.body}, type_function={self.type_function})"
+        return f"FunctionNode(return_type={self.return_type}, name={self.name}, params={self.params}, body={self.body}, qualifier={self.qualifier}, semantic={self.semantic})"
 
 
 class VariableNode(ASTNode):
diff --git a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
index 63ed820..3296828 100644
--- a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
+++ b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
@@ -34,6 +34,101 @@ class HLSLToCrossGLConverter:
             "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",
+        }
 
     def generate(self, ast):
         code = "shader main {\n"
@@ -42,7 +137,7 @@ class HLSLToCrossGLConverter:
             if isinstance(node, StructNode):
                 code += f"    struct {node.name} {{\n"
                 for member in node.members:
-                    code += f"        {self.map_type(member.vtype)} {member.name};\n"
+                    code += f"        {self.map_type(member.vtype)} {member.name} {self.map_semantic(member.semantic)};\n"
                 code += "    }\n"
         # Generate global variables
         for node in ast.global_variables:
@@ -54,21 +149,24 @@ class HLSLToCrossGLConverter:
 
         # Generate custom functions
         for func in ast.functions:
-            function_type_node = func.type_function
-            if function_type_node.custom:
+            if func.qualifier == "vertex":
+                code += "    // Vertex Shader\n"
+                code += "    vertex {\n"
                 code += self.generate_function(func)
-            if function_type_node.vertex:
-                code += f"vertex {{\n"
+                code += "    }\n\n"
+            elif func.qualifier == "fragment":
+                code += "    // Fragment Shader\n"
+                code += "    fragment {\n"
                 code += self.generate_function(func)
-                code += f"}}\n"
-            elif function_type_node.fragment:
-                code += f"fragment {{\n"
+                code += "    }\n\n"
+
+            elif func.qualifier == "compute":
+                code += "    // Compute Shader\n"
+                code += "    compute {\n"
                 code += self.generate_function(func)
-                code += f"}}\n"
-            elif function_type_node.compute:
-                code += f"compute {{\n"
+                code += "    }\n\n"
+            else:
                 code += self.generate_function(func)
-                code += f"}}\n"
 
         code += "}\n"
         return code
@@ -83,10 +181,15 @@ class HLSLToCrossGLConverter:
                 code += "    }\n"
         return code
 
-    def generate_function(self, func):
-        params = ", ".join(f"{self.map_type(p.vtype)} {p.name}" for p in func.params)
-        code = f"    {self.map_type(func.return_type)} {func.name}({params}) {{\n"
-        code += self.generate_function_body(func.body, indent=2)
+    def generate_function(self, func, indent=1):
+        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
 
@@ -187,3 +290,9 @@ class HLSLToCrossGLConverter:
         if hlsl_type:
             return self.type_map.get(hlsl_type, hlsl_type)
         return hlsl_type
+
+    def map_semantic(self, semantic):
+        if semantic is not None:
+            return f"@ {self.semantic_map.get(semantic, semantic)}"
+        else:
+            return ""
diff --git a/crosstl/src/backend/DirectX/DirectxLexer.py b/crosstl/src/backend/DirectX/DirectxLexer.py
index 6583503..07dbd6d 100644
--- a/crosstl/src/backend/DirectX/DirectxLexer.py
+++ b/crosstl/src/backend/DirectX/DirectxLexer.py
@@ -20,7 +20,6 @@ TOKENS = [
     ("ELSE", r"\belse\b"),
     ("FOR", r"\bfor\b"),
     ("REGISTER", r"\bregister\b"),
-    ("SEMANTIC", r": [A-Z_][A-Z0-9_]*"),
     ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"),
     ("NUMBER", r"\d+(\.\d+)?"),
     ("LBRACE", r"\{"),
diff --git a/crosstl/src/backend/DirectX/DirectxParser.py b/crosstl/src/backend/DirectX/DirectxParser.py
index e08eb47..b1ecfa6 100644
--- a/crosstl/src/backend/DirectX/DirectxParser.py
+++ b/crosstl/src/backend/DirectX/DirectxParser.py
@@ -12,7 +12,6 @@ from .DirectxAst import (
     UnaryOpNode,
     VariableNode,
     VectorConstructorNode,
-    shaderTypeNode,
 )
 from .DirectxLexer import HLSLLexer
 
@@ -38,21 +37,6 @@ class HLSLParser:
         else:
             raise SyntaxError(f"Expected {token_type}, got {self.current_token[0]}")
 
-    def parse_shader_type(self):
-        vertex = False
-        fragment = False
-        compute = False
-        custom = False
-        if self.current_token[1] == "VSMain":
-            vertex = True
-        elif self.current_token[1] == "PSMain":
-            fragment = True
-        elif self.current_token[1] == "CSMain":
-            compute = True
-        else:
-            custom = True
-        return shaderTypeNode(vertex, fragment, compute, custom)
-
     def parse(self):
         shader = self.parse_shader()
         self.eat("EOF")
@@ -137,9 +121,10 @@ class HLSLParser:
             var_name = self.current_token[1]
             self.eat("IDENTIFIER")
             semantic = None
-            if self.current_token[0] == "SEMANTIC":
+            if self.current_token[0] == "COLON":
+                self.eat("COLON")
                 semantic = self.current_token[1]
-                self.eat("SEMANTIC")
+                self.eat("IDENTIFIER")
             self.eat("SEMICOLON")
             members.append(VariableNode(vtype, var_name, semantic))
         self.eat("RBRACE")
@@ -148,16 +133,26 @@ class HLSLParser:
     def parse_function(self):
         return_type = self.current_token[1]
         self.eat(self.current_token[0])
-        type_function = self.parse_shader_type()
         name = self.current_token[1]
+        qualifier = None
+        if name == "VSMain":
+            qualifier = "vertex"
+        elif name == "PSMain":
+            qualifier = "fragment"
+        elif name == "CSMain":
+            qualifier = "compute"
+
         self.eat("IDENTIFIER")
         self.eat("LPAREN")
         params = self.parse_parameters()
         self.eat("RPAREN")
-        if self.current_token[0] == "SEMANTIC":
-            self.eat("SEMANTIC")
+        semantic = None
+        if self.current_token[0] == "COLON":
+            self.eat("COLON")
+            semantic = self.current_token[1]
+            self.eat("IDENTIFIER")
         body = self.parse_block()
-        return FunctionNode(return_type, name, params, body, type_function)
+        return FunctionNode(return_type, name, params, body, qualifier, semantic)
 
     def parse_parameters(self):
         params = []
@@ -166,9 +161,12 @@ class HLSLParser:
             self.eat(self.current_token[0])
             name = self.current_token[1]
             self.eat("IDENTIFIER")
-            if self.current_token[0] == "SEMANTIC":
-                self.eat("SEMANTIC")
-            params.append(VariableNode(vtype, name))
+            semantic = None
+            if self.current_token[0] == "COLON":
+                self.eat("COLON")
+                semantic = self.current_token[1]
+                self.eat("IDENTIFIER")
+            params.append(VariableNode(vtype, name, semantic))
             if self.current_token[0] == "COMMA":
                 self.eat("COMMA")
         return params
diff --git a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
index b57c5ee..6995836 100644
--- a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
+++ b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
@@ -43,6 +43,34 @@ class MetalToCrossGLConverter:
             "half4x4": "half4x4",
         }
 
+        self.map_semantics = {
+            "attribute(0)": "in_Position",
+            "attribute(1)": "in_Normal",
+            "attribute(2)": "in_Tangent",
+            "attribute(3)": "in_Binormal",
+            "attribute(4)": "TexCoord",
+            "attribute(5)": "TexCoord0",
+            "attribute(6)": "TexCoord1",
+            "attribute(7)": "TexCoord2",
+            "attribute(8)": "TexCoord3",
+            "vertex_id": "gl_VertexID",
+            "instance_id": "gl_InstanceID",
+            "base_vertex": "gl_BaseVertex",
+            "base_instance": "gl_BaseInstance",
+            "position": "out_Position",
+            "point_size": "gl_PointSize",
+            "clip_distance": "gl_ClipDistance",
+            "front_facing": "gl_IsFrontFace",
+            "point_coord": "gl_PointCoord",
+            "color(0)": "out_Color",
+            "color(1)": "out_Color1",
+            "color(2)": "out_Color2",
+            "color(3)": "out_Color3",
+            "color(4)": "out_Color4",
+            "depth(any)": "Out_Depth",
+            "stage_in": "gl_in",
+        }
+
     def generate(self, ast):
 
         code = "shader main {\n"
@@ -62,7 +90,7 @@ class MetalToCrossGLConverter:
                     code += f"    // Structs\n"
                     code += f"    struct {struct_node.name} {{\n"
                 for member in struct_node.members:
-                    code += f"        {self.map_type(member.vtype)} {member.name};\n"
+                    code += f"        {self.map_type(member.vtype)} {member.name} {self.map_semantic(member.attributes)};\n"
                 code += "    }\n\n"
 
         for f in ast.functions:
@@ -95,8 +123,11 @@ class MetalToCrossGLConverter:
     def generate_function(self, func, indent=2):
         code = ""
         code += "    " * indent
-        params = ", ".join(f"{self.map_type(p.vtype)} {p.name}" for p in func.params)
-        code += f"{self.map_type(func.return_type)} {func.name}({params}) {{\n"
+        params = ", ".join(
+            f"{self.map_type(p.vtype)} {p.name} {self.map_semantic(p.attributes)}"
+            for p in func.params
+        )
+        code += f"{self.map_type(func.return_type)} {func.name}({params})  {self.map_semantic(func.attributes)} {{\n"
         code += self.generate_function_body(func.body, indent=indent + 1)
         code += "    }\n\n"
         return code
@@ -204,3 +235,22 @@ class MetalToCrossGLConverter:
         if metal_type:
             return self.type_map.get(metal_type, metal_type)
         return metal_type
+
+    def map_semantic(self, semantic):
+        if semantic:
+            for semantic in semantic:
+                if isinstance(semantic, AttributeNode):
+                    name = semantic.name
+                    args = semantic.args
+                    if args:
+                        out = self.map_semantics.get(
+                            f"{name}({args[0]})", f"{name}({args[0]})"
+                        )
+                        return f"@{out}"
+                    else:
+                        out = self.map_semantics.get(f"{name}", f"{name}")
+                        return f"@{out}"
+                else:
+                    return ""
+        else:
+            return ""
diff --git a/crosstl/src/translator/codegen/directx_codegen.py b/crosstl/src/translator/codegen/directx_codegen.py
index cac1786..cfd41bd 100644
--- a/crosstl/src/translator/codegen/directx_codegen.py
+++ b/crosstl/src/translator/codegen/directx_codegen.py
@@ -359,6 +359,7 @@ class HLSLCodeGen:
             "GREATER_THAN": ">",
             "ASSIGN_ADD": "+=",
             "ASSIGN_SUB": "-=",
+            "ASSIGN_OR": "|=",
             "ASSIGN_MUL": "*=",
             "ASSIGN_DIV": "/=",
             "ASSIGN_MOD": "%=",
@@ -371,5 +372,7 @@ class HLSLCodeGen:
             "OR": "||",
             "EQUALS": "=",
             "ASSIGN_SHIFT_LEFT": "<<=",
+            "BITWISE_SHIFT_RIGHT": ">>",
+            "BITWISE_SHIFT_LEFT": "<<",
         }
         return op_map.get(op, op)
diff --git a/crosstl/src/translator/codegen/metal_codegen.py b/crosstl/src/translator/codegen/metal_codegen.py
index c6d120c..fe958af 100644
--- a/crosstl/src/translator/codegen/metal_codegen.py
+++ b/crosstl/src/translator/codegen/metal_codegen.py
@@ -418,6 +418,7 @@ class MetalCodeGen:
             "LESS_THAN": "<",
             "ASSIGN_ADD": "+=",
             "ASSIGN_SUB": "-=",
+            "ASSIGN_OR": "|=",
             "ASSIGN_MUL": "*=",
             "ASSIGN_DIV": "/=",
             "ASSIGN_MOD": "%=",
@@ -431,5 +432,7 @@ class MetalCodeGen:
             "OR": "||",
             "EQUALS": "=",
             "ASSIGN_SHIFT_LEFT": "<<=",
+            "BITWISE_SHIFT_RIGHT": ">>",
+            "BITWISE_SHIFT_LEFT": "<<",
         }
         return op_map.get(op, op)
diff --git a/crosstl/src/translator/codegen/opengl_codegen.py b/crosstl/src/translator/codegen/opengl_codegen.py
index e445a3c..317b7f6 100644
--- a/crosstl/src/translator/codegen/opengl_codegen.py
+++ b/crosstl/src/translator/codegen/opengl_codegen.py
@@ -275,6 +275,7 @@ class GLSLCodeGen:
             "LESS_THAN": "<",
             "ASSIGN_ADD": "+=",
             "ASSIGN_SUB": "-=",
+            "ASSIGN_OR": "|=",
             "ASSIGN_MUL": "*=",
             "ASSIGN_DIV": "/=",
             "ASSIGN_MOD": "%=",
@@ -288,5 +289,7 @@ class GLSLCodeGen:
             "OR": "||",
             "EQUALS": "=",
             "ASSIGN_SHIFT_LEFT": "<<=",
+            "BITWISE_SHIFT_RIGHT": ">>",
+            "BITWISE_SHIFT_LEFT": "<<",
         }
         return op_map.get(op, op)


Test patch:
diff --git a/tests/test_backend/test_directx/test_codegen.py b/tests/test_backend/test_directx/test_codegen.py
index 7250a7f..882e9f4 100644
--- a/tests/test_backend/test_directx/test_codegen.py
+++ b/tests/test_backend/test_directx/test_codegen.py
@@ -50,7 +50,7 @@ def test_struct_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
@@ -93,7 +93,7 @@ def test_if_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
@@ -139,7 +139,7 @@ def test_for_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
@@ -188,7 +188,7 @@ def test_else_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
@@ -238,7 +238,7 @@ def test_function_call_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
@@ -285,7 +285,7 @@ def test_else_if_codegen():
     };
 
     struct PSOutput {
-        float4 out_color : SV_TARGET0;
+        float4 out_color : SV_Target0;
     };
 
     PSOutput PSMain(PSInput input) {
diff --git a/tests/test_backend/test_directx/test_lexer.py b/tests/test_backend/test_directx/test_lexer.py
index 26a6885..62689be 100644
--- a/tests/test_backend/test_directx/test_lexer.py
+++ b/tests/test_backend/test_directx/test_lexer.py
@@ -6,13 +6,13 @@ from crosstl.src.backend.DirectX.DirectxLexer import HLSLLexer
 def tokenize_code(code: str) -> List:
     """Helper function to tokenize code."""
     lexer = HLSLLexer(code)
-    return lexer.tokenize()
+    return lexer.tokens
 
 
 def test_struct_tokenization():
     code = """
     struct VSInput {
-        float4 position : POSITION;
+        float4 position : SV_position;
         float4 color : TEXCOORD0;
     };
 
diff --git a/tests/test_backend/test_directx/test_parser.py b/tests/test_backend/test_directx/test_parser.py
index 2a0a580..17515d5 100644
--- a/tests/test_backend/test_directx/test_parser.py
+++ b/tests/test_backend/test_directx/test_parser.py
@@ -25,7 +25,7 @@ def tokenize_code(code: str) -> List:
 def test_struct_parsing():
     code = """
     struct VSInput {
-        float4 position : POSITION;
+        float4 position : SV_Position;
         float4 color : TEXCOORD0;
     };
 
diff --git a/tests/test_translator/test_codegen/test_directx_codegen.py b/tests/test_translator/test_codegen/test_directx_codegen.py
index d1e11d5..0e4160a 100644
--- a/tests/test_translator/test_codegen/test_directx_codegen.py
+++ b/tests/test_translator/test_codegen/test_directx_codegen.py
@@ -283,6 +283,40 @@ def test_function_call():
         pytest.fail("Struct parsing not implemented.")
 
 
+def test_assignment_or_operator():
+    code = """
+    shader ORShader {
+        vertex {
+            input vec3 position;
+            output vec2 vUV;
+            void main() {
+                vUV = position.xy * 10.0;
+                vUV.x |= 3.0;  // OR assignment operator
+                gl_Position = vec4(position, 1.0);
+            }
+        }
+        fragment {
+            input vec2 vUV;
+            output vec4 fragColor;
+            void main() {
+                float noise = perlinNoise(vUV);
+                float height = noise * 10.0;
+                height |= 2.0;  // OR assignment operator
+                vec3 color = vec3(height / 10.0, 1.0 - height / 10.0, 0.0);
+                fragColor = vec4(color, 1.0);
+            }
+        }
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("OR operator parsing not implemented.")
+
+
 def test_assignment_modulus_operator():
     code = """
     shader ModulusShader {
@@ -384,7 +418,36 @@ def test_assignment_shift_operators():
         code = generate_code(ast)
         print(code)
     except SyntaxError:
-        pytest.fail("Struct parsing not implemented.")
+        pytest.fail("Assignment shift parsing not implemented.")
+
+
+def test_bitwise_operators():
+    code = """
+        shader LightControl {
+        vertex {
+            input vec3 position;
+            output int isLightOn;
+            void main() {        
+                    isLightOn = 2 >> 1;
+            }
+        }
+        fragment {
+            input int isLightOn;
+            output vec4 fragColor;
+            void main() {
+                isLightOn = isLightOn << 1;
+            }
+        }
+    }
+
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        code = generate_code(ast)
+        print(code)
+    except SyntaxError:
+        pytest.fail("Bitwise Shift parsing not implemented.")
 
 
 if __name__ == "__main__":
diff --git a/tests/test_translator/test_codegen/test_metal_codegen.py b/tests/test_translator/test_codegen/test_metal_codegen.py
index f16c1df..9585b2d 100644
--- a/tests/test_translator/test_codegen/test_metal_codegen.py
+++ b/tests/test_translator/test_codegen/test_metal_codegen.py
@@ -323,5 +323,34 @@ def test_assignment_shift_operators():
         pytest.fail("Struct parsing not implemented.")
 
 
+def test_bitwise_operators():
+    code = """
+        shader LightControl {
+        vertex {
+            input vec3 position;
+            output int isLightOn;
+            void main() {        
+                    isLightOn = 2 >> 1;
+            }
+        }
+        fragment {
+            input int isLightOn;
+            output vec4 fragColor;
+            void main() {
+                isLightOn = isLightOn << 1;
+            }
+        }
+    }
+
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        code = generate_code(ast)
+        print(code)
+    except SyntaxError:
+        pytest.fail("Bitwise Shift parsing not implemented.")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_translator/test_codegen/test_opengl_codegen.py b/tests/test_translator/test_codegen/test_opengl_codegen.py
index 40f5502..ef56b95 100644
--- a/tests/test_translator/test_codegen/test_opengl_codegen.py
+++ b/tests/test_translator/test_codegen/test_opengl_codegen.py
@@ -323,5 +323,34 @@ def test_assignment_shift_operators():
         pytest.fail("Struct parsing not implemented.")
 
 
+def test_bitwise_operators():
+    code = """
+        shader LightControl {
+        vertex {
+            input vec3 position;
+            output int isLightOn;
+            void main() {        
+                    isLightOn = 2 >> 1;
+            }
+        }
+        fragment {
+            input int isLightOn;
+            output vec4 fragColor;
+            void main() {
+                isLightOn = isLightOn << 1;
+            }
+        }
+    }
+
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        code = generate_code(ast)
+        print(code)
+    except SyntaxError:
+        pytest.fail("Bitwise Shift parsing not implemented.")
+
+
 if __name__ == "__main__":
     pytest.main()