Repository: CrossGL/crosstl
Pull request #173 (2024-09-21 15:46:19)

fix: restructure the opengl parser
### PR Description
<!-- Provide a brief summary of the changes you have made. Explain the purpose and motivation behind these changes. -->

Re structured the opengl parser for crossgl. 
### Related Issue
<!-- Link to the related issue(s) that this PR addresses. Example: #123 -->

### shader Sample
<!-- Provide a shader sample or snippet on which you have tested your changes. like

```crossgl
shader PerlinNoise {
    vertex {
        input vec3 position;
        output vec2 vUV;

        void main() {
            vUV = position.xy * 10.0;
            gl_Position = vec4(position, 1.0);
        }
    }

    // Perlin Noise Function
    float perlinNoise(vec2 p) {
        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
    }

    // Fragment Shader
    fragment {
        input vec2 vUV;
        output vec4 fragColor;

        void main() {
            float noise = perlinNoise(vUV);
            float height = noise * 10.0;
            vec3 color = vec3(height / 10.0, 1.0 - height / 10.0, 0.0);
            fragColor = vec4(color, 1.0);
        }
    }
}
```-->


### Checklist
- [ ] Have you added the necessary tests?
- [ ] Only modified the files mentioned in the related issue(s)?
- [ ] Are all tests passing?





Patch:
diff --git a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
index 3296828..b84e3da 100644
--- a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
+++ b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py
@@ -5,10 +5,6 @@ from .DirectxLexer import *
 
 class HLSLToCrossGLConverter:
     def __init__(self):
