const std = @import("std"); const ArenaAllocator = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; const Token = @import("Token.zig"); const Parser = @This(); const Expr = @import("expr.zig").Expr; const TokenType = @import("token_type.zig").TokenType; const lox = @import("main.zig"); arena: ArenaAllocator, allocator: Allocator, tokens: []const Token, current: u32 = 0, pub fn init(child_allocator: Allocator, tokens: []const Token) !Parser { return .{ .arena = .init(child_allocator), .allocator = undefined, .tokens = tokens, }; } pub fn deinit(self: *Parser) void { self.arena.deinit(); } pub fn parse(self: *Parser) !?*Expr { self.allocator = self.arena.allocator(); return self.expression() catch |err| switch (err) { error.ParseError => return null, else => return err, }; } fn expression(self: *Parser) (Allocator.Error || std.Io.Writer.Error || error{ParseError})!*Expr { return self.equality(); } fn equality(self: *Parser) !*Expr { var expr = try self.comparison(); while (self.match(&.{ .bang_equal, .equal_equal })) { const operator = self.previous(); const right = try self.comparison(); const binary = try self.allocator.create(Expr); binary.* = .{ .binary = .init(expr, operator, right) }; expr = binary; } return expr; } fn comparison(self: *Parser) !*Expr { var expr = try self.term(); while (self.match(&.{ .greater, .greater_equal, .less, .less_equal })) { const operator = self.previous(); const right = try self.term(); const binary = try self.allocator.create(Expr); binary.* = .{ .binary = .init(expr, operator, right) }; expr = binary; } return expr; } fn term(self: *Parser) !*Expr { var expr = try self.factor(); while (self.match(&.{ .plus, .minus })) { const operator = self.previous(); const right = try self.factor(); const binary = try self.allocator.create(Expr); binary.* = .{ .binary = .init(expr, operator, right) }; expr = binary; } return expr; } fn factor(self: *Parser) !*Expr { var expr = try self.unary(); while (self.match(&.{ .slash, .star })) { const operator = self.previous(); const right = try self.unary(); const binary = try self.allocator.create(Expr); binary.* = .{ .binary = .init(expr, operator, right) }; expr = binary; } return expr; } fn unary(self: *Parser) !*Expr { if (self.match(&.{ .bang, .minus })) { const operator = self.previous(); const right = try self.unary(); const expr = try self.allocator.create(Expr); expr.* = .{ .unary = .init(operator, right) }; return expr; } return try self.primary(); } fn primary(self: *Parser) !*Expr { if (self.match(&.{.false})) { const expr = try self.allocator.create(Expr); expr.* = .{ .literal = .init(.{ .boolean = false }) }; return expr; } if (self.match(&.{.true})) { const expr = try self.allocator.create(Expr); expr.* = .{ .literal = .init(.{ .boolean = true }) }; return expr; } if (self.match(&.{.nil})) { const expr = try self.allocator.create(Expr); expr.* = .{ .literal = .init(.{ .nil = {} }) }; return expr; } if (self.match(&.{ .number, .string })) { const expr = try self.allocator.create(Expr); expr.* = .{ .literal = .init(self.previous().literal.?) }; return expr; } if (self.match(&.{.left_paren})) { const expr = try self.expression(); _ = try self.consume(.right_paren, "Expect ')' after expression."); const grouping = try self.allocator.create(Expr); grouping.* = .{ .grouping = .init(expr) }; return grouping; } try self.err(self.peek(), "Expect expression."); } fn match(self: *Parser, types: []const TokenType) bool { for (types) |@"type"| { if (self.check(@"type")) { _ = self.advance(); return true; } } return false; } fn consume(self: *Parser, @"type": TokenType, message: []const u8) !Token { if (self.check(@"type")) return self.advance(); try self.err(self.peek(), message); } fn check(self: *Parser, @"type": TokenType) bool { if (self.isAtEnd()) return false; return self.peek().type == @"type"; } fn advance(self: *Parser) Token { if (!self.isAtEnd()) self.current += 1; return self.previous(); } fn isAtEnd(self: *Parser) bool { return self.peek().type == .eof; } fn peek(self: *Parser) Token { return self.tokens[self.current]; } fn previous(self: *Parser) Token { return self.tokens[self.current - 1]; } fn err(self: *Parser, token: Token, message: []const u8) !noreturn { try lox.parseError(self.allocator, token, message); return error.ParseError; } fn synchronize(self: *Parser) !void { _ = self.advance(); while (!self.isAtEnd()) { if (self.previous().type == .semicolon) return; switch (self.peek().type) { .class, .fun, .@"var", .@"for", .@"if", .@"while", .print, .@"return", => return, } _ = self.advance(); } }