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
|
const std = @import("std");
const tokenizer = @import("tokenizer.zig");
const NodeType = enum {
PROGRAM,
VARIABLE_STATEMENT,
PRINT_STATEMENT,
NUMBER,
IDENTIFIER,
};
pub const Node = union(NodeType) {
PROGRAM: struct {
statements: []*Node,
},
VARIABLE_STATEMENT: struct {
is_declaration: bool,
name: []const u8,
expression: *Node,
},
PRINT_STATEMENT: struct {
expression: *Node,
},
NUMBER: struct {
value: i64,
},
IDENTIFIER: struct {
name: []const u8,
},
};
pub const Parser = struct {
tokens: []tokenizer.Token,
offset: u32,
pub fn init(tokens: []tokenizer.Token) *Parser {
return @constCast(&Parser{
.tokens = tokens,
.offset = 0,
});
}
pub fn parse(parser: *Parser) !Node {
return parser.parse_program();
}
fn parse_program(_: *Parser) !Node {
return Node{
.NUMBER = .{ .value = 9 },
};
}
fn parse_identifier(self: *Parser) !Node {
const token = self.peek_token() orelse return error.InvalidArgument;
if (token != .IDENTIFIER) return error.InvalidArgument;
_ = self.consume_token();
return Node{ .IDENTIFIER = .{
.name = token.IDENTIFIER,
} };
}
fn parse_number(self: *Parser) !Node {
const token = self.peek_token() orelse return error.InvalidArgument;
if (token != .NUMBER) return error.InvalidArgument;
_ = self.consume_token();
return Node{ .NUMBER = .{
.value = token.NUMBER,
} };
}
fn consume_token(self: *Parser) ?tokenizer.Token {
if (self.offset >= self.tokens.len) return null;
defer self.offset += 1;
return self.tokens[self.offset];
}
fn peek_token(self: Parser) ?tokenizer.Token {
if (self.offset >= self.tokens.len) return null;
return self.tokens[self.offset];
}
};
test "parse identifier" {
const tokens: []tokenizer.Token = @constCast(&[_]tokenizer.Token{
tokenizer.Token{ .IDENTIFIER = @constCast("i") },
});
var parser = Parser.init(tokens);
const ident = try parser.parse_identifier();
try std.testing.expectEqualDeep(Node{ .IDENTIFIER = .{
.name = @constCast("i"),
} }, ident);
}
test "parse number" {
const tokens: []tokenizer.Token = @constCast(&[_]tokenizer.Token{
tokenizer.Token{ .NUMBER = 7 },
});
var parser = Parser.init(tokens);
const number = try parser.parse_number();
try std.testing.expectEqualDeep(Node{ .NUMBER = .{
.value = 7,
} }, number);
}
test "simple e2e" {
const tokens: []tokenizer.Token = @constCast(&[_]tokenizer.Token{
tokenizer.Token{ .LET = void{} },
tokenizer.Token{ .IDENTIFIER = @constCast("i") },
tokenizer.Token{ .EQUALS = void{} },
tokenizer.Token{ .NUMBER = 2 },
tokenizer.Token{ .SEMICOLON = void{} },
});
const ast = try Parser.init(tokens).parse();
try std.testing.expectEqualDeep(Node{ .PROGRAM = .{ .statements = @constCast(&[_]*Node{
@constCast(&Node{ .VARIABLE_STATEMENT = .{ .is_declaration = true, .name = @constCast("i"), .expression = @constCast(&Node{
.NUMBER = .{ .value = 2 },
}) } }),
}) } }, ast);
}
|