Quellcode durchsuchen

add variable assignment

Johan Rong vor 1 Tag
Ursprung
Commit
05a488152f

+ 2 - 1
examples/testing/src/main.zk

@@ -15,7 +15,8 @@ fn add(a: i64, b: i64) -> i64 {
 }
 
 fn main() -> void {
-    val test: i64 = add(3, 5)
+    var test: i64 = add(5, 5)
+    test = test + 3
     #print_i64(test)
     #print_i64(test + 1)
     return

+ 21 - 6
src/zelkelc/ast/parser.py

@@ -125,7 +125,6 @@ class Parser:
             return self.parse_macro_definition()
         return self.parse_macro_call(ident)
 
-
     def parse_macro_definition(self) -> types.MacroDefinition:
         name = self.expect_type(token.Identifier).value
 
@@ -258,6 +257,9 @@ class Parser:
                     case "return":
                         return self.parse_return()
                     case _:
+                        if self.tokens[self.cursor + 1]:
+                            return self.parse_value_assignment()
+
                         raise ParseError(f"Invalid keyword in function body {t.value}")
             case token.Pound():
                 return self.parse_pound()
@@ -304,9 +306,7 @@ class Parser:
         if name == "main":
             irname = "zkmain"
 
-        return types.FunctionDeclaration(
-            name, typ, args, body, irname
-        )
+        return types.FunctionDeclaration(name, typ, args, body, irname)
 
     def parse_return(self) -> types.ReturnStatement:
         self.expect(token.Identifier("return"))
@@ -336,6 +336,20 @@ class Parser:
             name, mutable, typ, expr, irname=f"v_{next(self.ids)}_{name}"
         )
 
+    def parse_value_assignment(self) -> types.ValueAssignment:
+        name = self.expect_type(token.Identifier).value
+
+        self.expect(token.Equals())
+
+        expr = self.parse_expression()
+
+        return types.ValueAssignment(
+            name,
+            None,
+            expr,
+            None,
+        )
+
     def parse_expression(self) -> types.Expression:
         return self.parse_binary_expression(0)
 
@@ -389,11 +403,12 @@ class Parser:
 
                     self.expect(token.RParen())
                     return types.PrimaryExpression(
-                        types.FunctionCall(t.value, None, None, args), types.FunctionCall
+                        types.FunctionCall(t.value, None, None, args),
+                        types.FunctionCall,
                     )
                 else:
                     return types.PrimaryExpression(
-                        types.VariableRef(t.value, None, None), types.VariableRef
+                        types.VariableRef(t.value, None, None, None), types.VariableRef
                     )
 
             case token.Integer():

+ 7 - 0
src/zelkelc/ast/types.py

@@ -46,6 +46,12 @@ class FunctionDeclaration:
     body: list[Node]
     irname: str
 
+@dataclass
+class ValueAssignment:
+    name: str
+    typ: Value
+    expr: Expression
+    irname: str
 
 @dataclass
 class ReturnStatement:
@@ -151,6 +157,7 @@ class Void:
 @dataclass
 class VariableRef:
     name: str
+    mutable: bool
     irname: str
     typ: Value
 

+ 31 - 1
src/zelkelc/codegen/codegen.py

@@ -113,6 +113,8 @@ class Codegen:
             match child_node:
                 case types.ValueDeclaration():
                     ir += self.gen_value_declaration(child_node)
+                case types.ValueAssignment():
+                    ir += self.gen_value_assignment(child_node)
                 case types.InlineIR():
                     ir += "\n" + self.gen_inline_ir(child_node, is_top=False)
                 case types.ReturnStatement():
@@ -149,6 +151,23 @@ class Codegen:
                 sys.exit(1)
 
         return ir