-        self.vertex_inputs = []
-        self.vertex_outputs = []
-        self.fragment_inputs = []
-        self.fragment_outputs = []
         self.type_map = {
             "void": "void",
             "float2": "vec2",
diff --git a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
index 6995836..4de7d3d 100644
--- a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
+++ b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py
@@ -5,10 +5,6 @@ from .MetalLexer import *
 
 class MetalToCrossGLConverter:
     def __init__(self):
-        self.vertex_inputs = []
-        self.vertex_outputs = []
-        self.fragment_inputs = []
-        self.fragment_outputs = []
         self.type_map = {
             # Scalar Types
             "void": "void",
diff --git a/crosstl/src/backend/Opengl/OpenglAst.py b/crosstl/src/backend/Opengl/OpenglAst.py
index fae46f3..c603a5c 100644
--- a/crosstl/src/backend/Opengl/OpenglAst.py
+++ b/crosstl/src/backend/Opengl/OpenglAst.py
@@ -10,44 +10,17 @@ class UniformNode(ASTNode):
     def __repr__(self):
         return f"UniformNode(vtype={self.vtype}, name={self.name})"
 
-    def __str__(self):
-        return f"uniform {self.vtype} {self.name};"
-
 
 class ConstantNode(ASTNode):
-    def __init__(self, value):
-        self.value = value
-
-    def __repr__(self):
-        return f"ConstantNode(value={self.value})"
-
-    def __str__(self):
-        return str(self.value)
-
-
-class VersionDirectiveNode(ASTNode):
-    def __init__(self, number, profile):
-        self.version_number = number
-        self.profile = profile
-
-    def __repr__(self):
-        return f"VersionDirectiveNode(version_number={self.version_number}, profile={self.profile})"
-
-    def __str__(self):
-        return f"#version {self.version_number} {self.profile}"
-
-
-class LayoutNode:
-    def __init__(self, location_number, dtype, name):
-        self.location_number = location_number
-        self.dtype = dtype
+    def __init__(self, vtype, name, value):
+        self.vtype = vtype
         self.name = name
+        self.value = value
 
     def __repr__(self):
-        return f"LayoutNode(location={self.location_number}, dtype={self.dtype})"
-
-    def __str__(self):
-        return f"Layout(location={self.location_number}, type={self.dtype})"
+        return (
+            f"ConstantNode(vtype={self.vtype}, name ={self.name} ,value={self.value})"
+        )
 
 
 class TernaryOpNode:
@@ -61,105 +34,98 @@ class TernaryOpNode:
 
 
 class LayoutNode:
-    def __init__(self, section, location_number, dtype, name, io_type):
-        self.section = section
+    def __init__(self, location_number, dtype, name, io_type):
         self.location_number = location_number
         self.dtype = dtype
         self.name = name
         self.io_type = io_type
 
     def __repr__(self):
-        return f"LayoutNode(section={self.section}, location_number={self.location_number}, dtype={self.dtype}, name={self.name}, io_type={self.io_type})"
+        return f"LayoutNode(location_number={self.location_number}, dtype={self.dtype}, name={self.name}, io_type={self.io_type})"
 
 
-class ShaderNode:
+class ShaderNode(ASTNode):
     def __init__(
-        self,
-        version,
-        global_inputs,
-        global_outputs,
-        uniforms,
-        vertex_section,
-        fragment_section,
-        functions,
+        self, io_variables, constant, uniforms, global_variables, functions, shader_type
     ):
-        self.version = version
-        self.global_inputs = global_inputs
-        self.global_outputs = global_outputs
+        self.io_variables = io_variables
+        self.constant = constant
         self.uniforms = uniforms
-        self.vertex_section = vertex_section
-        self.fragment_section = fragment_section
+        self.global_variables = global_variables
         self.functions = functions
+        self.shader_type = shader_type
 
     def __repr__(self):
-        return f"ShaderNode({self.version!r}) {self.global_inputs!r} {self.global_outputs!r} {self.functions!r} {self.vertex_section!r} {self.fragment_section!r}"
-
-
-class VERTEXShaderNode:
-    def __init__(self, inputs, outputs, uniform, functions, layout_qualifiers=[]):
-        self.inputs = inputs
-        self.outputs = outputs
-        self.uniform = uniform
-        self.functions = functions
-        self.layout_qualifiers = layout_qualifiers
-
-    def __repr__(self):
-        return f"VERTEXShaderNode({self.inputs!r}) {self.outputs!r} {self.uniform!r}{self.functions!r}{self.layout_qualifiers!r}"
-
-
-class FRAGMENTShaderNode:
-    def __init__(self, inputs, outputs, uniform, functions, layout_qualifiers=[]):
-        self.inputs = inputs
-        self.outputs = outputs
-        self.uniform = uniform
-        self.functions = functions
-        self.layout_qualifiers = layout_qualifiers
-
-    def __repr__(self):
-        return f"FRAGMENTShaderNode({self.inputs!r}) {self.outputs!r}{self.uniform!r} {self.functions!r}{self.layout_qualifiers!r}"
+        return f"ShaderNode(io_variables={self.io_variables}, constant={self.constant},uniforms={self.uniforms} , global_variables={self.global_variables}, functions={self.functions}, shader_type={self.shader_type})"
 
 
 class FunctionNode(ASTNode):
-    def __init__(self, return_type, name, params, body):
+    def __init__(self, return_type, name, params, body, qualifier=None):
         self.return_type = return_type
         self.name = name
         self.params = params
         self.body = body
+        self.qualifier = qualifier
+
+    def __repr__(self):
+        return f"FunctionNode(return_type={self.return_type}, name={self.name}, params={self.params}, body={self.body}, qualifier={self.qualifier})"
+
+
+class ArrayAccessNode(ASTNode):
+    def __init__(self, array, index):
+        self.array = array
+        self.index = index
 
     def __repr__(self):
-        return f"FunctionNode(return_type={self.return_type}, name={self.name}, params={self.params}, body={self.body})"
+        return f"ArrayAccessNode(array={self.array}, index={self.index})"
 
 
 class VariableNode(ASTNode):
-    def __init__(self, vtype, name):
+    def __init__(self, vtype, name, io_type=None):
         self.vtype = vtype
         self.name = name
+        self.io_type = io_type
 
     def __repr__(self):
-        return f"VariableNode(vtype={self.vtype}, name={self.name})"
+        return f"VariableNode(vtype='{self.vtype}', name='{self.name}', io_type={self.io_type})"
 
 
 class AssignmentNode(ASTNode):
-    def __init__(self, name, value):
+    def __init__(self, name, value, operator="="):
         self.name = name
         self.value = value
+        self.operator = operator
+
+    def __repr__(self):
+        return f"AssignmentNode(name={self.name}, value={self.value}, operator='{self.operator}')"
+
+
+class VectorConstructorNode:
+    def __init__(self, type_name, args):
+        self.type_name = type_name
+        self.args = args
 
     def __repr__(self):
-        return f"AssignmentNode(name={self.name}, value={self.value})"
+        return f"VectorConstructorNode(type_name={self.type_name}, args={self.args})"
 
 
 class IfNode(ASTNode):
-    def __init__(self, condition, if_body, else_if_chain=None, else_body=None):
-        self.condition = condition
+    def __init__(
+        self,
+        if_condition,
+        if_body,
+        else_if_conditions=[],
+        else_if_bodies=[],
+        else_body=None,
+    ):
+        self.if_condition = if_condition
         self.if_body = if_body
-        self.else_if_chain = else_if_chain or []
+        self.else_if_conditions = else_if_conditions
+        self.else_if_bodies = else_if_bodies
         self.else_body = else_body
 
     def __repr__(self):
-        return (
-            f"IfNode(condition={self.condition}, if_body={self.if_body}, "
-            f"else_if_chain={self.else_if_chain}, else_body={self.else_body})"
-        )
+        return f"IfNode(if_condition={self.if_condition}, if_body={self.if_body}, else_if_conditions={self.else_if_conditions}, else_if_bodies={self.else_if_bodies}, else_body={self.else_body})"
 
 
 class ForNode(ASTNode):
diff --git a/crosstl/src/backend/Opengl/OpenglLexer.py b/crosstl/src/backend/Opengl/OpenglLexer.py
index 154ce15..bd9a24b 100644
--- a/crosstl/src/backend/Opengl/OpenglLexer.py
+++ b/crosstl/src/backend/Opengl/OpenglLexer.py
@@ -1,81 +1,102 @@
 import re
 
-# Token patterns
 TOKENS = [
     ("COMMENT_SINGLE", r"//.*"),
     ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"),
-    ("ELSE_IF", r"\belse\s+if\b"),
     ("VERSION", r"#version"),
-    ("NUMBER", r"\d+(\.\d+)?"),
-    ("CORE", r"\bcore\b"),
-    ("SHADER", r"\bshader\b"),
-    ("INPUT", r"\binput\b"),
-    ("OUTPUT", r"\boutput\b"),
-    ("VOID", r"\bvoid\b"),
-    ("MAIN", r"\bmain\b"),
+    ("PREPROCESSOR", r"#\w+"),
+    ("CONSTANT", r"\bconst\b"),
+    ("STRUCT", r"\bstruct\b"),
     ("UNIFORM", r"\buniform\b"),
-    ("VECTOR", r"\bvec[2-4]\b"),
-    ("MATRIX", r"\bmat[2-4]\b"),
-    ("BOOL", r"\bbool\b"),
+    ("SAMPLER2D", r"\bsampler2D\b"),
+    ("SAMPLERCUBE", r"\bsamplerCube\b"),
+    ("BUFFER", r"\bbuffer\b"),
+    ("VECTOR", r"\b(vec|ivec|uvec|bvec)[234]\b"),
+    ("MATRIX", r"\bmat[234](x[234])?\b"),
     ("FLOAT", r"\bfloat\b"),
     ("INT", r"\bint\b"),
-    ("SAMPLER2D", r"\bsampler2D\b"),
-    ("PRE_INCREMENT", r"\+\+(?=\w)"),
-    ("PRE_DECREMENT", r"--(?=\w)"),
-    ("POST_INCREMENT", r"(?<=\w)\+\+"),
-    ("POST_DECREMENT", r"(?<=\w)--"),
-    ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z_0-9]*"),
+    ("UINT", r"\buint\b"),
+    ("BOOL", r"\bbool\b"),
+    ("VOID", r"\bvoid\b"),
+    ("RETURN", r"\breturn\b"),
+    ("IF", r"\bif\b"),
+    ("ELSE", r"\belse\b"),
+    ("FOR", r"\bfor\b"),
+    ("WHILE", r"\bwhile\b"),
+    ("DO", r"\bdo\b"),
+    ("IN", r"\bin\b"),
+    ("OUT", r"\bout\b"),
+    ("INOUT", r"\binout\b"),
+    ("LAYOUT", r"\blayout\b"),
+    ("ATTRIBUTE", r"\battribute\b"),
+    ("VARYING", r"\bvarying\b"),
+    ("CONST", r"\bconst\b"),
+    ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"),
+    ("NUMBER", r"\d+(\.\d+)?([eE][+-]?\d+)?"),
     ("LBRACE", r"\{"),
     ("RBRACE", r"\}"),
     ("LPAREN", r"\("),
     ("RPAREN", r"\)"),
+    ("LBRACKET", r"\["),
+    ("RBRACKET", r"\]"),
     ("SEMICOLON", r";"),
+    ("STRING", r'"[^"]*"'),
     ("COMMA", r","),
-    ("ASSIGN_ADD", r"\+="),
-    ("ASSIGN_SUB", r"-="),
-    ("ASSIGN_MUL", r"\*="),
-    ("ASSIGN_DIV", r"/="),
-    ("EQUAL", r"=="),
-    ("NOT_EQUAL", r"!="),
-    ("WHITESPACE", r"\s+"),
-    ("IF", r"\bif\b"),
-    ("ELSE", r"\belse\b"),
-    ("FOR", r"\bfor\b"),
-    ("RETURN", r"\breturn\b"),
+    ("COLON", r":"),
     ("LESS_EQUAL", r"<="),
     ("GREATER_EQUAL", r">="),
     ("LESS_THAN", r"<"),
     ("GREATER_THAN", r">"),
+    ("EQUAL", r"=="),
     ("NOT_EQUAL", r"!="),
-    ("AND", r"&&"),
-    ("OR", r"\|\|"),
-    ("NOT", r"!"),
+    ("ASSIGN_AND", r"&="),
+    ("ASSIGN_OR", r"\|="),
+    ("ASSIGN_XOR", r"\^="),
+    ("LOGICAL_AND", r"&&"),
+    ("LOGICAL_OR", r"\|\|"),
+    ("ASSIGN_MOD", r"%="),
+    ("MOD", r"%"),
+    ("PLUS_EQUALS", r"\+="),
+    ("MINUS_EQUALS", r"-="),
+    ("MULTIPLY_EQUALS", r"\*="),
+    ("DIVIDE_EQUALS", r"/="),
     ("PLUS", r"\+"),
     ("MINUS", r"-"),
     ("MULTIPLY", r"\*"),
     ("DIVIDE", r"/"),
     ("DOT", r"\."),
     ("EQUALS", r"="),
-    ("QUESTION", r"\?"),
-    ("COLON", r":"),
-    ("LAYOUT", r"\blayout\b"),
-    ("IN", r"\bin\b"),
-    ("OUT", r"\bout\b"),
+    ("BITWISE_AND", r"&"),
+    ("BITWISE_OR", r"\|"),
+    ("BITWISE_XOR", r"\^"),
+    ("BITWISE_NOT", r"~"),
+    ("WHITESPACE", r"\s+"),
 ]
 
 KEYWORDS = {
+    "struct": "STRUCT",
+    "uniform": "UNIFORM",
+    "sampler2D": "SAMPLER2D",
+    "samplerCube": "SAMPLERCUBE",
+    "float": "FLOAT",
+    "int": "INT",
+    "uint": "UINT",
+    "bool": "BOOL",
     "void": "VOID",
-    "main": "MAIN",
-    "vertex": "VERTEX",
-    "fragment": "FRAGMENT",
+    "return": "RETURN",
     "else if": "ELSE_IF",
     "if": "IF",
     "else": "ELSE",
     "for": "FOR",
-    "return": "RETURN",
-    "layout": "LAYOUT",
+    "while": "WHILE",
+    "do": "DO",
     "in": "IN",
     "out": "OUT",
+    "inout": "INOUT",
+    "layout": "LAYOUT",
+    "attribute": "ATTRIBUTE",
+    "varying": "VARYING",
+    "const": "CONST",
 }
 
 
@@ -96,24 +117,17 @@ class GLSLLexer:
                     text = match.group(0)
                     if token_type == "IDENTIFIER" and text in KEYWORDS:
                         token_type = KEYWORDS[text]
-                    if token_type == "VERSION":
-                        self.tokens.append((token_type, text))
-                    elif token_type == "VERSION_NUMBER":
-                        self.tokens.append((token_type, text))
-                    elif token_type == "CORE":
-                        self.tokens.append((token_type, text))
-                    elif token_type != "WHITESPACE":  # Ignore whitespace tokens
+                    if token_type not in [
+                        "WHITESPACE",
+                        "COMMENT_SINGLE",
+                        "COMMENT_MULTI",
+                    ]:
                         token = (token_type, text)
                         self.tokens.append(token)
                     pos = match.end(0)
                     break
             if not match:
-                unmatched_char = self.code[pos]
-                highlighted_code = (
-                    self.code[:pos] + "[" + self.code[pos] + "]" + self.code[pos + 1 :]
-                )
                 raise SyntaxError(
-                    f"Illegal character '{unmatched_char}' at position {pos}\n{highlighted_code}"
+                    f"Illegal character '{self.code[pos]}' at position {pos}"
                 )
-
-        self.tokens.append(("EOF", None))
+        self.tokens.append(("EOF", ""))
diff --git a/crosstl/src/backend/Opengl/OpenglParser.py b/crosstl/src/backend/Opengl/OpenglParser.py
index e461a2c..7747619 100644
--- a/crosstl/src/backend/Opengl/OpenglParser.py
+++ b/crosstl/src/backend/Opengl/OpenglParser.py
@@ -1,33 +1,17 @@
-from .OpenglAst import (
-    LayoutNode,
-    ShaderNode,
-    FunctionNode,
-    AssignmentNode,
-    IfNode,
-    ForNode,
-    ReturnNode,
-    FunctionCallNode,
-    BinaryOpNode,
-    MemberAccessNode,
-    VariableNode,
-    UniformNode,
-    UnaryOpNode,
-    TernaryOpNode,
-    VERTEXShaderNode,
-    FRAGMENTShaderNode,
-    VersionDirectiveNode,
-)
+from .OpenglAst import *
 from .OpenglLexer import *
 
 
 class GLSLParser:
-    def __init__(self, tokens):
+    def __init__(self, tokens, shader_type):
         self.tokens = tokens
+        self.shader_type = shader_type
         self.pos = 0
         self.current_token = self.tokens[self.pos]
+        self.skip_comments()
 
     def skip_comments(self):
-        while self.current_token[0] in ["COMMENT_MULTI"]:
+        while self.current_token[0] in ["COMMENT_SINGLE", "COMMENT_MULTI"]:
             self.eat(self.current_token[0])
 
     def eat(self, token_type):
@@ -36,32 +20,89 @@ class GLSLParser:
             self.current_token = (
                 self.tokens[self.pos] if self.pos < len(self.tokens) else ("EOF", None)
             )
-            self.skip_comments()  # Skip comments after eating a token
+            self.skip_comments()
         else:
-            raise SyntaxError(
-                f"Expected {token_type}, got {self.current_token[0]},  {self.current_token[1]}"
-            )
+            raise SyntaxError(f"Expected {token_type}, got {self.current_token[0]}")
 
     def parse(self):
-        self.skip_comments()
-        version_node = self.parse_version_directive()  # Handle version directive
-        shader_node = self.parse_shader(version_node)
-        return shader_node
+        shader = self.parse_shader()
+        self.eat("EOF")
+        return shader
+
+    def parse_shader(self):
+        io_variables = []
+        constant = []
+        uniforms = []
+        global_variables = []
+        functions = []
+
+        while self.current_token[0] != "EOF":
+            if self.current_token[0] == "VERSION":
+                self.parse_version_directive()
+            elif self.current_token[0] == "LAYOUT":
+                io_variables.append(self.parse_layout())
+            elif self.current_token[0] == "IN" or self.current_token[0] == "OUT":
+                io_variables.append(self.parse_in_out())
+            elif self.current_token[0] == "CONSTANT":
+                constant.append(self.parse_constant())
+            elif self.current_token[0] == "UNIFORM":
+                uniforms.append(self.parse_uniform())
+            elif self.current_token[0] in [
+                "VOID",
+                "FLOAT",
+                "INT",
+                "VECTOR",
+                "MATRIX",
+                "IDENTIFIER",
+            ]:
+                if self.is_function():
+                    functions.append(self.parse_function())
+                else:
+                    global_variables.append(self.parse_global_variable())
+            else:
+                self.eat(self.current_token[0])  # Skip unknown tokens
+
+        return ShaderNode(
+            io_variables,
+            constant,
+            uniforms,
+            global_variables,
+            functions,
+            self.shader_type,
+        )
+
+    def is_function(self):
+        current_pos = self.pos
+        while self.tokens[current_pos][0] != "EOF":
+            if self.tokens[current_pos][0] == "LPAREN":
+                return True
+            if self.tokens[current_pos][0] == "SEMICOLON":
+                return False
+            current_pos += 1
+
+    def parse_constant(self):
+        self.eat("CONSTANT")
+        vtype = self.current_token[1]
+        self.eat(self.current_token[0])
+        name = self.current_token[1]
+        self.eat("IDENTIFIER")
+        self.eat("EQUALS")
+        value = self.parse_expression()
+        self.eat("SEMICOLON")
+        return ConstantNode(vtype, name, value)
 
     def parse_version_directive(self):
         if self.current_token[0] == "VERSION":
             self.eat("VERSION")
 
             if self.current_token[0] == "NUMBER":
-                number = self.current_token[1]
+                self.current_token[1]
                 self.eat("NUMBER")
 
-                version_identifier = None
                 # Handle any additional tokens after NUMBER (like 'core')
                 if self.current_token[0] == "CORE":
-                    version_identifier = self.current_token[1]
+                    self.current_token[1]
                     self.eat("CORE")
-                return VersionDirectiveNode(number, version_identifier)
             else:
                 raise SyntaxError(
                     f"Expected NUMBER after VERSION, got {self.current_token[0]}"
@@ -71,356 +112,114 @@ class GLSLParser:
                 f"Expected VERSION directive, got {self.current_token[0]}"
             )
 
-    def parse_layout(self, current_section):
-        self.eat("LAYOUT")
-        self.eat("LPAREN")
-
-        if (
-            self.current_token[0] == "IDENTIFIER"
-            and self.current_token[1] == "location"
-        ):
+    def parse_global_variable(self):
+        type_name = ""
+        if self.current_token[0] in ["FLOAT", "INT", "MATRIX", "VECTOR"]:
+            type_name = self.current_token[1]
+            self.eat(self.current_token[0])
+        if self.current_token[0] == "IDENTIFIER":
+            name = self.current_token[1]
             self.eat("IDENTIFIER")
-            self.eat("EQUALS")
-            location_number = self.current_token[1]
-            self.eat("NUMBER")
-
-            self.eat("RPAREN")
-            self.skip_comments()
-
-            if self.current_token[0] == "IN":
-                io_type = "input"
-                self.eat("IN")
-                dtype = self.parse_type()
-                name = self.current_token[1]
-                self.eat("IDENTIFIER")
+            if self.current_token[0] == "SEMICOLON":
                 self.eat("SEMICOLON")
-                return LayoutNode(
-                    section=current_section,
-                    location_number=location_number,
-                    dtype=dtype,
-                    name=name,
-                    io_type=io_type,
-                )
-            elif self.current_token[0] == "OUT":
-                io_type = "output"
-                self.eat("OUT")
-                dtype = self.parse_type()
-                name = self.current_token[1]
-                self.eat("IDENTIFIER")
+                return VariableNode(type_name, name)
+            elif self.current_token[0] == "EQUALS":
+                self.eat("EQUALS")
+                value = self.parse_expression()
                 self.eat("SEMICOLON")
-                return LayoutNode(
-                    section=current_section,
-                    location_number=location_number,
-                    dtype=dtype,
-                    name=name,
-                    io_type=io_type,
-                )
-            else:
-                raise SyntaxError("Expected 'IN' or 'OUT' after location in LAYOUT")
-        else:
-            raise SyntaxError("Expected IDENTIFIER 'location' in LAYOUT")
-
-    def parse_shader(self, version_node):
-        global_inputs = []
-        global_outputs = []
-        uniforms = []
-        vertex_section = VERTEXShaderNode([], [], [], [], [])
-        fragment_section = FRAGMENTShaderNode([], [], [], [], [])
-        current_section = None
-
-        while self.current_token[0] != "EOF":
-            if self.current_token[0] == "COMMENT_SINGLE":
-                comment_content = (
-                    self.current_token[1].strip().lower()
-                )  # Normalize content
-                if "vertex shader" in comment_content:
-                    current_section = "VERTEX"
-                elif "fragment shader" in comment_content:
-                    current_section = "FRAGMENT"
-                else:
-                    current_section = "VERTEX"
-
-                self.eat("COMMENT_SINGLE")
-
-            if self.current_token[0] == "LAYOUT":
-                self.skip_comments()
-                layout_node = self.parse_layout(current_section)
-                if current_section == "VERTEX":
-                    vertex_section.layout_qualifiers.append(layout_node)
-                elif current_section == "FRAGMENT":
-                    fragment_section.layout_qualifiers.append(layout_node)
-
-            elif self.current_token[0] == "IN":
-                self.skip_comments()
-                inputs = self.parse_inputs()
-                if current_section == "VERTEX":
-                    vertex_section.inputs.extend(inputs)
-                elif current_section == "FRAGMENT":
-                    fragment_section.inputs.extend(inputs)
-                else:
-                    global_inputs.extend(inputs)
-
-            elif self.current_token[0] == "OUT":
-                self.skip_comments()
-                outputs = self.parse_outputs()
-                if current_section == "VERTEX":
-                    vertex_section.outputs.extend(outputs)
-                elif current_section == "FRAGMENT":
-                    fragment_section.outputs.extend(outputs)
-                else:
-                    global_outputs.extend(outputs)
-
-            elif self.current_token[0] == "UNIFORM":
-                self.skip_comments()
-                uniforms.extend(self.parse_uniforms())
-
-            elif self.current_token[0] == "VERSION":
-                self.parse_version_directive()
-
-            elif self.current_token[0] in ["VOID", "FLOAT", "VECTOR"]:
-                self.skip_comments()
-                if current_section:
-                    function_node = self.parse_function()
-                    if current_section == "VERTEX":
-                        vertex_section.functions.append(function_node)
-                    elif current_section == "FRAGMENT":
-                        fragment_section.functions.append(function_node)
-                else:
-                    raise SyntaxError("Function found outside of shader section")
-            elif self.current_token[0] == "LBRACE":
-                if current_section is None:
-                    raise SyntaxError("LBRACE encountered outside of shader section")
-                self.eat("LBRACE")
-                section_content = self.parse_shader_section()
-                if current_section == "VERTEX":
-                    vertex_section.inputs.extend(section_content[0])
-                    vertex_section.outputs.extend(section_content[1])
-                    vertex_section.uniforms.extend(section_content[2])
-                    vertex_section.layout_qualifiers.extend(section_content[3])
-                    vertex_section.functions.extend(section_content[4])
-                elif current_section == "FRAGMENT":
-                    fragment_section.inputs.extend(section_content[0])
-                    fragment_section.outputs.extend(section_content[1])
-                    fragment_section.uniforms.extend(section_content[2])
-                    fragment_section.layout_qualifiers.extend(section_content[3])
-                    fragment_section.functions.extend(section_content[4])
-                self.eat("RBRACE")
-                current_section = None
-            else:
-                raise SyntaxError(f"Unexpected token {self.current_token[0]}")
-
-        return ShaderNode(
-            version=version_node,
-            global_inputs=global_inputs,
-            global_outputs=global_outputs,
-            uniforms=uniforms,
-            vertex_section=vertex_section,
-            fragment_section=fragment_section,
-            functions=[],
-        )
-
-    def parse_shader_section(self, current_section):
-        inputs = []
-        outputs = []
-        uniforms = []
-        functions = []
-        layout_qualifiers = []
-
-        self.eat("LBRACE")
-
-        while self.current_token[0] != "RBRACE" and self.current_token[0] != "EOF":
-            if self.current_token[0] == "LAYOUT":
-                self.skip_comments()
-                layout_node = self.parse_layout(current_section)
-                layout_qualifiers.append(layout_node)
-
-            elif self.current_token[0] == "IN":
-                self.skip_comments()
-                inputs.extend(self.parse_inputs())
-
-            elif self.current_token[0] == "OUT":
-                self.skip_comments()
-                outputs.extend(self.parse_outputs())
-
-            elif self.current_token[0] == "UNIFORM":
-                self.skip_comments()
-                uniforms.extend(self.parse_uniforms())
-
-            elif self.current_token[0] in ["VOID", "FLOAT", "VECTOR"]:
-                self.skip_comments()
-                functions.append(self.parse_function())
-
-            elif self.current_token[0] == "RBRACE":
-                self.eat("RBRACE")
-                return (inputs, outputs, uniforms, layout_qualifiers, functions)
-
+                return AssignmentNode(VariableNode(type_name, name), value)
             else:
                 raise SyntaxError(
-                    f"Unexpected token {self.current_token[0]} in shader section"
+                    f"Unexpected token in global variable declaration: {self.current_token[0]}"
                 )
+        else:
+            raise SyntaxError(
+                f"Expected IDENTIFIER after type name, got {self.current_token[0]}"
+            )
 
-        raise SyntaxError("Unexpected end of input in shader section")
-
-    def parse_inputs(self):
-        inputs = []
-        while self.current_token[0] == "IN":
-            self.eat("IN")
-            vtype = self.parse_type()
-            name = self.current_token[1]
-            self.eat("IDENTIFIER")
-            self.eat("SEMICOLON")
-            inputs.append((vtype, name))
-        return inputs
-
-    def parse_outputs(self):
-        outputs = []
-        while self.current_token[0] == "OUT":
-            self.eat("OUT")
-            vtype = self.parse_type()
-            name = self.current_token[1]
-            self.eat("IDENTIFIER")
-            self.eat("SEMICOLON")
-            outputs.append((vtype, name))
-        return outputs
-
-    def parse_uniforms(self):
-        uniforms = []
-        while self.current_token[0] == "UNIFORM":
-            self.eat("UNIFORM")
-            vtype = self.parse_type()
-            name = self.current_token[1]
-            self.eat("IDENTIFIER")
-            self.eat("SEMICOLON")
-            uniforms.append(UniformNode(vtype, name))
-        return uniforms
-
-    def parse_variable(self, type_name):
+    def parse_in_out(self):
+        if self.current_token[0] == "IN":
+            io_type = "IN"
+        else:
+            io_type = "OUT"
+        self.eat(io_type)
+        dtype = self.current_token[1]
+        self.eat(self.current_token[0])
         name = self.current_token[1]
         self.eat("IDENTIFIER")
+        self.eat("SEMICOLON")
+        return VariableNode(dtype, name, io_type)
 
-        while self.current_token[0] == "DOT":
-            self.eat("DOT")
-            member_name = self.current_token[1]
-            self.eat("IDENTIFIER")
-            name += "." + member_name
-
-        if self.current_token[0] == "SEMICOLON":
-            self.eat("SEMICOLON")
-            return VariableNode(type_name, name)
-
-        elif self.current_token[0] == "EQUALS":
-            self.eat("EQUALS")
-            value = self.parse_expression()
+    def parse_layout(self):
+        self.eat("LAYOUT")
+        self.eat("LPAREN")
 
-            if self.current_token[0] == "SEMICOLON":
-                self.eat("SEMICOLON")
-                return AssignmentNode(VariableNode(type_name, name), value)
-            else:
-                raise SyntaxError(
-                    f"Expected ';' after variable assignment, found: {self.current_token[0]}"
-                )
+        location_number = None
+        dtype = None
+        name = None
+        io_type = None
 
-        elif self.current_token[0] in (
-            "ASSIGN_ADD",
-            "ASSIGN_SUB",
-            "ASSIGN_MUL",
-            "ASSIGN_DIV",
-        ):
-            op = self.current_token[0]
-            op_name = self.current_token[1]
-            self.eat(op)
-            value = self.parse_expression()
-            if self.current_token[0] == "SEMICOLON":
-                self.eat("SEMICOLON")
-                return BinaryOpNode(VariableNode(type_name, name), op_name, value)
+        while self.current_token[0] != "SEMICOLON":
+            if self.current_token[0] == "IDENTIFIER":
+                self.eat("IDENTIFIER")
+                self.eat("EQUALS")
+                location_number = self.current_token[1]
+                self.eat("NUMBER")
+                self.eat("RPAREN")
+            elif self.current_token[0] == "IN" or self.current_token[0] == "OUT":
+                io_type = self.current_token[1]
+                self.eat(self.current_token[0])
+                dtype = self.current_token[1]
+                self.eat(self.current_token[0])
+                name = self.current_token[1]
+                self.eat("IDENTIFIER")
             else:
                 raise SyntaxError(
-                    f"Expected ';' after compound assignment, found: {self.current_token[0]}"
+                    f"Unexpected token in layout: {self.current_token[0]}"
                 )
-        else:
-            raise SyntaxError(
-                f"Unexpected token in variable declaration: {self.current_token[0]}"
-            )
 
-    def parse_assignment_or_function_call(self):
-        type_name = ""
-        if self.current_token[0] in ["VECTOR", "FLOAT", "INT", "MATRIX"]:
-            type_name = self.current_token[1]
-            self.eat(self.current_token[0])
-        if self.current_token[0] == "IDENTIFIER":
-            return self.parse_variable(type_name)
+            if self.current_token[0] == "COMMA":
+                self.eat("COMMA")
+        self.eat("SEMICOLON")
+        return LayoutNode(location_number, dtype, name, io_type)
 
+    def parse_uniform(self):
+        self.eat("UNIFORM")
+        dtype = self.current_token[1]
+        self.eat(self.current_token[0])
         name = self.current_token[1]
         self.eat("IDENTIFIER")
-
-        if self.current_token[0] in [
-            "EQUALS",
-            "ASSIGN_ADD",
-            "ASSIGN_SUB",
-            "ASSIGN_MUL",
-            "ASSIGN_DIV",
-        ]:
-            return self.parse_assignment(name)
-        elif self.current_token[0] == "INCREMENT":
-            self.eat("INCREMENT")
-            return AssignmentNode(name, UnaryOpNode("++", VariableNode("", name)))
-        elif self.current_token[0] == "DECREMENT":
-            self.eat("DECREMENT")
-            return AssignmentNode(name, UnaryOpNode("--", VariableNode("", name)))
-        elif self.current_token[0] == "LPAREN":
-            return self.parse_function_call(name)
-        else:
-            raise SyntaxError(
-                f"Unexpected token after identifier: {self.current_token[0]}"
-            )
-
-    def parse_function_call(self, name):
-        self.eat("LPAREN")
-        args = []
-        if self.current_token[0] != "RPAREN":
-            args.append(self.parse_expression())
-            while self.current_token[0] == "COMMA":
-                self.eat("COMMA")
-                args.append(self.parse_expression())
-        self.eat("RPAREN")
-        return FunctionCallNode(name, args)
+        self.eat("SEMICOLON")
+        return VariableNode(dtype, name)
 
     def parse_function(self):
-        return_type = self.parse_type()
-        if self.current_token[0] == "MAIN":
-            fname = self.current_token[1]
-            self.eat("MAIN")
-        elif self.current_token[0] == "IDENTIFIER":
-            fname = self.current_token[1]
-            self.eat("IDENTIFIER")
-        else:
-            raise SyntaxError(
-                f"Expected MAIN or IDENTIFIER, got {self.current_token[0]}"
-            )
+        return_type = self.current_token[1]
+        self.eat(self.current_token[0])
+        name = self.current_token[1]
+        qualifier = None
+        if name == "main":
+            qualifier = "main"
+
+        self.eat("IDENTIFIER")
         self.eat("LPAREN")
         params = self.parse_parameters()
         self.eat("RPAREN")
         self.eat("LBRACE")
         body = self.parse_body()
         self.eat("RBRACE")
-        return FunctionNode(return_type, fname, params, body)
-
-    def parse_body(self):
-        body = []
-        while self.current_token[0] not in ["RBRACE", "EOF"]:
-            if self.current_token[0] == "IF":
-                body.append(self.parse_if())
-            elif self.current_token[0] == "FOR":
-                body.append(self.parse_for())
-            elif self.current_token[0] == "RETURN":
-                body.append(self.parse_return())
-            elif self.current_token[0] in ["VECTOR", "IDENTIFIER", "FLOAT", "INT"]:
-                body.append(self.parse_assignment_or_function_call())
-            else:
-                raise SyntaxError(f"Unexpected token {self.current_token[0]}")
-        return body
+        return FunctionNode(return_type, name, params, body, qualifier)
 
     def parse_parameters(self):
+        """Parse function parameters
+
+        This method parses function parameters in the shader code.
+
+        Returns:
+
+            list: A list of function parameters
+
+        """
+
         params = []
         if self.current_token[0] != "RPAREN":
             params.append(self.parse_parameter())
@@ -430,172 +229,577 @@ class GLSLParser:
         return params
 
     def parse_parameter(self):
+        """Parse a function parameter
+
+        This method parses a function parameter in the shader code.
+
+        Returns:
+
+            tuple: A tuple containing the parameter type and name
+
+        """
         param_type = self.parse_type()
         param_name = self.current_token[1]
+
         self.eat("IDENTIFIER")
         return (param_type, param_name)
 
     def parse_type(self):
+        """Parse a type declaration
+
+        This method parses a type declaration in the shader code.
+
+        Returns:
+
+            str: The type name
+
+        Raises:
+
+            SyntaxError: If the current token is not a valid type declaration
+
+        """
         if self.current_token[0] == "VOID":
             self.eat("VOID")
             return "void"
         elif self.current_token[0] in [
             "VECTOR",
             "FLOAT",
+            "DOUBLE",
+            "UINT",
             "INT",
             "MATRIX",
-            "BOOLEAN",
             "SAMPLER2D",
         ]:
-            dtype = self.current_token[1]
+            vtype = self.current_token[1]
             self.eat(self.current_token[0])
-            return dtype
+            return vtype
         elif self.current_token[0] == "IDENTIFIER":
             type_name = self.current_token[1]
             self.eat("IDENTIFIER")
-            if type_name in ["int", "float"]:
+            if type_name in ["int", "uint", "float", "double"]:
                 return type_name
             return type_name
         else:
-            raise SyntaxError(f"Unexpected type token: {self.current_token[0]}")
+            raise SyntaxError(f"Expected type, got {self.current_token[0]}")
 
-    def parse_arguments(self):
-        args = []
-        while self.current_token[0] != "RPAREN":
-            args.append(self.parse_expression())
-            if self.current_token[0] == "COMMA":
-                self.eat("COMMA")
-        return args
+    def parse_body(self):
+        """Parse a function body
 
-    def parse_update(self):
-        if self.current_token[0] == "IDENTIFIER":
-            name = self.current_token[1]
-            self.eat("IDENTIFIER")
-            if self.current_token[0] == "POST_INCREMENT":
-                self.eat("POST_INCREMENT")
-                return UnaryOpNode("POST_INCREMENT", VariableNode("", name))
-            elif self.current_token[0] == "POST_DECREMENT":
-                self.eat("POST_DECREMENT")
-                return UnaryOpNode("POST_DECREMENT", VariableNode("", name))
+        This method parses a function body in the shader code.
+
+        Returns:
+
+            list: A list of statements in the function body
+
+        """
+        body = []
+        while self.current_token[0] not in ["RBRACE", "EOF"]:
+            if self.current_token[0] == "IF":
+                body.append(self.parse_if_statement())
+            elif self.current_token[0] == "FOR":
+                body.append(self.parse_for_loop())
+            elif self.current_token[0] == "RETURN":
+                body.append(self.parse_return_statement())
             elif self.current_token[0] in [
-                "EQUALS",
-                "ASSIGN_ADD",
-                "ASSIGN_SUB",
-                "ASSIGN_MUL",
-                "ASSIGN_DIV",
+                "VECTOR",
+                "IDENTIFIER",
+                "FLOAT",
+                "DOUBLE",
+                "UINT",
+                "INT",
             ]:
-                op = self.current_token[0]
-                self.eat(op)
-                value = self.parse_expression()
-                if op == "EQUALS":
-                    return AssignmentNode(name, value)
-                elif op == "ASSIGN_ADD":
-                    return AssignmentNode(
-                        name, BinaryOpNode(VariableNode("", name), "+", value)
-                    )
-                elif op == "ASSIGN_SUB":
-                    return AssignmentNode(
-                        name, BinaryOpNode(VariableNode("", name), "-", value)
-                    )
-                elif op == "ASSIGN_MUL":
-                    return AssignmentNode(
-                        name, BinaryOpNode(VariableNode("", name), "*", value)
-                    )
-                elif op == "ASSIGN_DIV":
-                    return AssignmentNode(
-                        name, BinaryOpNode(VariableNode("", name), "/", value)
-                    )
-                else:
-                    raise SyntaxError(
-                        f"Expected INCREMENT or DECREMENT, got {self.current_token[0]}"
-                    )
-        elif self.current_token[0] == "PRE_INCREMENT":
-            self.eat("PRE_INCREMENT")
-            if self.current_token[0] == "IDENTIFIER":
-                name = self.current_token[1]
-                self.eat("IDENTIFIER")
-                return UnaryOpNode("++", VariableNode("", name))
-            else:
-                raise SyntaxError(
-                    f"Expected IDENTIFIER after PRE_INCREMENT, got {self.current_token[0]}"
-                )
-        elif self.current_token[0] == "PRE_DECREMENT":
-            self.eat("PRE_DECREMENT")
-            if self.current_token[0] == "IDENTIFIER":
-                name = self.current_token[1]
-                self.eat("IDENTIFIER")
-                return UnaryOpNode("--", VariableNode("", name))
+                body.append(self.parse_assignment_or_function_call())
             else:
-                raise SyntaxError(
-                    f"Expected IDENTIFIER after PRE_DECREMENT, got {self.current_token[0]}"
-                )
-        else:
-            raise SyntaxError(f"Unexpected token in update: {self.current_token[0]}")
+                raise SyntaxError(f"Unexpected token {self.current_token[0]}")
+        return body
 
-    def parse_assignment(self):
-        var_name = self.current_token[1]
-        self.eat("IDENTIFIER")
-        self.eat("EQUALS")
-        expr = self.parse_expression()
-        self.eat("SEMICOLON")
-        return AssignmentNode(var_name, expr)
+    def parse_if_statement(self):
+        """Parse an if statement
 
-    def parse_function_call_or_identifier(self):
-        if self.current_token[0] == "VECTOR":
-            func_name = self.current_token[1]
-            self.eat("VECTOR")
-        else:
-            func_name = self.current_token[1]
+        This method parses an if statement in the shader code.
+
+        Returns:
+
+            IfNode: An IfNode object representing the if statement
+
+        """
+        self.eat("IF")
+        self.eat("LPAREN")
+        if_condition = self.parse_expression()
+        self.eat("RPAREN")
+        self.eat("LBRACE")
+        if_body = self.parse_body()
+        self.eat("RBRACE")
+        else_if_condition = []
+        else_if_body = []
+        else_body = None
+
+        while self.current_token[0] == "ELSE" and self.peak(1)[0] == "IF":
+            self.eat("ELSE")
+            self.eat("IF")
+            self.eat("LPAREN")
+            else_if_condition.append(self.parse_expression())
+            self.eat("RPAREN")
+            self.eat("LBRACE")
+            else_if_body.append(self.parse_body())
+            self.eat("RBRACE")
+
+        if self.current_token[0] == "ELSE":
+            self.eat("ELSE")
+            self.eat("LBRACE")
+            else_body = self.parse_body()
+            self.eat("RBRACE")
+        return IfNode(if_condition, if_body, else_if_condition, else_if_body, else_body)
+
+    def peak(self, n):
+        """Peek ahead in the token list
+
+        This method returns the nth token ahead in the token list.
+
+        Args:
+
+            n (int): The number of tokens to peek ahead
+
+        Returns:
+
+                tuple: The nth token ahead in the token list
+
+        """
+
+        return self.tokens[self.pos + n]
+
+    def parse_for_loop(self):
+        """Parse a for loop
+
+        This method parses a for loop in the shader code.
+
+        Returns:
+
+            ForNode: A ForNode object representing the for loop
+
+        """
+
+        self.eat("FOR")
+        self.eat("LPAREN")
+
+        init = self.parse_assignment_or_function_call()
+
+        condition = self.parse_assignment_or_function_call()
+
+        if self.peak(2)[0] == "RPAREN":
+            update = self.parse_update()
+        else:
+            update = self.parse_assignment_or_function_call(update_condition=True)
+
+        self.eat("RPAREN")
+        self.eat("LBRACE")
+
+        body = self.parse_body()
+        self.eat("RBRACE")
+
+        return ForNode(init, condition, update, body)
+
+    def parse_update(self):
+        """Parse an update statement
+
+        This method parses an update statement in the shader code.
+
+        Returns:
+
+            ASTNode: An ASTNode object representing the update statement
+
+        """
+        if self.current_token[0] == "IDENTIFIER":
+            name = self.current_token[1]
             self.eat("IDENTIFIER")
+            if self.current_token[0] == "INCREMENT":
+                op_name = self.current_token[1]
+                self.eat("INCREMENT")
+                return VariableNode("", name + op_name)
+            elif self.current_token[0] == "DECREMENT":
+                op_name = self.current_token[1]
+                self.eat("DECREMENT")
+                return VariableNode("", name + op_name)
+            else:
+                raise SyntaxError(
+                    f"Expected INCREMENT or DECREMENT, got {self.current_token[0]}"
+                )
+        elif self.current_token[0] in ["INCREMENT", "DECREMENT"]:
+            op = self.current_token[0]
+            op_name = self.current_token[1]
+            self.eat(op)
+            if self.current_token[0] == "IDENTIFIER":
+                name = self.current_token[1]
+                self.eat("IDENTIFIER")
+                return VariableNode("", op_name + name)
+        else:
+            raise SyntaxError(f"Unexpected token in update: {self.current_token[0]}")
 
-        if self.current_token[0] == "LPAREN":
-            return self.parse_function_call(func_name)
-        elif self.current_token[0] == "DOT":
-            return self.parse_member_access(func_name)
-        return VariableNode("", func_name)
+    def parse_return_statement(self):
+        """Parse a return statement
+
+        This method parses a return statement in the shader code.
+
+        Returns:
+
+            ReturnNode: A ReturnNode object representing the return statement
+
+        """
+        self.eat("RETURN")
+        return_value = []
+        return_value.append(self.parse_expression())
+        while self.current_token[0] == "COMMA":
+            self.eat("COMMA")
+            return_value.append(self.parse_expression())
+        self.eat("SEMICOLON")
+        return ReturnNode(return_value)
+
+    def parse_assignment_or_function_call(self, update_condition=False):
+        """Parse an assignment or function call
+
+        This method parses an assignment or function call in the shader code.
+
+        Returns:
+
+            ASTNode: An ASTNode object representing the assignment or function call
+
+        """
+        type_name = ""
+        inc_dec = False
+        if self.current_token[0] in [
+            "VECTOR",
+            "FLOAT",
+            "DOUBLE",
+            "UINT",
+            "INT",
+            "MATRIX",
+        ]:
+            type_name = self.current_token[1]
+            self.eat(self.current_token[0])
+        if self.current_token[0] == "IDENTIFIER":
+            return self.parse_variable_declaration(type_name, update_condition)
+        if self.current_token[0] in ["INCREMENT", "DECREMENT"]:
+            inc_dec = True
+            inc_dec_op = self.current_token[1]
+            self.eat(self.current_token[0])
+        name = self.current_token[1]
+        self.eat("IDENTIFIER")
+        if inc_dec:
+            name = VariableNode(type_name, VariableNode("", inc_dec_op + name))
+        if self.current_token[0] in [
+            "EQUALS",
+            "ASSIGN_ADD",
+            "ASSIGN_SUB",
+            "ASSIGN_MUL",
+            "ASSIGN_DIV",
+            "LESS_THAN",
+            "GREATER_THAN",
+            "LESS_EQUAL",
+            "GREATER_EQUAL",
+            "ASSIGN_AND",
+            "ASSIGN_OR",
+            "ASSIGN_XOR",
+            "ASSIGN_MOD",
+            "BITWISE_SHIFT_RIGHT",
+            "BITWISE_SHIFT_LEFT",
+            "BITWISE_XOR",
+            "ASSIGN_SHIFT_LEFT" "ASSIGN_SHIFT_RIGHT",
+        ]:
+            return self.parse_assignment(name)
+        elif self.current_token[0] == "INCREMENT":
+            self.eat("INCREMENT")
+            op_name = self.current_token[1]
+            return VariableNode(type_name, VariableNode("", name + op_name))
+        elif self.current_token[0] == "DECREMENT":
+            self.eat("DECREMENT")
+            op_name = self.current_token[1]
+            return VariableNode(type_name, VariableNode("", name + op_name))
+        elif self.current_token[0] == "LPAREN":
+            return self.parse_function_call(name)
+        else:
+            raise SyntaxError(
+                f"Unexpected token after identifier: {self.current_token[0]}"
+            )
+
+    def parse_variable_declaration(self, type_name, update_condition=False):
+        """Parse a variable declaration
+
+        This method parses a variable declaration in the shader code.
+
+        Args:
+
+            type_name (str): The type of the variable
+            update_condition (bool): A flag indicating if the variable is from for loop update statement
+        Returns:
+
+            VariableNode: A VariableNode object representing the variable declaration
+
+        Raises:
+
+            SyntaxError: If the current token is not a valid variable declaration
+
+        """
+        name = self.current_token[1]
+        self.eat("IDENTIFIER")
+        if self.current_token[0] == "DOT":
+            name = self.parse_member_access(name)
+        if self.current_token[0] == "SEMICOLON":
+            self.eat("SEMICOLON")
+            return VariableNode(type_name, name)
+
+        elif self.current_token[0] in [
+            "EQUALS",
+            "ASSIGN_ADD",
+            "ASSIGN_SUB",
+            "ASSIGN_MUL",
+            "ASSIGN_DIV",
+            "EQUAL",
+            "LESS_THAN",
+            "GREATER_THAN",
+            "LESS_EQUAL",
+            "GREATER_EQUAL",
+            "ASSIGN_AND",
+            "ASSIGN_OR",
+            "ASSIGN_XOR",
+            "ASSIGN_MOD",
+            "BITWISE_SHIFT_RIGHT",
+            "BITWISE_SHIFT_LEFT",
+            "BITWISE_XOR",
+            "ASSIGN_SHIFT_LEFT" "ASSIGN_SHIFT_RIGHT",
+        ]:
+            op = self.current_token[1]
+            self.eat(self.current_token[0])
+            value = self.parse_expression()
+            if self.current_token[0] == "DOT":
+                value = self.parse_member_access(value)
+            if self.current_token[0] == "SEMICOLON":
+                self.eat("SEMICOLON")
+                return BinaryOpNode(VariableNode(type_name, name), op, value)
+
+            else:
+                if update_condition:
+                    return BinaryOpNode(VariableNode(type_name, name), op, value)
+                else:
+                    raise SyntaxError(
+                        f"Expected ';' after variable assignment, found: {self.current_token[0]}"
+                    )
+
+        elif self.current_token[0] in (
+            "ASSIGN_ADD",
+            "ASSIGN_SUB",
+            "ASSIGN_MUL",
+            "ASSIGN_DIV",
+            "EQUALS",
+            "LESS_THAN",
+            "GREATER_THAN",
+            "LESS_EQUAL",
+            "GREATER_EQUAL",
+            "BITWISE_SHIFT_RIGHT",
+            "BITWISE_SHIFT_LEFT",
+            "BITWISE_XOR",
+            "EQUAL",
+            "ASSIGN_AND",
+            "ASSIGN_OR",
+            "ASSIGN_XOR",
+            "ASSIGN_MOD",
+            "ASSIGN_SHIFT_RIGHT",
+            "ASSIGN_SHIFT_LEFT",
+        ):
+            op = self.current_token[0]
+            self.eat(op)
+            value = self.parse_expression()
+            if self.current_token[0] == "SEMICOLON":
+                self.eat("SEMICOLON")
+                return BinaryOpNode(VariableNode(type_name, name), op, value)
+            else:
+                raise SyntaxError(
+                    f"Expected ';' after compound assignment, found: {self.current_token[0]}"
+                )
+
+        else:
+            raise SyntaxError(
+                f"Unexpected token in variable declaration: {self.current_token[0]}"
+            )
+
+    def parse_assignment(self, name):
+        """Parse an assignment statement
+
+        This method parses an assignment statement in the shader code.
+
+        Args:
+
+            name (str): The name of the variable being assigned
+
+        Returns:
+
+            AssignmentNode: An AssignmentNode object representing the assignment statement
+
+        Raises:
+
+            SyntaxError: If the current token is not a valid assignment statement
+
+        """
+
+        if self.current_token[0] in [
+            "EQUALS",
+            "ASSIGN_ADD",
+            "ASSIGN_SUB",
+            "ASSIGN_MUL",
+            "ASSIGN_DIV",
+            "EQUAL",
+            "LESS_THAN",
+            "GREATER_THAN",
+            "LESS_EQUAL",
+            "GREATER_EQUAL",
+            "ASSIGN_AND",
+            "ASSIGN_OR",
+            "ASSIGN_XOR",
+            "ASSIGN_MOD",
+            "BITWISE_SHIFT_RIGHT",
+            "BITWISE_SHIFT_LEFT",
+            "BITWISE_XOR",
+            "ASSIGN_SHIFT_RIGHT",
+            "ASSIGN_SHIFT_LEFT",
+        ]:
+            op = self.current_token[0]
+            op_name = self.current_token[1]
+            self.eat(op)
+            value = self.parse_expression()
+            if self.current_token[0] == "SEMICOLON":
+                self.eat("SEMICOLON")
+            return BinaryOpNode(name, op_name, value)
+        else:
+            raise SyntaxError(
+                f"Expected assignment operator, found: {self.current_token[0]}"
+            )
 
     def parse_additive(self):
-        left = self.parse_multiplicative()
+        """Parse an additive expression
+
+        This method parses an additive expression in the shader code.
+
+        Returns:
+
+                ASTNode: An ASTNode object representing the additive expression
+
+        """
+        expr = self.parse_multiplicative()
         while self.current_token[0] in ["PLUS", "MINUS"]:
             op = self.current_token[0]
             self.eat(op)
             right = self.parse_multiplicative()
-            left = BinaryOpNode(left, op, right)
-        return left
+            expr = BinaryOpNode(expr, op, right)
+        return expr
+
+    def parse_multiplicative(self):
+        """Parse a multiplicative expression
+
+        This method parses a multiplicative expression in the shader code.
+
+        Returns:
+
+            ASTNode: An ASTNode object representing the multiplicative expression
+
+        """
+        expr = self.parse_unary()
+        while self.current_token[0] in ["MULTIPLY", "DIVIDE"]:
+            op = self.current_token[0]
+            self.eat(op)
+            right = self.parse_unary()
+            expr = BinaryOpNode(expr, op, right)
+        return expr
+
+    def parse_unary(self):
+        """Parse a unary expression
+
+        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"]:
+            op = self.current_token[0]
+            self.eat(op)
+            expr = self.parse_unary()
+            return UnaryOpNode(op, expr)
+        return self.parse_primary()
 
     def parse_primary(self):
-        if self.current_token[0] == "MINUS":
-            self.eat("MINUS")
-            value = self.parse_primary()  # Handle the negation as a unary operation
-            return UnaryOpNode("-", value)
+        """Parse a primary expression
 
-        if self.current_token[0] in ("IDENTIFIER", "VECTOR", "FLOAT"):
-            return self.parse_function_call_or_identifier()
-        elif self.current_token[0] == "NUMBER":
-            value = self.current_token[1]
-            self.eat("NUMBER")
-            return value
-        elif self.current_token[0] == "LPAREN":
+        This method parses a primary expression in the shader code.
+
+        Returns:
+
+
+            ASTNode: An ASTNode object representing the primary expression
+
+        Raises:
+
+            SyntaxError: If the current token is not a valid primary expression
+
+        """
+        if self.current_token[0] == "LPAREN":
             self.eat("LPAREN")
             expr = self.parse_expression()
             self.eat("RPAREN")
             return expr
+        elif self.current_token[0] in ["NUMBER", "FLOAT_NUMBER"]:
+            value = self.current_token[1]
+            self.eat(self.current_token[0])
+            return value
+        elif self.current_token[0] in [
+            "IDENTIFIER",
+            "VECTOR",
+            "FLOAT",
+            "DOUBLE",
+            "UINT",
+            "INT",
+            "MATRIX",
+        ]:
+            return self.parse_function_call_or_identifier()
         else:
             raise SyntaxError(
                 f"Unexpected token in expression: {self.current_token[0]}"
             )
 
-    def parse_multiplicative(self):
-        left = self.parse_primary()
-        while self.current_token[0] in ["MULTIPLY", "DIVIDE"]:
-            op = self.current_token[0]
-            self.eat(op)
-            right = self.parse_primary()
-            left = BinaryOpNode(left, op, right)
-        return left
+    def parse_function_call(self, name):
+        """Parse a function call
+
+        This method parses a function call in the shader code.
+
+        Args:
+
+            name (str): The name of the function being called
+
+        Returns:
+
+            FunctionCallNode: A FunctionCallNode object representing the function call
+
+        """
+        self.eat("LPAREN")
+        args = []
+        if self.current_token[0] != "RPAREN":
+            args.append(self.parse_expression())
+            while self.current_token[0] == "COMMA":
+                self.eat("COMMA")
+                args.append(self.parse_expression())
+        self.eat("RPAREN")
+        return FunctionCallNode(name, args)
 
     def parse_expression(self):
-        left = self.parse_additive()
+        """Parse an expression
+
+        This method parses an expression in the shader code.
+
+        Returns:
+
+            ASTNode: An ASTNode object representing the expression
+
+        """
+        expr = self.parse_ternary()
         while self.current_token[0] in [
             "LESS_THAN",
             "GREATER_THAN",
@@ -603,79 +807,100 @@ class GLSLParser:
             "GREATER_EQUAL",
             "EQUAL",
             "NOT_EQUAL",
-            "AND",
-            "OR",
+            "LOGICAL_AND",
+            "LOGICAL_OR",
+            "PLUS",
+            "MINUS",
+            "MULTIPLY",
+            "DIVIDE",
+            "EQUALS",
+            "ASSIGN_ADD",
+            "ASSIGN_SUB",
+            "ASSIGN_MUL",
+            "ASSIGN_DIV",
+            "ASSIGN_AND",
+            "ASSIGN_OR",
+            "ASSIGN_XOR",
+            "ASSIGN_MOD",
+            "BITWISE_SHIFT_RIGHT",
+            "BITWISE_SHIFT_LEFT",
+            "BITWISE_XOR",
+            "ASSIGN_SHIFT_RIGHT",
+            "ASSIGN_SHIFT_LEFT",
         ]:
             op = self.current_token[0]
             self.eat(op)
-            right = self.parse_additive()
-            left = BinaryOpNode(left, op, right)
+            right = self.parse_ternary()
+            expr = BinaryOpNode(expr, op, right)
+        return expr
+
+    def parse_ternary(self):
+        """Parse a ternary expression
+
+        This method parses a ternary expression in the shader code.
+
+        Returns:
+
+            ASTNode: An ASTNode object representing the ternary expression
 
+        """
+        expr = self.parse_additive()
         if self.current_token[0] == "QUESTION":
             self.eat("QUESTION")
             true_expr = self.parse_expression()
             self.eat("COLON")
             false_expr = self.parse_expression()
-            left = TernaryOpNode(left, true_expr, false_expr)
+            expr = TernaryOpNode(expr, true_expr, false_expr)
+        return expr
 
-        return left
-
-    def parse_return(self):
-        self.eat("RETURN")
-        expr = self.parse_expression()
-        self.eat("SEMICOLON")
-        return ReturnNode(expr)
+    def parse_function_call_or_identifier(self):
+        """Parse a function call or identifier
 
-    def parse_else_if_chain(self):
-        else_if_chain = []
-        else_body = None
+        This method parses a function call or identifier in the shader code.
 
-        while self.current_token[0] in ["ELSE_IF", "ELSE"]:
-            if self.current_token[0] == "ELSE_IF":
-                self.eat("ELSE_IF")
-                self.eat("LPAREN")
-                elif_condition = self.parse_expression()
-                self.eat("RPAREN")
-                self.eat("LBRACE")
-                elif_body = self.parse_body()
-                self.eat("RBRACE")
-                else_if_chain.append((elif_condition, elif_body))
-            else:
-                # Handle `ELSE`
-                self.eat("ELSE")
-                self.eat("LBRACE")
-                else_body = self.parse_body()
-                self.eat("RBRACE")
-                break
-        return else_if_chain, else_body
-
-    def parse_if(self):
-        self.eat("IF")
-        self.eat("LPAREN")
-        condition = self.parse_expression()
-        self.eat("RPAREN")
-        self.eat("LBRACE")
-        body = self.parse_body()
-        self.eat("RBRACE")
+        Returns:
 
-        else_if_chain, else_body = self.parse_else_if_chain()
+            ASTNode: An ASTNode object representing the function call or identifier
 
-        return IfNode(condition, body, else_if_chain, else_body)
+        """
+        if self.current_token[0] in [
+            "VECTOR",
+            "FLOAT",
+            "DOUBLE",
+            "UINT",
+            "INT",
+            "MATRIX",
+        ]:
+            func_name = self.current_token[1]
+            self.eat(self.current_token[0])
+        else:
+            func_name = self.current_token[1]
+            self.eat("IDENTIFIER")
 
-    def parse_for(self):
-        self.eat("FOR")
-        self.eat("LPAREN")
-        init = self.parse_assignment_or_function_call()
-        condition = self.parse_expression()
-        self.eat("SEMICOLON")
-        update = self.parse_update()
-        self.eat("RPAREN")
-        self.eat("LBRACE")
-        body = self.parse_body()
-        self.eat("RBRACE")
-        return ForNode(init, condition, update, body)
+        if self.current_token[0] == "LPAREN":
+            return self.parse_function_call(func_name)
+        elif self.current_token[0] == "DOT":
+            return self.parse_member_access(func_name)
+        return VariableNode("", func_name)
 
     def parse_member_access(self, object):
+        """Parse a member access
+
+        This method parses a member access in the shader code.
+
+        Args:
+
+                object (str): The object being accessed
+
+        Returns:
+
+                MemberAccessNode: A MemberAccessNode object representing the member access
+
+        Raises:
+
+            SyntaxError: If the current token is not a valid member access
+
+        """
         self.eat("DOT")
         if self.current_token[0] != "IDENTIFIER":
             raise SyntaxError(
diff --git a/crosstl/src/backend/Opengl/openglCrossglCodegen.py b/crosstl/src/backend/Opengl/openglCrossglCodegen.py
index 4fdac11..de983a4 100644
--- a/crosstl/src/backend/Opengl/openglCrossglCodegen.py
+++ b/crosstl/src/backend/Opengl/openglCrossglCodegen.py
@@ -4,288 +4,221 @@ from .OpenglLexer import *
 
 
 class GLSLToCrossGLConverter:
-    def __init__(self):
-        self.current_shader = None
-        self.vertex_item = None
-        self.fragment_item = None
+    def __init__(self, shader_type: str):
+        self.shader_type = shader_type
+        self.structures = {"VSInput": [], "PSOutput": [], "PSInput": [], "VSOutput": []}
+        self.gl_position = False
+        self.type_mapping = {}
 
     def generate(self, ast):
-        if isinstance(ast, ShaderNode):
-            self.current_shader = ast
-            return self.generate_shader(ast)
-        return ""
-
-    def generate_shader(self, node):
-        # Set up the shader
-        self.shader_inputs = node.global_inputs
-        self.shader_outputs = node.global_outputs
-        self.uniforms = node.uniforms
 
         code = "shader main {\n"
-
-        # Generate vertex shader section
-        self.vertex_item = node.vertex_section
-        if self.vertex_item:
-            code += "    vertex {\n"
-            code += self.generate_layouts(self.vertex_item.layout_qualifiers)
-            for vtype, name in self.shader_inputs:
-                code += f"        input {self.map_type(vtype)} {name};\n"
-            for vtype, name in self.shader_outputs:
-                code += f"        output {self.map_type(vtype)} {name};\n"
-            for vtype, name in self.vertex_item.inputs:
-                code += f"        input {self.map_type(vtype)} {name};\n"
-            for vtype, name in self.vertex_item.outputs:
-                code += f"        output {self.map_type(vtype)} {name};\n"
-                code += "\n"
-            code += self.generate_uniforms() + "\n"
-            code += "\n"
-
-            # Generate functions
-            code += self.generate_functions(self.vertex_item.functions, "vertex")
-            code += "    }\n"
-        else:
-            raise ValueError("No vertex shader section to generate.")
-
-        # Generate fragment shader section if present
-        self.fragment_item = node.fragment_section
-        if self.fragment_item and (
-            self.fragment_item.layout_qualifiers or self.fragment_item.functions
-        ):
-            code += "    fragment {\n"
-            code += self.generate_layouts(self.fragment_item.layout_qualifiers)
-            # Process inputs and outputs
-            for vtype, name in self.fragment_item.inputs:
-                code += f"        input {self.map_type(vtype)} {name};\n"
-            for vtype, name in self.fragment_item.outputs:
-                code += f"        output {self.map_type(vtype)} {name};\n"
-                code += "\n"
-            code += self.generate_uniforms() + "\n"
-            code += "\n"
-
-            # Generate functions
-            code += self.generate_functions(self.fragment_item.functions, "fragment")
-            code += "    }\n"
-        else:
-            raise ValueError("No fragment shader section to generate.")
-
+        # Generate custom functions
+        code += " \n"
+        for uniform in ast.uniforms:
+            code += f"    cbuffer uniforms {{\n"
+            if isinstance(uniform, UniformNode):
+                code += f"        {uniform.vtype} {uniform.name};\n"
+            code += "    }\n\n"
+
+        for var in ast.io_variables:
+            if isinstance(var, LayoutNode):
+                if var.io_type == "in":
+                    self.structures["VSInput"].append((var.dtype, var.name))
+                elif var.io_type == "out":
+                    self.structures["PSOutput"].append((var.dtype, var.name))
+            elif isinstance(var, VariableNode):
+                if var.io_type == "IN":
+                    self.structures["PSInput"].append((var.vtype, var.name))
+                elif var.io_type == "OUT":
+                    self.structures["VSOutput"].append((var.vtype, var.name))
+
+        for struct_name, members in self.structures.items():
+            if members:
+                code += f"struct {struct_name} {{\n"
+                for dtype, name in members:
+                    code += f"    {dtype} {name};\n"
+                code += "};\n\n"
+
+        for constant in ast.constant:
+            code += f"    static {constant.vtype} {constant.name} = {constant.value};\n"
+        code += " \n"
+
+        for global_variable in ast.global_variables:
+            code += f"    {global_variable.vtype} {global_variable.name};\n"
+        for f in ast.functions:
+            if self.shader_type == "vertex":
+                code += self.generate_functions(f, "vertex")
+            elif self.shader_type == "fragment":
+                code += self.generate_functions(f, "fragment")
+            else:
+                raise ValueError("Invalid shader type")
         code += "}\n"
-
-        return code
-
-    def generate_uniforms(self):
-        uniform_lines = []
-        for uniform in self.uniforms:
-            uniform_lines.append(f"        {uniform}")
-        return "\n".join(uniform_lines)
-
-    def generate_layouts(self, layouts):
-        code = ""
-        for layout in layouts:
-            if layout.io_type == "input":
-                code += f"        input {layout.dtype} {layout.name};\n"
-            elif layout.io_type == "output":
-                code += f"        output {layout.dtype} {layout.name};\n"
         return code
 
     def generate_functions(self, functions, shader_type):
         code = ""
-
-        if shader_type in ["vertex", "fragment"]:
-            for function_node in functions:
-                # Generate parameter list
+        if shader_type == "vertex":
+            if functions.name == "main":
+                code += "vertex {\n\n"
+                code += "      VSOutput main(VSInput input) {\n"
+                code += "         VSOutput output;\n"
+            else:
                 params = ", ".join(
                     f"{self.map_type(param[0])} {param[1]}"
-                    for param in function_node.params
+                    for param in functions.params
                 )
-                # Generate function header
-                code += f"        {self.map_type(function_node.return_type)} {function_node.name}({params}) {{\n"
-                # Generate function body
-                for stmt in function_node.body:
-                    code += self.generate_statement(stmt, shader_type, 2)
-                # Close function definition
-                code += "        }\n"
+                return_type = self.map_type(functions.return_type)
+                code += f"{return_type} {functions.name}({params}) {{\n"
+        elif shader_type == "fragment":
+            if functions.name == "main":
+                code += "fragment {\n\n"
+                code += "       PSOutput main(PSInput input) {\n"
+                code += "         PSOutput output;\n"
+            else:
+                params = ", ".join(
+                    f"{self.map_type(param[0])} {param[1]}"
+                    for param in functions.params
+                )
+                return_type = self.map_type(functions.return_type)
+                code += f"{return_type} {functions.name}({params}) {{\n"
+        for stmt in functions.body:
+            code += self.generate_statement(stmt, 2, shader_type)
+        if functions.name == "main":
+            code += "            return output;\n"
+            code += "        }\n"
+            code += "    }\n"
+        else:
+            code += "        }\n\n"
 
         return code
 
-    def generate_statement(
-        self,
-        stmt,
-        shader_type,
-        indent=0,
-    ):
+    def generate_statement(self, stmt, indent=0, shader_type=None):
         indent_str = "    " * indent
         if isinstance(stmt, VariableNode):
             return f"{indent_str}{self.map_type(stmt.vtype)} {stmt.name};\n"
         elif isinstance(stmt, AssignmentNode):
-            return f"{indent_str}{self.generate_assignment(stmt,shader_type)};\n"
+            return f"{indent_str}{self.generate_assignment(stmt, shader_type)};\n"
         elif isinstance(stmt, IfNode):
-            return self.generate_if(stmt, shader_type, indent)
+            return self.generate_if(stmt, indent, shader_type)
         elif isinstance(stmt, ForNode):
-            return self.generate_for(stmt, shader_type, indent)
+            return self.generate_for(stmt, indent, shader_type)
         elif isinstance(stmt, ReturnNode):
-            return f"{indent_str}return {self.generate_expression(stmt.value,shader_type)};\n"
+            code = ""
+            for i, return_stmt in enumerate(stmt.value):
+                code += f"{self.generate_expression(return_stmt, shader_type)}"
+                if i < len(stmt.value) - 1:
+                    code += ", "
+            return f"{indent_str}return {code};\n"
         else:
-            return f"{indent_str}{self.generate_expression(stmt,shader_type)};\n"
-
-    def generate_assignment(self, node, shader_type):
-        lhs = self.generate_expression(node.name, shader_type)
-        rhs = self.generate_expression(node.value, shader_type)
-        return f"{lhs} = {rhs}"
+            return f"{indent_str}{self.generate_expression(stmt, shader_type)};\n"
+
+    def generate_assignment(self, node, shader_type=None):
+        if shader_type in ["vertex", "fragment"] and isinstance(node.name, str):
+            if node.name in [
+                output[1] for output in getattr(self, f"{shader_type}_item").outputs
+            ]:
+                return f"output.{node.name} = {self.generate_expression(node.value, shader_type)}"
+        if isinstance(node.name, VariableNode) and node.name.vtype:
+            return f"{self.map_type(node.name.vtype)} {node.name.name} = {self.generate_expression(node.value, shader_type)}"
+        else:
+            lhs = self.generate_expression(node.name, shader_type)
+            if lhs == "gl_Position" or lhs == "gl_position":
+                return f"output.position = {self.generate_expression(node.value, shader_type)}"
+            return f"{lhs} = {self.generate_expression(node.value, shader_type)}"
 
-    def generate_if(self, node: IfNode, shader_type, indent=0):
+    def generate_if(self, node, indent, shader_type=None):
         indent_str = "    " * indent
-        code = f"{indent_str}if {self.generate_expression(node.condition, shader_type)} {{\n"
+        code = f"{indent_str}if ({self.generate_expression(node.if_condition, shader_type)}) {{\n"
         for stmt in node.if_body:
-            code += self.generate_statement(stmt, shader_type, indent + 1)
+            code += self.generate_statement(stmt, indent + 1, shader_type)
         code += f"{indent_str}}}"
 
-        # Handle else_if_chain
-        for elif_condition, elif_body in node.else_if_chain:
-            code += f" else if ({self.generate_expression(elif_condition, shader_type)}) {{\n"
-            for stmt in elif_body:
-                code += self.generate_statement(stmt, shader_type, indent + 1)
+        for else_if_condition, else_if_body in zip(
+            node.else_if_conditions, node.else_if_bodies
+        ):
+            code += f" else if ({self.generate_expression(else_if_condition, shader_type)}) {{\n"
+            for stmt in else_if_body:
+                code += self.generate_statement(stmt, indent + 1, shader_type)
             code += f"{indent_str}}}"
 
-        # Handle 'else' block if present
         if node.else_body:
             code += " else {\n"
             for stmt in node.else_body:
-                code += self.generate_statement(stmt, shader_type, indent + 1)
+                code += self.generate_statement(stmt, indent + 1, shader_type)
             code += f"{indent_str}}}"
-
         code += "\n"
         return code
 
-    def generate_else_if(self, node: IfNode, shader_type, indent):
-        code = f" else if {self.generate_expression(node.condition, shader_type)} {{\n"
-        for stmt in node.if_body:
-            code += self.generate_statement(stmt, shader_type, indent + 1)
-        code += f"{'    ' * indent}}}"
-        return code
-
-    def generate_for(self, node: ForNode, shader_type, indent):
+    def generate_for(self, node, indent, shader_type=None):
         indent_str = "    " * indent
-        init = self.generate_statement(node.init, shader_type).strip().rstrip(";")
-        condition = self.generate_expression(node.condition, shader_type).strip()
-        update = self.generate_update(node.update, shader_type).strip().rstrip(";")
-        code = f"{indent_str}for ({init}; {condition}; {update}) {{\n"
-        for stmt in node.body:
-            code += self.generate_statement(stmt, shader_type, indent + 1)
-        code += f"{indent_str}}}\n"
-
-        return code
 
-    def generate_update(self, node, shader_type):
-        if isinstance(node, AssignmentNode):
-            if isinstance(node.value, UnaryOpNode):
-                operand = self.generate_expression(
-                    node.value.operand, shader_type
-                ).strip()
-                if node.value.op == "++":
-                    return f"++{operand}"
-                elif node.value.op == "POST_INCREMENT":
-                    return f"{operand}++"
-                elif node.value.op == "--":
-                    return f"--{operand}"
-                elif node.value.op == "POST_DECREMENT":
-                    return f"{operand}--"
-            else:
-                lhs = self.generate_expression(node.name, shader_type).strip()
-                rhs = self.generate_expression(node.value, shader_type).strip()
-                return f"{lhs} = {rhs}"
+        init = self.generate_statement(node.init, 0, shader_type).strip()[
+            :-1
+        ]  # Remove trailing semicolon
 
-        elif isinstance(node, UnaryOpNode):
-            operand = self.generate_expression(node.operand, shader_type).strip()
-            if node.op == "++":
-                return f"++{operand}"
-            elif node.op == "POST_INCREMENT":
-                return f"{operand}++"
-            elif node.op == "--":
-                return f"--{operand}"
-            elif node.op == "POST_DECREMENT":
-                return f"{operand}--"
-            else:
-                return f"{node.op}{operand}"
+        condition = self.generate_statement(node.condition, 0, shader_type).strip()[
+            :-1
+        ]  # Remove trailing semicolon
 
-        elif isinstance(node, BinaryOpNode):
-            left = self.generate_expression(node.left, shader_type).strip()
-            right = self.generate_expression(node.right, shader_type).strip()
-            op = self.map_operator(node.op)
-            return f"{left} {op} {right}"
+        update = self.generate_statement(node.update, 0, shader_type).strip()[:-1]
 
-        else:
-            raise ValueError(f"Unsupported update node type: {type(node)}")
+        code = f"{indent_str}for ({init}; {condition}; {update}) {{\n"
+        for stmt in node.body:
+            code += self.generate_statement(stmt, indent + 1, shader_type)
+        code += f"{indent_str}}}\n"
+        return code
 
-    def generate_expression(self, expr, shader_type):
+    def generate_expression(self, expr, shader_type=None):
         if isinstance(expr, str):
             return self.translate_expression(expr, shader_type)
         elif isinstance(expr, VariableNode):
-            return f"{expr.vtype} {self.translate_expression(expr.name, shader_type)}"
+            if isinstance(expr.name, str):
+                return f"{self.map_type(expr.vtype)} {self.translate_expression(expr.name, shader_type)}"
+            else:
+                return f"{self.map_type(expr.vtype)} {self.generate_expression(expr.name, shader_type)}"
         elif isinstance(expr, BinaryOpNode):
-            left = self.generate_expression(expr.left, shader_type)
-            right = self.generate_expression(expr.right, shader_type)
-            op = self.map_operator(expr.op)
-            return f"{left} {op} {right}"
+            return f"{self.generate_expression(expr.left, shader_type)} {self.map_operator(expr.op)} {self.generate_expression(expr.right, shader_type)}"
         elif isinstance(expr, FunctionCallNode):
             args = ", ".join(
                 self.generate_expression(arg, shader_type) for arg in expr.args
             )
-            func_name = self.translate_expression(expr.name, shader_type)
-            return f"{func_name}({args})"
-        elif isinstance(expr, UnaryOpNode):
-            operand = self.generate_expression(expr.operand, shader_type)
-            if expr.op in ["++", "--"]:
-                if expr.op == "++":
-                    return f"++{operand}"
-                elif expr.op == "--":
-                    return f"--{operand}"
+            if expr.name in self.type_mapping.keys():
+                return f"{self.map_type(expr.name)}({args})"
             else:
-                return f"{expr.op}{operand}"
+                func_name = self.translate_expression(expr.name, shader_type)
+                return f"{func_name}({args})"
+        elif isinstance(expr, UnaryOpNode):
+            return f"{self.map_operator(expr.op)} {self.generate_expression(expr.operand, shader_type)}"
         elif isinstance(expr, TernaryOpNode):
-            condition = self.generate_expression(expr.condition, shader_type)
-            true_expr = self.generate_expression(expr.true_expr, shader_type)
-            false_expr = self.generate_expression(expr.false_expr, shader_type)
-            return f"{condition} ? {true_expr} : {false_expr}"
+            return f"{self.generate_expression(expr.condition, shader_type)} ? {self.generate_expression(expr.true_expr, shader_type)} : {self.generate_expression(expr.false_expr, shader_type)}"
         elif isinstance(expr, MemberAccessNode):
-            obj = self.generate_expression(expr.object, shader_type)
-            member = expr.member
-            return f"{obj}.{member}"
+            return f"{self.generate_expression(expr.object, shader_type)}.{expr.member}"
         else:
             return str(expr)
 
     def translate_expression(self, expr, shader_type):
         if shader_type == "vertex":
-            if self.vertex_item:
-                if self.vertex_item.inputs:
-                    for _, input_name in self.vertex_item.inputs:
-                        if expr == input_name:
-                            return input_name
-                if self.vertex_item.outputs:
-                    for _, output_name in self.vertex_item.outputs:
-                        if expr == output_name:
-                            return output_name
-
+            for vsinput in self.structures["VSInput"]:
+                if vsinput[1] == expr:
+                    return f"input.{expr}"
+            for vsoutput in self.structures["VSOutput"]:
+                if vsoutput[1] == expr:
+                    return f"output.{expr}"
         elif shader_type == "fragment":
-            if self.fragment_item:
-                if self.fragment_item.inputs:
-                    for _, input_name in self.fragment_item.inputs:
-                        if expr == input_name:
-                            return input_name
-                if self.fragment_item.outputs:
-                    for _, output_name in self.fragment_item.outputs:
-                        if expr == output_name:
-                            return output_name
-        return expr
+            for psinput in self.structures["PSInput"]:
+                if psinput[1] == expr:
+                    return f"input.{expr}"
+            for psoutput in self.structures["PSOutput"]:
+                if psoutput[1] == expr:
+                    return f"output.{expr}"
+        return self.type_mapping.get(expr, expr)
 
     def map_type(self, vtype):
-        type_map = {
-            "vec3": "vec3",
-            "vec4": "vec4",
-            "float": "float",
-            "int": "int",
-            "bool": "bool",
-        }
-        return type_map.get(vtype, vtype)
+        if vtype == "":
+            return ""
+        else:
+            return self.type_mapping.get(vtype, vtype)
 
     def map_operator(self, op):
         op_map = {
@@ -293,13 +226,25 @@ class GLSLToCrossGLConverter:
             "MINUS": "-",
             "MULTIPLY": "*",
             "DIVIDE": "/",
+            "BITWISE_XOR": "^",
             "LESS_THAN": "<",
             "GREATER_THAN": ">",
+            "ASSIGN_ADD": "+=",
+            "ASSIGN_SUB": "-=",
+            "ASSIGN_OR": "|=",
+            "ASSIGN_MUL": "*=",
+            "ASSIGN_DIV": "/=",
+            "ASSIGN_MOD": "%=",
+            "ASSIGN_XOR": "^=",
             "LESS_EQUAL": "<=",
             "GREATER_EQUAL": ">=",
             "EQUAL": "==",
             "NOT_EQUAL": "!=",
             "AND": "&&",
             "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_opengl/test_codegen.py b/tests/test_backend/test_opengl/test_codegen.py
index 04fd006..c54c6c1 100644
--- a/tests/test_backend/test_opengl/test_codegen.py
+++ b/tests/test_backend/test_opengl/test_codegen.py
@@ -19,7 +19,7 @@ def tokenize_code(code: str) -> List:
     return lexer.tokens
 
 
-def parse_code(Tokens: List) -> List:
+def parse_code(Tokens: List, shader_type: str) -> List:
     """Helper function to parse code.
 
     Args:
@@ -29,11 +29,11 @@ def parse_code(Tokens: List) -> List:
 
 
     """
-    parser = GLSLParser(Tokens)
+    parser = GLSLParser(Tokens, shader_type)
     return parser.parse()
 
 
-def generate_code(ast: List) -> str:
+def generate_code(ast: List, shader_type: str) -> str:
     """Helper function to generate code.
 
     Args:
@@ -43,7 +43,7 @@ def generate_code(ast: List) -> str:
 
 
     """
-    converter = GLSLToCrossGLConverter()
+    converter = GLSLToCrossGLConverter(shader_type=shader_type)
     return converter.generate(ast)
 
 
@@ -53,26 +53,22 @@ def test_input_output():
     // Vertex shader
 
     layout(location = 0) in vec3 position;
+    layout(location = 0) in vec3 color;
     out vec2 vUV;
 
-    void main() {
-        vUV = position.xy * 10.0;
+    float perlinNoise(vec2 p) {
+        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
     }
 
-    // Fragment shader
-
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-
     void main() {
-        float noise = vUV.x;
-
+        vUV = position.xy * 10.0;
     }
+
     """
     try:
         tokens = tokenize_code(code)
-        ast = parse_code(tokens)
-        code = generate_code(ast)
+        ast = parse_code(tokens, "vertex")
+        code = generate_code(ast, "vertex")
         print(code)
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
@@ -86,19 +82,6 @@ def test_if_statement():
         return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
     }
 
