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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
const std = @import("std");
const Allocator = std.mem.Allocator;
const root = @import("root");
const Lexer = root.Lexer;
const Token = root.Lexer.Token;
pub const Stmt = struct {
loc: Lexer.Location,
type: Type,
pub const Type = union(enum) {
expr: *const Expr,
};
};
pub const Expr = struct {
loc: Lexer.Location,
type: Type,
pub const Type = union(enum) {
integer_literal,
bin_op: BinOp,
call: Call,
identifier,
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 const Call = struct {
proc: *const Expr,
arg: *const Expr,
};
};
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 }),
.call => |call| try writer.print("({} {})", .{ call.proc, call.arg }),
.identifier => try writer.print("<identifier>", .{}),
}
}
};
pub fn statements(allocator: Allocator, lexer: *Lexer) ![]Stmt {
var stmts: std.ArrayList(Stmt) = .init(allocator);
while (lexer.peek().type != .eof) {
try stmts.append(try statement(allocator, lexer));
}
return try stmts.toOwnedSlice();
}
pub fn statement(allocator: Allocator, lexer: *Lexer) !Stmt {
var expr = try expression(allocator, lexer);
const semicolon: Lexer.Token = if (lexer.peek().type == .semicolon) lexer.next() else return error.ExpectedSemicolon;
return .{
.loc = expr.loc.combine(semicolon.loc),
.type = .{ .expr = expr },
};
}
pub fn expression(allocator: Allocator, lexer: *Lexer) error{ OutOfMemory, ExpectedRightParen, UnexpectedToken }!*Expr {
return addExpr(allocator, lexer);
}
pub fn addExpr(allocator: Allocator, lexer: *Lexer) !*Expr {
var lhs = try callExpr(allocator, lexer);
while (true) {
const op: Expr.Type.BinOp.Op = switch (lexer.peek().type) {
.plus => .plus,
.minus => .minus,
else => break,
};
_ = lexer.next();
const rhs = try callExpr(allocator, lexer);
lhs = try allocate(Expr, allocator, .{
.loc = lhs.loc.combine(rhs.loc),
.type = .{ .bin_op = .{ .lhs = lhs, .op = op, .rhs = rhs } },
});
}
return lhs;
}
pub fn callExpr(allocator: Allocator, lexer: *Lexer) !*Expr {
var proc = try primaryExpr(allocator, lexer);
while (true) {
switch (lexer.peek().type) {
.left_paren, .integer_literal, .identifier => {},
else => break,
}
const arg = try primaryExpr(allocator, lexer);
proc = try allocate(Expr, allocator, .{
.loc = proc.loc.combine(arg.loc),
.type = .{ .call = .{ .proc = proc, .arg = arg } },
});
}
return proc;
}
pub fn primaryExpr(allocator: Allocator, lexer: *Lexer) !*Expr {
const token = lexer.next();
// std.debug.print("term {}\n", .{token});
return allocate(Expr, allocator, switch (token.type) {
.left_paren => {
const res = expression(allocator, lexer);
const right_paren = lexer.next();
if (right_paren.type != .right_paren)
return error.ExpectedRightParen;
return res;
},
.integer_literal => .{ .loc = token.loc, .type = .integer_literal },
.identifier => .{ .loc = token.loc, .type = .identifier },
else => |t| {
std.debug.print("Expected '(', integer literal, or identifier. Got {}\n", .{t});
return error.UnexpectedToken;
},
});
}
fn allocate(T: type, allocator: Allocator, t: T) !*T {
const res = try allocator.create(T);
res.* = t;
return res;
}
|