summary refs log tree commit diff
diff options
context:
space:
mode:
authorBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-18 23:29:31 +0100
committerBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-18 23:29:31 +0100
commit1da51fff503411454d5665bbc325a8a846862b88 (patch)
tree3c651c35d9de5e2897b9a1225d7965b8d3071feb
parentExamples: add future examples :^) (diff)
downloadinterpreter-1da51fff503411454d5665bbc325a8a846862b88.tar.gz
interpreter-1da51fff503411454d5665bbc325a8a846862b88.tar.bz2
interpreter-1da51fff503411454d5665bbc325a8a846862b88.zip
Feature: Add basic support for if statements
-rw-r--r--examples/7.src2
-rw-r--r--grammar.ebnf11
-rw-r--r--src/evaluator.zig60
-rw-r--r--src/parser.zig64
-rw-r--r--src/tokenizer.zig3
5 files changed, 104 insertions, 36 deletions
diff --git a/examples/7.src b/examples/7.src
index 36796cb..7b8fe88 100644
--- a/examples/7.src
+++ b/examples/7.src
@@ -4,7 +4,7 @@ let main = () => {
 	if i == 4 {
 		print(i);
 		return i;
-	}
+	};
 
 	return 1;
 };
diff --git a/grammar.ebnf b/grammar.ebnf
index 35a7079..caf8c73 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -1,21 +1,26 @@
 Program      ::= Statement+
 
-Statement    ::= (AssignmentStatement | FunctionCallStatement) SEMICOLON
+Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | ReturnStatement) SEMICOLON
 
 AssignmentStatement ::= "let" IDENTIFIER EQUALS Expression
 
 FunctionCallStatement ::= IDENTIFIER LPAREN FunctionArguments? RPAREN
 
+IfStatement ::= "if" Expression LBRACE Statement* RBRACE -- TODO: Should function definitions be allowed?
+
+ReturnStatement ::= RETURN Expression
+
 FunctionArguments ::= Expression ("," Expression)*
 
+--TODO: ==
+
 Expression   ::= AdditiveExpression | FunctionDefinition
 
 AdditiveExpression ::= PrimaryExpression ("+" AdditiveExpression)
 
 PrimaryExpression ::= NUMBER | IDENTIFIER | FunctionCallStatement
 
-FunctionDefinition ::= LPAREN FunctionParamters? RPAREN ARROW LBRACE Statement* ReturnStatement RBRACE
+FunctionDefinition ::= LPAREN FunctionParamters? RPAREN ARROW LBRACE Statement* ReturnStatement SEMICOLON RBRACE
 
 FunctionParameters ::= IDENTIFIER ("," IDENTIFIER)*
 
-ReturnStatement ::= RETURN Expression SEMICOLON --TODO: I dont like this
diff --git a/src/evaluator.zig b/src/evaluator.zig
index f4a56d8..ec5df8c 100644
--- a/src/evaluator.zig
+++ b/src/evaluator.zig
@@ -37,22 +37,34 @@ pub const Evaluator = struct {
         const program = ast.PROGRAM;
 
         for (program.statements) |stmt| {
-            try self.evaluate_statement(stmt);
+            _ = try self.evaluate_statement(stmt);
         }
 
         const main = self.environment.get_variable("main") orelse return EvaluatorError.EvaluationError;
         return try self.evaluate_function_definition(main.FUNCTION_DEFINITION, &[_]*parser.Node{});
     }
 