-    layout(location = 0) in vec3 position;
-    out vec2 vUV;
-
-    void main() {
-        vUV = position.xy * 10.0;
-        if (vUV.x > vUV.y) {
-            vUV = vec2(0.0, 0.0);
-        }
-        else {
-            vUV = vec2(1.0, 1.0);
-        }
-    }
-    // Fragment shader
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
 
@@ -117,8 +100,8 @@ def test_if_statement():
     """
     try:
         tokens = tokenize_code(code)
-        ast = parse_code(tokens)
-        code = generate_code(ast)
+        ast = parse_code(tokens, "fragment")
+        code = generate_code(ast, "fragment")
         print(code)
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
@@ -137,20 +120,11 @@ def test_for_statement():
             vUV = vec2(0.0, 0.0);
         }
     }
-    // Fragment shader
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-
-    void main() {
-        for (int i = 0; i < 10; i = i + 1) {
-            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
-        }
-    }
     """
     try:
         tokens = tokenize_code(code)
-        ast = parse_code(tokens)
-        code = generate_code(ast)
+        ast = parse_code(tokens, "vertex")
+        code = generate_code(ast, "vertex")
         print(code)
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
@@ -172,22 +146,12 @@ def test_else_statement():
             vUV = vec2(1.0, 1.0);
         }
     }
