Преглед изворни кода

More pretty hardcoded codegen and outline stuff in readme

Johan Rong пре 4 недеља
родитељ
комит
a9313a48e4
6 измењених фајлова са 101 додато и 30 уклоњено
  1. 37 1
      README.md
  2. 5 3
      src/zelkelc/__main__.py
  3. 0 1
      src/zelkelc/ast/nodes.py
  4. 5 11
      src/zelkelc/ast/parser.py
  5. 16 2
      src/zelkelc/ast/values.py
  6. 38 12
      src/zelkelc/codegen/codegen.py

+ 37 - 1
README.md

@@ -7,4 +7,40 @@
 ## TODO
 * [ ] Scope checking and stuff in parser
 * [ ] Parser should generate all names (unique), struct indexes, etc for codegen, this should also be in the scope
-    * [ ] Multi pass parser: first scope/symbol collection (what i've been doing now), then check if it makes sense :)
+    * [ ] Multi pass parser: first scope/symbol collection (what i've been doing now), then check if it makes sense :)
+
+## Planning code + ideas + sketch, ... etc
+```kotlin
+class Parser {
+  val tokens: Arr<Token>
+  val cursor: Int
+
+  // :: for constructor?, under the hood so vert denne funksjonen kjørt av ein anna der self har blitt laga, allocated, idk, noko sånn. Den hook constructor funksjonen har alle members av classen og returner pointer til objektet i minne
+  fn::new(self, tokens: Arr<Token>) {
+    self.tokens = tokens
+    self.cursor = Int::from(0)
+  }
+}
+
+fn main() -> i64 {
+  val parser: &Parser = Parser::new(etcetc...) // with hooked constructor, also puts reference counting
+  
+  val parser_manually: &Parser = mem.alloc(mem.sizeof(Parser)) // will not be tracked by reference counter, the pointer address is not added to the GC's pointer track list. GC only counts addresses in it's list
+  parser_manually.tokens = ...
+  parser_manually.cursor = ...
+
+  mem.free(parser_manually)
+
+  // Kanskje noko sånn heller?
+  val allocated: Result<&OtherClass> = mem.alloc(mem.sizeof(OtherClass))
+  val allocated_real: &OtherClass = allocated.expect("yes!")
+
+  mem.free(allocated_real)
+
+  // Alternativ, meire generics?
+  val allocated: Result<&OtherClass> = mem.alloc<OtherClass>()
+  val allocated_real: &OtherClass = allocated.expect("Couldn't allocate memory")
+
+  return 0
+}
+```

+ 5 - 3
src/zelkelc/__main__.py

@@ -7,15 +7,17 @@ import rich
 source = """
 //test
 class MyClass {
-    val immutable_value: String = "sdf"
-    var mutable_value: i64 = 128
+    val immutable_value: i64
+    var mutable_value: String
     
     fn function(test: i64, test2: String) -> void {
         return 0
     }
 }
 
-static fn main() -> i64 {
+fn main() -> i64 {
+    val test: i64 = 16
+    var test2: i64 = 2
     return 0
 }
 """

+ 0 - 1
src/zelkelc/ast/nodes.py

@@ -21,7 +21,6 @@ class ValueDeclaration:
 class FunctionDeclaration:
     name: str
     typ: values.Value
-    static: bool
     args: dict[str, values.Value]
     body: list[Node]
     

+ 5 - 11
src/zelkelc/ast/parser.py

@@ -39,7 +39,7 @@ class Parser:
             case "String":
                 return values.String
             case "i64":
-                return values.Integer
+                return values.Integer64
             case "void" if is_return_type == True:
                 return values.Void
             case _:
@@ -55,13 +55,8 @@ class Parser:
             match t:
                 case token.Identifier():
                     match t.value:
-                        case "static":
-                            self.expect(token.Identifier("static"))
-                            if self.current(token.Identifier("fn")):
-                                ast.append(self.parse_function_declaration(True))
-                            else:
-                                print(f"Keyword {self.tokens[self.cursor]} does not exist or may not be static")
-                                exit(1)
+                        case "fn":
+                            ast.append(self.parse_function_declaration())
                         case "class":
                             ast.append(self.parse_class_declaration())
                         case _:
@@ -114,7 +109,7 @@ class Parser:
                             
         return members, methods
     
-    def parse_function_declaration(self, static: bool) -> nodes.FunctionDeclaration:
+    def parse_function_declaration(self) -> nodes.FunctionDeclaration:
         self.expect(token.Identifier("fn"))
         name = self.expect_type(token.Identifier).value
         self.expect(token.LParen())
@@ -147,7 +142,6 @@ class Parser:
         return nodes.FunctionDeclaration(
             name = name,
             typ = typ,
-            static = static,
             args = args,
             body = body,
         )
@@ -261,7 +255,7 @@ class Parser:
 
             case token.Integer():
                 self.cursor += 1
-                return nodes.PrimaryExpression(value=values.Integer(t.value))
+                return nodes.PrimaryExpression(value=values.Integer64(t.value))
 
             case token.String():
                 self.cursor += 1

+ 16 - 2
src/zelkelc/ast/values.py

@@ -1,19 +1,33 @@
 from dataclasses import dataclass
 
 @dataclass
-class Integer:
+class Integer64:
     value: int
+    
+    def to_llvm() -> str:
+        return "i64"
 
 @dataclass
 class String:
     value: str
+    
+    def to_llvm() -> str:
+        print("Cannot convert String to llvm ir")
+        exit(1)
 
 @dataclass
 class Void:
     pass
 
+    def to_llvm() -> str:
+        return "void"
+
 @dataclass
 class Variable:
     name: str
+    
+    def to_llvm() -> str:
+        print("Cannot convert Variable to llvm ir")
+        exit(1)
 
-Value = Integer | String | Void | Variable
+Value = Integer64 | String | Void | Variable

+ 38 - 12
src/zelkelc/codegen/codegen.py

@@ -7,7 +7,7 @@ import src.zelkelc.ast.values as values
 class IRTable:
     structs: str
     struct_functions: str
-    functions: str
+    global_functions: str
 
 class Codegen:
     ast: list[nodes.FunctionDeclaration | nodes.ClassDeclaration]
@@ -17,21 +17,47 @@ class Codegen:
         self.ast = ast
         self.ir = IRTable("", "", "")
         
-    def generate(self):        
+    def generate(self) -> str:
         for node in self.ast:
             match node:
                 case nodes.FunctionDeclaration():
-                    print("TODO: functiondeclaration codegen unimplemented")
-                    continue
+                    self.ir.global_functions += self.gen_function_declaration(node)
                 case nodes.ClassDeclaration():
-                    self.gen_class_declaration(node)
+                    self.ir.structs += self.gen_class_declaration(node)
         
-        return self.ir.structs + "\n" + self.ir.struct_functions + "\n" + self.ir.functions
+        return self.ir.structs + "\n" + self.ir.struct_functions + "\n" + self.ir.global_functions
 
-    def gen_class_declaration(self, node: nodes.ClassDeclaration):
-        member_types: list[str] = []
-        for m in node.members:
-            member_types.append(str(m.typ))
-        member_str = ", ".join(member_types)
+    def gen_class_declaration(self, node: nodes.ClassDeclaration) -> str:
+        members_str = ""
+        for i, m in enumerate(node.members):
+            members_str += f"{", " if i != 0 else ""}{m.typ.to_llvm()}"
+        
+        return f"\n%{node.name} = type " + "{ " + members_str + " }"
+    
+    def gen_function_declaration(self, node: nodes.FunctionDeclaration) -> str:
+        args_str = ""
+        for i, (name, typ) in enumerate(node.args.items()):
+            args_str += f"{", " if i != 0 else ""}{typ.to_llvm()} %{name}"
+        
+        ir = f"define {str(node.typ.to_llvm())} @{node.name}({args_str}) " + "{"
+        ir += "\nentry:"
+        
+        for child_node in node.body:
+            match child_node:
+                case nodes.ValueDeclaration():
+                    ir += self.gen_value_declaration(child_node)
+                case _:
+                    continue
+        
+        return ir + "\n}"
+    
+    def gen_value_declaration(self, node: nodes.ValueDeclaration) -> str:
+        ir = "\n"
         
-        self.ir.structs += f"\n%{node.name} = type { {member_str} }"
+        if node.mutable == True: # TODO: both are hardcoded to just put int: 16
+            ir += f"%{node.name} = alloca {node.typ.to_llvm()}, align 8" # TODO: this would only work for i64s, just testing
+            ir += f"\nstore {node.typ.to_llvm()} 16, {node.typ.to_llvm()}* %{node.name}, align 8"
+        else:
+            ir += f"%{node.name} = add {node.typ.to_llvm()} 16, 0"
+            
+        return ir