const std = @import("std"); const Allocator = std.mem.Allocator; const Expr = @import("expr.zig").Expr; const Token = @import("Token.zig"); pub fn print(allocator: Allocator, expr: *const Expr) Allocator.Error![]const u8 { return switch (expr.*) { .binary => |binary| parenthesize(allocator, binary.operator.lexeme, &.{ binary.left, binary.right }), .grouping => |grouping| parenthesize(allocator, "group", &.{grouping.expression}), .literal => |literal| switch (literal.value) { .string => |string| allocator.dupe(u8, string), .number => |number| std.fmt.allocPrint(allocator, "{}", .{number}), .boolean => |boolean| std.fmt.allocPrint(allocator, "{}", .{boolean}), .nil => allocator.dupe(u8, "nil"), }, .unary => |unary| parenthesize(allocator, unary.operator.lexeme, &.{unary.right}), }; } fn parenthesize(allocator: Allocator, name: []const u8, exprs: []const *const Expr) ![]const u8 { var builder: std.ArrayList(u8) = .empty; try builder.append(allocator, '('); try builder.appendSlice(allocator, name); for (exprs) |expr| { try builder.append(allocator, ' '); const printed = try print(allocator, expr); defer allocator.free(printed); try builder.appendSlice(allocator, printed); } try builder.append(allocator, ')'); return builder.toOwnedSlice(allocator); }