-    // Fragment shader
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-    void main() {
-        if (noise > 0.5) {
-            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
-        }
-        else {
-            fragColor = vec4(0.0, 0.0, 0.0, 1.0);
-        }
-    }
+    
     """
     try:
         tokens = tokenize_code(code)
-        ast = parse_code(tokens)
-        code = generate_code(ast)
+        ast = parse_code(tokens, "vertex")
+        code = generate_code(ast, "vertex")
         print(code)
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
@@ -196,19 +160,12 @@ def test_else_statement():
 def test_function_call():
     code = """
     #version 450
-    // Vertex shader
+    // Fragment shader
+    
     float perlinNoise(vec2 p) {
         return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
     }
-    layout(location = 0) in vec3 position;
-    out vec2 vUV;
 
-    void main() {
-        vUV = position.xy * 10.0;
-        float noise = perlinNoise(vUV);
-
-    }
-    // Fragment shader
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
 
@@ -221,8 +178,8 @@ def test_function_call():
 
     try:
         tokens = tokenize_code(code)
-        ast = parse_code(tokens)
-        code = generate_code(ast)
+        ast = parse_code(tokens, "fragment")
+        code = generate_code(ast, "fragment")
         print(code)
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
diff --git a/tests/test_backend/test_opengl/test_lexer.py b/tests/test_backend/test_opengl/test_lexer.py
index db1543b..a970005 100644
--- a/tests/test_backend/test_opengl/test_lexer.py
+++ b/tests/test_backend/test_opengl/test_lexer.py
@@ -14,18 +14,19 @@ def tokenize_code(code: str) -> List:
 
     """
     lexer = GLSLLexer(code)
-    return lexer.tokenize()
+    return lexer.tokens
 
 
 def test_input_output_tokenization():
     code = """
