about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBaitinq <[email protected]>2025-03-12 00:47:31 +0100
committerBaitinq <[email protected]>2025-03-12 00:47:43 +0100
commitda0788140e7afbc9b0bcbb937a29e2b08de08ec7 (patch)
treeef6435c7c91f40926f01f5faa9b4dbc2946a5caf
parentCodegen: add bundled llvm (diff)
parentCodegen: Fix bug with functions without name (diff)
downloadinterpreter-da0788140e7afbc9b0bcbb937a29e2b08de08ec7.tar.gz
interpreter-da0788140e7afbc9b0bcbb937a29e2b08de08ec7.tar.bz2
interpreter-da0788140e7afbc9b0bcbb937a29e2b08de08ec7.zip
Merge branch 'master' into native-llvm
-rw-r--r--README.md50
-rw-r--r--build.zig3
-rw-r--r--examples/10.src10
-rw-r--r--examples/12.src42
-rw-r--r--examples/13.src31
-rw-r--r--examples/7.src2
-rw-r--r--examples/8.src18
-rw-r--r--grammar.ebnf4
-rw-r--r--src/codegen.zig172
-rw-r--r--src/evaluator.zig13
-rw-r--r--src/parser.zig61
-rw-r--r--src/tokenizer.zig6
12 files changed, 257 insertions, 155 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a6e18d3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# Programming Language
+
+Simple statically typed and compiled programming language implemented in Zig, with support for variables, control flow, functions, and code generation using LLVM.
+
+## Building and Running
+
+1. Ensure you have Zig and LLVM installed on your system.
+2. Run the compiler on an example file:
+   ```
+   zig build run -- examples/8.src compile
+   ```
+3. Link the generated object file to create an executable:
+   ```
+   cc output.o
+   ```
+4. Run the executable:
+   ```
+   ./a.out
+   ```
+
+## Language Features
+
+- **Variables and Declarations**: Uses `let` for variable declaration.
+- **Control Flow**: Supports `if` and `while` statements.
+- **Functions**: Supports function declarations with parameters and return types.
+- **Expressions**: Includes additive, multiplicative, equality, and unary expressions.
+- **Code Generation with LLVM**: Translates AST to LLVM IR and generates object files for native execution.
+
+## Example Program
+
+```js
+let main = () => i64 {
+	let fib = (n: i64) => i64 {
+		if n == 0 {
+			return 0;
+		};
+		if n == 1 {
+			return 1;
+		};
+		return fib(n-2) + fib(n-1);
+	};
+
+	let result = fib(30);
+	print(result);
+	return result;
+};
+```
+```
+Output: 832040
+```
diff --git a/build.zig b/build.zig
index 45fafa1..49df9a8 100644
--- a/build.zig
+++ b/build.zig
@@ -26,7 +26,8 @@ pub fn build(b: *std.Build) !void {
         .optimize = optimize,
     });
 
-    exe_mod.linkSystemLibrary("llvm", .{});
+    exe_mod.linkSystemLibrary("LLVM-18", .{});
+    exe_mod.link_libc = true;
 
     // This creates another `std.Build.Step.Compile`, but this one builds an executable
     // rather than a static library.
