const std = @import("std"); const Allocator = std.mem.Allocator; const AstPrinter = @This(); const Expr = @import("expr.zig").Expr; const Token = @import("Token.zig"); allocator: Allocator, pub fn init(allocator: Allocator) AstPrinter { return .{ .allocator = allocator, }; } pub fn print(self: *const AstPrinter, expr: *const Expr) Allocator.Error![]const u8 { return switch (expr.*) { .binary => |binary| visitBinaryExpr(self, binary), .grouping => |grouping| visitGroupingExpr(self, grouping), .literal => |literal| visitLiteralExpr(self, literal), .unary => |unary| visitUnaryExpr(self, unary), }; } fn visitBinaryExpr(self: *const AstPrinter, expr: Expr.Binary) ![]const u8 { return self.parenthesize(expr.operator.lexeme, &.{ expr.left, expr.right }); } fn visitGroupingExpr(self: *const AstPrinter, expr: Expr.Grouping) ![]const u8 { return self.parenthesize("group", &.{expr.expression}); } fn visitLiteralExpr(self: *const AstPrinter, expr: Expr.Literal) ![]const u8 { return switch (expr.value) { .string => |string| self.allocator.dupe(u8, string), .number => |number| std.fmt.allocPrint(self.allocator, "{}", .{number}), .boolean => |boolean| std.fmt.allocPrint(self.allocator, "{}", .{boolean}), .nil => self.allocator.dupe(u8, "nil"), }; } fn visitUnaryExpr(self: *const AstPrinter, expr: Expr.Unary) ![]const u8 { return self.parenthesize(expr.operator.lexeme, &.{expr.right}); } fn parenthesize(self: *const AstPrinter, name: []const u8, exprs: []const *const Expr) ![]const u8 { var builder: std.ArrayList(u8) = .empty; try builder.append(self.allocator, '('); try builder.appendSlice(self.allocator, name); for (exprs) |expr| { try builder.append(self.allocator, ' '); const printed = try print(self, expr); defer self.allocator.free(printed); try builder.appendSlice(self.allocator, printed); } try builder.append(self.allocator, ')'); return builder.toOwnedSlice(self.allocator); } test { const allocator = std.testing.allocator; const expression = Expr{ .binary = .init( &.{ .unary = .init( .init(.minus, "-", null, 1), &.{ .literal = .init(.{ .number = 123 }) }, ) }, .init(.star, "*", null, 1), &.{ .grouping = .init( &.{ .literal = .init(.{ .number = 45.67 }) }, ) }, ), }; const ast_printer = AstPrinter.init(allocator); const printed = try ast_printer.print(&expression); defer allocator.free(printed); std.debug.print("{s}\n", .{printed}); }