summary refs log tree commit diff
diff options
context:
space:
mode:
authorBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-21 23:54:17 +0100
committerBaitinq <manuelpalenzuelamerino@gmail.com>2025-01-21 23:54:17 +0100
commit5965c25a1b32599c8b95e8713b8002e11b891ba4 (patch)
treefda0d378008c0b89ef1ff1453bd4e2a4ba80b1fe
parentTokenizer: Rename NOT token to BANG (diff)
downloadinterpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.tar.gz
interpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.tar.bz2
interpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.zip
Feature: Add support for while statements
-rw-r--r--examples/10.src1
-rw-r--r--grammar.ebnf4
-rw-r--r--src/evaluator.zig21
-rw-r--r--src/parser.zig32
-rw-r--r--src/tokenizer.zig15
5 files changed, 64 insertions, 9 deletions
diff --git a/examples/10.src b/examples/10.src
index 38e6259..428ac92 100644
--- a/examples/10.src
+++ b/examples/10.src
@@ -3,6 +3,7 @@ let main = () => {
 
 	while !(counter == 10) {
 		print(counter);
+		counter = counter + 1;
 	};
 
 	while counter == 0 {
diff --git a/grammar.ebnf b/grammar.ebnf
index 1af657b..890ba2d 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -1,6 +1,6 @@
 Program      ::= Statement+
 
-Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | ReturnStatement) SEMICOLON
+Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | WhileStatement | ReturnStatement) SEMICOLON
 
 AssignmentStatement ::= "let" IDENTIFIER EQUALS Expression
 
@@ -8,6 +8,8 @@ FunctionCallStatement ::= IDENTIFIER LPAREN FunctionArguments? RPAREN
 
 IfStatement ::= "if" Expression LBRACE Statement* RBRACE -- TODO: Should function definitions be allowed?
 
+WhileStatement ::= "while" Expression LBRACE Statement* RBRACE
+
 ReturnStatement ::= RETURN Expression
 
 FunctionArguments ::= Expression ("," Expression)*
diff --git a/src/evaluator.zig b/src/evaluator.zig
index c1997f3..9e89fc4 100644
--- a/src/evaluator.zig
+++ b/src/evaluator.zig
@@ -62,6 +62,9 @@ pub const Evaluator = struct {
             .IF_STATEMENT => |*if_statement| {
                 return try self.evaluate_if_statement(@ptrCast(if_statement));
             },
+            .WHILE_STATEMENT => |*while_statement| {
+                return try self.evaluate_while_statement(@ptrCast(while_statement));
+            },
             .RETURN_STATEMENT => |*return_statement| return try self.evaluate_return_statement(@ptrCast(return_statement)),
             else => unreachable,
         }
@@ -124,6 +127,24 @@ pub const Evaluator = struct {
         return null;
     }
 
+    fn evaluate_while_statement(self: *Evaluator, node: *parser.Node) !?*Variable {
+        errdefer std.debug.print("Error evaluating while statement\n", .{});
+        std.debug.assert(node.* == parser.Node.WHILE_STATEMENT);
+
+        const while_statement = node.WHILE_STATEMENT;
+
+        while (true) {
+            const while_condition_val = try self.get_expression_value(while_statement.condition);
+            std.debug.assert(while_condition_val.?.* == .BOOLEAN);
+
+            if (!while_condition_val.?.BOOLEAN) return null;
+
+            if (try self.evaluate_block_statements(while_statement.statements)) |ret| return ret;
+        }
+
+        return null;
+    }
+
     fn evaluate_return_statement(self: *Evaluator, return_statement: *parser.Node) !*Variable {
         errdefer std.debug.print("Error evaluating return statement\n", .{});
         std.debug.assert(return_statement.* == parser.Node.RETURN_STATEMENT);
diff --git a/src/parser.zig b/src/parser.zig
index 8d531c7..13509ea 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -12,6 +12,7 @@ const NodeType = enum {
     ASSIGNMENT_STATEMENT,
     FUNCTION_CALL_STATEMENT,
     IF_STATEMENT,
+    WHILE_STATEMENT,
     EQUALITY_EXPRESSION,
     ADDITIVE_EXPRESSION,
     UNARY_EXPRESSION,
@@ -40,6 +41,10 @@ pub const Node = union(NodeType) {
         condition: *Node,
         statements: []*Node,
     },
+    WHILE_STATEMENT: struct {
+        condition: *Node,
+        statements: []*Node,
+    },
     EQUALITY_EXPRESSION: struct {
         lhs: *Node,
         rhs: *Node,
@@ -111,12 +116,13 @@ pub const Parser = struct {
         } });
     }
 
