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

    pub fn getInt(self: *const @This(), file_source: []const u8) u64 {
        var value: u64 = 0;
        for (file_source[self.loc.start..self.loc.end]) |c| {
            std.debug.assert('0' <= c and c <= '9');
            value = value * 10 + c - '0';
        }
        return value;
    }
};

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,
            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;
}