diff options
author | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-21 23:54:17 +0100 |
---|---|---|
committer | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-21 23:54:17 +0100 |
commit | 5965c25a1b32599c8b95e8713b8002e11b891ba4 (patch) | |
tree | fda0d378008c0b89ef1ff1453bd4e2a4ba80b1fe | |
parent | Tokenizer: Rename NOT token to BANG (diff) | |
download | interpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.tar.gz interpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.tar.bz2 interpreter-5965c25a1b32599c8b95e8713b8002e11b891ba4.zip |
Feature: Add support for while statements
-rw-r--r-- | examples/10.src | 1 | ||||
-rw-r--r-- | grammar.ebnf | 4 | ||||
-rw-r--r-- | src/evaluator.zig | 21 | ||||
-rw-r--r-- | src/parser.zig | 32 | ||||
-rw-r--r-- | src/tokenizer.zig | 15 |
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 }; |