about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBaitinq <[email protected]>2025-03-29 11:22:13 +0100
committerBaitinq <[email protected]>2025-03-29 11:27:15 +0100
commitd74c6a363cf457a2197f3f10c0d567a77573e79c (patch)
treeb82cad7904e8493c28cfa1612de2e0e081484e0c
parentCodegen: Support void type (diff)
downloadpry-lang-d74c6a363cf457a2197f3f10c0d567a77573e79c.tar.gz
pry-lang-d74c6a363cf457a2197f3f10c0d567a77573e79c.tar.bz2
pry-lang-d74c6a363cf457a2197f3f10c0d567a77573e79c.zip
Feature: Add basic support for pointer references and dereferences
-rw-r--r--examples/17.src12
-rw-r--r--grammar.ebnf4
-rw-r--r--src/codegen.zig31
-rw-r--r--src/evaluator.zig15
-rw-r--r--src/parser.zig25
5 files changed, 70 insertions, 17 deletions
diff --git a/examples/17.src b/examples/17.src
new file mode 100644
index 0000000..0e73fae
--- /dev/null
+++ b/examples/17.src
@@ -0,0 +1,12 @@
+extern printf = (*i64, varargs) => void;
+extern malloc = (i64) => *i64;
+extern free = (*i64) => void;
+
+let main = () => i64 {
+	let x = malloc(8);
+	*x = 10;
+	printf("%p\n", x);
+	printf("%d\n", *x);
+	free(x);
+	return 0;
+};
diff --git a/grammar.ebnf b/grammar.ebnf
index 83da90a..85d824d 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -2,7 +2,7 @@ Program      ::= Statement+
 
 Statement    ::= (AssignmentStatement | ExternDeclaration | FunctionCallStatement | IfStatement | WhileStatement | ReturnStatement) SEMICOLON
 
-AssignmentStatement ::= "let"? IDENTIFIER EQUALS Expression
+AssignmentStatement ::= ("let")? ("*")? IDENTIFIER EQUALS Expression
 
 ExternDeclaration ::= "extern" IDENTIFIER EQUALS Type
 
