const std = @import("std"); const scanners = @import("scanner.zig"); const main = @import("main.zig"); const ast = @import("ast.zig"); const tokens = @import("tokens.zig"); const err = @import("errors.zig"); const Allocator = std.mem.Allocator; const Scanner = scanners.Scanner; const Token = tokens.Token; const TokenType = tokens.TokenType; const Result = main.Result; const Expr = ast.Expr; pub const Parser = struct { allocator: *Allocator, scanner: *Scanner, tokens: []Token = undefined, current: usize = 0, pub fn init(allocator: *Allocator, scanner: *Scanner) Parser { return Parser{ .allocator = allocator, .scanner = scanner }; } fn doError(self: *Parser, comptime fmt: []const u8, args: ...) !void { std.debug.warn("parser error at line {}\n\t", self.scanner.line); std.debug.warn(fmt, args); std.debug.warn("\n"); return Result.CompileError; } fn peek(self: *Parser) Token { return self.tokens[self.current]; } fn previous(self: *Parser) Token { return self.tokens[self.current - 1]; } fn tokenError(self: *Parser, token: Token, msg: []const u8) Result!void { if (token.ttype == .EOF) { err.report(token.line, " at end", msg); } else { err.reportFmt(token.line, " at '{}': {}", token.lexeme, msg); } return Result.CompileError; } fn synchronize(self: *Parser) void { _ = self.advance(); while (!self.isAtEnd()) { if (self.previous().ttype == .Semicolon) return; switch (self.peek().ttype) { .Struct, .Fn, .For, .If, .Return => return, else => {}, } _ = self.advance(); } } fn isAtEnd(self: *Parser) bool { return self.peek().ttype == .EOF; } fn advance(self: *Parser) Token { if (!self.isAtEnd()) self.current += 1; return self.previous(); } fn check(self: *Parser, ttype: TokenType) bool { if (self.isAtEnd()) return false; return self.peek().ttype == ttype; } fn match(self: *Parser, ttypes: []TokenType) bool { for (ttypes) |ttype| { if (self.check(ttype)) { _ = self.advance(); return true; } } return false; } fn matchSingle(self: *Parser, ttype: TokenType) bool { if (self.check(ttype)) { _ = self.advance(); return true; } return false; } fn consume(self: *Parser, ttype: TokenType, comptime msg: []const u8) Result!Token { if (self.check(ttype)) return self.advance(); try self.tokenError(self.peek(), msg); return Result.CompileError; } fn equality(self: *Parser) !Expr { var expr = try self.comparison(); while (self.match(&[_]TokenType{ TokenType.BangEqual, TokenType.EqualEqual })) { var operator = self.previous(); var right = try self.comparison(); expr = ast.mkBinary(&expr, operator, &right); } return expr; } fn comparison(self: *Parser) !Expr { var expr = try self.addition(); while (self.match(&[_]TokenType{ TokenType.Greater, TokenType.GreaterEqual, TokenType.Less, TokenType.LessEqual, })) { var operator = self.previous(); var right = try self.addition(); expr = ast.mkBinary(&expr, operator, &right); } return expr; } fn addition(self: *Parser) !Expr { var expr = try self.multiplication(); while (self.match(&[_]TokenType{ TokenType.Minus, TokenType.Plus })) { var operator = self.previous(); var right = try self.multiplication(); expr = ast.mkBinary(&expr, operator, &right); } return expr; } fn multiplication(self: *Parser) anyerror!Expr { var expr = try self.unary(); while (self.match(&[_]TokenType{ TokenType.Slash, TokenType.Star })) { var operator = self.previous(); var right = try self.unary(); expr = ast.mkBinary(&expr, operator, &right); } return expr; } fn unary(self: *Parser) anyerror!Expr { if (self.match(&[_]TokenType{ TokenType.Bang, TokenType.Minus })) { var operator = self.previous(); var right = try self.unary(); return ast.mkUnary(operator, &right); } return try self.primary(); } fn doInt(self: *Parser) !Expr { var token = self.previous(); // try to parse it as an i32 first, if that fails, do i64. var num_32 = std.fmt.parseInt(i32, token.lexeme, 10) catch |pi_err| { if (pi_err == error.Overflow) { var num_64 = std.fmt.parseInt(i64, token.lexeme, 10) catch |pi_err2| { if (pi_err2 == error.Overflow) { try self.tokenError(token, "Failed to parse number: overflow"); return Result.CompileError; } else { return pi_err2; } }; return ast.mkNum(i64, num_64); } else { return pi_err; } }; return ast.mkNum(i32, num_32); } fn primary(self: *Parser) !Expr { if (self.matchSingle(TokenType.False)) return ast.Expr{ .Bool = false }; if (self.matchSingle(TokenType.True)) return ast.Expr{ .Bool = true }; if (self.matchSingle(TokenType.None)) return ast.Expr{ .Nil = {} }; if (self.matchSingle(TokenType.Integer)) { return try self.doInt(); } if (self.matchSingle(TokenType.String)) { var lexeme = self.previous().lexeme; var slice = try self.allocator.alloc(u8, lexeme.len); std.mem.copy(u8, slice, lexeme); return ast.Expr{ .String = slice }; } if (self.matchSingle(TokenType.LeftParen)) { var expr = try self.expression(); _ = try self.consume(.RightParen, "Expect ')' after expression"); return ast.mkGrouping(&expr); } try self.tokenError(self.peek(), "Expect expression"); return Result.CompileError; } fn expression(self: *Parser) !Expr { return try self.equality(); } pub fn parse(self: *Parser) !?*Expr { self.tokens = try self.allocator.alloc(Token, 0); var i: usize = 0; while (true) { var tok_opt = try self.scanner.nextToken(); if (tok_opt) |token| { self.tokens = try self.allocator.realloc(self.tokens, i + 1); self.tokens[i] = token; i += 1; if (token.ttype == .EOF) break; } } std.debug.warn("{} tokens\n", i); //return Expr{ .Number = ast.Number{ .Integer32 = 69 } }; //return self.root; var expr = self.expression() catch |parse_err| { return null; }; return &expr; } };