Repository: CrossGL/crosstl

Issue:
Add Parsing for `Bitwise NOT`  Token
Update the parser to handle the BITWISE_NOT token, allowing it to correctly parse expressions involving the ~ operator.

<!-- BOT_STATE: {} -->

Discussion:
@AtulC137: hey hii ... i would like to work on this task if possible ! please this one .@shubhamprasad07: Hey @samthakur587, I would like to work on this issue.
@Barkule: hi can i get this task@ShreyasNaphad: I want to work on this. I would love it if you could assign this to me.@Manali-Naik18M: @NripeshN , i would like to work on this @Manali-Naik18M: # tests/test_translator/test_parser.py
import pytest
from translator.lexer import Lexer
from translator.parser import Parser
from translator.ast import UnaryOpNode, VariableNode, BinaryOpNode

def test_bitwise_not():
    lexer = Lexer("~a;")
    tokens = lexer.tokenize()
    parser = Parser(tokens)
    ast = parser.parse_body()
    assert len(ast) == 1
    assert isinstance(ast[0], UnaryOpNode)
    assert ast[0].op == "BITWISE_NOT"
    assert isinstance(ast[0].expr, VariableNode)
    assert ast[0].expr.name == "a"

def test_bitwise_not_complex_expression():
    lexer = Lexer("b = ~a + 5;")
    tokens = lexer.tokenize()
    parser = Parser(tokens)
    ast = parser.parse_body()
    assert len(ast) == 1
    assert isinstance(ast[0], BinaryOpNode)
    assert ast[0].op == "EQUALS"
    assert isinstance(ast[0].left, VariableNode)
    assert ast[0].left.name == "b"
    assert isinstance(ast[0].right, BinaryOpNode)
    assert ast[0].right.op == "PLUS"
    assert isinstance(ast[0].right.left, UnaryOpNode)
    assert ast[0].right.left.op == "BITWISE_NOT"
    assert isinstance(ast[0].right.left.expr, VariableNode)
    assert ast[0].right.left.expr.name == "a"
    assert ast[0].right.right == 5

def test_double_bitwise_not():
    lexer = Lexer("~~a;")
    tokens = lexer.tokenize()
    parser = Parser(tokens)
    ast = parser.parse_body()
    assert len(ast) == 1
    assert isinstance(ast[0], UnaryOpNode)
    assert ast[0].op == "BITWISE_NOT"
    assert isinstance(ast[0].expr, UnaryOpNode)
    assert ast[0].expr.op == "BITWISE_NOT"
    assert isinstance(ast[0].expr.expr, VariableNode)
    assert ast[0].expr.expr.name == "a"

def test_bitwise_not_with_parenthesis():
    lexer = Lexer("b = ~(a + 5);")
    tokens = lexer.tokenize()
    parser = Parser(tokens)
    ast = parser.parse_body()
    assert len(ast) == 1
    assert isinstance(ast[0], BinaryOpNode)
    assert ast[0].op == "EQUALS"
    assert isinstance(ast[0].left, VariableNode)
    assert ast[0].left.name == "b"
    assert isinstance(ast[0].right, UnaryOpNode)
    assert ast[0].right.op == "BITWISE_NOT"
    assert isinstance(ast[0].right.expr, BinaryOpNode)
    assert ast[0].right.expr.op == "PLUS"
    assert isinstance(ast[0].right.expr.left, VariableNode)
    assert ast[0].right.expr.left.name == "a"
    assert ast[0].right.expr.right == 5
    
    
    @NripeshN  , issue resoved@Manali-Naik18M: kindly let me know if any changes has to be made and done , @NripeshN @ShreyasNaphad: @NripeshN I want to work on this. I would love it if you could assign this to me.@dheeraxspidey: @CrossGL-issue-bot assign me@CrossGL-issue-bot: Thank you for taking on this issue! If you have any questions or need assistance, feel free to ask.

