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 Node = ast.Node; const Expr = ast.Expr; pub const Parser = struct { allocator: *Allocator, scanner: *Scanner, tokens: []Token = undefined, hadError: bool = false, pub fn init(allocator: *Allocator, scanner: *Scanner) Parser { return Parser{ .allocator = allocator, .scanner = scanner }; } fn doError(self: *Parser, comptime fmt: []const u8, args: ...) void { self.hadError = true; std.debug.warn("parser error at line {}\n\t", self.scanner.line); std.debug.warn(fmt, args); std.debug.warn("\n"); } fn peek(self: *Parser) Token { return self.tokens[self.tokens.len - 1]; } fn previous(self: *Parser) Token { return self.tokens[self.tokens.len - 2]; } 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 isAtEnd(self: *Parser) bool { return self.peek().ttype == .EOF; } fn check(self: *Parser, ttype: TokenType) bool { if (self.isAtEnd()) return false; return self.peek().ttype == ttype; } fn nextToken(self: *Parser) !Token { var token: Token = undefined; while (true) { var next_token_opt = try self.scanner.nextToken(); if (next_token_opt) |token_nice| { token = token_nice; break; } } self.tokens = try self.allocator.realloc( self.tokens, self.tokens.len + 1, ); self.tokens[self.tokens.len - 1] = token; std.debug.warn("skip to {}\n", token); return token; } fn consume(self: *Parser, ttype: TokenType, comptime msg: []const u8) !Token { if (self.check(ttype)) return try self.nextToken(); try self.tokenError(self.peek(), msg); return Result.CompileError; } fn consumeSingle(self: *Parser, ttype: TokenType) !Token { if (self.check(ttype)) { var cur = self.peek(); _ = try self.nextToken(); std.debug.warn("consumed {}, now has {}\n", ttype, self.peek()); return cur; } var buf_main: [1000]u8 = undefined; var buf = try std.fmt.bufPrint( buf_main[0..], "expected {}, got {}", ttype, self.peek().ttype, ); try self.tokenError(self.peek(), buf); return Result.CompileError; } fn compareAnyOf(self: *@This(), ttypes: []TokenType) bool { for (ttypes) |ttype| { if (self.check(ttype)) return true; } return false; } fn mkFnDecl(self: *Parser, name: Token, params: ast.ParamList, block: ast.ExprList) !*ast.Node { var node = try self.allocator.create(Node); node.* = Node{ .FnDecl = ast.FnDecl{ .func_name = name, .params = params, // TODO stmt .body = block, }, }; return node; } fn mkConstDecl(self: *Parser, consts: ast.ConstList) !*ast.Node { var node = try self.allocator.create(Node); node.* = Node{ .ConstDecl = consts }; return node; } fn mkBlock(self: *Parser, exprs: ast.ExprList) !*ast.Node { var node = try self.allocator.create(Node); node.* = Node{ .Block = exprs }; return node; } fn mkExpr(self: *Parser, expr: *Expr) !*ast.Node { var node = try self.allocator.create(Node); node.* = Node{ .Expr = expr }; return node; } fn mkGrouping(self: *Parser, expr: *Expr) !*ast.Expr { var grouping = try self.allocator.create(Expr); grouping.* = Expr{ .Grouping = expr }; return grouping; } fn mkUnary(self: *Parser, op: Token, right: *Expr) !*Expr { std.debug.warn("Unary\n"); var expr = try self.allocator.create(Expr); expr.* = Expr{ .Unary = ast.UnaryExpr{ .op = op, .right = right, }, }; return expr; } fn mkBinary(self: *Parser, left: *Expr, op: Token, right: *Expr) !*Expr { std.debug.warn("Binary\n"); var expr = try self.allocator.create(Expr); expr.* = Expr{ .Binary = ast.BinaryExpr{ .left = left, .op = op, .right = right, }, }; return expr; } fn mkBool(self: *Parser, val: bool) !*ast.Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ .Literal = ast.LiteralExpr{ .Bool = val, }, }; return expr; } fn mkInteger(self: *Parser, val: []const u8) !*ast.Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ .Literal = ast.LiteralExpr{ .Integer = val, }, }; return expr; } fn mkFloat(self: *Parser, val: []const u8) !*ast.Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ .Literal = ast.LiteralExpr{ .Float = val, }, }; return expr; } fn mkString(self: *Parser, val: []const u8) !*ast.Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ .Literal = ast.LiteralExpr{ .String = val, }, }; return expr; } pub fn parse(self: *Parser) !*ast.Node { self.tokens = try self.allocator.alloc(Token, 0); var root = try ast.mkRoot(self.allocator); var token_opt: ?Token = null; while (true) { if (token_opt == null) { token_opt = try self.nextToken(); } else { token_opt = self.peek(); } var token = token_opt.?; if (token.ttype == .EOF) break; var node = try self.parseTopDecl(); if (node == null) continue; try root.Root.append(node.?); } if (self.hadError) { return error.ParseError; } return root; } fn functionDecl(self: *Parser) !*ast.Node { // function body _ = try self.nextToken(); _ = try self.consumeSingle(.LeftBrace); var body = ast.NodeList.init(self.allocator); while (true) { var tok = self.peek(); if (tok.ttype == .RightBrace) break; // TODO statements //var node = try self.processToken(tok); //std.debug.warn("stmt:{}\n", node); //try body.append(node); tok = try self.nextToken(); } _ = try self.consumeSingle(.RightBrace); return try self.mkFnDecl(name, param_list); } fn processToken(self: *Parser, token: Token) anyerror!*ast.Node { var node = switch (token.ttype) { .Fn => try self.functionDecl(), .Identifier => try self.functionCall(), else => blk: { try self.doError("TODO handle {}\n", token.ttype); return Result.CompileError; }, }; return node; } fn parseFnDecl(self: *@This()) !?*Node { var param_list = ast.ParamList.init(self.allocator); errdefer param_list.deinit(); _ = try self.consumeSingle(.Fn); const name = try self.consumeSingle(.Identifier); _ = try self.consumeSingle(.LeftParen); while (self.peek().ttype != .RightParen) { const param_name = try self.consumeSingle(.Identifier); const param_type = try self.consumeSingle(.Identifier); try param_list.append(ast.ParamDecl{ .name = param_name, .typ = param_type, }); } _ = try self.consumeSingle(.RightParen); // TODO return type const return_type = try self.consumeSingle(.Identifier); var block = try self.parseBlock(); std.debug.warn("!fn name: {}\n", name); return try self.mkFnDecl(name, param_list, block.Block); } fn parseConstDecl(self: *@This()) !?*Node { var consts = ast.ConstList.init(self.allocator); errdefer consts.deinit(); _ = try self.consumeSingle(.Const); _ = try self.consumeSingle(.LeftParen); while (self.peek().ttype != .RightParen) { const const_name = try self.consumeSingle(.Identifier); _ = try self.consumeSingle(.Equal); // TODO expr const const_value = self.peek(); _ = try self.nextToken(); //const const_value = try self.consumeSingle(.Identifier); try consts.append(ast.SingleConst{ .name = const_name, .value = const_value }); } _ = try self.consumeSingle(.RightParen); return self.mkConstDecl(consts); } fn parseTopDecl(self: *@This()) !?*Node { return switch (self.peek().ttype) { .Fn => try self.parseFnDecl(), .Const => try self.parseConstDecl(), else => |ttype| blk: { self.doError("(basic) expected fn/const, got {}\n", ttype); break :blk null; }, }; } fn parseBlock(self: *@This()) !*Node { var exprs = ast.ExprList.init(self.allocator); errdefer exprs.deinit(); _ = try self.consumeSingle(.LeftBrace); while (self.peek().ttype != .RightBrace) { var node = try self.parseExpr(); ast.printNode(node, 0); try exprs.append(node.Expr); //_ = try self.nextToken(); } _ = try self.consumeSingle(.RightBrace); return try self.mkBlock(exprs); } fn parseExpr(self: *@This()) anyerror!*Node { var expr = try self.parseEquality(); return self.mkExpr(expr); } fn parseEquality(self: *@This()) !*Expr { var expr = try self.parseComparison(); while (self.peek().ttype == .EqualEqual) { var op = self.peek(); var right = try self.parseComparison(); expr.* = ast.Expr{ .Binary = ast.BinaryExpr{ .left = expr, .op = op, .right = right, }, }; } return expr; } fn parseComparison(self: *@This()) !*Expr { var expr = try self.parseAddition(); while (self.compareAnyOf(&[_]TokenType{ .Greater, .GreaterEqual, .Less, .LessEqual, })) { var op = self.previous(); var right = try self.parseAddition(); expr.* = ast.Expr{ .Binary = ast.BinaryExpr{ .left = expr, .op = op, .right = right, }, }; } return expr; } fn parseAddition(self: *@This()) !*Expr { var expr = try self.parseMultiplication(); std.debug.warn("left expr at addition:"); ast.printExpr(expr); std.debug.warn("\n"); while (self.compareAnyOf(&[_]TokenType{ .Minus, .Plus, })) { var op = self.peek(); _ = try self.nextToken(); std.debug.warn("==OP token {}\n", op); var right = try self.parseMultiplication(); std.debug.warn("right expr at addition:"); ast.printExpr(right); std.debug.warn("\n"); std.debug.warn("left expr at combination:"); ast.printExpr(expr); std.debug.warn("\n"); expr = try self.mkBinary(expr, op, right); //expr.* = ast.Expr{ // .Binary = ast.BinaryExpr{ // .left = expr, // .op = op, // .right = right, // }, //}; std.debug.warn("final expr at addition:"); ast.printExpr(expr); std.debug.warn("\n"); } std.debug.warn("ret expr at addition:"); ast.printExpr(expr); std.debug.warn("\n"); return expr; } fn parseMultiplication(self: *@This()) !*Expr { var expr = try self.parseUnary(); while (self.compareAnyOf(&[_]TokenType{ .Star, .Slash, })) { var op = self.previous(); var right = try self.parseUnary(); expr.* = ast.Expr{ .Binary = ast.BinaryExpr{ .left = expr, .op = op, .right = right, }, }; } return expr; } fn parseUnary(self: *@This()) anyerror!*Expr { if (self.compareAnyOf(&[_]TokenType{ .Bang, .Minus })) { var op = self.previous(); var right = try self.parseUnary(); return try self.mkUnary(op, right); } var expr = try self.parsePrimary(); return expr; } fn parsePrimary(self: *@This()) !*Expr { const curtype = self.peek().ttype; const lexeme = self.peek().lexeme; var expr = switch (curtype) { .False => try self.mkBool(false), .True => try self.mkBool(true), .Integer => try self.mkInteger(lexeme), .Float => try self.mkFloat(lexeme), .String => try self.mkString(lexeme), .LeftParen => blk: { _ = try self.nextToken(); var expr = (try self.parseExpr()).Expr; _ = try self.consume(.RightParen, "Expected ')' after expression"); break :blk try self.mkGrouping(expr); }, else => blk: { self.doError("expected false/true/int/float/string/leftparen, got {}", curtype); return Result.CompileError; }, }; _ = try self.nextToken(); return expr; } };