diff options
author | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-18 23:29:31 +0100 |
---|---|---|
committer | Baitinq <manuelpalenzuelamerino@gmail.com> | 2025-01-18 23:29:31 +0100 |
commit | 1da51fff503411454d5665bbc325a8a846862b88 (patch) | |
tree | 3c651c35d9de5e2897b9a1225d7965b8d3071feb | |
parent | Examples: add future examples :^) (diff) | |
download | interpreter-1da51fff503411454d5665bbc325a8a846862b88.tar.gz interpreter-1da51fff503411454d5665bbc325a8a846862b88.tar.bz2 interpreter-1da51fff503411454d5665bbc325a8a846862b88.zip |
Feature: Add basic support for if statements
-rw-r--r-- | examples/7.src | 2 | ||||
-rw-r--r-- | grammar.ebnf | 11 | ||||
-rw-r--r-- | src/evaluator.zig | 60 | ||||
-rw-r--r-- | src/parser.zig | 64 | ||||
-rw-r--r-- | src/tokenizer.zig | 3 |
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 }; |