@@ -24,7 +24,7 @@ AdditiveExpression ::= MultiplicativeExpression (("+" | "-") MultiplicativeExpre
 
 MultiplicativeExpression ::= UnaryExpression (("*" | "/" | "%") UnaryExpression)*
 
-UnaryExpression ::= ("!" | "-") UnaryExpression | PrimaryExpression
+UnaryExpression ::= ("!" | "-" | "*") UnaryExpression | PrimaryExpression
 
 PrimaryExpression ::= NUMBER | BOOLEAN | STRING | IDENTIFIER | FunctionCallStatement | FunctionDefinition | LPAREN Expression RPAREN
 
diff --git a/src/codegen.zig b/src/codegen.zig
index da1c8ca..a6f4b08 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -127,6 +127,7 @@ pub const CodeGen = struct {
         const assignment_statement = statement.ASSIGNMENT_STATEMENT;
 
         if (assignment_statement.is_declaration and self.environment.scope_stack.items.len > 1) {
+            std.debug.assert(assignment_statement.is_dereference == false);
             // TODO: vv Int64Type is a problem
             const alloca = llvm.LLVMBuildAlloca(self.builder, llvm.LLVMInt64Type(), try std.fmt.allocPrintZ(self.arena, "{s}", .{assignment_statement.name})); //TODO: Correct type
             try self.environment.add_variable(assignment_statement.name, try self.create_variable(.{
@@ -137,8 +138,26 @@ pub const CodeGen = struct {
             }));
         }
 
+        var undereferenced_variable: ?*Variable = null;
+        if (assignment_statement.is_dereference) {
+            const ptr = self.environment.get_variable(assignment_statement.name) orelse unreachable;
+            undereferenced_variable = ptr;
+            const x = llvm.LLVMBuildLoad2(self.builder, ptr.type, ptr.value, "") orelse return CodeGenError.CompilationError;
+            try self.environment.add_variable(assignment_statement.name, try self.create_variable(.{
+                .value = x,
+                .type = ptr.type,
+                .stack_level = null,
+                .node = statement,
+            }));
+        }
+
         const variable = try self.generate_expression_value(assignment_statement.expression, assignment_statement.name);
-        try self.environment.add_variable(assignment_statement.name, variable);
+
+        if (!assignment_statement.is_dereference) {
+            try self.environment.add_variable(assignment_statement.name, variable);
+        } else {
+            try self.environment.add_variable(assignment_statement.name, undereferenced_variable.?);
+        }
     }
 
     fn generate_function_call_statement(self: *CodeGen, statement: *parser.Node) CodeGenError!*Variable {
@@ -430,16 +449,20 @@ pub const CodeGen = struct {
 
                 var r: llvm.LLVMValueRef = undefined;
                 var t: llvm.LLVMTypeRef = undefined;
-                switch (exp.negation) {
-                    true => {
+                switch (exp.typ) {
+                    .NOT => {
                         std.debug.assert(k.type == llvm.LLVMInt1Type());
                         r = llvm.LLVMBuildICmp(self.builder, llvm.LLVMIntEQ, k.value, llvm.LLVMConstInt(llvm.LLVMInt1Type(), 0, 0), "");
                         t = llvm.LLVMInt1Type();
                     },
-                    false => {
+                    .MINUS => {
                         r = llvm.LLVMBuildNeg(self.builder, k.value, "");
                         t = llvm.LLVMInt64Type();
                     },
+                    .STAR => {
+                        r = llvm.LLVMBuildLoad2(self.builder, k.type, k.value, "");
+                        t = llvm.LLVMInt64Type();
+                    },
                 }
 
                 return self.generate_literal(r, t, name, expression);
diff --git a/src/evaluator.zig b/src/evaluator.zig
index f3c0a6c..5d97eed 100644
--- a/src/evaluator.zig
+++ b/src/evaluator.zig
@@ -186,12 +186,17 @@ pub const Evaluator = struct {
             },
             .UNARY_EXPRESSION => |x| {
                 const val = try self.get_expression_value(x.expression) orelse return EvaluatorError.EvaluationError;
-                if (!x.negation) {
-                    std.debug.assert(val.* == .NUMBER);
-                    return try self.create_variable(.{ .NUMBER = -val.NUMBER });
+                switch (x.typ) {
+                    .NOT => {
+                        std.debug.assert(val.* == .BOOLEAN);
+                        return try self.create_variable(.{ .BOOLEAN = !val.BOOLEAN });
+                    },
+                    .MINUS => {
+                        std.debug.assert(val.* == .NUMBER);
+                        return try self.create_variable(.{ .NUMBER = -val.NUMBER });
+                    },
+                    else => unreachable,
                 }
-                std.debug.assert(val.* == .BOOLEAN);
-                return try self.create_variable(.{ .BOOLEAN = !val.BOOLEAN });
             },
             .EQUALITY_EXPRESSION => |x| {
                 const lhs = try self.get_expression_value(x.lhs) orelse return EvaluatorError.EvaluationError;
diff --git a/src/parser.zig b/src/parser.zig
index a66817c..a40c811 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -15,6 +15,7 @@ pub const Node = union(enum) {
     },
     ASSIGNMENT_STATEMENT: struct {
         is_declaration: bool,
+        is_dereference: bool,
         name: []const u8,
         expression: *Node,
     },
@@ -42,7 +43,11 @@ pub const Node = union(enum) {
         typ: MultiplicativeExpressionType,
     },
     UNARY_EXPRESSION: struct {
-        negation: bool,
+        typ: enum {
+            NOT,
+            MINUS,
+            STAR,
+        },
         expression: *Node,
     },
     PRIMARY_EXPRESSION: union(enum) {
@@ -149,15 +154,20 @@ pub const Parser = struct {
         });
     }
 
-    // AssignmentStatement ::= "let"? IDENTIFIER EQUALS Expression
+    // AssignmentStatement ::= ("let")? ("*")? IDENTIFIER EQUALS Expression
     fn parse_assignment_statement(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing assignment statement {any}\n", .{self.peek_token()});
 
-        var is_declaration: bool = false;
+        var is_declaration = false;
         if (self.accept_token(.LET) != null) {
             is_declaration = true;
         }
 
+        var is_dereference = false;
+        if (self.accept_token(.MUL) != null) {
+            is_dereference = true;
+        }
+
         const identifier = try self.parse_token(tokenizer.TokenType.IDENTIFIER);
 
         _ = try self.parse_token(tokenizer.TokenType.EQUALS);
@@ -167,6 +177,7 @@ pub const Parser = struct {
         return self.create_node(.{
             .ASSIGNMENT_STATEMENT = .{
                 .is_declaration = is_declaration,
+                .is_dereference = is_dereference,
                 .name = try self.arena.dupe(u8, identifier.type.IDENTIFIER),
                 .expression = @constCast(expression),
             },
@@ -188,6 +199,7 @@ pub const Parser = struct {
         return self.create_node(.{
             .ASSIGNMENT_STATEMENT = .{
                 .is_declaration = true,
+                .is_dereference = false,
                 .name = try self.arena.dupe(u8, identifier.type.IDENTIFIER),
                 .expression = @constCast(typ),
             },
@@ -392,19 +404,20 @@ pub const Parser = struct {
         return lhs;
     }
 
-    // UnaryExpression ::= ("!" | "-") UnaryExpression | PrimaryExpression
+    // UnaryExpression ::= ("!" | "-" | "*") UnaryExpression | PrimaryExpression
     fn parse_unary_expression(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing unary expression {any}\n", .{self.peek_token()});
 
         const not = self.accept_token(tokenizer.TokenType.BANG) != null;
         const minus = self.accept_token(tokenizer.TokenType.MINUS) != null;
+        const star = self.accept_token(tokenizer.TokenType.MUL) != null;
 
-        if (!not and !minus) {
+        if (!not and !minus and !star) {
             return try self.parse_primary_expression();
         }
 
         return self.create_node(.{ .UNARY_EXPRESSION = .{
-            .negation = not,
+            .typ = if (not) .NOT else if (minus) .MINUS else .STAR,
             .expression = try self.parse_unary_expression(),
         } });
     }