aboutsummaryrefslogtreecommitdiff
path: root/src/parse.zig
blob: 7daa3f831e603ab2e1853b7397531a43a34e003e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
const std = @import("std");
const Allocator = std.mem.Allocator;

const root = @import("root");
const Lexer = root.Lexer;
const Token = root.Lexer.Token;
const Peekable = root.Peekable;
const peekable = root.peekable;

pub const Expr = struct {
    loc: Lexer.Location,
    type: Type,

    pub const Type = union(enum) {
        integer_literal,
        bin_op: BinOp,
        invalid: Token,

        pub const BinOp = struct {
            lhs: *const Expr,
            rhs: *const Expr,
            op: Op,

            const Op = enum {
                plus,
                minus,

                pub fn format(self: Op, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
                    _ = fmt;
                    _ = options;
                    try writer.writeByte(switch (self) {
                        .plus => '+',
                        .minus => '-',
                    });
                }
            };
        };
    };

    pub fn format(self: Expr, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
        _ = fmt;
        _ = options;
        switch (self.type) {
            .integer_literal => try writer.print("<int>", .{}),
            .bin_op => |bin_op| try writer.print("({} {} {})", .{ bin_op.lhs, bin_op.op, bin_op.rhs }),
            .invalid => try writer.print("<invalid>", .{}),
        }
    }
};

pub fn expression(allocator: Allocator, lexer: *Peekable(Lexer)) error{OutOfMemory}!*Expr {
    return addExpr(allocator, lexer);
}

pub fn addExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
    var lhs = try primaryExpr(allocator, lexer);
    while (true) {
        const token: Lexer.Token = if (lexer.peek()) |t| t else break;
        const op: Expr.Type.BinOp.Op = switch (token.type) {
            .plus => .plus,
            .minus => .minus,
            else => break,
        };

        _ = lexer.next();

        const rhs = try primaryExpr(allocator, lexer);
        lhs = try allocate(allocator, .{
            .loc = lhs.loc.combine(rhs.loc),
            .type = .{ .bin_op = .{ .lhs = lhs, .op = op, .rhs = rhs } },
        });
    }
    return lhs;
}

pub fn primaryExpr(allocator: Allocator, lexer: *Peekable(Lexer)) !*Expr {
    const token = lexer.next().?;
    // std.debug.print("term {}\n", .{token});
    return allocate(allocator, switch (token.type) {
        .left_paren => {
            const res = expression(allocator, lexer);
            const right_paren = lexer.next().?;
            if (right_paren.type != .right_paren)
                return allocate(allocator, .{
                    .loc = right_paren.loc,
                    .type = .{ .invalid = right_paren },
                });
            return res;
        },
        .integer_literal => .{ .loc = token.loc, .type = .integer_literal },
        else => .{ .loc = token.loc, .type = .{ .invalid = token } },
    });
}

fn allocate(allocator: Allocator, expr: Expr) !*Expr {
    const res = try allocator.create(Expr);
    res.* = expr;
    return res;
}