]> Repositories - zlox.git/blob - src/AstPrinter.zig
Implement print and expression statements
[zlox.git] / src / AstPrinter.zig
1 const std = @import("std");
2 const Allocator = std.mem.Allocator;
3 const AstPrinter = @This();
4 const Expr = @import("expr.zig").Expr;
5 const Token = @import("Token.zig");
6
7 allocator: Allocator,
8
9 pub fn init(allocator: Allocator) AstPrinter {
10     return .{
11         .allocator = allocator,
12     };
13 }
14
15 pub fn print(self: *const AstPrinter, expr: *const Expr) Allocator.Error![]const u8 {
16     return switch (expr.*) {
17         .binary => |binary| visitBinaryExpr(self, binary),
18         .grouping => |grouping| visitGroupingExpr(self, grouping),
19         .literal => |literal| visitLiteralExpr(self, literal),
20         .unary => |unary| visitUnaryExpr(self, unary),
21     };
22 }
23
24 fn visitBinaryExpr(self: *const AstPrinter, expr: Expr.Binary) ![]const u8 {
25     return self.parenthesize(expr.operator.lexeme, &.{ expr.left, expr.right });
26 }
27
28 fn visitGroupingExpr(self: *const AstPrinter, expr: Expr.Grouping) ![]const u8 {
29     return self.parenthesize("group", &.{expr.expression});
30 }
31
32 fn visitLiteralExpr(self: *const AstPrinter, expr: Expr.Literal) ![]const u8 {
33     return switch (expr.value) {
34         .string => |string| self.allocator.dupe(u8, string),
35         .number => |number| std.fmt.allocPrint(self.allocator, "{}", .{number}),
36         .boolean => |boolean| std.fmt.allocPrint(self.allocator, "{}", .{boolean}),
37         .nil => self.allocator.dupe(u8, "nil"),
38     };
39 }
40
41 fn visitUnaryExpr(self: *const AstPrinter, expr: Expr.Unary) ![]const u8 {
42     return self.parenthesize(expr.operator.lexeme, &.{expr.right});
43 }
44
45 fn parenthesize(self: *const AstPrinter, name: []const u8, exprs: []const *const Expr) ![]const u8 {
46     var builder: std.ArrayList(u8) = .empty;
47
48     try builder.append(self.allocator, '(');
49     try builder.appendSlice(self.allocator, name);
50     for (exprs) |expr| {
51         try builder.append(self.allocator, ' ');
52         const printed = try print(self, expr);
53         defer self.allocator.free(printed);
54         try builder.appendSlice(self.allocator, printed);
55     }
56     try builder.append(self.allocator, ')');
57
58     return builder.toOwnedSlice(self.allocator);
59 }
60
61 test {
62     const allocator = std.testing.allocator;
63
64     const expression = Expr{
65         .binary = .init(
66             &.{ .unary = .init(
67                 .init(.minus, "-", null, 1),
68                 &.{ .literal = .init(.{ .number = 123 }) },
69             ) },
70             .init(.star, "*", null, 1),
71             &.{ .grouping = .init(
72                 &.{ .literal = .init(.{ .number = 45.67 }) },
73             ) },
74         ),
75     };
76
77     const ast_printer = AstPrinter.init(allocator);
78     const printed = try ast_printer.print(&expression);
79     defer allocator.free(printed);
80     std.debug.print("{s}\n", .{printed});
81 }