+    #version 330 core
     layout(location = 0) in vec3 position;
     out vec2 vUV;
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
     """
     try:
-        tokenize_code(code)
+        print(tokenize_code(code))
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
diff --git a/tests/test_backend/test_opengl/test_parser.py b/tests/test_backend/test_opengl/test_parser.py
index c2b35f7..d61aff0 100644
--- a/tests/test_backend/test_opengl/test_parser.py
+++ b/tests/test_backend/test_opengl/test_parser.py
@@ -18,7 +18,7 @@ def tokenize_code(code: str) -> List:
     return lexer.tokens
 
 
-def parse_code(Tokens: List) -> List:
+def parse_code(Tokens: List, shader_type: str) -> List:
     """Helper function to parse code.
 
     Args:
@@ -28,38 +28,20 @@ def parse_code(Tokens: List) -> List:
 
 
     """
-    parser = GLSLParser(Tokens)
+    parser = GLSLParser(Tokens, shader_type)
     return parser.parse()
 
 
 def test_input_output():
     code = """
-    #version 450
-    // Vertex shader
-
-    float perlinNoise(vec2 p) {
-        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
-    }
-
     layout(location = 0) in vec3 position;
     out vec2 vUV;
-
-    void main() {
-        vUV = position.xy * 10.0;
-    }
-
-    // Fragment shader
-
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
-
-    void main() {
-        float noise = perlinNoise(vUV);
-    }
     """
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "vertex")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
@@ -67,19 +49,6 @@ def test_input_output():
 def test_if_statement():
     code = """
     #version 450