Solution patch:
diff --git a/crosstl/backend/DirectX/DirectxLexer.py b/crosstl/backend/DirectX/DirectxLexer.py
index 22c12f81..e2ac1da8 100644
--- a/crosstl/backend/DirectX/DirectxLexer.py
+++ b/crosstl/backend/DirectX/DirectxLexer.py
@@ -9,6 +9,7 @@
     [
         ("COMMENT_SINGLE", r"//.*"),
         ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
+        ("BITWISE_NOT", r"~"),
         ("INCLUDE", r"\#include\b"),
         ("STRUCT", r"\bstruct\b"),
         ("CBUFFER", r"\bcbuffer\b"),
diff --git a/crosstl/backend/DirectX/DirectxParser.py b/crosstl/backend/DirectX/DirectxParser.py
index 79de030f..572847c1 100644
--- a/crosstl/backend/DirectX/DirectxParser.py
+++ b/crosstl/backend/DirectX/DirectxParser.py
@@ -527,7 +527,7 @@ def parse_multiplicative(self):
         return left
 
     def parse_unary(self):
-        if self.current_token[0] in ["PLUS", "MINUS"]:
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
             op = self.current_token[1]
             self.eat(self.current_token[0])
             operand = self.parse_unary()
diff --git a/crosstl/backend/Metal/MetalLexer.py b/crosstl/backend/Metal/MetalLexer.py
index 963b22c8..349144cc 100644
--- a/crosstl/backend/Metal/MetalLexer.py
+++ b/crosstl/backend/Metal/MetalLexer.py
@@ -8,6 +8,7 @@
     [
         ("COMMENT_SINGLE", r"//.*"),
         ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
+        ("BITWISE_NOT", r"~"),
         ("PREPROCESSOR", r"#\w+"),
         ("STRUCT", r"\bstruct\b"),
         ("CONSTANT", r"\bconstant\b"),
diff --git a/crosstl/backend/Metal/MetalParser.py b/crosstl/backend/Metal/MetalParser.py
index d121507b..01da9d6f 100644
--- a/crosstl/backend/Metal/MetalParser.py
+++ b/crosstl/backend/Metal/MetalParser.py
@@ -460,7 +460,7 @@ def parse_multiplicative(self):
         return left
 
     def parse_unary(self):
-        if self.current_token[0] in ["PLUS", "MINUS"]:
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
             op = self.current_token[1]
             self.eat(self.current_token[0])
             operand = self.parse_unary()
diff --git a/crosstl/backend/Mojo/MojoLexer.py b/crosstl/backend/Mojo/MojoLexer.py
index 4faa8f3a..2d692516 100644
--- a/crosstl/backend/Mojo/MojoLexer.py
+++ b/crosstl/backend/Mojo/MojoLexer.py
@@ -8,6 +8,7 @@
     [
         ("COMMENT_SINGLE", r"#.*"),
         ("COMMENT_MULTI", r'"""[\s\S]*?"""'),
+        ("BITWISE_NOT", r"~"),
         ("STRUCT", r"\bstruct\b"),
         ("LET", r"\blet\b"),
         ("VAR", r"\bvar\b"),
diff --git a/crosstl/backend/Mojo/MojoParser.py b/crosstl/backend/Mojo/MojoParser.py
index e2ac3031..4daa727c 100644
--- a/crosstl/backend/Mojo/MojoParser.py
+++ b/crosstl/backend/Mojo/MojoParser.py
@@ -468,7 +468,7 @@ def parse_multiplicative(self):
         return left
 
     def parse_unary(self):
-        if self.current_token[0] in ["PLUS", "MINUS"]:
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
             op = self.current_token[1]
             self.eat(self.current_token[0])
             operand = self.parse_unary()
diff --git a/crosstl/backend/Opengl/OpenglParser.py b/crosstl/backend/Opengl/OpenglParser.py
index 370334cb..01f30c4e 100644
--- a/crosstl/backend/Opengl/OpenglParser.py
+++ b/crosstl/backend/Opengl/OpenglParser.py
@@ -729,11 +729,11 @@ def parse_unary(self):
             ASTNode: An ASTNode object representing the unary expression
 
         """
-        if self.current_token[0] in ["PLUS", "MINUS"]:
-            op = self.current_token[0]
-            self.eat(op)
-            expr = self.parse_unary()
-            return UnaryOpNode(op, expr)
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
+            op = self.current_token[1]
+            self.eat(self.current_token[0])
+            operand = self.parse_unary()
+            return UnaryOpNode(op, operand)
         return self.parse_primary()
 
     def parse_primary(self):
diff --git a/crosstl/backend/Vulkan/VulkanLexer.py b/crosstl/backend/Vulkan/VulkanLexer.py
index b89f4d58..00aa5d57 100644
--- a/crosstl/backend/Vulkan/VulkanLexer.py
+++ b/crosstl/backend/Vulkan/VulkanLexer.py
@@ -8,6 +8,7 @@
     [
         ("COMMENT_SINGLE", r"//.*"),
         ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
+        ("BITWISE_NOT", r"~"),
         ("WHITESPACE", r"\s+"),
         ("SEMANTIC", r":\w+"),
         ("PRE_INCREMENT", r"\+\+(?=\w)"),
diff --git a/crosstl/backend/Vulkan/VulkanParser.py b/crosstl/backend/Vulkan/VulkanParser.py
index f72d68d0..b0015d0c 100644
--- a/crosstl/backend/Vulkan/VulkanParser.py
+++ b/crosstl/backend/Vulkan/VulkanParser.py
@@ -445,6 +445,11 @@ def parse_primary(self):
             value = self.parse_primary()
             return UnaryOpNode("-", value)
 
+        if self.current_token[0] == "BITWISE_NOT":
+            self.eat("BITWISE_NOT")
+            value = self.parse_primary()
+            return UnaryOpNode("~", value)
+
         if (
             self.current_token[0] == "IDENTIFIER"
             or self.current_token[1] in VALID_DATA_TYPES
@@ -663,3 +668,11 @@ def parse_uniform(self):
         self.eat("IDENTIFIER")
         self.eat("SEMICOLON")
         return UniformNode(name, var_type)
+
+    def parse_unary(self):
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
+            op = self.current_token[1]
+            self.eat(self.current_token[0])
+            operand = self.parse_unary()
+            return UnaryOpNode(op, operand)
+        return self.parse_primary()
diff --git a/crosstl/backend/slang/SlangLexer.py b/crosstl/backend/slang/SlangLexer.py
index 44dc39f4..e7740b89 100644
--- a/crosstl/backend/slang/SlangLexer.py
+++ b/crosstl/backend/slang/SlangLexer.py
@@ -8,6 +8,7 @@
     [
         ("COMMENT_SINGLE", r"//.*"),
         ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
+        ("BITWISE_NOT", r"~"),
         ("STRUCT", r"\bstruct\b"),
         ("CBUFFER", r"\bcbuffer\b"),
         ("TYPE_SHADER", r'\[shader\("(vertex|fragment|compute)"\)\]'),
diff --git a/crosstl/backend/slang/SlangParser.py b/crosstl/backend/slang/SlangParser.py
index daa5200d..02ea2630 100644
--- a/crosstl/backend/slang/SlangParser.py
+++ b/crosstl/backend/slang/SlangParser.py
@@ -497,7 +497,7 @@ def parse_multiplicative(self):
         return left
 
     def parse_unary(self):
-        if self.current_token[0] in ["PLUS", "MINUS"]:
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
             op = self.current_token[1]
             self.eat(self.current_token[0])
             operand = self.parse_unary()
diff --git a/crosstl/translator/lexer.py b/crosstl/translator/lexer.py
index 21929417..84cad4f3 100644
--- a/crosstl/translator/lexer.py
+++ b/crosstl/translator/lexer.py
@@ -7,6 +7,7 @@
         ("COMMENT_SINGLE", r"//.*"),
         ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
         ("SHADER", r"\bshader\b"),
+        ("BITWISE_NOT", r"~"),
         ("VOID", r"\bvoid\b"),
         ("STRUCT", r"\bstruct\b"),
         ("CBUFFER", r"\bcbuffer\b"),
diff --git a/crosstl/translator/parser.py b/crosstl/translator/parser.py
index c17f72a5..18c76d2f 100644
--- a/crosstl/translator/parser.py
+++ b/crosstl/translator/parser.py
@@ -690,11 +690,9 @@ def parse_unary(self):
         This method parses a unary expression in the shader code.
 
         Returns:
-
             ASTNode: An ASTNode object representing the unary expression
-
         """
-        if self.current_token[0] in ["PLUS", "MINUS"]:
+        if self.current_token[0] in ["PLUS", "MINUS", "BITWISE_NOT"]:
             op = self.current_token[0]
             self.eat(op)
             expr = self.parse_unary()


Test patch:
diff --git a/tests/test_backend/test_directx/test_codegen.py b/tests/test_backend/test_directx/test_codegen.py
index 292a824a..0ffca73e 100644
--- a/tests/test_backend/test_directx/test_codegen.py
+++ b/tests/test_backend/test_directx/test_codegen.py
@@ -718,5 +718,21 @@ def test_half_dtype_codegen():
         pytest.fail("half dtype parsing or code generation not implemented.")
 
 
+def test_bitwise_not_codegen():
+    code = """
+    void main() {
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator code generation not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_directx/test_lexer.py b/tests/test_backend/test_directx/test_lexer.py
index 02f78a2a..cf1b7b05 100644
--- a/tests/test_backend/test_directx/test_lexer.py
+++ b/tests/test_backend/test_directx/test_lexer.py
@@ -299,5 +299,20 @@ def test_half_dtype_tokenization():
         pytest.fail("half dtype tokenization is not implemented.")
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_directx/test_parser.py b/tests/test_backend/test_directx/test_parser.py
index 7af3a86f..fb1a46df 100644
--- a/tests/test_backend/test_directx/test_parser.py
+++ b/tests/test_backend/test_directx/test_parser.py
@@ -415,5 +415,19 @@ def test_double_dtype_parsing():
         pytest.fail("half dtype not implemented.")
 
 
+def test_bitwise_not_parsing():
+    code = """
+    void main() {
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator parsing not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_metal/test_codegen.py b/tests/test_backend/test_metal/test_codegen.py
index 12a0426a..5e507296 100644
--- a/tests/test_backend/test_metal/test_codegen.py
+++ b/tests/test_backend/test_metal/test_codegen.py
@@ -331,5 +331,21 @@ def test_else_if():
     print(generated_code)
 
 
+def test_bitwise_not_codegen():
+    code = """
+    void main() {
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        generated_code = generate_code(ast)
+        print(generated_code)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator code generation not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_metal/test_lexer.py b/tests/test_backend/test_metal/test_lexer.py
index 8ea7b838..2569891f 100644
--- a/tests/test_backend/test_metal/test_lexer.py
+++ b/tests/test_backend/test_metal/test_lexer.py
@@ -142,5 +142,20 @@ def test_mod_tokenization():
     assert has_mod, "Modulus operator (%) not tokenized correctly"
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_metal/test_parser.py b/tests/test_backend/test_metal/test_parser.py
index 15b2f6ff..35e97647 100644
--- a/tests/test_backend/test_metal/test_parser.py
+++ b/tests/test_backend/test_metal/test_parser.py
@@ -167,5 +167,19 @@ def test_mod_parsing():
         pytest.fail("Modulus operator parsing not implemented")
 
 
+def test_bitwise_not_parsing():
+    code = """
+    void main() {
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator parsing not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_mojo/test_lexer.py b/tests/test_backend/test_mojo/test_lexer.py
index 95f7d18b..3b987289 100644
--- a/tests/test_backend/test_mojo/test_lexer.py
+++ b/tests/test_backend/test_mojo/test_lexer.py
@@ -25,5 +25,18 @@ def test_mod_tokenization():
     assert has_mod, "Modulus operator (%) not tokenized correctly"
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_opengl/test_lexer.py b/tests/test_backend/test_opengl/test_lexer.py
index ab677383..61c490ce 100644
--- a/tests/test_backend/test_opengl/test_lexer.py
+++ b/tests/test_backend/test_opengl/test_lexer.py
@@ -128,17 +128,27 @@ def test_mod_tokenization():
         int a = 10 % 3;  // Basic modulus
     """
     tokens = tokenize_code(code)
-
-    # Find the modulus operator in tokens
     has_mod = False
     for token in tokens:
         if token == ("MOD", "%"):
             has_mod = True
             break
-
     assert has_mod, "Modulus operator (%) not tokenized correctly"
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 def test_unsigned_int_dtype_tokenization():
     code = """
     double ComputeArea(double radius) {
diff --git a/tests/test_backend/test_slang/test_lexer.py b/tests/test_backend/test_slang/test_lexer.py
index 1ef8bd23..2f7245b5 100644
--- a/tests/test_backend/test_slang/test_lexer.py
+++ b/tests/test_backend/test_slang/test_lexer.py
@@ -106,16 +106,26 @@ def test_mod_tokenization():
         int a = 10 % 3;  // Basic modulus
     """
     tokens = tokenize_code(code)
-
-    # Find the modulus operator in tokens
     has_mod = False
     for token in tokens:
         if token == ("MOD", "%"):
             has_mod = True
             break
-
     assert has_mod, "Modulus operator (%) not tokenized correctly"
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_vulkan/test_lexer.py b/tests/test_backend/test_vulkan/test_lexer.py
index e68db359..bd8938e7 100644
--- a/tests/test_backend/test_vulkan/test_lexer.py
+++ b/tests/test_backend/test_vulkan/test_lexer.py
@@ -25,5 +25,18 @@ def test_mod_tokenization():
     assert has_mod, "Modulus operator (%) not tokenized correctly"
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = ~5;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_backend/test_vulkan/test_parser.py b/tests/test_backend/test_vulkan/test_parser.py
index ec7789aa..36f9976d 100644
--- a/tests/test_backend/test_vulkan/test_parser.py
+++ b/tests/test_backend/test_vulkan/test_parser.py
@@ -36,5 +36,19 @@ def test_mod_parsing():
         pytest.fail("Modulus operator parsing not implemented")
 
 
+def test_bitwise_not_parsing():
+    code = """
+    void main() {
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        parse_code(tokens)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator parsing not implemented")
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_translator/test_lexer.py b/tests/test_translator/test_lexer.py
index 35c778a1..f4793b03 100644
--- a/tests/test_translator/test_lexer.py
+++ b/tests/test_translator/test_lexer.py
@@ -256,5 +256,19 @@ def test_illegal_character():
         tokenize_code(code)
 
 
+def test_bitwise_not_tokenization():
+    code = """
+        int a = 5;
+        int b = ~a;  // Bitwise NOT
+    """
+    tokens = tokenize_code(code)
+    has_not = False
+    for token in tokens:
+        if token == ("BITWISE_NOT", "~"):
+            has_not = True
+            break
+    assert has_not, "Bitwise NOT operator (~) not tokenized correctly"
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/tests/test_translator/test_parser.py b/tests/test_translator/test_parser.py
index 8f3ca4a9..e2e34636 100644
--- a/tests/test_translator/test_parser.py
+++ b/tests/test_translator/test_parser.py
@@ -2,6 +2,7 @@
 import pytest
 from typing import List
 from crosstl.translator.parser import Parser
+from crosstl.translator.ast import ShaderNode
 
 
 def tokenize_code(code: str) -> List:
@@ -621,5 +622,42 @@ def test_modulus_operations():
         pytest.fail("Modulus operations not working")
 
 
+def test_bitwise_not():
+    code = """
+    shader test {
+        void main() {
+            int a = 5;
+            int b = ~a;  // Bitwise NOT
+        }
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        assert isinstance(ast, ShaderNode)
+    except SyntaxError:
+        pytest.fail("Bitwise NOT operator parsing failed")
+
+
+def test_bitwise_expressions():
+    code = """
+    shader test {
+        void main() {
+            int a = 5;
+            int b = ~a;  // Bitwise NOT
+            int c = a & b;  // Bitwise AND
+            int d = a | b;  // Bitwise OR
+            int e = a ^ b;  // Bitwise XOR
+        }
+    }
+    """
+    try:
+        tokens = tokenize_code(code)
+        ast = parse_code(tokens)
+        assert isinstance(ast, ShaderNode)
+    except SyntaxError:
+        pytest.fail("Bitwise expressions parsing failed")
+
+
 if __name__ == "__main__":
     pytest.main()