diff --git a/examples/10.src b/examples/10.src
index 0ec38ea..59f91e1 100644
--- a/examples/10.src
+++ b/examples/10.src
@@ -1,14 +1,16 @@
 let main = () => i64 {
 	let counter = 0;
 
-	while !(counter == 10) {
+	while counter < 10 {
 		print(counter);
 		counter = counter + 1;
 	};
 
-	while counter == 0 {
-		print(0);
+	while true {
+		if counter == 10 {
+			return counter;
+		};
 	};
 
-	return counter;
+	return 1;
 };
diff --git a/examples/12.src b/examples/12.src
index 1bff280..a680efa 100644
--- a/examples/12.src
+++ b/examples/12.src
@@ -1,8 +1,38 @@
-let print_int = (n: i64) => i64 {
-	print(n);
-	return n;
-};
+let main = () => i64 {
+    let factorial = (n: i64) => i64 {
+        let f = (acc: i64, n: i64) => i64 {
+            if n == 0 {
+                return acc;
+            };
+            return f(acc * n, n - 1);
+        };
+        return f(1, n);
+    };
+
+    let is_even = (n: i64) => bool {
+        if n % 2 == 0 {
+            return true;
+        };
+        return false;
+    };
+
+    let sum_if = (predicate: (i64) => bool, limit: i64) => i64 {
+        let sum = 0;
+        let i = 0;
+        while i < limit {
+            if predicate(i) {
+                sum = sum + i;
+            };
+            i = i + 1;
+        };
+        return sum;
+    };
+
+    let fact_val = factorial(6);
+    print(fact_val);
+
+    let even_sum = sum_if(is_even, 20);
+    print(even_sum);
 
-let main = (argc: i64) => i64 {
-	return print_int(argc);
+    return 0;
 };
diff --git a/examples/13.src b/examples/13.src
new file mode 100644
index 0000000..eeb5b32
--- /dev/null
+++ b/examples/13.src
@@ -0,0 +1,31 @@
+let main = () => i64 {
+    /* Iterative Fibonacci using while loop. */
+    let fibonacci_iter = (n: i64) => i64 {
+        let a = 0;
+        let b = 1;
+        let i = 0;
+        while i < n {
+            let temp = b;
+            b = a + b;
+            a = temp;
+            i = i + 1;
+        };
+        return a;
+    };
+
+    /* Recursive GCD using Euclid's algorithm. */
+    let gcd = (a: i64, b: i64) => i64 {
+        if b == 0 {
+            return a;
+        };
+        return gcd(b, a % b);
+    };
+
+    let fib_val = fibonacci_iter(10);
+    print(fib_val);
+
+    let gcd_val = gcd(48, 18);
+    print(gcd_val);
+
+    return 0;
+};
diff --git a/examples/7.src b/examples/7.src
index c0690b6..b10a350 100644
--- a/examples/7.src
+++ b/examples/7.src
@@ -2,7 +2,7 @@ let ten = () => i64 {
 	return () => i64 {
 		return 10;
 	}();
-}; /* TODO */
+};
 
 let main = () => i64 {
 	let i = 4;
diff --git a/examples/8.src b/examples/8.src
index 73ea7aa..985ca77 100644
--- a/examples/8.src
+++ b/examples/8.src
@@ -1,14 +1,14 @@
-let fib = (n: i64) => i64 {
-	if n == 0 {
-		return 0;
-	};
-	if n == 1 {
-		return 1;
+let main = () => i64 {
+	let fib = (n: i64) => i64 {
+		if n == 0 {
+			return 0;
+		};
+		if n == 1 {
+			return 1;
+		};
+		return fib(n-2) + fib(n-1);
 	};
-	return fib(n-2) + fib(n-1);
-};
 
-let main = () => i64 {
 	let result = fib(30);
 	print(result);
 	return result;
diff --git a/grammar.ebnf b/grammar.ebnf
index ed87d7f..bbfe488 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -16,11 +16,11 @@ FunctionArguments ::= Expression ("," Expression)*
 
 Expression ::= EqualityExpression | AdditiveExpression
 
-EqualityExpression ::= AdditiveExpression "==" AdditiveExpression
+EqualityExpression ::= AdditiveExpression ("==" | "<" | ">") AdditiveExpression
 
 AdditiveExpression ::= MultiplicativeExpression (("+" | "-") MultiplicativeExpression)*
 
-MultiplicativeExpression ::= UnaryExpression (("*" | "/") UnaryExpression)*
+MultiplicativeExpression ::= UnaryExpression (("*" | "/" | "%") UnaryExpression)*
 
 UnaryExpression ::= ("!" | "-") UnaryExpression | PrimaryExpression
 
diff --git a/src/codegen.zig b/src/codegen.zig
index eb80c96..f734018 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -32,9 +32,10 @@ pub const CodeGen = struct {
     pub fn init(arena: std.mem.Allocator) !*CodeGen {
         // Initialize LLVM
         target.LLVMInitializeAllTargetInfos();
-        _ = target.LLVMInitializeAllTargets();
-        _ = target.LLVMInitializeAllAsmPrinters();
-        _ = target.LLVMInitializeAllAsmParsers();
+        target.LLVMInitializeAllTargetMCs();
+        target.LLVMInitializeAllTargets();
+        target.LLVMInitializeAllAsmPrinters();
+        target.LLVMInitializeAllAsmParsers();
 
         const module: types.LLVMModuleRef = core.LLVMModuleCreateWithName("module");
         const builder = core.LLVMCreateBuilder();
@@ -50,8 +51,7 @@ pub const CodeGen = struct {
 
         const printf_function_type = core.LLVMFunctionType(core.LLVMVoidType(), @constCast(&[_]types.LLVMTypeRef{
             core.LLVMPointerType(core.LLVMInt8Type(), 0),
-            core.LLVMInt64Type(),
-        }), 2, 1);
+        }), 1, 1);
         const printf_function = core.LLVMAddFunction(self.llvm_module, "printf", printf_function_type) orelse return CodeGenError.CompilationError;
         try self.environment.add_variable("printf", try self.create_variable(.{
             .value = printf_function,
@@ -72,10 +72,11 @@ pub const CodeGen = struct {
         // Generate code
         const triple = target_m.LLVMGetDefaultTargetTriple();
         var target_ref: types.LLVMTargetRef = undefined;
-        var message: [*c]u8 = undefined;
-        _ = target_m.LLVMGetTargetFromTriple(triple, &target_ref, &message);
-        std.debug.print("Target output: {s}.\n", .{message});
-        core.LLVMDisposeMessage(message);
+        var message: ?[*c]u8 = undefined;
+        const xd = target_m.LLVMGetTargetFromTriple(triple, &target_ref, &message.?);
+        std.debug.print("XD: {any}.\n", .{xd});
+        // std.debug.print("Target output: {any}.\n", .{message});
+        // core.LLVMDisposeMessage(message);
         const target_machine = target_m.LLVMCreateTargetMachine(
             target_ref,
             triple,
@@ -97,9 +98,9 @@ pub const CodeGen = struct {
         );
         std.debug.print("Object file generated: {s}\n", .{filename});
 
-        _ = analysis.LLVMVerifyModule(self.llvm_module, types.LLVMAbortProcessAction, &message);
-        std.debug.print("Verification output: {s}.\n", .{message});
-        core.LLVMDisposeMessage(message);
+        _ = analysis.LLVMVerifyModule(self.llvm_module, types.LLVMAbortProcessAction, &message.?);
+        // std.debug.print("Verification output: {any}.\n", .{message});
+        // core.LLVMDisposeMessage(message);
 
         // Clean up LLVM resources
         defer core.LLVMDisposeBuilder(self.builder);
@@ -165,9 +166,7 @@ pub const CodeGen = struct {
             .PRIMARY_EXPRESSION => |primary_expression| {
                 std.debug.assert(primary_expression == .IDENTIFIER);
                 function = self.environment.get_variable(primary_expression.IDENTIFIER.name) orelse return CodeGenError.CompilationError;
-                std.debug.print("STACK LVEL: {any} {s}\n", .{ function.stack_level.?, primary_expression.IDENTIFIER.name });
-                if (function.stack_level.? > 0) {
-                    std.debug.print("NOT GLOBAL FN! {s} {any}\n", .{ primary_expression.IDENTIFIER.name, function.stack_level });
+                if (core.LLVMGetValueKind(function.value) != types.LLVMFunctionValueKind) {
                     function.value = core.LLVMBuildLoad2(self.builder, core.LLVMPointerType(function.type, 0), function.value, "");
                 }
             },
@@ -212,7 +211,7 @@ pub const CodeGen = struct {
         for (if_statement.statements) |stmt| {
             try self.generate_statement(stmt);
         }
-        const merge_block = core.LLVMAppendBasicBlock(core.LLVMGetLastFunction(self.llvm_module), "else_block");
+        const merge_block = core.LLVMAppendBasicBlock(core.LLVMGetLastFunction(self.llvm_module), "merge_block");
         const last_instr = core.LLVMGetLastInstruction(then_block);
         if (core.LLVMIsATerminatorInst(last_instr) == null) {
             _ = core.LLVMBuildBr(self.builder, merge_block);
@@ -242,10 +241,8 @@ pub const CodeGen = struct {
         for (while_statement.statements) |stmt| {
             try self.generate_statement(stmt);
         }
-        const last_instr = core.LLVMGetLastInstruction(inner_block);
-        if (core.LLVMIsATerminatorInst(last_instr) == null) {
-            _ = core.LLVMBuildBr(self.builder, while_block);
-        }
+
+        _ = core.LLVMBuildBr(self.builder, while_block);
 
         core.LLVMPositionBuilderAtEnd(self.builder, outer_block);
     }
@@ -271,29 +268,23 @@ pub const CodeGen = struct {
                 const function_type = core.LLVMFunctionType(return_type, paramtypes.items.ptr, @intCast(paramtypes.items.len), 0) orelse return CodeGenError.CompilationError;
                 const function = core.LLVMAddFunction(self.llvm_module, try std.fmt.allocPrintZ(self.arena, "{s}", .{name orelse "unnamed_func"}), function_type) orelse return CodeGenError.CompilationError;
                 const function_entry = core.LLVMAppendBasicBlock(function, "entrypoint") orelse return CodeGenError.CompilationError;
-
-                // Needed for recursive functions
-                if (name != null) {
-                    const ptr = self.environment.get_variable(name.?);
-                    // Global fn
-                    if (ptr == null) {
-                        try self.environment.add_variable(name.?, try self.create_variable(.{
-                            .value = function,
-                            .type = function_type,
-                            .stack_level = null,
-                        }));
-                    } else {
-                        _ = core.LLVMBuildStore(self.builder, function, ptr.?.value) orelse return CodeGenError.CompilationError;
-                        ptr.?.type = function_type;
-                        try self.environment.add_variable(name.?, ptr.?);
-                    }
-                }
-
                 core.LLVMPositionBuilderAtEnd(self.builder, function_entry);
 
                 try self.environment.create_scope();
                 defer self.environment.drop_scope();
 
+                var ptr: ?*Variable = null;
+
+                // Needed for recursive functions
+                if (name != null) {
+                    ptr = self.environment.get_variable(name.?);
+                    try self.environment.add_variable(name.?, try self.create_variable(.{
+                        .value = function,
+                        .type = function_type,
+                        .stack_level = null,
+                    }));
+                }
+
                 const params = try self.arena.alloc(types.LLVMValueRef, function_definition.parameters.len);
                 core.LLVMGetParams(function, params.ptr);
 
@@ -323,17 +314,21 @@ pub const CodeGen = struct {
                     try self.generate_statement(stmt);
                 }
 
+                // TODO: This should be done with a defer when `builder_pos` is declared, but for some reason it doesn't work
                 core.LLVMPositionBuilderAtEnd(self.builder, builder_pos);
 
                 // Global functions
-                if (self.environment.scope_stack.items.len == 2) {
+                if (name == null or self.environment.scope_stack.items.len == 2) {
                     return try self.create_variable(.{
                         .value = function,
                         .type = function_type,
                         .stack_level = null,
                     });
                 }
-                return self.environment.get_variable(name.?) orelse unreachable;
+
+                _ = core.LLVMBuildStore(self.builder, function, ptr.?.value) orelse return CodeGenError.CompilationError;
+                ptr.?.type = function_type;
+                return ptr.?;
             },
             .FUNCTION_CALL_STATEMENT => |*fn_call| {
                 if (name != null) {
@@ -371,18 +366,7 @@ pub const CodeGen = struct {
                     }
                     const loaded = core.LLVMBuildLoad2(self.builder, param_type, variable.value, "");
 
-                    if (name != null) {
-                        const ptr = self.environment.get_variable(name.?).?;
-                        _ = core.LLVMBuildStore(self.builder, loaded, ptr.value);
-                        ptr.type = variable.type;
-                        return ptr;
-                    }
-
-                    return try self.create_variable(.{
-                        .value = loaded,
-                        .type = variable.type,
-                        .stack_level = null,
-                    });
+                    return self.generate_literal(loaded, variable.type, name);
                 },
             },
             .ADDITIVE_EXPRESSION => |exp| {
@@ -396,44 +380,26 @@ pub const CodeGen = struct {
                     result = core.LLVMBuildSub(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
                 }
 
-                if (name != null) {
-                    const ptr = self.environment.get_variable(name.?) orelse unreachable;
-                    _ = core.LLVMBuildStore(self.builder, result, ptr.value);
-                    ptr.type = core.LLVMInt64Type();
-
-                    return ptr;
-                } else {
-                    return try self.create_variable(.{
-                        .value = result,
-                        .type = core.LLVMInt64Type(),
-                        .stack_level = null,
-                    });
-                }
+                return self.generate_literal(result, core.LLVMInt64Type(), name);
             },
             .MULTIPLICATIVE_EXPRESSION => |exp| {
                 const lhs_value = try self.generate_expression_value(exp.lhs, null);
                 const rhs_value = try self.generate_expression_value(exp.rhs, null);
 
                 var result: types.LLVMValueRef = undefined;
-                if (exp.multiplication) {
-                    result = core.LLVMBuildMul(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
-                } else {
-                    result = core.LLVMBuildSDiv(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
+                switch (exp.typ) {
+                    .MUL => {
+                        result = core.LLVMBuildMul(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
+                    },
+                    .DIV => {
+                        result = core.LLVMBuildSDiv(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
+                    },
+                    .MOD => {
+                        result = core.LLVMBuildSRem(self.builder, lhs_value.value, rhs_value.value, "") orelse return CodeGenError.CompilationError;
+                    },
                 }
 
-                if (name != null) {
-                    const ptr = self.environment.get_variable(name.?) orelse unreachable;
-                    _ = core.LLVMBuildStore(self.builder, result, ptr.value);
-                    ptr.type = core.LLVMInt64Type();
-
-                    return ptr;
-                } else {
-                    return try self.create_variable(.{
-                        .value = result,
-                        .type = core.LLVMInt64Type(),
-                        .stack_level = null,
-                    });
-                }
+                return self.generate_literal(result, core.LLVMInt64Type(), name);
             },
             .UNARY_EXPRESSION => |exp| {
                 const k = try self.generate_expression_value(exp.expression, null);
@@ -452,48 +418,26 @@ pub const CodeGen = struct {
                     },
                 }
 
-                if (name != null) {
-                    const ptr = self.environment.get_variable(name.?) orelse unreachable;
-
-                    _ = core.LLVMBuildStore(self.builder, r, ptr.value);
-                    ptr.type = t;
-
-                    return ptr;
-                } else {
-                    return try self.create_variable(.{
-                        .value = r,
-                        .type = t,
-                        .stack_level = null,
-                    });
-                }
+                return self.generate_literal(r, t, name);
             },
             .EQUALITY_EXPRESSION => |exp| {
                 const lhs_value = try self.generate_expression_value(exp.lhs, null);
                 const rhs_value = try self.generate_expression_value(exp.rhs, null);
 
-                const cmp = core.LLVMBuildICmp(self.builder, types.LLVMIntEQ, lhs_value.value, rhs_value.value, "");
-
-                if (name != null) {
-                    const ptr = self.environment.get_variable(name.?) orelse unreachable;
-
-                    _ = core.LLVMBuildStore(self.builder, cmp, ptr.value);
-                    ptr.type = core.LLVMInt1Type();
+                const op: c_uint = switch (exp.typ) {
+                    .EQ => types.LLVMIntEQ,
+                    .LT => types.LLVMIntSLT,
+                    .GT => types.LLVMIntSGT,
+                };
+                const cmp = core.LLVMBuildICmp(self.builder, op, lhs_value.value, rhs_value.value, "");
 
-                    return ptr;
-                } else {
-                    return try self.create_variable(.{
-                        .value = cmp,
-                        .type = core.LLVMInt1Type(),
-                        .stack_level = null,
-                    });
-                }
+                return self.generate_literal(cmp, core.LLVMInt1Type(), name);
             },
             else => unreachable,
         };
     }
 
     fn generate_literal(self: *CodeGen, literal_val: types.LLVMValueRef, literal_type: types.LLVMTypeRef, name: ?[]const u8) !*Variable {
-        var variable: types.LLVMValueRef = undefined;
         if (name != null) {
             if (self.environment.scope_stack.items.len == 1) {
                 const ptr = try self.create_variable(.{
@@ -508,12 +452,10 @@ pub const CodeGen = struct {
             _ = core.LLVMBuildStore(self.builder, literal_val, ptr.value) orelse return CodeGenError.CompilationError;
             ptr.type = literal_type;
             return ptr;
-        } else {
-            variable = literal_val;
         }
 
         return try self.create_variable(.{
-            .value = variable,
+            .value = literal_val,
             .type = literal_type,
             .stack_level = null,
         });
diff --git a/src/evaluator.zig b/src/evaluator.zig
index 43380a1..fef08a2 100644
--- a/src/evaluator.zig
+++ b/src/evaluator.zig
@@ -177,8 +177,11 @@ pub const Evaluator = struct {
                 const lhs = try self.get_expression_value(x.lhs) orelse return EvaluatorError.EvaluationError;
                 const rhs = try self.get_expression_value(x.rhs) orelse return EvaluatorError.EvaluationError;
                 std.debug.assert(lhs.* == .NUMBER and rhs.* == .NUMBER);
-                if (x.multiplication) return try self.create_variable(.{ .NUMBER = lhs.NUMBER * rhs.NUMBER });
-                return try self.create_variable(.{ .NUMBER = @divFloor(lhs.NUMBER, rhs.NUMBER) });
+                switch (x.typ) {
+                    .MUL => return try self.create_variable(.{ .NUMBER = lhs.NUMBER * rhs.NUMBER }),
+                    .DIV => return try self.create_variable(.{ .NUMBER = @divFloor(lhs.NUMBER, rhs.NUMBER) }),
+                    .MOD => return try self.create_variable(.{ .NUMBER = @rem(lhs.NUMBER, rhs.NUMBER) }),
+                }
             },
             .UNARY_EXPRESSION => |x| {
                 const val = try self.get_expression_value(x.expression) orelse return EvaluatorError.EvaluationError;
@@ -193,7 +196,11 @@ pub const Evaluator = struct {
                 const lhs = try self.get_expression_value(x.lhs) orelse return EvaluatorError.EvaluationError;
                 const rhs = try self.get_expression_value(x.rhs) orelse return EvaluatorError.EvaluationError;
                 std.debug.assert(lhs.* == .NUMBER and rhs.* == .NUMBER); //TODO: Generic
-                return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER == rhs.NUMBER) });
+                switch (x.typ) {
+                    .EQ => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER == rhs.NUMBER) }),
+                    .GT => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER > rhs.NUMBER) }),
+                    .LT => return try self.create_variable(.{ .BOOLEAN = (lhs.NUMBER < rhs.NUMBER) }),
+                }
             },
             .PRIMARY_EXPRESSION => |x| {
                 switch (x) {
diff --git a/src/parser.zig b/src/parser.zig
index f7a6806..b0a469c 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -30,19 +30,16 @@ pub const Node = union(enum) {
         condition: *Node,
         statements: []*Node,
     },
-    EQUALITY_EXPRESSION: struct {
-        lhs: *Node,
-        rhs: *Node,
-    },
+    EQUALITY_EXPRESSION: struct { lhs: *Node, rhs: *Node, typ: EqualityExpressionType },
     ADDITIVE_EXPRESSION: struct {
         addition: bool,
         lhs: *Node,
         rhs: *Node,
     },
     MULTIPLICATIVE_EXPRESSION: struct {
-        multiplication: bool,
         lhs: *Node,
         rhs: *Node,
+        typ: MultiplicativeExpressionType,
     },
     UNARY_EXPRESSION: struct {
         negation: bool,
@@ -79,6 +76,18 @@ pub const Node = union(enum) {
     },
 };
 
+pub const EqualityExpressionType = enum {
+    EQ,
+    LT,
+    GT,
+};
+
+pub const MultiplicativeExpressionType = enum {
+    MUL,
+    DIV,
+    MOD,
+};
+
 pub const Parser = struct {
     tokens: []tokenizer.Token,
     offset: u32,
@@ -266,20 +275,38 @@ pub const Parser = struct {
             return ParserError.ParsingError;
     }
 
-    // EqualityExpression ::= AdditiveExpression "==" AdditiveExpression
+    // EqualityExpression ::= AdditiveExpression ("==" | "<" | ">") AdditiveExpression
     fn parse_equality_expression(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing equality expression {any}\n", .{self.peek_token()});
 
         const lhs = try self.parse_additive_expression();
 
-        _ = try self.parse_token(tokenizer.TokenType.EQUALS);
-        _ = try self.parse_token(tokenizer.TokenType.EQUALS);
+        var typ: EqualityExpressionType = undefined;
+
+        if (self.accept_parse(struct {
+            fn parse(iself: *Parser) ParserError!*Node {
+                _ = try iself.parse_token(tokenizer.TokenType.EQUALS);
+                _ = try iself.parse_token(tokenizer.TokenType.EQUALS);
+                return try iself.create_node(.{ .PROGRAM = .{
+                    .statements = &[_]*Node{},
+                } });
+            }
+        }.parse) != null) {
+            typ = .EQ;
+        } else if (self.accept_token(tokenizer.TokenType.LESS) != null) {
+            typ = .LT;
+        } else if (self.accept_token(tokenizer.TokenType.GREATER) != null) {
+            typ = .GT;
+        } else {
+            return ParserError.ParsingError;
+        }
 
         const rhs = try self.parse_additive_expression();
 
         return self.create_node(.{ .EQUALITY_EXPRESSION = .{
             .lhs = lhs,
             .rhs = rhs,
+            .typ = typ,
         } });
     }
 
@@ -307,24 +334,30 @@ pub const Parser = struct {
         return lhs;
     }
 
-    // MultiplicativeExpression ::= UnaryExpression (("*" | "/") UnaryExpression)*
+    // MultiplicativeExpression ::= UnaryExpression (("*" | "/" | "%") UnaryExpression)*
     fn parse_multiplicative_expression(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing additive expression {any}\n", .{self.peek_token()});
 
         var lhs = try self.parse_unary_expression();
 
         while (true) {
-            const mul = self.accept_token(tokenizer.TokenType.MUL);
-            const div = self.accept_token(tokenizer.TokenType.DIV);
-
-            if (mul == null and div == null) break;
+            var typ: MultiplicativeExpressionType = undefined;
+            if (self.accept_token(tokenizer.TokenType.MUL) != null) {
+                typ = .MUL;
+            } else if (self.accept_token(tokenizer.TokenType.DIV) != null) {
+                typ = .DIV;
+            } else if (self.accept_token(tokenizer.TokenType.MOD) != null) {
+                typ = .MOD;
+            } else {
+                break;
+            }
 
             const rhs = try self.parse_unary_expression();
 
             lhs = try self.create_node(.{ .MULTIPLICATIVE_EXPRESSION = .{
-                .multiplication = mul != null,
                 .lhs = lhs,
                 .rhs = rhs,
+                .typ = typ,
             } });
         }
 
diff --git a/src/tokenizer.zig b/src/tokenizer.zig
index 908c016..f0dcd26 100644
--- a/src/tokenizer.zig
+++ b/src/tokenizer.zig
@@ -25,7 +25,10 @@ pub const TokenType = union(enum) {
     MINUS: void,
     MUL: void,
     DIV: void,
+    MOD: void,
     BANG: void,
+    LESS: void,
+    GREATER: void,
 
     // Punctuation
     SEMICOLON: void,
@@ -83,7 +86,10 @@ pub const Tokenizer = struct {
         if (self.accept_string("-")) return self.create_token(.{ .MINUS = void{} });
         if (self.accept_string("*")) return self.create_token(.{ .MUL = void{} });
         if (self.accept_string("/")) return self.create_token(.{ .DIV = void{} });
+        if (self.accept_string("%")) return self.create_token(.{ .MOD = void{} });
         if (self.accept_string("!")) return self.create_token(.{ .BANG = void{} });
+        if (self.accept_string("<")) return self.create_token(.{ .LESS = void{} });
+        if (self.accept_string(">")) return self.create_token(.{ .GREATER = void{} });
 
         const string = self.consume_string();
         if (string.len == 0) return TokenizerError.TokenizingError;