aboutsummaryrefslogtreecommitdiff
path: root/src/parse.zig
blob: 24d6e42c961a7ad64c6b1b9a0e341e604713661d (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
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 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;
}