diff --git a/crosstl/src/backend/DirectX/DirectxAst.py b/crosstl/src/backend/DirectX/DirectxAst.py index 96e4c4e2..6c6f2605 100644 --- a/crosstl/src/backend/DirectX/DirectxAst.py +++ b/crosstl/src/backend/DirectX/DirectxAst.py @@ -116,7 +116,7 @@ def __init__(self, left, op, right): self.right = right def __repr__(self): - return f"BinaryOpNode(left={self.left}, operator={self.op}, right={self.right})" + return f"BinaryOpNode(left={self.left}, op={self.op}, right={self.right})" class MemberAccessNode(ASTNode): diff --git a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py index 9dc170a6..910eec2c 100644 --- a/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py +++ b/crosstl/src/backend/DirectX/DirectxCrossGLCodeGen.py @@ -121,12 +121,15 @@ def generate_function_body(self, body, indent=0, is_main=False): for stmt in body: code += " " * indent if isinstance(stmt, VariableNode): - if stmt.vtype not in self.type_map.keys(): + if stmt.vtype in ["VSOutput", "PSOutput"]: continue else: code += f"{self.map_type(stmt.vtype)} {stmt.name};\n" elif isinstance(stmt, AssignmentNode): code += self.generate_assignment(stmt, is_main) + ";\n" + + elif isinstance(stmt, BinaryOpNode): + code += f"{self.generate_expression(stmt.left, is_main)} {stmt.op} {self.generate_expression(stmt.right, is_main)};\n" elif isinstance(stmt, ReturnNode): if not is_main: code += f"return {self.generate_expression(stmt.value, is_main)};\n" diff --git a/crosstl/src/backend/DirectX/DirectxLexer.py b/crosstl/src/backend/DirectX/DirectxLexer.py index 4c303d1e..70e72f91 100644 --- a/crosstl/src/backend/DirectX/DirectxLexer.py +++ b/crosstl/src/backend/DirectX/DirectxLexer.py @@ -3,22 +3,22 @@ TOKENS = [ ("COMMENT_SINGLE", r"//.*"), ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"), - ("STRUCT", r"struct"), - ("CBUFFER", r"cbuffer"), - ("TEXTURE2D", r"Texture2D"), - ("SAMPLER_STATE", r"SamplerState"), - ("FVECTOR", r"float[2-4]"), - ("FLOAT", r"float"), - ("INT", r"int"), - ("UINT", r"uint"), - ("BOOL", r"bool"), - ("MATRIX", r"float[2-4]x[2-4]"), - ("VOID", r"void"), - ("RETURN", r"return"), - ("IF", r"if"), - ("ELSE", r"else"), - ("FOR", r"for"), - ("REGISTER", r"register"), + ("STRUCT", r"\bstruct\b"), + ("CBUFFER", r"\bcbuffer\b"), + ("TEXTURE2D", r"\bTexture2D\b"), + ("SAMPLER_STATE", r"\bSamplerState\b"), + ("FVECTOR", r"\bfloat[2-4]\b"), + ("FLOAT", r"\bfloat\b"), + ("INT", r"\bint\b"), + ("UINT", r"\buint\b"), + ("BOOL", r"\bbool\b"), + ("MATRIX", r"\bfloat[2-4]x[2-4]\b"), + ("VOID", r"\bvoid\b"), + ("RETURN", r"\breturn\b"), + ("IF", r"\bif\b"), + ("ELSE", r"\belse\b"), + ("FOR", r"\bfor\b"), + ("REGISTER", r"\bregister\b"), ("SEMANTIC", r": [A-Z_][A-Z0-9_]*"), ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"), ("NUMBER", r"\d+(\.\d+)?"), @@ -32,20 +32,24 @@ ("COMMA", r","), ("COLON", r":"), ("QUESTION", r"\?"), - ("EQUALS", r"="), - ("PLUS", r"\+"), - ("MINUS", r"-"), - ("MULTIPLY", r"\*"), - ("DIVIDE", r"/"), - ("LESS_THAN", r"<"), - ("GREATER_THAN", r">"), ("LESS_EQUAL", r"<="), ("GREATER_EQUAL", r">="), + ("LESS_THAN", r"<"), + ("GREATER_THAN", r">"), ("EQUAL", r"=="), ("NOT_EQUAL", r"!="), + ("PLUS_EQUALS", r"\+="), + ("MINUS_EQUALS", r"-="), + ("MULTIPLY_EQUALS", r"\*="), + ("DIVIDE_EQUALS", r"/="), ("AND", r"&&"), ("OR", r"\|\|"), ("DOT", r"\."), + ("MULTIPLY", r"\*"), + ("DIVIDE", r"/"), + ("PLUS", r"\+"), + ("MINUS", r"-"), + ("EQUALS", r"="), ("WHITESPACE", r"\s+"), ] diff --git a/crosstl/src/backend/DirectX/DirectxParser.py b/crosstl/src/backend/DirectX/DirectxParser.py index d020dfd6..1ebe60a3 100644 --- a/crosstl/src/backend/DirectX/DirectxParser.py +++ b/crosstl/src/backend/DirectX/DirectxParser.py @@ -183,9 +183,23 @@ def parse_variable_declaration_or_assignment(self): self.eat("SEMICOLON") return left else: - expr = self.parse_expression() - self.eat("SEMICOLON") - return expr + if self.current_token[0] in [ + "PLUS_EQUALS", + "MINUS_EQUALS", + "MULTIPLY_EQUALS", + "DIVIDE_EQUALS", + "EQUAL", + ]: + op = self.current_token[1] + self.eat(self.current_token[0]) + expr = self.parse_expression() + self.eat("SEMICOLON") + return BinaryOpNode(VariableNode("", first_token[1]), op, expr) + # This handles cases like "float3(1.0, 1.0, 1.0);" + else: + expr = self.parse_expression() + self.eat("SEMICOLON") + return expr else: expr = self.parse_expression() self.eat("SEMICOLON") diff --git a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py index f8fe89a5..ca7c2e7e 100644 --- a/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py +++ b/crosstl/src/backend/Metal/MetalCrossGLCodeGen.py @@ -139,7 +139,7 @@ def generate_function_body(self, body, indent=0, is_main=False): for stmt in body: code += " " * indent if isinstance(stmt, VariableNode): - if stmt.vtype not in self.type_map.keys(): + if stmt.vtype in ["Vertex_OUTPUT", "Fragment_OUTPUT"]: continue else: code += f"{self.map_type(stmt.vtype)} {stmt.name};\n" @@ -148,6 +148,8 @@ def generate_function_body(self, body, indent=0, is_main=False): elif isinstance(stmt, ReturnNode): if not is_main: code += f"return {self.generate_expression(stmt.value, is_main)};\n" + elif isinstance(stmt, BinaryOpNode): + code += f"{self.generate_expression(stmt.left, is_main)} {stmt.op} {self.generate_expression(stmt.right, is_main)};\n" elif isinstance(stmt, ForNode): code += self.generate_for_loop(stmt, indent, is_main) elif isinstance(stmt, IfNode): diff --git a/crosstl/src/backend/Metal/MetalLexer.py b/crosstl/src/backend/Metal/MetalLexer.py index 1b992f13..ab280a82 100644 --- a/crosstl/src/backend/Metal/MetalLexer.py +++ b/crosstl/src/backend/Metal/MetalLexer.py @@ -4,31 +4,31 @@ ("COMMENT_SINGLE", r"//.*"), ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"), ("PREPROCESSOR", r"#\w+"), - ("STRUCT", r"struct"), - ("CONSTANT", r"constant"), - ("TEXTURE2D", r"texture2d"), - ("SAMPLER", r"sampler"), - ("VECTOR", r"(float|half|int|uint)[2-4]"), - ("FLOAT", r"float"), - ("HALF", r"half"), - ("INT", r"int"), - ("UINT", r"uint"), + ("STRUCT", r"\bstruct\b"), + ("CONSTANT", r"\bconstant\b"), + ("TEXTURE2D", r"\btexture2d\b"), + ("SAMPLER", r"\bsampler\b"), + ("VECTOR", r"\b(float|half|int|uint)[2-4]\b"), + ("FLOAT", r"\bfloat\b"), + ("HALF", r"\bhalf\b"), + ("INT", r"\bint\b"), + ("UINT", r"\buint\b"), ("QUESTION", r"\?"), - ("BOOL", r"bool"), - ("VOID", r"void"), - ("RETURN", r"return"), - ("IF", r"if"), - ("ELSE", r"else"), - ("FOR", r"for"), - ("KERNEL", r"kernel"), - ("VERTEX", r"vertex"), - ("FRAGMENT", r"fragment"), - ("USING", r"using"), - ("NAMESPACE", r"namespace"), - ("METAL", r"metal"), - ("DEVICE", r"device"), - ("THREADGROUP", r"threadgroup"), - ("THREAD", r"thread"), + ("BOOL", r"\bbool\b"), + ("VOID", r"\bvoid\b"), + ("RETURN", r"\breturn\b"), + ("IF", r"\bif\b"), + ("ELSE", r"\belse\b"), + ("FOR", r"\bfor\b"), + ("KERNEL", r"\bkernel\b"), + ("VERTEX", r"\bvertex\b"), + ("FRAGMENT", r"\bfragment\b"), + ("USING", r"\busing\b"), + ("NAMESPACE", r"\bnamespace\b"), + ("METAL", r"\bmetal\b"), + ("DEVICE", r"\bdevice\b"), + ("THREADGROUP", r"\bthreadgroup\b"), + ("THREAD", r"\bthread\b"), ("ATTRIBUTE", r"\[\[.*?\]\]"), ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z0-9_]*"), ("NUMBER", r"\d+(\.\d+)?"), @@ -42,20 +42,24 @@ ("STRING", r'"[^"]*"'), ("COMMA", r","), ("COLON", r":"), - ("EQUALS", r"="), - ("PLUS", r"\+"), - ("MINUS", r"-"), - ("MULTIPLY", r"\*"), - ("DIVIDE", r"/"), - ("LESS_THAN", r"<"), - ("GREATER_THAN", r">"), ("LESS_EQUAL", r"<="), ("GREATER_EQUAL", r">="), + ("LESS_THAN", r"<"), + ("GREATER_THAN", r">"), ("EQUAL", r"=="), ("NOT_EQUAL", r"!="), + ("PLUS_EQUALS", r"\+="), + ("MINUS_EQUALS", r"-="), + ("MULTIPLY_EQUALS", r"\*="), + ("DIVIDE_EQUALS", r"/="), + ("PLUS", r"\+"), + ("MINUS", r"-"), + ("MULTIPLY", r"\*"), + ("DIVIDE", r"/"), ("AND", r"&&"), ("OR", r"\|\|"), ("DOT", r"\."), + ("EQUALS", r"="), ("WHITESPACE", r"\s+"), ] diff --git a/crosstl/src/backend/Metal/MetalParser.py b/crosstl/src/backend/Metal/MetalParser.py index e07c4e20..c1aab171 100644 --- a/crosstl/src/backend/Metal/MetalParser.py +++ b/crosstl/src/backend/Metal/MetalParser.py @@ -282,9 +282,23 @@ def parse_variable_declaration_or_assignment(self): self.eat("SEMICOLON") return left else: - expr = self.parse_expression() - self.eat("SEMICOLON") - return expr + if self.current_token[0] in [ + "PLUS_EQUALS", + "MINUS_EQUALS", + "MULTIPLY_EQUALS", + "DIVIDE_EQUALS", + "EQUAL", + ]: + op = self.current_token[1] + self.eat(self.current_token[0]) + expr = self.parse_expression() + self.eat("SEMICOLON") + return BinaryOpNode(VariableNode("", first_token[1]), op, expr) + # This handles cases like "float3(1.0, 1.0, 1.0);" + else: + expr = self.parse_expression() + self.eat("SEMICOLON") + return expr else: expr = self.parse_expression() self.eat("SEMICOLON") diff --git a/crosstl/src/backend/Opengl/OpenglAst.py b/crosstl/src/backend/Opengl/OpenglAst.py index 84670a44..01cbb61d 100644 --- a/crosstl/src/backend/Opengl/OpenglAst.py +++ b/crosstl/src/backend/Opengl/OpenglAst.py @@ -61,14 +61,15 @@ def __repr__(self): class LayoutNode: - def __init__(self, section, location_number, dtype, name): + def __init__(self, section, location_number, dtype, name,io_type): self.section = section 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})" + return f"LayoutNode(section={self.section}, location_number={self.location_number}, dtype={self.dtype}, name={self.name}, io_type={self.io_type})" class ShaderNode: diff --git a/crosstl/src/backend/Opengl/OpenglLexer.py b/crosstl/src/backend/Opengl/OpenglLexer.py index 3ccc71af..0da1dd3a 100644 --- a/crosstl/src/backend/Opengl/OpenglLexer.py +++ b/crosstl/src/backend/Opengl/OpenglLexer.py @@ -4,25 +4,25 @@ TOKENS = [ ("COMMENT_SINGLE", r"//.*"), ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"), - ("VERSION", r"#version"), # Match #version as a separate token - ("NUMBER", r"\d+(\.\d+)?"), # Match version numbers like 330 or 330.0 - ("CORE", r"core"), # Match the keyword core - ("SHADER", r"shader"), - ("INPUT", r"input"), - ("OUTPUT", r"output"), - ("VOID", r"void"), - ("MAIN", r"main"), - ("UNIFORM", r"uniform"), - ("VECTOR", r"vec[2-4]"), - ("MATRIX", r"mat[2-4]"), - ("BOOL", r"bool"), - ("FLOAT", r"float"), - ("INT", r"int"), - ("SAMPLER2D", r"sampler2D"), - ("PRE_INCREMENT", r"\+\+(?=\w)"), # Lookahead to match pre-increment - ("PRE_DECREMENT", r"--(?=\w)"), # Lookahead to match pre-decrement - ("POST_INCREMENT", r"(?<=\w)\+\+"), # Lookbehind to match post-increment - ("POST_DECREMENT", r"(?<=\w)--"), # Lookbehind to match post-decrement + ("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"), + ("UNIFORM", r"\buniform\b"), + ("VECTOR", r"\bvec[2-4]\b"), + ("MATRIX", r"\bmat[2-4]\b"), + ("BOOL", r"\bbool\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]*"), ("LBRACE", r"\{"), ("RBRACE", r"\}"), @@ -34,19 +34,17 @@ ("ASSIGN_SUB", r"-="), ("ASSIGN_MUL", r"\*="), ("ASSIGN_DIV", r"/="), - ("EQUALS", r"="), + ("EQUAL", r"=="), + ("NOT_EQUAL", r"!="), ("WHITESPACE", r"\s+"), - ("IF", r"if"), - ("ELSE", r"else"), - ("FOR", r"for"), - ("RETURN", r"return"), - ("LESS_THAN", r"<"), - ("GREATER_THAN", r">"), - ("INCREMENT", r"\+\+"), - ("DECREMENT", r"--"), + ("IF", r"\bif\b"), + ("ELSE", r"\belse\b"), + ("FOR", r"\bfor\b"), + ("RETURN", r"\breturn\b"), ("LESS_EQUAL", r"<="), ("GREATER_EQUAL", r">="), - ("EQUAL", r"=="), + ("LESS_THAN", r"<"), + ("GREATER_THAN", r">"), ("NOT_EQUAL", r"!="), ("AND", r"&&"), ("OR", r"\|\|"), @@ -56,11 +54,12 @@ ("MULTIPLY", r"\*"), ("DIVIDE", r"/"), ("DOT", r"\."), + ("EQUALS", r"="), ("QUESTION", r"\?"), ("COLON", r":"), - ("LAYOUT", r"layout"), - ("IN", r"in"), - ("OUT", r"out"), + ("LAYOUT", r"\blayout\b"), + ("IN", r"\bin\b"), + ("OUT", r"\bout\b"), ] KEYWORDS = { @@ -115,4 +114,4 @@ def tokenize(self): f"Illegal character '{unmatched_char}' at position {pos}\n{highlighted_code}" ) - self.tokens.append(("EOF", None)) # End of file token + self.tokens.append(("EOF", None)) diff --git a/crosstl/src/backend/Opengl/OpenglParser.py b/crosstl/src/backend/Opengl/OpenglParser.py index 757d7596..e9408f9e 100644 --- a/crosstl/src/backend/Opengl/OpenglParser.py +++ b/crosstl/src/backend/Opengl/OpenglParser.py @@ -88,6 +88,7 @@ def parse_layout(self, current_section): self.skip_comments() if self.current_token[0] == "IN": + io_type = "input" self.eat("IN") dtype = self.parse_type() name = self.current_token[1] @@ -98,8 +99,10 @@ def parse_layout(self, 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] @@ -110,6 +113,7 @@ def parse_layout(self, 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") @@ -205,9 +209,6 @@ def parse_shader(self, version_node): else: raise SyntaxError(f"Unexpected token {self.current_token[0]}") - # print(f"Final vertex section: {vertex_section}") - # print(f"Final fragment section: {fragment_section}") - return ShaderNode( version=version_node, global_inputs=global_inputs, @@ -236,22 +237,18 @@ def parse_shader_section(self, current_section): elif self.current_token[0] == "IN": self.skip_comments() inputs.extend(self.parse_inputs()) - # print(f"Inputs collected: {inputs}") elif self.current_token[0] == "OUT": self.skip_comments() outputs.extend(self.parse_outputs()) - # print(f"Outputs collected: {outputs}") elif self.current_token[0] == "UNIFORM": self.skip_comments() uniforms.extend(self.parse_uniforms()) - # print(f"Uniforms collected: {uniforms}") elif self.current_token[0] in ["VOID", "FLOAT", "VECTOR"]: self.skip_comments() functions.append(self.parse_function()) - # print(f"Functions collected: {functions}") elif self.current_token[0] == "RBRACE": self.eat("RBRACE") @@ -301,6 +298,12 @@ def parse_variable(self, type_name): name = self.current_token[1] self.eat("IDENTIFIER") + 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) @@ -324,11 +327,12 @@ def parse_variable(self, type_name): "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 AssignmentNode(VariableNode(type_name, name), value) + return BinaryOpNode(VariableNode(type_name, name), op_name, value) else: raise SyntaxError( f"Expected ';' after compound assignment, found: {self.current_token[0]}" @@ -467,12 +471,12 @@ def parse_update(self): if self.current_token[0] == "IDENTIFIER": name = self.current_token[1] self.eat("IDENTIFIER") - if 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))) + 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)) elif self.current_token[0] in [ "EQUALS", "ASSIGN_ADD", @@ -483,27 +487,47 @@ def parse_update(self): 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) + 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)) else: raise SyntaxError( - f"Expected INCREMENT or DECREMENT, got {self.current_token[0]}" + f"Expected IDENTIFIER after PRE_DECREMENT, got {self.current_token[0]}" ) else: raise SyntaxError(f"Unexpected token in update: {self.current_token[0]}") @@ -610,11 +634,17 @@ def parse_if(self): self.eat("LBRACE") body = self.parse_body() self.eat("RBRACE") + + else_body = None if self.current_token[0] == "ELSE": self.eat("ELSE") - self.eat("LBRACE") - else_body = self.parse_body() - self.eat("RBRACE") + if self.current_token[0] == "IF": + # Handle nested if + else_body = self.parse_if() + else: + self.eat("LBRACE") + else_body = self.parse_body() + self.eat("RBRACE") return IfNode(condition, body, else_body) else: return IfNode(condition, body) diff --git a/crosstl/src/backend/Opengl/openglCrossglCodegen.py b/crosstl/src/backend/Opengl/openglCrossglCodegen.py index 346843f3..b21430b3 100644 --- a/crosstl/src/backend/Opengl/openglCrossglCodegen.py +++ b/crosstl/src/backend/Opengl/openglCrossglCodegen.py @@ -27,9 +27,7 @@ def generate_shader(self, node): self.vertex_item = node.vertex_section if self.vertex_item: code += " vertex {\n" - # Print the vertex section to check its content code += self.generate_layouts(self.vertex_item.layout_qualifiers) - # Process inputs and outputs for vtype, name in self.shader_inputs: code += f" input {self.map_type(vtype)} {name};\n" for vtype, name in self.shader_outputs: @@ -42,14 +40,11 @@ def generate_shader(self, node): code += self.generate_uniforms() + "\n" code += "\n" - # Print the functions to check if they're there - # print(f"Vertex functions: {self.vertex_item.functions}") - # Generate functions code += self.generate_functions(self.vertex_item.functions, "vertex") code += " }\n" else: - print("No vertex shader section to generate.") + raise ValueError("No vertex shader section to generate.") # Generate fragment shader section if present self.fragment_item = node.fragment_section @@ -67,14 +62,11 @@ def generate_shader(self, node): code += self.generate_uniforms() + "\n" code += "\n" - # Print the functions to check if they're there - # print(f"Fragment functions: {self.fragment_item.functions}") - # Generate functions code += self.generate_functions(self.fragment_item.functions, "fragment") code += " }\n" else: - print("No fragment shader section to generate.") + raise ValueError("No fragment shader section to generate.") code += "}\n" @@ -83,13 +75,16 @@ def generate_shader(self, node): def generate_uniforms(self): uniform_lines = [] for uniform in self.uniforms: - uniform_lines.append(f" {uniform};") + uniform_lines.append(f" {uniform}") return "\n".join(uniform_lines) def generate_layouts(self, layouts): code = "" for layout in layouts: - code += f" input {layout.dtype} {layout.name};\n" + 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): @@ -106,7 +101,7 @@ def generate_functions(self, functions, shader_type): 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, 2) + code += self.generate_statement(stmt, shader_type, 2) # Close function definition code += " }\n" @@ -115,76 +110,170 @@ def generate_functions(self, functions, shader_type): def generate_statement( self, stmt, + shader_type, indent=0, ): 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)};\n" + return f"{indent_str}{self.generate_assignment(stmt,shader_type)};\n" elif isinstance(stmt, IfNode): - return self.generate_if(stmt, indent) + return self.generate_if(stmt, shader_type, indent) elif isinstance(stmt, ForNode): - return self.generate_for(stmt, indent) + return self.generate_for(stmt, shader_type, indent) elif isinstance(stmt, ReturnNode): - return f"{indent_str}return {self.generate_expression(stmt.value)};\n" + return f"{indent_str}return {self.generate_expression(stmt.value,shader_type)};\n" else: - return f"{indent_str}{self.generate_expression(stmt)};\n" + return f"{indent_str}{self.generate_expression(stmt,shader_type)};\n" - def generate_assignment(self, node): - lhs = self.generate_expression(node.name) - rhs = self.generate_expression(node.value) + 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}" - def generate_if(self, node: IfNode, indent): + def generate_if(self, node: IfNode, shader_type, indent=0): indent_str = " " * indent - code = f"{indent_str}if ({self.generate_expression(node.condition)}) {{\n" + code = f"{indent_str}if {self.generate_expression(node.condition,shader_type)} {{\n" for stmt in node.if_body: - code += self.generate_statement(stmt, indent + 1) + code += self.generate_statement(stmt, shader_type, indent + 1) code += f"{indent_str}}}" + while isinstance(node.else_body, IfNode): + node = node.else_body + 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_str}}}" + + # Handle 'else' block if present if node.else_body: code += " else {\n" for stmt in node.else_body: - code += self.generate_statement(stmt, indent + 1) + code += self.generate_statement(stmt, shader_type, indent + 1) code += f"{indent_str}}}" + code += "\n" return code - def generate_for(self, node: ForNode, indent): - indent_str = " " * indent - - init = self.generate_statement(node.init, 0).strip() - condition = self.generate_expression(node.condition) - update = self.generate_statement(node.update, 0).strip() + 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): + 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, indent + 1) + code += self.generate_statement(stmt, shader_type, indent + 1) code += f"{indent_str}}}\n" + return code - def generate_expression(self, expr): + 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}" + + 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}" + + 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}" + + else: + raise ValueError(f"Unsupported update node type: {type(node)}") + + def generate_expression(self, expr, shader_type): if isinstance(expr, str): - return self.translate_expression(expr) + return self.translate_expression(expr, shader_type) elif isinstance(expr, VariableNode): - return self.translate_expression(expr.name) + return f"{expr.vtype} {self.translate_expression(expr.name, shader_type)}" elif isinstance(expr, BinaryOpNode): - return f"({self.generate_expression(expr.left)} {self.map_operator(expr.op)} {self.generate_expression(expr.right)})" + 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}" elif isinstance(expr, FunctionCallNode): - args = ", ".join(self.generate_expression(arg) for arg in expr.args) - func_name = self.translate_expression(expr.name) + 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.op) - return f"({expr.op}{operand})" + operand = self.generate_expression(expr.operand, shader_type) + if expr.op in ["++", "--"]: + if expr.op == "++": + return f"++{operand}" + elif expr.op == "--": + return f"--{operand}" + else: + return f"{expr.op}{operand}" elif isinstance(expr, TernaryOpNode): - return f"{self.generate_expression(expr.condition)} ? {self.generate_expression(expr.true_expr)} : {self.generate_expression(expr.false_expr)}" + 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}" elif isinstance(expr, MemberAccessNode): - return f"{self.generate_expression(expr.object)}.{expr.member}" + obj = self.generate_expression(expr.object, shader_type) + member = expr.member + return f"{obj}.{member}" else: return str(expr) - def translate_expression(self, 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 + + 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 def map_type(self, vtype): diff --git a/crosstl/src/translator/ast.py b/crosstl/src/translator/ast.py index 0ea57da5..48c741fb 100644 --- a/crosstl/src/translator/ast.py +++ b/crosstl/src/translator/ast.py @@ -141,7 +141,7 @@ def __init__(self, left, op, right): self.right = right def __repr__(self): - return f"BinaryOpNode(left={self.left}, operator={self.op}, right={self.right})" + return f"BinaryOpNode(left={self.left}, op={self.op}, right={self.right})" class MemberAccessNode(ASTNode): diff --git a/crosstl/src/translator/codegen/directx_codegen.py b/crosstl/src/translator/codegen/directx_codegen.py index a1a62892..93d01798 100644 --- a/crosstl/src/translator/codegen/directx_codegen.py +++ b/crosstl/src/translator/codegen/directx_codegen.py @@ -331,6 +331,10 @@ def map_operator(self, op): "DIVIDE": "/", "LESS_THAN": "<", "GREATER_THAN": ">", + "ASSIGN_ADD": "+=", + "ASSIGN_SUB": "-=", + "ASSIGN_MUL": "*=", + "ASSIGN_DIV": "/=", "LESS_EQUAL": "<=", "GREATER_EQUAL": ">=", "EQUAL": "==", diff --git a/crosstl/src/translator/codegen/metal_codegen.py b/crosstl/src/translator/codegen/metal_codegen.py index 0fa4013b..53a73c62 100644 --- a/crosstl/src/translator/codegen/metal_codegen.py +++ b/crosstl/src/translator/codegen/metal_codegen.py @@ -387,6 +387,10 @@ def map_operator(self, op): "MULTIPLY": "*", "DIVIDE": "/", "LESS_THAN": "<", + "ASSIGN_ADD": "+=", + "ASSIGN_SUB": "-=", + "ASSIGN_MUL": "*=", + "ASSIGN_DIV": "/=", "GREATER_THAN": ">", "LESS_EQUAL": "<=", "GREATER_EQUAL": ">=", diff --git a/crosstl/src/translator/codegen/opengl_codegen.py b/crosstl/src/translator/codegen/opengl_codegen.py index dd3ab239..e524461f 100644 --- a/crosstl/src/translator/codegen/opengl_codegen.py +++ b/crosstl/src/translator/codegen/opengl_codegen.py @@ -230,6 +230,10 @@ def map_operator(self, op): "MULTIPLY": "*", "DIVIDE": "/", "LESS_THAN": "<", + "ASSIGN_ADD": "+=", + "ASSIGN_SUB": "-=", + "ASSIGN_MUL": "*=", + "ASSIGN_DIV": "/=", "GREATER_THAN": ">", "LESS_EQUAL": "<=", "GREATER_EQUAL": ">=", diff --git a/crosstl/src/translator/lexer.py b/crosstl/src/translator/lexer.py index 955b9d61..b40247ed 100644 --- a/crosstl/src/translator/lexer.py +++ b/crosstl/src/translator/lexer.py @@ -3,21 +3,21 @@ TOKENS = [ ("COMMENT_SINGLE", r"//.*"), ("COMMENT_MULTI", r"/\*[\s\S]*?\*/"), - ("SHADER", r"shader"), - ("INPUT", r"input"), - ("OUTPUT", r"output"), - ("VOID", r"void"), - ("MAIN", r"main"), - ("UNIFORM", r"uniform"), - ("VECTOR", r"vec[2-4]"), - ("MATRIX", r"mat[2-4]"), - ("BOOL", r"bool"), - ("VERTEX", r"vertex"), - ("FRAGMENT", r"fragment"), + ("SHADER", r"\bshader\b"), + ("INPUT", r"\binput\b"), + ("OUTPUT", r"\boutput\b"), + ("VOID", r"\bvoid\b"), + ("MAIN", r"\bmain\b"), + ("UNIFORM", r"\buniform\b"), + ("VECTOR", r"\bvec[2-4]\b"), + ("MATRIX", r"\bmat[2-4]\b"), + ("BOOL", r"\bbool\b"), + ("VERTEX", r"\bvertex\b"), + ("FRAGMENT", r"\bfragment\b"), # ("INCREMENT_DECREMENT", r"\b[a-zA-Z_][a-zA-Z_0-9]*\s*(\+\+|--)\b"), - ("FLOAT", r"float"), - ("INT", r"int"), - ("SAMPLER2D", r"sampler2D"), + ("FLOAT", r"\bfloat\b"), + ("INT", r"\bint\b"), + ("SAMPLER2D", r"\bsampler2D\b"), ("IDENTIFIER", r"[a-zA-Z_][a-zA-Z_0-9]*"), ("NUMBER", r"\d+(\.\d+)?"), ("LBRACE", r"\{"), @@ -30,18 +30,17 @@ ("ASSIGN_SUB", r"-="), ("ASSIGN_MUL", r"\*="), ("ASSIGN_DIV", r"/="), - ("EQUALS", r"="), ("WHITESPACE", r"\s+"), - ("IF", r"if"), - ("ELSE", r"else"), - ("FOR", r"for"), - ("RETURN", r"return"), + ("IF", r"\bif\b"), + ("ELSE", r"\belse\b"), + ("FOR", r"\bfor\b"), + ("RETURN", r"\breturn\b"), + ("LESS_EQUAL", r"<="), + ("GREATER_EQUAL", r">="), ("LESS_THAN", r"<"), ("GREATER_THAN", r">"), ("INCREMENT", r"\+\+"), ("DECREMENT", r"--"), - ("LESS_EQUAL", r"<="), - ("GREATER_EQUAL", r">="), ("EQUAL", r"=="), ("NOT_EQUAL", r"!="), ("AND", r"&&"), @@ -54,6 +53,7 @@ ("MULTIPLY", r"\*"), ("DIVIDE", r"/"), ("DOT", r"\."), + ("EQUALS", r"="), ("QUESTION", r"\?"), ("COLON", r":"), ] diff --git a/crosstl/src/translator/parser.py b/crosstl/src/translator/parser.py index cafc35d0..91fbc47b 100644 --- a/crosstl/src/translator/parser.py +++ b/crosstl/src/translator/parser.py @@ -117,8 +117,8 @@ def parse_shader_section(self, section_type): self.eat(section_type) self.eat("LBRACE") inputs = self.parse_inputs() - self.parse_uniforms() outputs = self.parse_outputs() + self.parse_uniforms() functions = [] while self.current_token[0] != "RBRACE": @@ -400,7 +400,7 @@ def parse_variable_declaration(self, type_name): value = self.parse_expression() if self.current_token[0] == "SEMICOLON": self.eat("SEMICOLON") - return AssignmentNode(VariableNode(type_name, name), value) + return BinaryOpNode(VariableNode(type_name, name), op, value) else: raise SyntaxError( f"Expected ';' after compound assignment, found: {self.current_token[0]}" diff --git a/getting_start.ipynb b/getting_start.ipynb index dba154af..8e0d2f4a 100644 --- a/getting_start.ipynb +++ b/getting_start.ipynb @@ -140,23 +140,23 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "--2024-08-05 16:10:03-- https://raw.githubusercontent.com/CrossGL/demos/main/samples/Basic%20Fog%20Shader.cgl\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.111.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.\n", + "--2024-08-06 19:19:03-- https://raw.githubusercontent.com/CrossGL/demos/main/samples/Basic%20Fog%20Shader.cgl\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 725 [text/plain]\n", "Saving to: ‘test.cgl’\n", "\n", "test.cgl 100%[===================>] 725 --.-KB/s in 0s \n", "\n", - "2024-08-05 16:10:04 (30.0 MB/s) - ‘test.cgl’ saved [725/725]\n", + "2024-08-06 19:19:03 (57.3 MB/s) - ‘test.cgl’ saved [725/725]\n", "\n" ] } @@ -167,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -223,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -232,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -272,7 +272,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -330,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -347,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -403,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -420,7 +420,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -466,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -510,7 +510,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -521,28 +521,28 @@ " vertex {\n", " input vec3 position;\n", " input vec2 texCoord;\n", - " input vec3 fragPosition;\n", + " output vec3 fragPosition;\n", " output vec2 fragTexCoord;\n", "\n", "\n", "\n", " void main() {\n", - " fragTexCoord = texCoord;\n", - " fragPosition = position;\n", - " gl_Position = vec4(position, 1.0);\n", + " fragTexCoord = texCoord;\n", + " fragPosition = position;\n", + " gl_Position = vec4( position, 1.0);\n", " }\n", " }\n", " fragment {\n", " input vec3 fragPosition;\n", - " input vec4 fragColor;\n", + " output vec4 fragColor;\n", " input vec2 fragTexCoord;\n", "\n", "\n", " void main() {\n", - " depth = length(fragPosition);\n", - " fogFactor = exp(((--) * depth));\n", - " finalColor = mix(fogColor, baseColor, fogFactor);\n", - " fragColor = vec4(finalColor, 1.0);\n", + " float depth = length( fragPosition);\n", + " float fogFactor = exp(- fogDensity * depth);\n", + " vec3 finalColor = mix( fogColor, baseColor, fogFactor);\n", + " fragColor = vec4( finalColor, 1.0);\n", " }\n", " }\n", "}\n", @@ -564,7 +564,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -579,8 +579,7 @@ " output vec2 fragTexCoord;\n", " output vec3 fragPosition;\n", " void main() {\n", - " VSOutput output;\n", - " fragTexCoord = texCoord;\n", + " fragTexCoord = texCoord;\n", " fragPosition = position;\n", " gl_Position = vec4(position, 1.0);\n", " }\n", @@ -592,10 +591,9 @@ " input vec3 fragPosition;\n", " output vec4 fragColor;\n", " void main() {\n", - " PSOutput output;\n", - " float depth = length(fragPosition);\n", + " float depth = length(fragPosition);\n", " float fogFactor = exp(- fogDensity * depth);\n", - " float3 finalColor = mix( fogColor, baseColor, fogFactor);\n", + " vec3 finalColor = mix( fogColor, baseColor, fogFactor);\n", " fragColor = vec4( finalColor, 1.0);\n", " }\n", " }\n", @@ -618,7 +616,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -634,8 +632,7 @@ " output vec2 fragTexCoord;\n", " output vec3 fragPosition;\n", " void main() {\n", - " Vertex_OUTPUT output;\n", - " fragTexCoord = texCoord;\n", + " fragTexCoord = texCoord;\n", " fragPosition = position;\n", " gl_Position = vec4(position, 1.0);\n", " }\n", @@ -647,10 +644,9 @@ " input vec3 fragPosition;\n", " output vec4 fragColor;\n", " void main() {\n", - " Fragment_OUTPUT output;\n", - " float depth = length(fragPosition);\n", + " float depth = length(fragPosition);\n", " float fogFactor = exp((- fogDensity) * depth);\n", - " float3 finalColor = mix( fogColor, baseColor, fogFactor);\n", + " vec3 finalColor = mix( fogColor, baseColor, fogFactor);\n", " fragColor = vec4( finalColor, 1.0);\n", " }\n", " }\n",