+    
+    def gen_value_assignment(self, node: types.ValueAssignment) -> str:
+        ir = ""
+        
+        operand, expr = self.gen_expression(node.expr)
+        ir += expr
+        
+        match node.typ:
+            case types.Integer64:
+                ir += f"\nstore i64 {operand}, i64* %{node.irname}, align 8"
+            case types.Float64:
+                ir += f"\nstore double {operand}, double* %{node.irnmae}, align 8"
+            case _:
+                print(f"gen_value_assignment() for {node.typ} is unimplemented")
+                sys.exit(1)
+        
+        return ir
 
     def gen_return_statement(self, node: types.ReturnStatement) -> str:
         ir = ""
@@ -261,7 +280,18 @@ class Codegen:
                     case types.Float64():
                         return self.llvm_double(node.value.value), ""
                     case types.VariableRef():
-                        return f"%{node.value.irname}", ""
+                        if node.value.mutable:
+                            tmp = self.new_temp()
+                            match node.value.typ:
+                                case types.Integer64:
+                                    return tmp, f"\n{tmp} = load i64, i64* %{node.value.irname}, align 8"
+                                case types.Float64:
+                                    return tmp, f"\n{tmp} = load double, double* %{node.value.irname}, align 8"
+                                case _:
+                                    print(f"Type {self.value.typ} is unimplemented for mutable VariableRef")
+                                    sys.exit(1)
+                        else:
+                            return f"%{node.value.irname}", ""
                     case types.FunctionCall():
                         args_ir, args_str = self.get_function_args_llvm(node.value)
                         tmp = self.new_temp()

+ 34 - 4
src/zelkelc/validator/scope.py

@@ -39,7 +39,12 @@ class ValueSignature:
     name: str
     irname: str
     mutable: bool
-    typ: type[types.Integer64] | type[types.Float64] | type[types.String] | type[types.Void]
+    typ: (
+        type[types.Integer64]
+        | type[types.Float64]
+        | type[types.String]
+        | type[types.Void]
+    )
 
 
 @dataclass
@@ -120,7 +125,7 @@ class Scope:
         arg_types: list[types.Value] = []
         if args is None:
             return []
-        
+
         for arg in args:
             typ = scope_checker._infer_expression_type(arg, self)
             if typ is None:
@@ -322,6 +327,32 @@ class ScopeChecker:
                         typ=child.typ,
                     )
 
+                case types.ValueAssignment():
+                    if child.name not in function_scope.values:
+                        self._error(
+                            f"Variable '{child.name} is undefined in function '{fn.name}'"
+                        )
+                        continue
+
+                    if not function_scope.values[child.name].mutable:
+                        self._error(f"Variable '{child.name}' is immutable")
+                        continue
+                    
+                    expression_type = self._infer_expression_type(
+                        child.expr, function_scope
+                    )
+                    if expression_type is not None and not self._same_type(
+                        expression_type, function_scope.values[child.name].typ
+                    ):
+                        self._error(
+                            f"Type mismatch for '{child.name}' in function '{fn.name}': "
+                            f"expected {self._type_name(function_scope.values[child.name].typ)}, got {self._type_name(expression_type)}"
+                        )
+
+                    child.typ = expression_type
+                    child.irname = function_scope.values[child.name].irname
+                    continue
+
                 case types.ReturnStatement():
                     has_return = True
                     if child.expr is None:
@@ -386,8 +417,6 @@ class ScopeChecker:
     ) -> type[types.Integer64 | types.String | types.Void] | None:
         typ = self._infer_expression_type_inner(expr, active_scope)
         if typ is not None and hasattr(expr, "typ"):
-            # Parser only knows placeholders here (e.g. VariableRef); store the
-            # real type so codegen never has to guess from the operands.
             expr.typ = typ
         return typ
 
@@ -407,6 +436,7 @@ class ScopeChecker:
                     self._error(f"Unknown variable '{name}'")
                     return None
 
+                expr.value.mutable = signature.mutable
                 expr.value.irname = signature.irname
                 expr.value.typ = signature.typ
                 return signature.typ