-    fn evaluate_statement(self: *Evaluator, statement: *parser.Node) EvaluatorError!void {
+    fn evaluate_statement(self: *Evaluator, statement: *parser.Node) EvaluatorError!?i64 {
         errdefer std.debug.print("Error evaluating statement\n", .{});
         std.debug.assert(statement.* == parser.Node.STATEMENT);
 
-        return switch (statement.STATEMENT.statement.*) {
-            .ASSIGNMENT_STATEMENT => |*assignment_statement| try self.evaluate_assignment_statement(@ptrCast(assignment_statement)),
-            .FUNCTION_CALL_STATEMENT => |*function_call_statement| _ = try self.evaluate_function_call_statement(@ptrCast(function_call_statement)),
+        switch (statement.STATEMENT.statement.*) {
+            .ASSIGNMENT_STATEMENT => |*assignment_statement| {
+                try self.evaluate_assignment_statement(@ptrCast(assignment_statement));
+                return null;
+            },
+            .FUNCTION_CALL_STATEMENT => |*function_call_statement| {
+                _ = try self.evaluate_function_call_statement(@ptrCast(function_call_statement));
+                return null;
+            },
+            .IF_STATEMENT => |*if_statement| {
+                return try self.evaluate_if_statement(@ptrCast(if_statement));
+            },
+            .RETURN_STATEMENT => |*return_statement| return try self.evaluate_return_statement(@ptrCast(return_statement)),
             else => unreachable,
-        };
+        }
+
+        return null;
     }
 
     fn evaluate_assignment_statement(self: *Evaluator, node: *parser.Node) !void {
@@ -93,6 +105,21 @@ pub const Evaluator = struct {
         return self.evaluate_function_definition(function_definition.FUNCTION_DEFINITION, function_call_statement.arguments);
     }
 
+    fn evaluate_if_statement(self: *Evaluator, node: *parser.Node) !?i64 {
+        errdefer std.debug.print("Error evaluating if statement\n", .{});
+        std.debug.assert(node.* == parser.Node.IF_STATEMENT);
+
+        const if_statement = node.IF_STATEMENT;
+
+        const if_condition_val = try self.get_expression_value(if_statement.condition);
+
+        if (if_condition_val != 0) return null;
+
+        if (try self.evaluate_block_statements(if_statement.statements)) |ret| return ret;
+
+        return null;
+    }
+
     fn evaluate_return_statement(self: *Evaluator, return_statement: *parser.Node) !i64 {
         errdefer std.debug.print("Error evaluating return statement\n", .{});
         std.debug.assert(return_statement.* == parser.Node.RETURN_STATEMENT);
@@ -151,15 +178,20 @@ pub const Evaluator = struct {
             try self.environment.add_variable(parameter.PRIMARY_EXPRESSION.IDENTIFIER.name, try self.create_variable(argument));
         }
 
-        i = 0;
-        while (i < function_definition.statements.len - 1) {
-            const stmt = function_definition.statements[i];
-            try self.evaluate_statement(stmt);
-            i += 1;
-        }
+        if (try self.evaluate_block_statements(function_definition.statements)) |ret| return ret;
 
-        const return_stmt = function_definition.statements[i];
-        return try self.evaluate_return_statement(return_stmt);
+        // We should never get here as there should be a return statement
+        return EvaluatorError.EvaluationError;
+    }
+
+    fn evaluate_block_statements(self: *Evaluator, statements: []*parser.Node) !?i64 {
+        var i: usize = 0;
+        while (i < statements.len) : (i += 1) {
+            const stmt = statements[i];
+            const res = try self.evaluate_statement(stmt);
+            if (res != null) return res.?;
+        }
+        return null;
     }
 
     fn create_variable(self: *Evaluator, node: *parser.Node) !*Variable {
diff --git a/src/parser.zig b/src/parser.zig
index cf7f7d3..81006f8 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -11,6 +11,7 @@ const NodeType = enum {
     STATEMENT,
     ASSIGNMENT_STATEMENT,
     FUNCTION_CALL_STATEMENT,
+    IF_STATEMENT,
     EXPRESSION,
     ADDITIVE_EXPRESSION,
     PRIMARY_EXPRESSION,
@@ -34,6 +35,10 @@ pub const Node = union(NodeType) {
         name: []const u8,
         arguments: []*Node,
     },
+    IF_STATEMENT: struct {
+        condition: *Node,
+        statements: []*Node,
+    },
     EXPRESSION: union(enum) {
         ADDITIVE_EXPRESSION: struct {
             expression: *Node,
@@ -101,14 +106,16 @@ pub const Parser = struct {
         } });
     }
 
-    // Statement ::= (AssignmentStatement | FunctionCallStatement) SEMICOLON
+    // Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | ReturnStatement) SEMICOLON
     fn parse_statement(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing statement\n", .{});
 
         const statement = self.accept_parse(parse_function_call_statement) orelse
+            self.accept_parse(parse_if_statement) orelse
+            self.accept_parse(parse_return_statement) orelse
             try self.parse_assignment_statement();
 
-        _ = try self.require_token(tokenizer.TokenType.SEMICOLON);
+        _ = try self.parse_token(tokenizer.TokenType.SEMICOLON);
 
         return self.create_node(.{
             .STATEMENT = .{
@@ -126,9 +133,9 @@ pub const Parser = struct {
             is_declaration = true;
         }
 
-        const identifier = try self.require_token(tokenizer.TokenType.IDENTIFIER);
+        const identifier = try self.parse_token(tokenizer.TokenType.IDENTIFIER);
 
-        _ = try self.require_token(tokenizer.TokenType.EQUALS);
+        _ = try self.parse_token(tokenizer.TokenType.EQUALS);
 
         const expression = try self.parse_expression();
 
@@ -145,13 +152,13 @@ pub const Parser = struct {
     fn parse_function_call_statement(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing function call statement\n", .{});
 
-        const identifier = try self.require_token(tokenizer.TokenType.IDENTIFIER);
+        const identifier = try self.parse_token(tokenizer.TokenType.IDENTIFIER);
 
-        _ = try self.require_token(tokenizer.TokenType.LPAREN);
+        _ = try self.parse_token(tokenizer.TokenType.LPAREN);
 
         const arguments = try self.parse_function_arguments();
 
-        _ = try self.require_token(tokenizer.TokenType.RPAREN);
+        _ = try self.parse_token(tokenizer.TokenType.RPAREN);
 
         return self.create_node(.{ .FUNCTION_CALL_STATEMENT = .{
             .name = try self.allocator.dupe(u8, identifier.IDENTIFIER),
@@ -177,6 +184,29 @@ pub const Parser = struct {
         return node_list.items;
     }
 
+    // IfStatement ::= "if" Expression LBRACE Statement* RBRACE
+    fn parse_if_statement(self: *Parser) ParserError!*Node {
+        errdefer if (!self.try_context) std.debug.print("Error parsing if statement\n", .{});
+
+        _ = try self.parse_token(tokenizer.TokenType.IF);
+
+        const expression = try self.parse_expression();
+
+        _ = try self.parse_token(tokenizer.TokenType.LBRACE);
+
+        var statements = std.ArrayList(*Node).init(self.allocator);
+        while (self.accept_parse(parse_statement)) |expr| {
+            try statements.append(expr);
+        }
+
+        _ = try self.parse_token(tokenizer.TokenType.RBRACE);
+
+        return try self.create_node(.{ .IF_STATEMENT = .{
+            .condition = expression,
+            .statements = statements.items,
+        } });
+    }
+
     // Expression   ::= AdditiveExpression | FunctionDefinition
     fn parse_expression(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing expression\n", .{});
@@ -234,23 +264,23 @@ pub const Parser = struct {
     fn parse_function_definition(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing function definition\n", .{});
 
-        _ = try self.require_token(tokenizer.TokenType.LPAREN);
+        _ = try self.parse_token(tokenizer.TokenType.LPAREN);
 
         const parameters = try self.parse_function_parameters();
 
-        _ = try self.require_token(tokenizer.TokenType.RPAREN);
+        _ = try self.parse_token(tokenizer.TokenType.RPAREN);
 
-        _ = try self.require_token(tokenizer.TokenType.ARROW);
-        _ = try self.require_token(tokenizer.TokenType.LBRACE);
+        _ = try self.parse_token(tokenizer.TokenType.ARROW);
+        _ = try self.parse_token(tokenizer.TokenType.LBRACE);
 
         var nodes = std.ArrayList(*Node).init(self.allocator);
         while (self.accept_parse(parse_statement)) |expression| {
             try nodes.append(expression);
         }
 
-        try nodes.append(try self.parse_return_statement());
+        std.debug.assert(nodes.getLast().STATEMENT.statement.* == .RETURN_STATEMENT);
 
-        _ = try self.require_token(tokenizer.TokenType.RBRACE);
+        _ = try self.parse_token(tokenizer.TokenType.RBRACE);
 
         return self.create_node(.{ .FUNCTION_DEFINITION = .{
             .statements = nodes.items,
@@ -283,15 +313,13 @@ pub const Parser = struct {
         return node_list.items;
     }
 
-    // ReturnStatement :== RETURN Expression
+    // ReturnStatement ::= RETURN Expression
     fn parse_return_statement(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing return statement\n", .{});
-        _ = try self.require_token(tokenizer.TokenType.RETURN);
+        _ = try self.parse_token(tokenizer.TokenType.RETURN);
 
         const expression = try self.parse_expression();
 
-        _ = try self.require_token(tokenizer.TokenType.SEMICOLON); //TODO: I dont like this
-
         return self.create_node(.{
             .RETURN_STATEMENT = .{
                 .expression = @constCast(expression),
@@ -318,7 +346,7 @@ pub const Parser = struct {
         return null;
     }
 
-    fn require_token(self: *Parser, expected_token: tokenizer.TokenType) ParserError!tokenizer.Token {
+    fn parse_token(self: *Parser, expected_token: tokenizer.TokenType) ParserError!tokenizer.Token {
         errdefer if (!self.try_context) std.debug.print("Error accepting token: {any}\n", .{expected_token});
         const token = self.peek_token() orelse return ParserError.ParsingError;
 
diff --git a/src/tokenizer.zig b/src/tokenizer.zig
index 83615b3..f5908b2 100644
--- a/src/tokenizer.zig
+++ b/src/tokenizer.zig
@@ -7,6 +7,7 @@ const TokenizerError = error{
 pub const TokenType = enum {
     // Keywords
     LET,
+    IF,
     RETURN,
     ARROW,
 
@@ -31,6 +32,7 @@ pub const TokenType = enum {
 
 pub const Token = union(TokenType) {
     LET: void,
+    IF: void,
     RETURN: void,
     ARROW: void,
     IDENTIFIER: []u8,
@@ -77,6 +79,7 @@ pub const Tokenizer = struct {
         if (string.len == 0) return TokenizerError.TokenizingError;
 
         if (std.mem.eql(u8, string, "let")) return Token{ .LET = void{} };
+        if (std.mem.eql(u8, string, "if")) return Token{ .IF = void{} };
         if (std.mem.eql(u8, string, "return")) return Token{ .RETURN = void{} };
 
         if (std.fmt.parseInt(i32, string, 10) catch null) |i| return Token{ .NUMBER = i };