-    // Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | ReturnStatement) SEMICOLON
+    // Statement    ::= (AssignmentStatement | FunctionCallStatement | IfStatement | WhileStatement | 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_while_statement) orelse
             self.accept_parse(parse_return_statement) orelse
             try self.parse_assignment_statement();
 
@@ -212,6 +218,29 @@ pub const Parser = struct {
         } });
     }
 
+    // WhileStatement ::= "while" Expression LBRACE Statement* RBRACE
+    fn parse_while_statement(self: *Parser) ParserError!*Node {
+        errdefer if (!self.try_context) std.debug.print("Error parsing while statement\n", .{});
+
+        _ = try self.parse_token(tokenizer.TokenType.WHILE);
+
+        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(.{ .WHILE_STATEMENT = .{
+            .condition = expression,
+            .statements = statements.items,
+        } });
+    }
+
     // Expression   ::= EqualityExpression | AdditiveExpression | FunctionDefinition
     fn parse_expression(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing expression\n", .{});
@@ -286,7 +315,6 @@ pub const Parser = struct {
         if (self.accept_token(tokenizer.TokenType.LPAREN)) |_| {
             const expr = try self.parse_expression();
             _ = try self.parse_token(tokenizer.TokenType.RPAREN);
-            std.debug.print("HERE!\n", .{});
             return expr;
         }
 
diff --git a/src/tokenizer.zig b/src/tokenizer.zig
index 4e9d383..a5c0e9c 100644
--- a/src/tokenizer.zig
+++ b/src/tokenizer.zig
@@ -8,6 +8,7 @@ pub const TokenType = enum {
     // Keywords
     LET,
     IF,
+    WHILE,
     RETURN,
     ARROW,
 
@@ -36,6 +37,7 @@ pub const TokenType = enum {
 pub const Token = union(TokenType) {
     LET: void,
     IF: void,
+    WHILE: void,
     RETURN: void,
     ARROW: void,
     IDENTIFIER: []u8,
@@ -71,6 +73,13 @@ pub const Tokenizer = struct {
 
         const c = self.buf[self.offset];
 
+        if (self.accept_substr("let")) return Token{ .LET = void{} };
+        if (self.accept_substr("if")) return Token{ .IF = void{} };
+        if (self.accept_substr("while")) return Token{ .WHILE = void{} };
+        if (self.accept_substr("return")) return Token{ .RETURN = void{} };
+        if (self.accept_substr("true")) return Token{ .BOOLEAN = true };
+        if (self.accept_substr("false")) return Token{ .BOOLEAN = false };
+
         if (self.accept_substr("=>")) return Token{ .ARROW = void{} };
         if (c == ';') return Token{ .SEMICOLON = void{} };
         if (c == ',') return Token{ .COMMA = void{} };
@@ -86,12 +95,6 @@ pub const Tokenizer = struct {
         const string = self.consume_string();
         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.mem.eql(u8, string, "true")) return Token{ .BOOLEAN = true };
-        if (std.mem.eql(u8, string, "false")) return Token{ .BOOLEAN = false };
-
         if (std.fmt.parseInt(i32, string, 10) catch null) |i| return Token{ .NUMBER = i };
 
         return Token{ .IDENTIFIER = string };