-    // Vertex shader
-    float perlinNoise(vec2 p) {
-        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
-    }
-    layout(location = 0) in vec3 position;
-    out vec2 vUV;
-
-    void main() {
-        vUV = position.xy * 10.0;
-        if (vUV.x > vUV.y) {
-            vUV = vec2(0.0, 0.0);
-        }
-    }
     // Fragment shader
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
@@ -87,13 +56,13 @@ def test_if_statement():
     void main() {
         float noise = perlinNoise(vUV);
         if (noise > 0.5) {
-            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
+            float fragColor = vec4(1.0, 1.0, 1.0, 1.0);
         }
     }
     """
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "fragment")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
@@ -114,20 +83,10 @@ def test_for_statement():
             vUV = vec2(0.0, 0.0);
         }
     }
-    // Fragment shader
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-
-    void main() {
-        float noise = perlinNoise(vUV);
-        for (int i = 0; i < 10; i = i + 1) {
-            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
-        }
-    }
     """
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "vertex")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
@@ -151,22 +110,10 @@ def test_else_statement():
             vUV = vec2(1.0, 1.0);
         }
     }
-    // Fragment shader
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-    void main() {
-        float noise = perlinNoise(vUV);
-        if (noise > 0.5) {
-            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
-        }
-        else {
-            fragColor = vec4(0.0, 0.0, 0.0, 1.0);
-        }
-    }
     """
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "vertex")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
@@ -174,23 +121,6 @@ def test_else_statement():
 def test_else_if_statement():
     code = """
     #version 450
-    // Vertex shader
-    float perlinNoise(vec2 p) {
-        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
-    }
-
-    layout(location = 0) in vec3 position;
-    out vec2 vUV;
-
-    void main() {
-        vUV = position.xy * 10.0;
-        if (vUV.x > vUV.y) {
-            vUV = vec2(0.0, 0.0);
-        }
-        else {
-            vUV = vec2(1.0, 1.0);
-        }
-    }
     // Fragment shader
     in vec2 vUV;
     layout(location = 0) out vec4 fragColor;
@@ -210,7 +140,7 @@ def test_else_if_statement():
     """
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "fragment")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")
 
@@ -230,20 +160,11 @@ def test_function_call():
         float noise = perlinNoise(vUV);
 
     }
-    // Fragment shader
-    in vec2 vUV;
-    layout(location = 0) out vec4 fragColor;
-
-    void main() {
-        float noise = perlinNoise(vUV);
-        float height = noise * 10.0;
-        vec3 color = vec3(height / 10.0, 1.0 - height / 10.0, 0.0);
-    }
     """
 
     try:
         tokens = tokenize_code(code)
-        parse_code(tokens)
+        parse_code(tokens, "vertex")
     except SyntaxError:
         pytest.fail("Struct parsing not implemented.")