authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-09 13:08:02-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-09 13:08:02-05:00
log1fb308ceeea0259ad021d67945ea5adc10960a85
treeeceb252e06a6ed0cc179bc4cdf5a698057da6761
parent3919afcad26d2359efe52f98cd4f2f0573527369

self hosted compiler: move tokenization and parsing to std lib


12 files changed, 2110 insertions(+), 2102 deletions(-)

CMakeLists.txt+4
...@@ -477,6 +477,10 @@ set(ZIG_STD_FILES...@@ -477,6 +477,10 @@ set(ZIG_STD_FILES
477 "special/panic.zig"477 "special/panic.zig"
478 "special/test_runner.zig"478 "special/test_runner.zig"
479 "unicode.zig"479 "unicode.zig"
480 "zig/ast.zig"
481 "zig/index.zig"
482 "zig/parser.zig"
483 "zig/tokenizer.zig"
480)484)
481485
482set(ZIG_C_HEADER_FILES486set(ZIG_C_HEADER_FILES
build.zig-4
...@@ -108,10 +108,6 @@ pub fn build(b: &Builder) !void {...@@ -108,10 +108,6 @@ pub fn build(b: &Builder) !void {
108 "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",108 "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",
109 with_lldb));109 with_lldb));
110110
111 test_step.dependOn(tests.addPkgTests(b, test_filter,
112 "src-self-hosted/main.zig", "fmt", "Run the fmt tests",
113 with_lldb));
114
115 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));111 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
116 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));112 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
117 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));113 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
src-self-hosted/ast.zig deleted-271
...@@ -1,271 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const Token = @import("tokenizer.zig").Token;
5const mem = std.mem;
6
7pub const Node = struct {
8 id: Id,
9
10 pub const Id = enum {
11 Root,
12 VarDecl,
13 Identifier,
14 FnProto,
15 ParamDecl,
16 Block,
17 InfixOp,
18 PrefixOp,
19 IntegerLiteral,
20 FloatLiteral,
21 };
22
23 pub fn iterate(base: &Node, index: usize) ?&Node {
24 return switch (base.id) {
25 Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
26 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
27 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
28 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
29 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
30 Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
31 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
32 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
33 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
34 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
35 };
36 }
37
38 pub fn destroy(base: &Node, allocator: &mem.Allocator) void {
39 return switch (base.id) {
40 Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
41 Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
42 Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
43 Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
44 Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
45 Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
46 Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
47 Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
48 Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
49 Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
50 };
51 }
52};
53
54pub const NodeRoot = struct {
55 base: Node,
56 decls: ArrayList(&Node),
57
58 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
59 if (index < self.decls.len) {
60 return self.decls.items[self.decls.len - index - 1];
61 }
62 return null;
63 }
64};
65
66pub const NodeVarDecl = struct {
67 base: Node,
68 visib_token: ?Token,
69 name_token: Token,
70 eq_token: Token,
71 mut_token: Token,
72 comptime_token: ?Token,
73 extern_token: ?Token,
74 lib_name: ?&Node,
75 type_node: ?&Node,
76 align_node: ?&Node,
77 init_node: ?&Node,
78
79 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
80 var i = index;
81
82 if (self.type_node) |type_node| {
83 if (i < 1) return type_node;
84 i -= 1;
85 }
86
87 if (self.align_node) |align_node| {
88 if (i < 1) return align_node;
89 i -= 1;
90 }
91
92 if (self.init_node) |init_node| {
93 if (i < 1) return init_node;
94 i -= 1;
95 }
96
97 return null;
98 }
99};
100
101pub const NodeIdentifier = struct {
102 base: Node,
103 name_token: Token,
104
105 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
106 return null;
107 }
108};
109
110pub const NodeFnProto = struct {
111 base: Node,
112 visib_token: ?Token,
113 fn_token: Token,
114 name_token: ?Token,
115 params: ArrayList(&Node),
116 return_type: &Node,
117 var_args_token: ?Token,
118 extern_token: ?Token,
119 inline_token: ?Token,
120 cc_token: ?Token,
121 body_node: ?&Node,
122 lib_name: ?&Node, // populated if this is an extern declaration
123 align_expr: ?&Node, // populated if align(A) is present
124
125 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
126 var i = index;
127
128 if (self.body_node) |body_node| {
129 if (i < 1) return body_node;
130 i -= 1;
131 }
132
133 if (i < 1) return self.return_type;
134 i -= 1;
135
136 if (self.align_expr) |align_expr| {
137 if (i < 1) return align_expr;
138 i -= 1;
139 }
140
141 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
142 i -= self.params.len;
143
144 if (self.lib_name) |lib_name| {
145 if (i < 1) return lib_name;
146 i -= 1;
147 }
148
149 return null;
150 }
151};
152
153pub const NodeParamDecl = struct {
154 base: Node,
155 comptime_token: ?Token,
156 noalias_token: ?Token,
157 name_token: ?Token,
158 type_node: &Node,
159 var_args_token: ?Token,
160
161 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {
162 var i = index;
163
164 if (i < 1) return self.type_node;
165 i -= 1;
166
167 return null;
168 }
169};
170
171pub const NodeBlock = struct {
172 base: Node,
173 begin_token: Token,
174 end_token: Token,
175 statements: ArrayList(&Node),
176
177 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
178 var i = index;
179
180 if (i < self.statements.len) return self.statements.items[i];
181 i -= self.statements.len;
182
183 return null;
184 }
185};
186
187pub const NodeInfixOp = struct {
188 base: Node,
189 op_token: Token,
190 lhs: &Node,
191 op: InfixOp,
192 rhs: &Node,
193
194 const InfixOp = enum {
195 EqualEqual,
196 BangEqual,
197 };
198
199 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
200 var i = index;
201
202 if (i < 1) return self.lhs;
203 i -= 1;
204
205 switch (self.op) {
206 InfixOp.EqualEqual => {},
207 InfixOp.BangEqual => {},
208 }
209
210 if (i < 1) return self.rhs;
211 i -= 1;
212
213 return null;
214 }
215};
216
217pub const NodePrefixOp = struct {
218 base: Node,
219 op_token: Token,
220 op: PrefixOp,
221 rhs: &Node,
222
223 const PrefixOp = union(enum) {
224 Return,
225 AddrOf: AddrOfInfo,
226 };
227 const AddrOfInfo = struct {
228 align_expr: ?&Node,
229 bit_offset_start_token: ?Token,
230 bit_offset_end_token: ?Token,
231 const_token: ?Token,
232 volatile_token: ?Token,
233 };
234
235 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
236 var i = index;
237
238 switch (self.op) {
239 PrefixOp.Return => {},
240 PrefixOp.AddrOf => |addr_of_info| {
241 if (addr_of_info.align_expr) |align_expr| {
242 if (i < 1) return align_expr;
243 i -= 1;
244 }
245 },
246 }
247
248 if (i < 1) return self.rhs;
249 i -= 1;
250
251 return null;
252 }
253};
254
255pub const NodeIntegerLiteral = struct {
256 base: Node,
257 token: Token,
258
259 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
260 return null;
261 }
262};
263
264pub const NodeFloatLiteral = struct {
265 base: Node,
266 token: Token,
267
268 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
269 return null;
270 }
271};
src-self-hosted/main.zig-5
...@@ -622,8 +622,3 @@ fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {...@@ -622,8 +622,3 @@ fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
622622
623 return error.FileNotFound;623 return error.FileNotFound;
624}624}
625
626test "import tests" {
627 _ = @import("tokenizer.zig");
628 _ = @import("parser.zig");
629}
src-self-hosted/module.zig+3-3
...@@ -8,9 +8,9 @@ const c = @import("c.zig");...@@ -8,9 +8,9 @@ const c = @import("c.zig");
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const Target = @import("target.zig").Target;9const Target = @import("target.zig").Target;
10const warn = std.debug.warn;10const warn = std.debug.warn;
11const Tokenizer = @import("tokenizer.zig").Tokenizer;11const Tokenizer = std.zig.Tokenizer;
12const Token = @import("tokenizer.zig").Token;12const Token = std.zig.Token;
13const Parser = @import("parser.zig").Parser;13const Parser = std.zig.Parser;
14const ArrayList = std.ArrayList;14const ArrayList = std.ArrayList;
1515
16pub const Module = struct {16pub const Module = struct {
src-self-hosted/parser.zig deleted-1160
...@@ -1,1160 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const mem = std.mem;
5const ast = @import("ast.zig");
6const Tokenizer = @import("tokenizer.zig").Tokenizer;
7const Token = @import("tokenizer.zig").Token;
8const builtin = @import("builtin");
9const io = std.io;
10
11// TODO when we make parse errors into error types instead of printing directly,
12// get rid of this
13const warn = std.debug.warn;
14
15pub const Parser = struct {
16 allocator: &mem.Allocator,
17 tokenizer: &Tokenizer,
18 put_back_tokens: [2]Token,
19 put_back_count: usize,
20 source_file_name: []const u8,
21
22 pub const Tree = struct {
23 root_node: &ast.NodeRoot,
24
25 pub fn deinit(self: &const Tree) void {
26 // TODO free the whole arena
27 }
28 };
29
30 // This memory contents are used only during a function call. It's used to repurpose memory;
31 // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
32 // source rendering.
33 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
34 utility_bytes: []align(utility_bytes_align) u8,
35
36 /// `allocator` should be an arena allocator. Parser never calls free on anything. After you're
37 /// done with a Parser, free the arena. After the arena is freed, no member functions of Parser
38 /// may be called.
39 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
40 return Parser {
41 .allocator = allocator,
42 .tokenizer = tokenizer,
43 .put_back_tokens = undefined,
44 .put_back_count = 0,
45 .source_file_name = source_file_name,
46 .utility_bytes = []align(utility_bytes_align) u8{},
47 };
48 }
49
50 pub fn deinit(self: &Parser) void {
51 self.allocator.free(self.utility_bytes);
52 }
53
54 const TopLevelDeclCtx = struct {
55 visib_token: ?Token,
56 extern_token: ?Token,
57 };
58
59 const DestPtr = union(enum) {
60 Field: &&ast.Node,
61 NullableField: &?&ast.Node,
62 List: &ArrayList(&ast.Node),
63
64 pub fn store(self: &const DestPtr, value: &ast.Node) !void {
65 switch (*self) {
66 DestPtr.Field => |ptr| *ptr = value,
67 DestPtr.NullableField => |ptr| *ptr = value,
68 DestPtr.List => |list| try list.append(value),
69 }
70 }
71 };
72
73 const State = union(enum) {
74 TopLevel,
75 TopLevelExtern: ?Token,
76 TopLevelDecl: TopLevelDeclCtx,
77 Expression: DestPtr,
78 ExpectOperand,
79 Operand: &ast.Node,
80 AfterOperand,
81 InfixOp: &ast.NodeInfixOp,
82 PrefixOp: &ast.NodePrefixOp,
83 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
84 TypeExpr: DestPtr,
85 VarDecl: &ast.NodeVarDecl,
86 VarDeclAlign: &ast.NodeVarDecl,
87 VarDeclEq: &ast.NodeVarDecl,
88 ExpectToken: @TagType(Token.Id),
89 FnProto: &ast.NodeFnProto,
90 FnProtoAlign: &ast.NodeFnProto,
91 ParamDecl: &ast.NodeFnProto,
92 ParamDeclComma,
93 FnDef: &ast.NodeFnProto,
94 Block: &ast.NodeBlock,
95 Statement: &ast.NodeBlock,
96 };
97
98 /// Returns an AST tree, allocated with the parser's allocator.
99 /// Result should be freed with `freeAst` when done.
100 pub fn parse(self: &Parser) !Tree {
101 var stack = self.initUtilityArrayList(State);
102 defer self.deinitUtilityArrayList(stack);
103
104 const root_node = try self.createRoot();
105 // TODO errdefer arena free root node
106
107 try stack.append(State.TopLevel);
108
109 while (true) {
110 //{
111 // const token = self.getNextToken();
112 // warn("{} ", @tagName(token.id));
113 // self.putBackToken(token);
114 // var i: usize = stack.len;
115 // while (i != 0) {
116 // i -= 1;
117 // warn("{} ", @tagName(stack.items[i]));
118 // }
119 // warn("\n");
120 //}
121
122 // This gives us 1 free append that can't fail
123 const state = stack.pop();
124
125 switch (state) {
126 State.TopLevel => {
127 const token = self.getNextToken();
128 switch (token.id) {
129 Token.Id.Keyword_pub, Token.Id.Keyword_export => {
130 stack.append(State { .TopLevelExtern = token }) catch unreachable;
131 continue;
132 },
133 Token.Id.Eof => return Tree {.root_node = root_node},
134 else => {
135 self.putBackToken(token);
136 // TODO shouldn't need this cast
137 stack.append(State { .TopLevelExtern = null }) catch unreachable;
138 continue;
139 },
140 }
141 },
142 State.TopLevelExtern => |visib_token| {
143 const token = self.getNextToken();
144 if (token.id == Token.Id.Keyword_extern) {
145 stack.append(State {
146 .TopLevelDecl = TopLevelDeclCtx {
147 .visib_token = visib_token,
148 .extern_token = token,
149 },
150 }) catch unreachable;
151 continue;
152 }
153 self.putBackToken(token);
154 stack.append(State {
155 .TopLevelDecl = TopLevelDeclCtx {
156 .visib_token = visib_token,
157 .extern_token = null,
158 },
159 }) catch unreachable;
160 continue;
161 },
162 State.TopLevelDecl => |ctx| {
163 const token = self.getNextToken();
164 switch (token.id) {
165 Token.Id.Keyword_var, Token.Id.Keyword_const => {
166 stack.append(State.TopLevel) catch unreachable;
167 // TODO shouldn't need these casts
168 const var_decl_node = try self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
169 token, (?Token)(null), ctx.extern_token);
170 try stack.append(State { .VarDecl = var_decl_node });
171 continue;
172 },
173 Token.Id.Keyword_fn => {
174 stack.append(State.TopLevel) catch unreachable;
175 // TODO shouldn't need these casts
176 const fn_proto = try self.createAttachFnProto(&root_node.decls, token,
177 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
178 try stack.append(State { .FnDef = fn_proto });
179 try stack.append(State { .FnProto = fn_proto });
180 continue;
181 },
182 Token.Id.StringLiteral => {
183 @panic("TODO extern with string literal");
184 },
185 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
186 stack.append(State.TopLevel) catch unreachable;
187 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
188 // TODO shouldn't need this cast
189 const fn_proto = try self.createAttachFnProto(&root_node.decls, fn_token,
190 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
191 try stack.append(State { .FnDef = fn_proto });
192 try stack.append(State { .FnProto = fn_proto });
193 continue;
194 },
195 else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
196 }
197 },
198 State.VarDecl => |var_decl| {
199 var_decl.name_token = try self.eatToken(Token.Id.Identifier);
200 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
201
202 const next_token = self.getNextToken();
203 if (next_token.id == Token.Id.Colon) {
204 try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
205 continue;
206 }
207
208 self.putBackToken(next_token);
209 continue;
210 },
211 State.VarDeclAlign => |var_decl| {
212 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
213
214 const next_token = self.getNextToken();
215 if (next_token.id == Token.Id.Keyword_align) {
216 _ = try self.eatToken(Token.Id.LParen);
217 try stack.append(State { .ExpectToken = Token.Id.RParen });
218 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
219 continue;
220 }
221
222 self.putBackToken(next_token);
223 continue;
224 },
225 State.VarDeclEq => |var_decl| {
226 const token = self.getNextToken();
227 if (token.id == Token.Id.Equal) {
228 var_decl.eq_token = token;
229 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
230 try stack.append(State {
231 .Expression = DestPtr {.NullableField = &var_decl.init_node},
232 });
233 continue;
234 }
235 if (token.id == Token.Id.Semicolon) {
236 continue;
237 }
238 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
239 },
240 State.ExpectToken => |token_id| {
241 _ = try self.eatToken(token_id);
242 continue;
243 },
244
245 State.Expression => |dest_ptr| {
246 // save the dest_ptr for later
247 stack.append(state) catch unreachable;
248 try stack.append(State.ExpectOperand);
249 continue;
250 },
251 State.ExpectOperand => {
252 // we'll either get an operand (like 1 or x),
253 // or a prefix operator (like ~ or return).
254 const token = self.getNextToken();
255 switch (token.id) {
256 Token.Id.Keyword_return => {
257 try stack.append(State { .PrefixOp = try self.createPrefixOp(token,
258 ast.NodePrefixOp.PrefixOp.Return) });
259 try stack.append(State.ExpectOperand);
260 continue;
261 },
262 Token.Id.Ampersand => {
263 const prefix_op = try self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
264 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
265 .align_expr = null,
266 .bit_offset_start_token = null,
267 .bit_offset_end_token = null,
268 .const_token = null,
269 .volatile_token = null,
270 }
271 });
272 try stack.append(State { .PrefixOp = prefix_op });
273 try stack.append(State.ExpectOperand);
274 try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
275 continue;
276 },
277 Token.Id.Identifier => {
278 try stack.append(State {
279 .Operand = &(try self.createIdentifier(token)).base
280 });
281 try stack.append(State.AfterOperand);
282 continue;
283 },
284 Token.Id.IntegerLiteral => {
285 try stack.append(State {
286 .Operand = &(try self.createIntegerLiteral(token)).base
287 });
288 try stack.append(State.AfterOperand);
289 continue;
290 },
291 Token.Id.FloatLiteral => {
292 try stack.append(State {
293 .Operand = &(try self.createFloatLiteral(token)).base
294 });
295 try stack.append(State.AfterOperand);
296 continue;
297 },
298 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
299 }
300 },
301
302 State.AfterOperand => {
303 // we'll either get an infix operator (like != or ^),
304 // or a postfix operator (like () or {}),
305 // otherwise this expression is done (like on a ; or else).
306 var token = self.getNextToken();
307 switch (token.id) {
308 Token.Id.EqualEqual => {
309 try stack.append(State {
310 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
311 });
312 try stack.append(State.ExpectOperand);
313 continue;
314 },
315 Token.Id.BangEqual => {
316 try stack.append(State {
317 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
318 });
319 try stack.append(State.ExpectOperand);
320 continue;
321 },
322 else => {
323 // no postfix/infix operator after this operand.
324 self.putBackToken(token);
325 // reduce the stack
326 var expression: &ast.Node = stack.pop().Operand;
327 while (true) {
328 switch (stack.pop()) {
329 State.Expression => |dest_ptr| {
330 // we're done
331 try dest_ptr.store(expression);
332 break;
333 },
334 State.InfixOp => |infix_op| {
335 infix_op.rhs = expression;
336 infix_op.lhs = stack.pop().Operand;
337 expression = &infix_op.base;
338 continue;
339 },
340 State.PrefixOp => |prefix_op| {
341 prefix_op.rhs = expression;
342 expression = &prefix_op.base;
343 continue;
344 },
345 else => unreachable,
346 }
347 }
348 continue;
349 },
350 }
351 },
352
353 State.AddrOfModifiers => |addr_of_info| {
354 var token = self.getNextToken();
355 switch (token.id) {
356 Token.Id.Keyword_align => {
357 stack.append(state) catch unreachable;
358 if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
359 _ = try self.eatToken(Token.Id.LParen);
360 try stack.append(State { .ExpectToken = Token.Id.RParen });
361 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
362 continue;
363 },
364 Token.Id.Keyword_const => {
365 stack.append(state) catch unreachable;
366 if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
367 addr_of_info.const_token = token;
368 continue;
369 },
370 Token.Id.Keyword_volatile => {
371 stack.append(state) catch unreachable;
372 if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
373 addr_of_info.volatile_token = token;
374 continue;
375 },
376 else => {
377 self.putBackToken(token);
378 continue;
379 },
380 }
381 },
382
383 State.TypeExpr => |dest_ptr| {
384 const token = self.getNextToken();
385 if (token.id == Token.Id.Keyword_var) {
386 @panic("TODO param with type var");
387 }
388 self.putBackToken(token);
389
390 stack.append(State { .Expression = dest_ptr }) catch unreachable;
391 continue;
392 },
393
394 State.FnProto => |fn_proto| {
395 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
396 try stack.append(State { .ParamDecl = fn_proto });
397 try stack.append(State { .ExpectToken = Token.Id.LParen });
398
399 const next_token = self.getNextToken();
400 if (next_token.id == Token.Id.Identifier) {
401 fn_proto.name_token = next_token;
402 continue;
403 }
404 self.putBackToken(next_token);
405 continue;
406 },
407
408 State.FnProtoAlign => |fn_proto| {
409 const token = self.getNextToken();
410 if (token.id == Token.Id.Keyword_align) {
411 @panic("TODO fn proto align");
412 }
413 self.putBackToken(token);
414 stack.append(State {
415 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},
416 }) catch unreachable;
417 continue;
418 },
419
420 State.ParamDecl => |fn_proto| {
421 var token = self.getNextToken();
422 if (token.id == Token.Id.RParen) {
423 continue;
424 }
425 const param_decl = try self.createAttachParamDecl(&fn_proto.params);
426 if (token.id == Token.Id.Keyword_comptime) {
427 param_decl.comptime_token = token;
428 token = self.getNextToken();
429 } else if (token.id == Token.Id.Keyword_noalias) {
430 param_decl.noalias_token = token;
431 token = self.getNextToken();
432 }
433 if (token.id == Token.Id.Identifier) {
434 const next_token = self.getNextToken();
435 if (next_token.id == Token.Id.Colon) {
436 param_decl.name_token = token;
437 token = self.getNextToken();
438 } else {
439 self.putBackToken(next_token);
440 }
441 }
442 if (token.id == Token.Id.Ellipsis3) {
443 param_decl.var_args_token = token;
444 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
445 continue;
446 } else {
447 self.putBackToken(token);
448 }
449
450 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
451 try stack.append(State.ParamDeclComma);
452 try stack.append(State {
453 .TypeExpr = DestPtr {.Field = &param_decl.type_node}
454 });
455 continue;
456 },
457
458 State.ParamDeclComma => {
459 const token = self.getNextToken();
460 switch (token.id) {
461 Token.Id.RParen => {
462 _ = stack.pop(); // pop off the ParamDecl
463 continue;
464 },
465 Token.Id.Comma => continue,
466 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
467 }
468 },
469
470 State.FnDef => |fn_proto| {
471 const token = self.getNextToken();
472 switch(token.id) {
473 Token.Id.LBrace => {
474 const block = try self.createBlock(token);
475 fn_proto.body_node = &block.base;
476 stack.append(State { .Block = block }) catch unreachable;
477 continue;
478 },
479 Token.Id.Semicolon => continue,
480 else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
481 }
482 },
483
484 State.Block => |block| {
485 const token = self.getNextToken();
486 switch (token.id) {
487 Token.Id.RBrace => {
488 block.end_token = token;
489 continue;
490 },
491 else => {
492 self.putBackToken(token);
493 stack.append(State { .Block = block }) catch unreachable;
494 try stack.append(State { .Statement = block });
495 continue;
496 },
497 }
498 },
499
500 State.Statement => |block| {
501 {
502 // Look for comptime var, comptime const
503 const comptime_token = self.getNextToken();
504 if (comptime_token.id == Token.Id.Keyword_comptime) {
505 const mut_token = self.getNextToken();
506 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
507 // TODO shouldn't need these casts
508 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
509 mut_token, (?Token)(comptime_token), (?Token)(null));
510 try stack.append(State { .VarDecl = var_decl });
511 continue;
512 }
513 self.putBackToken(mut_token);
514 }
515 self.putBackToken(comptime_token);
516 }
517 {
518 // Look for const, var
519 const mut_token = self.getNextToken();
520 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
521 // TODO shouldn't need these casts
522 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
523 mut_token, (?Token)(null), (?Token)(null));
524 try stack.append(State { .VarDecl = var_decl });
525 continue;
526 }
527 self.putBackToken(mut_token);
528 }
529
530 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
531 try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
532 continue;
533 },
534
535 // These are data, not control flow.
536 State.InfixOp => unreachable,
537 State.PrefixOp => unreachable,
538 State.Operand => unreachable,
539 }
540 @import("std").debug.panic("{}", @tagName(state));
541 //unreachable;
542 }
543 }
544
545 fn createRoot(self: &Parser) !&ast.NodeRoot {
546 const node = try self.allocator.create(ast.NodeRoot);
547
548 *node = ast.NodeRoot {
549 .base = ast.Node {.id = ast.Node.Id.Root},
550 .decls = ArrayList(&ast.Node).init(self.allocator),
551 };
552 return node;
553 }
554
555 fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
556 extern_token: &const ?Token) !&ast.NodeVarDecl
557 {
558 const node = try self.allocator.create(ast.NodeVarDecl);
559
560 *node = ast.NodeVarDecl {
561 .base = ast.Node {.id = ast.Node.Id.VarDecl},
562 .visib_token = *visib_token,
563 .mut_token = *mut_token,
564 .comptime_token = *comptime_token,
565 .extern_token = *extern_token,
566 .type_node = null,
567 .align_node = null,
568 .init_node = null,
569 .lib_name = null,
570 // initialized later
571 .name_token = undefined,
572 .eq_token = undefined,
573 };
574 return node;
575 }
576
577 fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
578 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
579 {
580 const node = try self.allocator.create(ast.NodeFnProto);
581
582 *node = ast.NodeFnProto {
583 .base = ast.Node {.id = ast.Node.Id.FnProto},
584 .visib_token = *visib_token,
585 .name_token = null,
586 .fn_token = *fn_token,
587 .params = ArrayList(&ast.Node).init(self.allocator),
588 .return_type = undefined,
589 .var_args_token = null,
590 .extern_token = *extern_token,
591 .inline_token = *inline_token,
592 .cc_token = *cc_token,
593 .body_node = null,
594 .lib_name = null,
595 .align_expr = null,
596 };
597 return node;
598 }
599
600 fn createParamDecl(self: &Parser) !&ast.NodeParamDecl {
601 const node = try self.allocator.create(ast.NodeParamDecl);
602
603 *node = ast.NodeParamDecl {
604 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
605 .comptime_token = null,
606 .noalias_token = null,
607 .name_token = null,
608 .type_node = undefined,
609 .var_args_token = null,
610 };
611 return node;
612 }
613
614 fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock {
615 const node = try self.allocator.create(ast.NodeBlock);
616
617 *node = ast.NodeBlock {
618 .base = ast.Node {.id = ast.Node.Id.Block},
619 .begin_token = *begin_token,
620 .end_token = undefined,
621 .statements = ArrayList(&ast.Node).init(self.allocator),
622 };
623 return node;
624 }
625
626 fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
627 const node = try self.allocator.create(ast.NodeInfixOp);
628
629 *node = ast.NodeInfixOp {
630 .base = ast.Node {.id = ast.Node.Id.InfixOp},
631 .op_token = *op_token,
632 .lhs = undefined,
633 .op = *op,
634 .rhs = undefined,
635 };
636 return node;
637 }
638
639 fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
640 const node = try self.allocator.create(ast.NodePrefixOp);
641
642 *node = ast.NodePrefixOp {
643 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
644 .op_token = *op_token,
645 .op = *op,
646 .rhs = undefined,
647 };
648 return node;
649 }
650
651 fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier {
652 const node = try self.allocator.create(ast.NodeIdentifier);
653
654 *node = ast.NodeIdentifier {
655 .base = ast.Node {.id = ast.Node.Id.Identifier},
656 .name_token = *name_token,
657 };
658 return node;
659 }
660
661 fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral {
662 const node = try self.allocator.create(ast.NodeIntegerLiteral);
663
664 *node = ast.NodeIntegerLiteral {
665 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
666 .token = *token,
667 };
668 return node;
669 }
670
671 fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral {
672 const node = try self.allocator.create(ast.NodeFloatLiteral);
673
674 *node = ast.NodeFloatLiteral {
675 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
676 .token = *token,
677 };
678 return node;
679 }
680
681 fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
682 const node = try self.createIdentifier(name_token);
683 try dest_ptr.store(&node.base);
684 return node;
685 }
686
687 fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
688 const node = try self.createParamDecl();
689 try list.append(&node.base);
690 return node;
691 }
692
693 fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
694 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
695 inline_token: &const ?Token) !&ast.NodeFnProto
696 {
697 const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
698 try list.append(&node.base);
699 return node;
700 }
701
702 fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
703 mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
704 {
705 const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
706 try list.append(&node.base);
707 return node;
708 }
709
710 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error {
711 const loc = self.tokenizer.getTokenLocation(token);
712 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
713 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
714 {
715 var i: usize = 0;
716 while (i < loc.column) : (i += 1) {
717 warn(" ");
718 }
719 }
720 {
721 const caret_count = token.end - token.start;
722 var i: usize = 0;
723 while (i < caret_count) : (i += 1) {
724 warn("~");
725 }
726 }
727 warn("\n");
728 return error.ParseError;
729 }
730
731 fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
732 if (token.id != id) {
733 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
734 }
735 }
736
737 fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
738 const token = self.getNextToken();
739 try self.expectToken(token, id);
740 return token;
741 }
742
743 fn putBackToken(self: &Parser, token: &const Token) void {
744 self.put_back_tokens[self.put_back_count] = *token;
745 self.put_back_count += 1;
746 }
747
748 fn getNextToken(self: &Parser) Token {
749 if (self.put_back_count != 0) {
750 const put_back_index = self.put_back_count - 1;
751 const put_back_token = self.put_back_tokens[put_back_index];
752 self.put_back_count = put_back_index;
753 return put_back_token;
754 } else {
755 return self.tokenizer.next();
756 }
757 }
758
759 const RenderAstFrame = struct {
760 node: &ast.Node,
761 indent: usize,
762 };
763
764 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
765 var stack = self.initUtilityArrayList(RenderAstFrame);
766 defer self.deinitUtilityArrayList(stack);
767
768 try stack.append(RenderAstFrame {
769 .node = &root_node.base,
770 .indent = 0,
771 });
772
773 while (stack.popOrNull()) |frame| {
774 {
775 var i: usize = 0;
776 while (i < frame.indent) : (i += 1) {
777 try stream.print(" ");
778 }
779 }
780 try stream.print("{}\n", @tagName(frame.node.id));
781 var child_i: usize = 0;
782 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
783 try stack.append(RenderAstFrame {
784 .node = child,
785 .indent = frame.indent + 2,
786 });
787 }
788 }
789 }
790
791 const RenderState = union(enum) {
792 TopLevelDecl: &ast.Node,
793 FnProtoRParen: &ast.NodeFnProto,
794 ParamDecl: &ast.Node,
795 Text: []const u8,
796 Expression: &ast.Node,
797 VarDecl: &ast.NodeVarDecl,
798 Statement: &ast.Node,
799 PrintIndent,
800 Indent: usize,
801 };
802
803 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
804 var stack = self.initUtilityArrayList(RenderState);
805 defer self.deinitUtilityArrayList(stack);
806
807 {
808 var i = root_node.decls.len;
809 while (i != 0) {
810 i -= 1;
811 const decl = root_node.decls.items[i];
812 try stack.append(RenderState {.TopLevelDecl = decl});
813 }
814 }
815
816 const indent_delta = 4;
817 var indent: usize = 0;
818 while (stack.popOrNull()) |state| {
819 switch (state) {
820 RenderState.TopLevelDecl => |decl| {
821 switch (decl.id) {
822 ast.Node.Id.FnProto => {
823 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
824 if (fn_proto.visib_token) |visib_token| {
825 switch (visib_token.id) {
826 Token.Id.Keyword_pub => try stream.print("pub "),
827 Token.Id.Keyword_export => try stream.print("export "),
828 else => unreachable,
829 }
830 }
831 if (fn_proto.extern_token) |extern_token| {
832 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
833 }
834 try stream.print("fn");
835
836 if (fn_proto.name_token) |name_token| {
837 try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
838 }
839
840 try stream.print("(");
841
842 try stack.append(RenderState { .Text = "\n" });
843 if (fn_proto.body_node == null) {
844 try stack.append(RenderState { .Text = ";" });
845 }
846
847 try stack.append(RenderState { .FnProtoRParen = fn_proto});
848 var i = fn_proto.params.len;
849 while (i != 0) {
850 i -= 1;
851 const param_decl_node = fn_proto.params.items[i];
852 try stack.append(RenderState { .ParamDecl = param_decl_node});
853 if (i != 0) {
854 try stack.append(RenderState { .Text = ", " });
855 }
856 }
857 },
858 ast.Node.Id.VarDecl => {
859 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
860 try stack.append(RenderState { .Text = "\n"});
861 try stack.append(RenderState { .VarDecl = var_decl});
862
863 },
864 else => unreachable,
865 }
866 },
867
868 RenderState.VarDecl => |var_decl| {
869 if (var_decl.visib_token) |visib_token| {
870 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
871 }
872 if (var_decl.extern_token) |extern_token| {
873 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
874 if (var_decl.lib_name != null) {
875 @panic("TODO");
876 }
877 }
878 if (var_decl.comptime_token) |comptime_token| {
879 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
880 }
881 try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
882 try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
883
884 try stack.append(RenderState { .Text = ";" });
885 if (var_decl.init_node) |init_node| {
886 try stack.append(RenderState { .Expression = init_node });
887 try stack.append(RenderState { .Text = " = " });
888 }
889 if (var_decl.align_node) |align_node| {
890 try stack.append(RenderState { .Text = ")" });
891 try stack.append(RenderState { .Expression = align_node });
892 try stack.append(RenderState { .Text = " align(" });
893 }
894 if (var_decl.type_node) |type_node| {
895 try stream.print(": ");
896 try stack.append(RenderState { .Expression = type_node });
897 }
898 },
899
900 RenderState.ParamDecl => |base| {
901 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
902 if (param_decl.comptime_token) |comptime_token| {
903 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
904 }
905 if (param_decl.noalias_token) |noalias_token| {
906 try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
907 }
908 if (param_decl.name_token) |name_token| {
909 try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
910 }
911 if (param_decl.var_args_token) |var_args_token| {
912 try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
913 } else {
914 try stack.append(RenderState { .Expression = param_decl.type_node});
915 }
916 },
917 RenderState.Text => |bytes| {
918 try stream.write(bytes);
919 },
920 RenderState.Expression => |base| switch (base.id) {
921 ast.Node.Id.Identifier => {
922 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
923 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
924 },
925 ast.Node.Id.Block => {
926 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
927 try stream.write("{");
928 try stack.append(RenderState { .Text = "}"});
929 try stack.append(RenderState.PrintIndent);
930 try stack.append(RenderState { .Indent = indent});
931 try stack.append(RenderState { .Text = "\n"});
932 var i = block.statements.len;
933 while (i != 0) {
934 i -= 1;
935 const statement_node = block.statements.items[i];
936 try stack.append(RenderState { .Statement = statement_node});
937 try stack.append(RenderState.PrintIndent);
938 try stack.append(RenderState { .Indent = indent + indent_delta});
939 try stack.append(RenderState { .Text = "\n" });
940 }
941 },
942 ast.Node.Id.InfixOp => {
943 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
944 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
945 switch (prefix_op_node.op) {
946 ast.NodeInfixOp.InfixOp.EqualEqual => {
947 try stack.append(RenderState { .Text = " == "});
948 },
949 ast.NodeInfixOp.InfixOp.BangEqual => {
950 try stack.append(RenderState { .Text = " != "});
951 },
952 else => unreachable,
953 }
954 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
955 },
956 ast.Node.Id.PrefixOp => {
957 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
958 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
959 switch (prefix_op_node.op) {
960 ast.NodePrefixOp.PrefixOp.Return => {
961 try stream.write("return ");
962 },
963 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
964 try stream.write("&");
965 if (addr_of_info.volatile_token != null) {
966 try stack.append(RenderState { .Text = "volatile "});
967 }
968 if (addr_of_info.const_token != null) {
969 try stack.append(RenderState { .Text = "const "});
970 }
971 if (addr_of_info.align_expr) |align_expr| {
972 try stream.print("align(");
973 try stack.append(RenderState { .Text = ") "});
974 try stack.append(RenderState { .Expression = align_expr});
975 }
976 },
977 else => unreachable,
978 }
979 },
980 ast.Node.Id.IntegerLiteral => {
981 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
982 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
983 },
984 ast.Node.Id.FloatLiteral => {
985 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
986 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
987 },
988 else => unreachable,
989 },
990 RenderState.FnProtoRParen => |fn_proto| {
991 try stream.print(")");
992 if (fn_proto.align_expr != null) {
993 @panic("TODO");
994 }
995 try stream.print(" ");
996 if (fn_proto.body_node) |body_node| {
997 try stack.append(RenderState { .Expression = body_node});
998 try stack.append(RenderState { .Text = " "});
999 }
1000 try stack.append(RenderState { .Expression = fn_proto.return_type});
1001 },
1002 RenderState.Statement => |base| {
1003 switch (base.id) {
1004 ast.Node.Id.VarDecl => {
1005 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
1006 try stack.append(RenderState { .VarDecl = var_decl});
1007 },
1008 else => {
1009 try stack.append(RenderState { .Text = ";"});
1010 try stack.append(RenderState { .Expression = base});
1011 },
1012 }
1013 },
1014 RenderState.Indent => |new_indent| indent = new_indent,
1015 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1016 }
1017 }
1018 }
1019
1020 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
1021 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1022 self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1023 const typed_slice = ([]T)(self.utility_bytes);
1024 return ArrayList(T) {
1025 .allocator = self.allocator,
1026 .items = typed_slice,
1027 .len = 0,
1028 };
1029 }
1030
1031 fn deinitUtilityArrayList(self: &Parser, list: var) void {
1032 self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
1033 }
1034
1035};
1036
1037var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1038
1039fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1040 var padded_source: [0x100]u8 = undefined;
1041 std.mem.copy(u8, padded_source[0..source.len], source);
1042 padded_source[source.len + 0] = '\n';
1043 padded_source[source.len + 1] = '\n';
1044 padded_source[source.len + 2] = '\n';
1045
1046 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
1047 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1048 defer parser.deinit();
1049
1050 const tree = try parser.parse();
1051 defer tree.deinit();
1052
1053 var buffer = try std.Buffer.initSize(allocator, 0);
1054 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1055 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1056 return buffer.toOwnedSlice();
1057}
1058
1059// TODO test for memory leaks
1060// TODO test for valid frees
1061fn testCanonical(source: []const u8) !void {
1062 const needed_alloc_count = x: {
1063 // Try it once with unlimited memory, make sure it works
1064 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1065 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1066 const result_source = try testParse(source, &failing_allocator.allocator);
1067 if (!mem.eql(u8, result_source, source)) {
1068 warn("\n====== expected this output: =========\n");
1069 warn("{}", source);
1070 warn("\n======== instead found this: =========\n");
1071 warn("{}", result_source);
1072 warn("\n======================================\n");
1073 return error.TestFailed;
1074 }
1075 failing_allocator.allocator.free(result_source);
1076 break :x failing_allocator.index;
1077 };
1078
1079 var fail_index: usize = 0;
1080 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1081 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1082 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1083 if (testParse(source, &failing_allocator.allocator)) |_| {
1084 return error.NondeterministicMemoryUsage;
1085 } else |err| {
1086 assert(err == error.OutOfMemory);
1087 // TODO make this pass
1088 //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1089 // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1090 // fail_index, needed_alloc_count,
1091 // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1092 // failing_allocator.index, failing_allocator.deallocations);
1093 // return error.MemoryLeakDetected;
1094 //}
1095 }
1096 }
1097}
1098
1099test "zig fmt" {
1100 try testCanonical(
1101 \\extern fn puts(s: &const u8) c_int;
1102 \\
1103 );
1104
1105 try testCanonical(
1106 \\const a = b;
1107 \\pub const a = b;
1108 \\var a = b;
1109 \\pub var a = b;
1110 \\const a: i32 = b;
1111 \\pub const a: i32 = b;
1112 \\var a: i32 = b;
1113 \\pub var a: i32 = b;
1114 \\
1115 );
1116
1117 try testCanonical(
1118 \\extern var foo: c_int;
1119 \\
1120 );
1121
1122 try testCanonical(
1123 \\var foo: c_int align(1);
1124 \\
1125 );
1126
1127 try testCanonical(
1128 \\fn main(argc: c_int, argv: &&u8) c_int {
1129 \\ const a = b;
1130 \\}
1131 \\
1132 );
1133
1134 try testCanonical(
1135 \\fn foo(argc: c_int, argv: &&u8) c_int {
1136 \\ return 0;
1137 \\}
1138 \\
1139 );
1140
1141 try testCanonical(
1142 \\extern fn f1(s: &align(&u8) u8) c_int;
1143 \\
1144 );
1145
1146 try testCanonical(
1147 \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
1148 \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
1149 \\extern fn f3(s: &align(1) const volatile u8) c_int;
1150 \\
1151 );
1152
1153 try testCanonical(
1154 \\fn f1(a: bool, b: bool) bool {
1155 \\ a != b;
1156 \\ return a == b;
1157 \\}
1158 \\
1159 );
1160}
src-self-hosted/tokenizer.zig deleted-659
...@@ -1,659 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3
4pub const Token = struct {
5 id: Id,
6 start: usize,
7 end: usize,
8
9 const KeywordId = struct {
10 bytes: []const u8,
11 id: Id,
12 };
13
14 const keywords = []KeywordId {
15 KeywordId{.bytes="align", .id = Id.Keyword_align},
16 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
20 KeywordId{.bytes="const", .id = Id.Keyword_const},
21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
22 KeywordId{.bytes="defer", .id = Id.Keyword_defer},
23 KeywordId{.bytes="else", .id = Id.Keyword_else},
24 KeywordId{.bytes="enum", .id = Id.Keyword_enum},
25 KeywordId{.bytes="error", .id = Id.Keyword_error},
26 KeywordId{.bytes="export", .id = Id.Keyword_export},
27 KeywordId{.bytes="extern", .id = Id.Keyword_extern},
28 KeywordId{.bytes="false", .id = Id.Keyword_false},
29 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
30 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
32 KeywordId{.bytes="if", .id = Id.Keyword_if},
33 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
34 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
35 KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
36 KeywordId{.bytes="null", .id = Id.Keyword_null},
37 KeywordId{.bytes="or", .id = Id.Keyword_or},
38 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
39 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
40 KeywordId{.bytes="return", .id = Id.Keyword_return},
41 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
42 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
43 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
44 KeywordId{.bytes="test", .id = Id.Keyword_test},
45 KeywordId{.bytes="this", .id = Id.Keyword_this},
46 KeywordId{.bytes="true", .id = Id.Keyword_true},
47 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
48 KeywordId{.bytes="union", .id = Id.Keyword_union},
49 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
50 KeywordId{.bytes="use", .id = Id.Keyword_use},
51 KeywordId{.bytes="var", .id = Id.Keyword_var},
52 KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
53 KeywordId{.bytes="while", .id = Id.Keyword_while},
54 };
55
56 fn getKeyword(bytes: []const u8) ?Id {
57 for (keywords) |kw| {
58 if (mem.eql(u8, kw.bytes, bytes)) {
59 return kw.id;
60 }
61 }
62 return null;
63 }
64
65 const StrLitKind = enum {Normal, C};
66
67 pub const Id = union(enum) {
68 Invalid,
69 Identifier,
70 StringLiteral: StrLitKind,
71 Eof,
72 Builtin,
73 Bang,
74 Equal,
75 EqualEqual,
76 BangEqual,
77 LParen,
78 RParen,
79 Semicolon,
80 Percent,
81 LBrace,
82 RBrace,
83 Period,
84 Ellipsis2,
85 Ellipsis3,
86 Minus,
87 Arrow,
88 Colon,
89 Slash,
90 Comma,
91 Ampersand,
92 AmpersandEqual,
93 IntegerLiteral,
94 FloatLiteral,
95 Keyword_align,
96 Keyword_and,
97 Keyword_asm,
98 Keyword_break,
99 Keyword_comptime,
100 Keyword_const,
101 Keyword_continue,
102 Keyword_defer,
103 Keyword_else,
104 Keyword_enum,
105 Keyword_error,
106 Keyword_export,
107 Keyword_extern,
108 Keyword_false,
109 Keyword_fn,
110 Keyword_for,
111 Keyword_goto,
112 Keyword_if,
113 Keyword_inline,
114 Keyword_nakedcc,
115 Keyword_noalias,
116 Keyword_null,
117 Keyword_or,
118 Keyword_packed,
119 Keyword_pub,
120 Keyword_return,
121 Keyword_stdcallcc,
122 Keyword_struct,
123 Keyword_switch,
124 Keyword_test,
125 Keyword_this,
126 Keyword_true,
127 Keyword_undefined,
128 Keyword_union,
129 Keyword_unreachable,
130 Keyword_use,
131 Keyword_var,
132 Keyword_volatile,
133 Keyword_while,
134 };
135};
136
137pub const Tokenizer = struct {
138 buffer: []const u8,
139 index: usize,
140 pending_invalid_token: ?Token,
141
142 pub const Location = struct {
143 line: usize,
144 column: usize,
145 line_start: usize,
146 line_end: usize,
147 };
148
149 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
150 var loc = Location {
151 .line = 0,
152 .column = 0,
153 .line_start = 0,
154 .line_end = 0,
155 };
156 for (self.buffer) |c, i| {
157 if (i == token.start) {
158 loc.line_end = i;
159 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
160 return loc;
161 }
162 if (c == '\n') {
163 loc.line += 1;
164 loc.column = 0;
165 loc.line_start = i + 1;
166 } else {
167 loc.column += 1;
168 }
169 }
170 return loc;
171 }
172
173 /// For debugging purposes
174 pub fn dump(self: &Tokenizer, token: &const Token) void {
175 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
176 }
177
178 /// buffer must end with "\n\n\n". This is so that attempting to decode
179 /// a the 3 trailing bytes of a 4-byte utf8 sequence is never a buffer overflow.
180 pub fn init(buffer: []const u8) Tokenizer {
181 std.debug.assert(buffer[buffer.len - 1] == '\n');
182 std.debug.assert(buffer[buffer.len - 2] == '\n');
183 std.debug.assert(buffer[buffer.len - 3] == '\n');
184 return Tokenizer {
185 .buffer = buffer,
186 .index = 0,
187 .pending_invalid_token = null,
188 };
189 }
190
191 const State = enum {
192 Start,
193 Identifier,
194 Builtin,
195 C,
196 StringLiteral,
197 StringLiteralBackslash,
198 Equal,
199 Bang,
200 Minus,
201 Slash,
202 LineComment,
203 Zero,
204 IntegerLiteral,
205 IntegerLiteralWithRadix,
206 NumberDot,
207 FloatFraction,
208 FloatExponentUnsigned,
209 FloatExponentNumber,
210 Ampersand,
211 Period,
212 Period2,
213 };
214
215 pub fn next(self: &Tokenizer) Token {
216 if (self.pending_invalid_token) |token| {
217 self.pending_invalid_token = null;
218 return token;
219 }
220 var state = State.Start;
221 var result = Token {
222 .id = Token.Id.Eof,
223 .start = self.index,
224 .end = undefined,
225 };
226 while (self.index < self.buffer.len) : (self.index += 1) {
227 const c = self.buffer[self.index];
228 switch (state) {
229 State.Start => switch (c) {
230 ' ', '\n' => {
231 result.start = self.index + 1;
232 },
233 'c' => {
234 state = State.C;
235 result.id = Token.Id.Identifier;
236 },
237 '"' => {
238 state = State.StringLiteral;
239 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
240 },
241 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
242 state = State.Identifier;
243 result.id = Token.Id.Identifier;
244 },
245 '@' => {
246 state = State.Builtin;
247 result.id = Token.Id.Builtin;
248 },
249 '=' => {
250 state = State.Equal;
251 },
252 '!' => {
253 state = State.Bang;
254 },
255 '(' => {
256 result.id = Token.Id.LParen;
257 self.index += 1;
258 break;
259 },
260 ')' => {
261 result.id = Token.Id.RParen;
262 self.index += 1;
263 break;
264 },
265 ';' => {
266 result.id = Token.Id.Semicolon;
267 self.index += 1;
268 break;
269 },
270 ',' => {
271 result.id = Token.Id.Comma;
272 self.index += 1;
273 break;
274 },
275 ':' => {
276 result.id = Token.Id.Colon;
277 self.index += 1;
278 break;
279 },
280 '%' => {
281 result.id = Token.Id.Percent;
282 self.index += 1;
283 break;
284 },
285 '{' => {
286 result.id = Token.Id.LBrace;
287 self.index += 1;
288 break;
289 },
290 '}' => {
291 result.id = Token.Id.RBrace;
292 self.index += 1;
293 break;
294 },
295 '.' => {
296 state = State.Period;
297 },
298 '-' => {
299 state = State.Minus;
300 },
301 '/' => {
302 state = State.Slash;
303 },
304 '&' => {
305 state = State.Ampersand;
306 },
307 '0' => {
308 state = State.Zero;
309 result.id = Token.Id.IntegerLiteral;
310 },
311 '1'...'9' => {
312 state = State.IntegerLiteral;
313 result.id = Token.Id.IntegerLiteral;
314 },
315 else => {
316 result.id = Token.Id.Invalid;
317 self.index += 1;
318 break;
319 },
320 },
321 State.Ampersand => switch (c) {
322 '=' => {
323 result.id = Token.Id.AmpersandEqual;
324 self.index += 1;
325 break;
326 },
327 else => {
328 result.id = Token.Id.Ampersand;
329 break;
330 },
331 },
332 State.Identifier => switch (c) {
333 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
334 else => {
335 if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
336 result.id = id;
337 }
338 break;
339 },
340 },
341 State.Builtin => switch (c) {
342 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
343 else => break,
344 },
345 State.C => switch (c) {
346 '\\' => @panic("TODO"),
347 '"' => {
348 state = State.StringLiteral;
349 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
350 },
351 'a'...'z', 'A'...'Z', '_', '0'...'9' => {
352 state = State.Identifier;
353 },
354 else => break,
355 },
356 State.StringLiteral => switch (c) {
357 '\\' => {
358 state = State.StringLiteralBackslash;
359 },
360 '"' => {
361 self.index += 1;
362 break;
363 },
364 '\n' => break, // Look for this error later.
365 else => self.checkLiteralCharacter(),
366 },
367
368 State.StringLiteralBackslash => switch (c) {
369 '\n' => break, // Look for this error later.
370 else => {
371 state = State.StringLiteral;
372 },
373 },
374
375 State.Bang => switch (c) {
376 '=' => {
377 result.id = Token.Id.BangEqual;
378 self.index += 1;
379 break;
380 },
381 else => {
382 result.id = Token.Id.Bang;
383 break;
384 },
385 },
386
387 State.Equal => switch (c) {
388 '=' => {
389 result.id = Token.Id.EqualEqual;
390 self.index += 1;
391 break;
392 },
393 else => {
394 result.id = Token.Id.Equal;
395 break;
396 },
397 },
398
399 State.Minus => switch (c) {
400 '>' => {
401 result.id = Token.Id.Arrow;
402 self.index += 1;
403 break;
404 },
405 else => {
406 result.id = Token.Id.Minus;
407 break;
408 },
409 },
410
411 State.Period => switch (c) {
412 '.' => {
413 state = State.Period2;
414 },
415 else => {
416 result.id = Token.Id.Period;
417 break;
418 },
419 },
420
421 State.Period2 => switch (c) {
422 '.' => {
423 result.id = Token.Id.Ellipsis3;
424 self.index += 1;
425 break;
426 },
427 else => {
428 result.id = Token.Id.Ellipsis2;
429 break;
430 },
431 },
432
433 State.Slash => switch (c) {
434 '/' => {
435 result.id = undefined;
436 state = State.LineComment;
437 },
438 else => {
439 result.id = Token.Id.Slash;
440 break;
441 },
442 },
443 State.LineComment => switch (c) {
444 '\n' => {
445 state = State.Start;
446 result = Token {
447 .id = Token.Id.Eof,
448 .start = self.index + 1,
449 .end = undefined,
450 };
451 },
452 else => self.checkLiteralCharacter(),
453 },
454 State.Zero => switch (c) {
455 'b', 'o', 'x' => {
456 state = State.IntegerLiteralWithRadix;
457 },
458 else => {
459 // reinterpret as a normal number
460 self.index -= 1;
461 state = State.IntegerLiteral;
462 },
463 },
464 State.IntegerLiteral => switch (c) {
465 '.' => {
466 state = State.NumberDot;
467 },
468 'p', 'P', 'e', 'E' => {
469 state = State.FloatExponentUnsigned;
470 },
471 '0'...'9' => {},
472 else => break,
473 },
474 State.IntegerLiteralWithRadix => switch (c) {
475 '.' => {
476 state = State.NumberDot;
477 },
478 'p', 'P' => {
479 state = State.FloatExponentUnsigned;
480 },
481 '0'...'9', 'a'...'f', 'A'...'F' => {},
482 else => break,
483 },
484 State.NumberDot => switch (c) {
485 '.' => {
486 self.index -= 1;
487 state = State.Start;
488 break;
489 },
490 else => {
491 self.index -= 1;
492 result.id = Token.Id.FloatLiteral;
493 state = State.FloatFraction;
494 },
495 },
496 State.FloatFraction => switch (c) {
497 'p', 'P' => {
498 state = State.FloatExponentUnsigned;
499 },
500 '0'...'9', 'a'...'f', 'A'...'F' => {},
501 else => break,
502 },
503 State.FloatExponentUnsigned => switch (c) {
504 '+', '-' => {
505 state = State.FloatExponentNumber;
506 },
507 else => {
508 // reinterpret as a normal exponent number
509 self.index -= 1;
510 state = State.FloatExponentNumber;
511 }
512 },
513 State.FloatExponentNumber => switch (c) {
514 '0'...'9', 'a'...'f', 'A'...'F' => {},
515 else => break,
516 },
517 }
518 }
519 result.end = self.index;
520
521 if (result.id == Token.Id.Eof) {
522 if (self.pending_invalid_token) |token| {
523 self.pending_invalid_token = null;
524 return token;
525 }
526 }
527
528 return result;
529 }
530
531 pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
532 return self.buffer[token.start..token.end];
533 }
534
535 fn checkLiteralCharacter(self: &Tokenizer) void {
536 if (self.pending_invalid_token != null) return;
537 const invalid_length = self.getInvalidCharacterLength();
538 if (invalid_length == 0) return;
539 self.pending_invalid_token = Token {
540 .id = Token.Id.Invalid,
541 .start = self.index,
542 .end = self.index + invalid_length,
543 };
544 }
545
546 fn getInvalidCharacterLength(self: &Tokenizer) u3 {
547 const c0 = self.buffer[self.index];
548 if (c0 < 0x80) {
549 if (c0 < 0x20 or c0 == 0x7f) {
550 // ascii control codes are never allowed
551 // (note that \n was checked before we got here)
552 return 1;
553 }
554 // looks fine to me.
555 return 0;
556 } else {
557 // check utf8-encoded character.
558 const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1;
559 // the last 3 bytes in the buffer are guaranteed to be '\n',
560 // which means we don't need to do any bounds checking here.
561 const bytes = self.buffer[self.index..self.index + length];
562 switch (length) {
563 2 => {
564 const value = std.unicode.utf8Decode2(bytes) catch return length;
565 if (value == 0x85) return length; // U+0085 (NEL)
566 },
567 3 => {
568 const value = std.unicode.utf8Decode3(bytes) catch return length;
569 if (value == 0x2028) return length; // U+2028 (LS)
570 if (value == 0x2029) return length; // U+2029 (PS)
571 },
572 4 => {
573 _ = std.unicode.utf8Decode4(bytes) catch return length;
574 },
575 else => unreachable,
576 }
577 self.index += length - 1;
578 return 0;
579 }
580 }
581};
582
583
584
585test "tokenizer" {
586 testTokenize("test", []Token.Id {
587 Token.Id.Keyword_test,
588 });
589}
590
591test "tokenizer - invalid token characters" {
592 testTokenize("#", []Token.Id{Token.Id.Invalid});
593 testTokenize("`", []Token.Id{Token.Id.Invalid});
594}
595
596test "tokenizer - invalid literal/comment characters" {
597 testTokenize("\"\x00\"", []Token.Id {
598 Token.Id { .StringLiteral = Token.StrLitKind.Normal },
599 Token.Id.Invalid,
600 });
601 testTokenize("//\x00", []Token.Id {
602 Token.Id.Invalid,
603 });
604 testTokenize("//\x1f", []Token.Id {
605 Token.Id.Invalid,
606 });
607 testTokenize("//\x7f", []Token.Id {
608 Token.Id.Invalid,
609 });
610}
611
612test "tokenizer - utf8" {
613 testTokenize("//\xc2\x80", []Token.Id{});
614 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
615}
616
617test "tokenizer - invalid utf8" {
618 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
619 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
620 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
621 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
622 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
623 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
624 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
625 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
626}
627
628test "tokenizer - illegal unicode codepoints" {
629 // unicode newline characters.U+0085, U+2028, U+2029
630 testTokenize("//\xc2\x84", []Token.Id{});
631 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
632 testTokenize("//\xc2\x86", []Token.Id{});
633 testTokenize("//\xe2\x80\xa7", []Token.Id{});
634 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
635 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
636 testTokenize("//\xe2\x80\xaa", []Token.Id{});
637}
638
639fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
640 // (test authors, just make this bigger if you need it)
641 var padded_source: [0x100]u8 = undefined;
642 std.mem.copy(u8, padded_source[0..source.len], source);
643 padded_source[source.len + 0] = '\n';
644 padded_source[source.len + 1] = '\n';
645 padded_source[source.len + 2] = '\n';
646
647 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
648 for (expected_tokens) |expected_token_id| {
649 const token = tokenizer.next();
650 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
651 switch (expected_token_id) {
652 Token.Id.StringLiteral => |expected_kind| {
653 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
654 },
655 else => {},
656 }
657 }
658 std.debug.assert(tokenizer.next().id == Token.Id.Eof);
659}
std/index.zig+2
...@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");...@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");
28pub const rand = @import("rand.zig");28pub const rand = @import("rand.zig");
29pub const sort = @import("sort.zig");29pub const sort = @import("sort.zig");
30pub const unicode = @import("unicode.zig");30pub const unicode = @import("unicode.zig");
31pub const zig = @import("zig/index.zig");
3132
32test "std" {33test "std" {
33 // run tests from these34 // run tests from these
...@@ -58,4 +59,5 @@ test "std" {...@@ -58,4 +59,5 @@ test "std" {
58 _ = @import("rand.zig");59 _ = @import("rand.zig");
59 _ = @import("sort.zig");60 _ = @import("sort.zig");
60 _ = @import("unicode.zig");61 _ = @import("unicode.zig");
62 _ = @import("zig/index.zig");
61}63}
std/zig/ast.zig created+271
...@@ -0,0 +1,271 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const Token = std.zig.Token;
5const mem = std.mem;
6
7pub const Node = struct {
8 id: Id,
9
10 pub const Id = enum {
11 Root,
12 VarDecl,
13 Identifier,
14 FnProto,
15 ParamDecl,
16 Block,
17 InfixOp,
18 PrefixOp,
19 IntegerLiteral,
20 FloatLiteral,
21 };
22
23 pub fn iterate(base: &Node, index: usize) ?&Node {
24 return switch (base.id) {
25 Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
26 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
27 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
28 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
29 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
30 Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
31 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
32 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
33 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
34 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
35 };
36 }
37
38 pub fn destroy(base: &Node, allocator: &mem.Allocator) void {
39 return switch (base.id) {
40 Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
41 Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
42 Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
43 Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
44 Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
45 Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
46 Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
47 Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
48 Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
49 Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
50 };
51 }
52};
53
54pub const NodeRoot = struct {
55 base: Node,
56 decls: ArrayList(&Node),
57
58 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
59 if (index < self.decls.len) {
60 return self.decls.items[self.decls.len - index - 1];
61 }
62 return null;
63 }
64};
65
66pub const NodeVarDecl = struct {
67 base: Node,
68 visib_token: ?Token,
69 name_token: Token,
70 eq_token: Token,
71 mut_token: Token,
72 comptime_token: ?Token,
73 extern_token: ?Token,
74 lib_name: ?&Node,
75 type_node: ?&Node,
76 align_node: ?&Node,
77 init_node: ?&Node,
78
79 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
80 var i = index;
81
82 if (self.type_node) |type_node| {
83 if (i < 1) return type_node;
84 i -= 1;
85 }
86
87 if (self.align_node) |align_node| {
88 if (i < 1) return align_node;
89 i -= 1;
90 }
91
92 if (self.init_node) |init_node| {
93 if (i < 1) return init_node;
94 i -= 1;
95 }
96
97 return null;
98 }
99};
100
101pub const NodeIdentifier = struct {
102 base: Node,
103 name_token: Token,
104
105 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
106 return null;
107 }
108};
109
110pub const NodeFnProto = struct {
111 base: Node,
112 visib_token: ?Token,
113 fn_token: Token,
114 name_token: ?Token,
115 params: ArrayList(&Node),
116 return_type: &Node,
117 var_args_token: ?Token,
118 extern_token: ?Token,
119 inline_token: ?Token,
120 cc_token: ?Token,
121 body_node: ?&Node,
122 lib_name: ?&Node, // populated if this is an extern declaration
123 align_expr: ?&Node, // populated if align(A) is present
124
125 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
126 var i = index;
127
128 if (self.body_node) |body_node| {
129 if (i < 1) return body_node;
130 i -= 1;
131 }
132
133 if (i < 1) return self.return_type;
134 i -= 1;
135
136 if (self.align_expr) |align_expr| {
137 if (i < 1) return align_expr;
138 i -= 1;
139 }
140
141 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
142 i -= self.params.len;
143
144 if (self.lib_name) |lib_name| {
145 if (i < 1) return lib_name;
146 i -= 1;
147 }
148
149 return null;
150 }
151};
152
153pub const NodeParamDecl = struct {
154 base: Node,
155 comptime_token: ?Token,
156 noalias_token: ?Token,
157 name_token: ?Token,
158 type_node: &Node,
159 var_args_token: ?Token,
160
161 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {
162 var i = index;
163
164 if (i < 1) return self.type_node;
165 i -= 1;
166
167 return null;
168 }
169};
170
171pub const NodeBlock = struct {
172 base: Node,
173 begin_token: Token,
174 end_token: Token,
175 statements: ArrayList(&Node),
176
177 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
178 var i = index;
179
180 if (i < self.statements.len) return self.statements.items[i];
181 i -= self.statements.len;
182
183 return null;
184 }
185};
186
187pub const NodeInfixOp = struct {
188 base: Node,
189 op_token: Token,
190 lhs: &Node,
191 op: InfixOp,
192 rhs: &Node,
193
194 const InfixOp = enum {
195 EqualEqual,
196 BangEqual,
197 };
198
199 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
200 var i = index;
201
202 if (i < 1) return self.lhs;
203 i -= 1;
204
205 switch (self.op) {
206 InfixOp.EqualEqual => {},
207 InfixOp.BangEqual => {},
208 }
209
210 if (i < 1) return self.rhs;
211 i -= 1;
212
213 return null;
214 }
215};
216
217pub const NodePrefixOp = struct {
218 base: Node,
219 op_token: Token,
220 op: PrefixOp,
221 rhs: &Node,
222
223 const PrefixOp = union(enum) {
224 Return,
225 AddrOf: AddrOfInfo,
226 };
227 const AddrOfInfo = struct {
228 align_expr: ?&Node,
229 bit_offset_start_token: ?Token,
230 bit_offset_end_token: ?Token,
231 const_token: ?Token,
232 volatile_token: ?Token,
233 };
234
235 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
236 var i = index;
237
238 switch (self.op) {
239 PrefixOp.Return => {},
240 PrefixOp.AddrOf => |addr_of_info| {
241 if (addr_of_info.align_expr) |align_expr| {
242 if (i < 1) return align_expr;
243 i -= 1;
244 }
245 },
246 }
247
248 if (i < 1) return self.rhs;
249 i -= 1;
250
251 return null;
252 }
253};
254
255pub const NodeIntegerLiteral = struct {
256 base: Node,
257 token: Token,
258
259 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
260 return null;
261 }
262};
263
264pub const NodeFloatLiteral = struct {
265 base: Node,
266 token: Token,
267
268 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
269 return null;
270 }
271};
std/zig/index.zig created+11
...@@ -0,0 +1,11 @@
1const tokenizer = @import("tokenizer.zig");
2pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;
4pub const Parser = @import("parser.zig").Parser;
5pub const ast = @import("ast.zig");
6
7test "std.zig tests" {
8 _ = @import("tokenizer.zig");
9 _ = @import("parser.zig");
10 _ = @import("ast.zig");
11}
std/zig/parser.zig created+1160
...@@ -0,0 +1,1160 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const mem = std.mem;
5const ast = std.zig.ast;
6const Tokenizer = std.zig.Tokenizer;
7const Token = std.zig.Token;
8const builtin = @import("builtin");
9const io = std.io;
10
11// TODO when we make parse errors into error types instead of printing directly,
12// get rid of this
13const warn = std.debug.warn;
14
15pub const Parser = struct {
16 allocator: &mem.Allocator,
17 tokenizer: &Tokenizer,
18 put_back_tokens: [2]Token,
19 put_back_count: usize,
20 source_file_name: []const u8,
21
22 pub const Tree = struct {
23 root_node: &ast.NodeRoot,
24
25 pub fn deinit(self: &const Tree) void {
26 // TODO free the whole arena
27 }
28 };
29
30 // This memory contents are used only during a function call. It's used to repurpose memory;
31 // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
32 // source rendering.
33 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
34 utility_bytes: []align(utility_bytes_align) u8,
35
36 /// `allocator` should be an arena allocator. Parser never calls free on anything. After you're
37 /// done with a Parser, free the arena. After the arena is freed, no member functions of Parser
38 /// may be called.
39 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
40 return Parser {
41 .allocator = allocator,
42 .tokenizer = tokenizer,
43 .put_back_tokens = undefined,
44 .put_back_count = 0,
45 .source_file_name = source_file_name,
46 .utility_bytes = []align(utility_bytes_align) u8{},
47 };
48 }
49
50 pub fn deinit(self: &Parser) void {
51 self.allocator.free(self.utility_bytes);
52 }
53
54 const TopLevelDeclCtx = struct {
55 visib_token: ?Token,
56 extern_token: ?Token,
57 };
58
59 const DestPtr = union(enum) {
60 Field: &&ast.Node,
61 NullableField: &?&ast.Node,
62 List: &ArrayList(&ast.Node),
63
64 pub fn store(self: &const DestPtr, value: &ast.Node) !void {
65 switch (*self) {
66 DestPtr.Field => |ptr| *ptr = value,
67 DestPtr.NullableField => |ptr| *ptr = value,
68 DestPtr.List => |list| try list.append(value),
69 }
70 }
71 };
72
73 const State = union(enum) {
74 TopLevel,
75 TopLevelExtern: ?Token,
76 TopLevelDecl: TopLevelDeclCtx,
77 Expression: DestPtr,
78 ExpectOperand,
79 Operand: &ast.Node,
80 AfterOperand,
81 InfixOp: &ast.NodeInfixOp,
82 PrefixOp: &ast.NodePrefixOp,
83 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
84 TypeExpr: DestPtr,
85 VarDecl: &ast.NodeVarDecl,
86 VarDeclAlign: &ast.NodeVarDecl,
87 VarDeclEq: &ast.NodeVarDecl,
88 ExpectToken: @TagType(Token.Id),
89 FnProto: &ast.NodeFnProto,
90 FnProtoAlign: &ast.NodeFnProto,
91 ParamDecl: &ast.NodeFnProto,
92 ParamDeclComma,
93 FnDef: &ast.NodeFnProto,
94 Block: &ast.NodeBlock,
95 Statement: &ast.NodeBlock,
96 };
97
98 /// Returns an AST tree, allocated with the parser's allocator.
99 /// Result should be freed with `freeAst` when done.
100 pub fn parse(self: &Parser) !Tree {
101 var stack = self.initUtilityArrayList(State);
102 defer self.deinitUtilityArrayList(stack);
103
104 const root_node = try self.createRoot();
105 // TODO errdefer arena free root node
106
107 try stack.append(State.TopLevel);
108
109 while (true) {
110 //{
111 // const token = self.getNextToken();
112 // warn("{} ", @tagName(token.id));
113 // self.putBackToken(token);
114 // var i: usize = stack.len;
115 // while (i != 0) {
116 // i -= 1;
117 // warn("{} ", @tagName(stack.items[i]));
118 // }
119 // warn("\n");
120 //}
121
122 // This gives us 1 free append that can't fail
123 const state = stack.pop();
124
125 switch (state) {
126 State.TopLevel => {
127 const token = self.getNextToken();
128 switch (token.id) {
129 Token.Id.Keyword_pub, Token.Id.Keyword_export => {
130 stack.append(State { .TopLevelExtern = token }) catch unreachable;
131 continue;
132 },
133 Token.Id.Eof => return Tree {.root_node = root_node},
134 else => {
135 self.putBackToken(token);
136 // TODO shouldn't need this cast
137 stack.append(State { .TopLevelExtern = null }) catch unreachable;
138 continue;
139 },
140 }
141 },
142 State.TopLevelExtern => |visib_token| {
143 const token = self.getNextToken();
144 if (token.id == Token.Id.Keyword_extern) {
145 stack.append(State {
146 .TopLevelDecl = TopLevelDeclCtx {
147 .visib_token = visib_token,
148 .extern_token = token,
149 },
150 }) catch unreachable;
151 continue;
152 }
153 self.putBackToken(token);
154 stack.append(State {
155 .TopLevelDecl = TopLevelDeclCtx {
156 .visib_token = visib_token,
157 .extern_token = null,
158 },
159 }) catch unreachable;
160 continue;
161 },
162 State.TopLevelDecl => |ctx| {
163 const token = self.getNextToken();
164 switch (token.id) {
165 Token.Id.Keyword_var, Token.Id.Keyword_const => {
166 stack.append(State.TopLevel) catch unreachable;
167 // TODO shouldn't need these casts
168 const var_decl_node = try self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
169 token, (?Token)(null), ctx.extern_token);
170 try stack.append(State { .VarDecl = var_decl_node });
171 continue;
172 },
173 Token.Id.Keyword_fn => {
174 stack.append(State.TopLevel) catch unreachable;
175 // TODO shouldn't need these casts
176 const fn_proto = try self.createAttachFnProto(&root_node.decls, token,
177 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
178 try stack.append(State { .FnDef = fn_proto });
179 try stack.append(State { .FnProto = fn_proto });
180 continue;
181 },
182 Token.Id.StringLiteral => {
183 @panic("TODO extern with string literal");
184 },
185 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
186 stack.append(State.TopLevel) catch unreachable;
187 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
188 // TODO shouldn't need this cast
189 const fn_proto = try self.createAttachFnProto(&root_node.decls, fn_token,
190 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
191 try stack.append(State { .FnDef = fn_proto });
192 try stack.append(State { .FnProto = fn_proto });
193 continue;
194 },
195 else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
196 }
197 },
198 State.VarDecl => |var_decl| {
199 var_decl.name_token = try self.eatToken(Token.Id.Identifier);
200 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
201
202 const next_token = self.getNextToken();
203 if (next_token.id == Token.Id.Colon) {
204 try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
205 continue;
206 }
207
208 self.putBackToken(next_token);
209 continue;
210 },
211 State.VarDeclAlign => |var_decl| {
212 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
213
214 const next_token = self.getNextToken();
215 if (next_token.id == Token.Id.Keyword_align) {
216 _ = try self.eatToken(Token.Id.LParen);
217 try stack.append(State { .ExpectToken = Token.Id.RParen });
218 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
219 continue;
220 }
221
222 self.putBackToken(next_token);
223 continue;
224 },
225 State.VarDeclEq => |var_decl| {
226 const token = self.getNextToken();
227 if (token.id == Token.Id.Equal) {
228 var_decl.eq_token = token;
229 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
230 try stack.append(State {
231 .Expression = DestPtr {.NullableField = &var_decl.init_node},
232 });
233 continue;
234 }
235 if (token.id == Token.Id.Semicolon) {
236 continue;
237 }
238 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
239 },
240 State.ExpectToken => |token_id| {
241 _ = try self.eatToken(token_id);
242 continue;
243 },
244
245 State.Expression => |dest_ptr| {
246 // save the dest_ptr for later
247 stack.append(state) catch unreachable;
248 try stack.append(State.ExpectOperand);
249 continue;
250 },
251 State.ExpectOperand => {
252 // we'll either get an operand (like 1 or x),
253 // or a prefix operator (like ~ or return).
254 const token = self.getNextToken();
255 switch (token.id) {
256 Token.Id.Keyword_return => {
257 try stack.append(State { .PrefixOp = try self.createPrefixOp(token,
258 ast.NodePrefixOp.PrefixOp.Return) });
259 try stack.append(State.ExpectOperand);
260 continue;
261 },
262 Token.Id.Ampersand => {
263 const prefix_op = try self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
264 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
265 .align_expr = null,
266 .bit_offset_start_token = null,
267 .bit_offset_end_token = null,
268 .const_token = null,
269 .volatile_token = null,
270 }
271 });
272 try stack.append(State { .PrefixOp = prefix_op });
273 try stack.append(State.ExpectOperand);
274 try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
275 continue;
276 },
277 Token.Id.Identifier => {
278 try stack.append(State {
279 .Operand = &(try self.createIdentifier(token)).base
280 });
281 try stack.append(State.AfterOperand);
282 continue;
283 },
284 Token.Id.IntegerLiteral => {
285 try stack.append(State {
286 .Operand = &(try self.createIntegerLiteral(token)).base
287 });
288 try stack.append(State.AfterOperand);
289 continue;
290 },
291 Token.Id.FloatLiteral => {
292 try stack.append(State {
293 .Operand = &(try self.createFloatLiteral(token)).base
294 });
295 try stack.append(State.AfterOperand);
296 continue;
297 },
298 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
299 }
300 },
301
302 State.AfterOperand => {
303 // we'll either get an infix operator (like != or ^),
304 // or a postfix operator (like () or {}),
305 // otherwise this expression is done (like on a ; or else).
306 var token = self.getNextToken();
307 switch (token.id) {
308 Token.Id.EqualEqual => {
309 try stack.append(State {
310 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
311 });
312 try stack.append(State.ExpectOperand);
313 continue;
314 },
315 Token.Id.BangEqual => {
316 try stack.append(State {
317 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
318 });
319 try stack.append(State.ExpectOperand);
320 continue;
321 },
322 else => {
323 // no postfix/infix operator after this operand.
324 self.putBackToken(token);
325 // reduce the stack
326 var expression: &ast.Node = stack.pop().Operand;
327 while (true) {
328 switch (stack.pop()) {
329 State.Expression => |dest_ptr| {
330 // we're done
331 try dest_ptr.store(expression);
332 break;
333 },
334 State.InfixOp => |infix_op| {
335 infix_op.rhs = expression;
336 infix_op.lhs = stack.pop().Operand;
337 expression = &infix_op.base;
338 continue;
339 },
340 State.PrefixOp => |prefix_op| {
341 prefix_op.rhs = expression;
342 expression = &prefix_op.base;
343 continue;
344 },
345 else => unreachable,
346 }
347 }
348 continue;
349 },
350 }
351 },
352
353 State.AddrOfModifiers => |addr_of_info| {
354 var token = self.getNextToken();
355 switch (token.id) {
356 Token.Id.Keyword_align => {
357 stack.append(state) catch unreachable;
358 if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
359 _ = try self.eatToken(Token.Id.LParen);
360 try stack.append(State { .ExpectToken = Token.Id.RParen });
361 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
362 continue;
363 },
364 Token.Id.Keyword_const => {
365 stack.append(state) catch unreachable;
366 if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
367 addr_of_info.const_token = token;
368 continue;
369 },
370 Token.Id.Keyword_volatile => {
371 stack.append(state) catch unreachable;
372 if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
373 addr_of_info.volatile_token = token;
374 continue;
375 },
376 else => {
377 self.putBackToken(token);
378 continue;
379 },
380 }
381 },
382
383 State.TypeExpr => |dest_ptr| {
384 const token = self.getNextToken();
385 if (token.id == Token.Id.Keyword_var) {
386 @panic("TODO param with type var");
387 }
388 self.putBackToken(token);
389
390 stack.append(State { .Expression = dest_ptr }) catch unreachable;
391 continue;
392 },
393
394 State.FnProto => |fn_proto| {
395 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
396 try stack.append(State { .ParamDecl = fn_proto });
397 try stack.append(State { .ExpectToken = Token.Id.LParen });
398
399 const next_token = self.getNextToken();
400 if (next_token.id == Token.Id.Identifier) {
401 fn_proto.name_token = next_token;
402 continue;
403 }
404 self.putBackToken(next_token);
405 continue;
406 },
407
408 State.FnProtoAlign => |fn_proto| {
409 const token = self.getNextToken();
410 if (token.id == Token.Id.Keyword_align) {
411 @panic("TODO fn proto align");
412 }
413 self.putBackToken(token);
414 stack.append(State {
415 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},
416 }) catch unreachable;
417 continue;
418 },
419
420 State.ParamDecl => |fn_proto| {
421 var token = self.getNextToken();
422 if (token.id == Token.Id.RParen) {
423 continue;
424 }
425 const param_decl = try self.createAttachParamDecl(&fn_proto.params);
426 if (token.id == Token.Id.Keyword_comptime) {
427 param_decl.comptime_token = token;
428 token = self.getNextToken();
429 } else if (token.id == Token.Id.Keyword_noalias) {
430 param_decl.noalias_token = token;
431 token = self.getNextToken();
432 }
433 if (token.id == Token.Id.Identifier) {
434 const next_token = self.getNextToken();
435 if (next_token.id == Token.Id.Colon) {
436 param_decl.name_token = token;
437 token = self.getNextToken();
438 } else {
439 self.putBackToken(next_token);
440 }
441 }
442 if (token.id == Token.Id.Ellipsis3) {
443 param_decl.var_args_token = token;
444 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
445 continue;
446 } else {
447 self.putBackToken(token);
448 }
449
450 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
451 try stack.append(State.ParamDeclComma);
452 try stack.append(State {
453 .TypeExpr = DestPtr {.Field = &param_decl.type_node}
454 });
455 continue;
456 },
457
458 State.ParamDeclComma => {
459 const token = self.getNextToken();
460 switch (token.id) {
461 Token.Id.RParen => {
462 _ = stack.pop(); // pop off the ParamDecl
463 continue;
464 },
465 Token.Id.Comma => continue,
466 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
467 }
468 },
469
470 State.FnDef => |fn_proto| {
471 const token = self.getNextToken();
472 switch(token.id) {
473 Token.Id.LBrace => {
474 const block = try self.createBlock(token);
475 fn_proto.body_node = &block.base;
476 stack.append(State { .Block = block }) catch unreachable;
477 continue;
478 },
479 Token.Id.Semicolon => continue,
480 else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
481 }
482 },
483
484 State.Block => |block| {
485 const token = self.getNextToken();
486 switch (token.id) {
487 Token.Id.RBrace => {
488 block.end_token = token;
489 continue;
490 },
491 else => {
492 self.putBackToken(token);
493 stack.append(State { .Block = block }) catch unreachable;
494 try stack.append(State { .Statement = block });
495 continue;
496 },
497 }
498 },
499
500 State.Statement => |block| {
501 {
502 // Look for comptime var, comptime const
503 const comptime_token = self.getNextToken();
504 if (comptime_token.id == Token.Id.Keyword_comptime) {
505 const mut_token = self.getNextToken();
506 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
507 // TODO shouldn't need these casts
508 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
509 mut_token, (?Token)(comptime_token), (?Token)(null));
510 try stack.append(State { .VarDecl = var_decl });
511 continue;
512 }
513 self.putBackToken(mut_token);
514 }
515 self.putBackToken(comptime_token);
516 }
517 {
518 // Look for const, var
519 const mut_token = self.getNextToken();
520 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
521 // TODO shouldn't need these casts
522 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
523 mut_token, (?Token)(null), (?Token)(null));
524 try stack.append(State { .VarDecl = var_decl });
525 continue;
526 }
527 self.putBackToken(mut_token);
528 }
529
530 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
531 try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
532 continue;
533 },
534
535 // These are data, not control flow.
536 State.InfixOp => unreachable,
537 State.PrefixOp => unreachable,
538 State.Operand => unreachable,
539 }
540 @import("std").debug.panic("{}", @tagName(state));
541 //unreachable;
542 }
543 }
544
545 fn createRoot(self: &Parser) !&ast.NodeRoot {
546 const node = try self.allocator.create(ast.NodeRoot);
547
548 *node = ast.NodeRoot {
549 .base = ast.Node {.id = ast.Node.Id.Root},
550 .decls = ArrayList(&ast.Node).init(self.allocator),
551 };
552 return node;
553 }
554
555 fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
556 extern_token: &const ?Token) !&ast.NodeVarDecl
557 {
558 const node = try self.allocator.create(ast.NodeVarDecl);
559
560 *node = ast.NodeVarDecl {
561 .base = ast.Node {.id = ast.Node.Id.VarDecl},
562 .visib_token = *visib_token,
563 .mut_token = *mut_token,
564 .comptime_token = *comptime_token,
565 .extern_token = *extern_token,
566 .type_node = null,
567 .align_node = null,
568 .init_node = null,
569 .lib_name = null,
570 // initialized later
571 .name_token = undefined,
572 .eq_token = undefined,
573 };
574 return node;
575 }
576
577 fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
578 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
579 {
580 const node = try self.allocator.create(ast.NodeFnProto);
581
582 *node = ast.NodeFnProto {
583 .base = ast.Node {.id = ast.Node.Id.FnProto},
584 .visib_token = *visib_token,
585 .name_token = null,
586 .fn_token = *fn_token,
587 .params = ArrayList(&ast.Node).init(self.allocator),
588 .return_type = undefined,
589 .var_args_token = null,
590 .extern_token = *extern_token,
591 .inline_token = *inline_token,
592 .cc_token = *cc_token,
593 .body_node = null,
594 .lib_name = null,
595 .align_expr = null,
596 };
597 return node;
598 }
599
600 fn createParamDecl(self: &Parser) !&ast.NodeParamDecl {
601 const node = try self.allocator.create(ast.NodeParamDecl);
602
603 *node = ast.NodeParamDecl {
604 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
605 .comptime_token = null,
606 .noalias_token = null,
607 .name_token = null,
608 .type_node = undefined,
609 .var_args_token = null,
610 };
611 return node;
612 }
613
614 fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock {
615 const node = try self.allocator.create(ast.NodeBlock);
616
617 *node = ast.NodeBlock {
618 .base = ast.Node {.id = ast.Node.Id.Block},
619 .begin_token = *begin_token,
620 .end_token = undefined,
621 .statements = ArrayList(&ast.Node).init(self.allocator),
622 };
623 return node;
624 }
625
626 fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
627 const node = try self.allocator.create(ast.NodeInfixOp);
628
629 *node = ast.NodeInfixOp {
630 .base = ast.Node {.id = ast.Node.Id.InfixOp},
631 .op_token = *op_token,
632 .lhs = undefined,
633 .op = *op,
634 .rhs = undefined,
635 };
636 return node;
637 }
638
639 fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
640 const node = try self.allocator.create(ast.NodePrefixOp);
641
642 *node = ast.NodePrefixOp {
643 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
644 .op_token = *op_token,
645 .op = *op,
646 .rhs = undefined,
647 };
648 return node;
649 }
650
651 fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier {
652 const node = try self.allocator.create(ast.NodeIdentifier);
653
654 *node = ast.NodeIdentifier {
655 .base = ast.Node {.id = ast.Node.Id.Identifier},
656 .name_token = *name_token,
657 };
658 return node;
659 }
660
661 fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral {
662 const node = try self.allocator.create(ast.NodeIntegerLiteral);
663
664 *node = ast.NodeIntegerLiteral {
665 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
666 .token = *token,
667 };
668 return node;
669 }
670
671 fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral {
672 const node = try self.allocator.create(ast.NodeFloatLiteral);
673
674 *node = ast.NodeFloatLiteral {
675 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
676 .token = *token,
677 };
678 return node;
679 }
680
681 fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
682 const node = try self.createIdentifier(name_token);
683 try dest_ptr.store(&node.base);
684 return node;
685 }
686
687 fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
688 const node = try self.createParamDecl();
689 try list.append(&node.base);
690 return node;
691 }
692
693 fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
694 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
695 inline_token: &const ?Token) !&ast.NodeFnProto
696 {
697 const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
698 try list.append(&node.base);
699 return node;
700 }
701
702 fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
703 mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
704 {
705 const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
706 try list.append(&node.base);
707 return node;
708 }
709
710 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error {
711 const loc = self.tokenizer.getTokenLocation(token);
712 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
713 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
714 {
715 var i: usize = 0;
716 while (i < loc.column) : (i += 1) {
717 warn(" ");
718 }
719 }
720 {
721 const caret_count = token.end - token.start;
722 var i: usize = 0;
723 while (i < caret_count) : (i += 1) {
724 warn("~");
725 }
726 }
727 warn("\n");
728 return error.ParseError;
729 }
730
731 fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
732 if (token.id != id) {
733 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
734 }
735 }
736
737 fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
738 const token = self.getNextToken();
739 try self.expectToken(token, id);
740 return token;
741 }
742
743 fn putBackToken(self: &Parser, token: &const Token) void {
744 self.put_back_tokens[self.put_back_count] = *token;
745 self.put_back_count += 1;
746 }
747
748 fn getNextToken(self: &Parser) Token {
749 if (self.put_back_count != 0) {
750 const put_back_index = self.put_back_count - 1;
751 const put_back_token = self.put_back_tokens[put_back_index];
752 self.put_back_count = put_back_index;
753 return put_back_token;
754 } else {
755 return self.tokenizer.next();
756 }
757 }
758
759 const RenderAstFrame = struct {
760 node: &ast.Node,
761 indent: usize,
762 };
763
764 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
765 var stack = self.initUtilityArrayList(RenderAstFrame);
766 defer self.deinitUtilityArrayList(stack);
767
768 try stack.append(RenderAstFrame {
769 .node = &root_node.base,
770 .indent = 0,
771 });
772
773 while (stack.popOrNull()) |frame| {
774 {
775 var i: usize = 0;
776 while (i < frame.indent) : (i += 1) {
777 try stream.print(" ");
778 }
779 }
780 try stream.print("{}\n", @tagName(frame.node.id));
781 var child_i: usize = 0;
782 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
783 try stack.append(RenderAstFrame {
784 .node = child,
785 .indent = frame.indent + 2,
786 });
787 }
788 }
789 }
790
791 const RenderState = union(enum) {
792 TopLevelDecl: &ast.Node,
793 FnProtoRParen: &ast.NodeFnProto,
794 ParamDecl: &ast.Node,
795 Text: []const u8,
796 Expression: &ast.Node,
797 VarDecl: &ast.NodeVarDecl,
798 Statement: &ast.Node,
799 PrintIndent,
800 Indent: usize,
801 };
802
803 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
804 var stack = self.initUtilityArrayList(RenderState);
805 defer self.deinitUtilityArrayList(stack);
806
807 {
808 var i = root_node.decls.len;
809 while (i != 0) {
810 i -= 1;
811 const decl = root_node.decls.items[i];
812 try stack.append(RenderState {.TopLevelDecl = decl});
813 }
814 }
815
816 const indent_delta = 4;
817 var indent: usize = 0;
818 while (stack.popOrNull()) |state| {
819 switch (state) {
820 RenderState.TopLevelDecl => |decl| {
821 switch (decl.id) {
822 ast.Node.Id.FnProto => {
823 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
824 if (fn_proto.visib_token) |visib_token| {
825 switch (visib_token.id) {
826 Token.Id.Keyword_pub => try stream.print("pub "),
827 Token.Id.Keyword_export => try stream.print("export "),
828 else => unreachable,
829 }
830 }
831 if (fn_proto.extern_token) |extern_token| {
832 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
833 }
834 try stream.print("fn");
835
836 if (fn_proto.name_token) |name_token| {
837 try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
838 }
839
840 try stream.print("(");
841
842 try stack.append(RenderState { .Text = "\n" });
843 if (fn_proto.body_node == null) {
844 try stack.append(RenderState { .Text = ";" });
845 }
846
847 try stack.append(RenderState { .FnProtoRParen = fn_proto});
848 var i = fn_proto.params.len;
849 while (i != 0) {
850 i -= 1;
851 const param_decl_node = fn_proto.params.items[i];
852 try stack.append(RenderState { .ParamDecl = param_decl_node});
853 if (i != 0) {
854 try stack.append(RenderState { .Text = ", " });
855 }
856 }
857 },
858 ast.Node.Id.VarDecl => {
859 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
860 try stack.append(RenderState { .Text = "\n"});
861 try stack.append(RenderState { .VarDecl = var_decl});
862
863 },
864 else => unreachable,
865 }
866 },
867
868 RenderState.VarDecl => |var_decl| {
869 if (var_decl.visib_token) |visib_token| {
870 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
871 }
872 if (var_decl.extern_token) |extern_token| {
873 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
874 if (var_decl.lib_name != null) {
875 @panic("TODO");
876 }
877 }
878 if (var_decl.comptime_token) |comptime_token| {
879 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
880 }
881 try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
882 try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
883
884 try stack.append(RenderState { .Text = ";" });
885 if (var_decl.init_node) |init_node| {
886 try stack.append(RenderState { .Expression = init_node });
887 try stack.append(RenderState { .Text = " = " });
888 }
889 if (var_decl.align_node) |align_node| {
890 try stack.append(RenderState { .Text = ")" });
891 try stack.append(RenderState { .Expression = align_node });
892 try stack.append(RenderState { .Text = " align(" });
893 }
894 if (var_decl.type_node) |type_node| {
895 try stream.print(": ");
896 try stack.append(RenderState { .Expression = type_node });
897 }
898 },
899
900 RenderState.ParamDecl => |base| {
901 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
902 if (param_decl.comptime_token) |comptime_token| {
903 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
904 }
905 if (param_decl.noalias_token) |noalias_token| {
906 try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
907 }
908 if (param_decl.name_token) |name_token| {
909 try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
910 }
911 if (param_decl.var_args_token) |var_args_token| {
912 try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
913 } else {
914 try stack.append(RenderState { .Expression = param_decl.type_node});
915 }
916 },
917 RenderState.Text => |bytes| {
918 try stream.write(bytes);
919 },
920 RenderState.Expression => |base| switch (base.id) {
921 ast.Node.Id.Identifier => {
922 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
923 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
924 },
925 ast.Node.Id.Block => {
926 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
927 try stream.write("{");
928 try stack.append(RenderState { .Text = "}"});
929 try stack.append(RenderState.PrintIndent);
930 try stack.append(RenderState { .Indent = indent});
931 try stack.append(RenderState { .Text = "\n"});
932 var i = block.statements.len;
933 while (i != 0) {
934 i -= 1;
935 const statement_node = block.statements.items[i];
936 try stack.append(RenderState { .Statement = statement_node});
937 try stack.append(RenderState.PrintIndent);
938 try stack.append(RenderState { .Indent = indent + indent_delta});
939 try stack.append(RenderState { .Text = "\n" });
940 }
941 },
942 ast.Node.Id.InfixOp => {
943 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
944 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
945 switch (prefix_op_node.op) {
946 ast.NodeInfixOp.InfixOp.EqualEqual => {
947 try stack.append(RenderState { .Text = " == "});
948 },
949 ast.NodeInfixOp.InfixOp.BangEqual => {
950 try stack.append(RenderState { .Text = " != "});
951 },
952 else => unreachable,
953 }
954 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
955 },
956 ast.Node.Id.PrefixOp => {
957 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
958 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
959 switch (prefix_op_node.op) {
960 ast.NodePrefixOp.PrefixOp.Return => {
961 try stream.write("return ");
962 },
963 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
964 try stream.write("&");
965 if (addr_of_info.volatile_token != null) {
966 try stack.append(RenderState { .Text = "volatile "});
967 }
968 if (addr_of_info.const_token != null) {
969 try stack.append(RenderState { .Text = "const "});
970 }
971 if (addr_of_info.align_expr) |align_expr| {
972 try stream.print("align(");
973 try stack.append(RenderState { .Text = ") "});
974 try stack.append(RenderState { .Expression = align_expr});
975 }
976 },
977 else => unreachable,
978 }
979 },
980 ast.Node.Id.IntegerLiteral => {
981 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
982 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
983 },
984 ast.Node.Id.FloatLiteral => {
985 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
986 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
987 },
988 else => unreachable,
989 },
990 RenderState.FnProtoRParen => |fn_proto| {
991 try stream.print(")");
992 if (fn_proto.align_expr != null) {
993 @panic("TODO");
994 }
995 try stream.print(" ");
996 if (fn_proto.body_node) |body_node| {
997 try stack.append(RenderState { .Expression = body_node});
998 try stack.append(RenderState { .Text = " "});
999 }
1000 try stack.append(RenderState { .Expression = fn_proto.return_type});
1001 },
1002 RenderState.Statement => |base| {
1003 switch (base.id) {
1004 ast.Node.Id.VarDecl => {
1005 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
1006 try stack.append(RenderState { .VarDecl = var_decl});
1007 },
1008 else => {
1009 try stack.append(RenderState { .Text = ";"});
1010 try stack.append(RenderState { .Expression = base});
1011 },
1012 }
1013 },
1014 RenderState.Indent => |new_indent| indent = new_indent,
1015 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1016 }
1017 }
1018 }
1019
1020 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
1021 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1022 self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1023 const typed_slice = ([]T)(self.utility_bytes);
1024 return ArrayList(T) {
1025 .allocator = self.allocator,
1026 .items = typed_slice,
1027 .len = 0,
1028 };
1029 }
1030
1031 fn deinitUtilityArrayList(self: &Parser, list: var) void {
1032 self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
1033 }
1034
1035};
1036
1037var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1038
1039fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1040 var padded_source: [0x100]u8 = undefined;
1041 std.mem.copy(u8, padded_source[0..source.len], source);
1042 padded_source[source.len + 0] = '\n';
1043 padded_source[source.len + 1] = '\n';
1044 padded_source[source.len + 2] = '\n';
1045
1046 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
1047 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1048 defer parser.deinit();
1049
1050 const tree = try parser.parse();
1051 defer tree.deinit();
1052
1053 var buffer = try std.Buffer.initSize(allocator, 0);
1054 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1055 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1056 return buffer.toOwnedSlice();
1057}
1058
1059// TODO test for memory leaks
1060// TODO test for valid frees
1061fn testCanonical(source: []const u8) !void {
1062 const needed_alloc_count = x: {
1063 // Try it once with unlimited memory, make sure it works
1064 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1065 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1066 const result_source = try testParse(source, &failing_allocator.allocator);
1067 if (!mem.eql(u8, result_source, source)) {
1068 warn("\n====== expected this output: =========\n");
1069 warn("{}", source);
1070 warn("\n======== instead found this: =========\n");
1071 warn("{}", result_source);
1072 warn("\n======================================\n");
1073 return error.TestFailed;
1074 }
1075 failing_allocator.allocator.free(result_source);
1076 break :x failing_allocator.index;
1077 };
1078
1079 var fail_index: usize = 0;
1080 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1081 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1082 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1083 if (testParse(source, &failing_allocator.allocator)) |_| {
1084 return error.NondeterministicMemoryUsage;
1085 } else |err| {
1086 assert(err == error.OutOfMemory);
1087 // TODO make this pass
1088 //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1089 // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1090 // fail_index, needed_alloc_count,
1091 // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1092 // failing_allocator.index, failing_allocator.deallocations);
1093 // return error.MemoryLeakDetected;
1094 //}
1095 }
1096 }
1097}
1098
1099test "zig fmt" {
1100 try testCanonical(
1101 \\extern fn puts(s: &const u8) c_int;
1102 \\
1103 );
1104
1105 try testCanonical(
1106 \\const a = b;
1107 \\pub const a = b;
1108 \\var a = b;
1109 \\pub var a = b;
1110 \\const a: i32 = b;
1111 \\pub const a: i32 = b;
1112 \\var a: i32 = b;
1113 \\pub var a: i32 = b;
1114 \\
1115 );
1116
1117 try testCanonical(
1118 \\extern var foo: c_int;
1119 \\
1120 );
1121
1122 try testCanonical(
1123 \\var foo: c_int align(1);
1124 \\
1125 );
1126
1127 try testCanonical(
1128 \\fn main(argc: c_int, argv: &&u8) c_int {
1129 \\ const a = b;
1130 \\}
1131 \\
1132 );
1133
1134 try testCanonical(
1135 \\fn foo(argc: c_int, argv: &&u8) c_int {
1136 \\ return 0;
1137 \\}
1138 \\
1139 );
1140
1141 try testCanonical(
1142 \\extern fn f1(s: &align(&u8) u8) c_int;
1143 \\
1144 );
1145
1146 try testCanonical(
1147 \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
1148 \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
1149 \\extern fn f3(s: &align(1) const volatile u8) c_int;
1150 \\
1151 );
1152
1153 try testCanonical(
1154 \\fn f1(a: bool, b: bool) bool {
1155 \\ a != b;
1156 \\ return a == b;
1157 \\}
1158 \\
1159 );
1160}
std/zig/tokenizer.zig created+659
...@@ -0,0 +1,659 @@
1const std = @import("../index.zig");
2const mem = std.mem;
3
4pub const Token = struct {
5 id: Id,
6 start: usize,
7 end: usize,
8
9 const KeywordId = struct {
10 bytes: []const u8,
11 id: Id,
12 };
13
14 const keywords = []KeywordId {
15 KeywordId{.bytes="align", .id = Id.Keyword_align},
16 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
20 KeywordId{.bytes="const", .id = Id.Keyword_const},
21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
22 KeywordId{.bytes="defer", .id = Id.Keyword_defer},
23 KeywordId{.bytes="else", .id = Id.Keyword_else},
24 KeywordId{.bytes="enum", .id = Id.Keyword_enum},
25 KeywordId{.bytes="error", .id = Id.Keyword_error},
26 KeywordId{.bytes="export", .id = Id.Keyword_export},
27 KeywordId{.bytes="extern", .id = Id.Keyword_extern},
28 KeywordId{.bytes="false", .id = Id.Keyword_false},
29 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
30 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
32 KeywordId{.bytes="if", .id = Id.Keyword_if},
33 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
34 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
35 KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
36 KeywordId{.bytes="null", .id = Id.Keyword_null},
37 KeywordId{.bytes="or", .id = Id.Keyword_or},
38 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
39 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
40 KeywordId{.bytes="return", .id = Id.Keyword_return},
41 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
42 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
43 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
44 KeywordId{.bytes="test", .id = Id.Keyword_test},
45 KeywordId{.bytes="this", .id = Id.Keyword_this},
46 KeywordId{.bytes="true", .id = Id.Keyword_true},
47 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
48 KeywordId{.bytes="union", .id = Id.Keyword_union},
49 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
50 KeywordId{.bytes="use", .id = Id.Keyword_use},
51 KeywordId{.bytes="var", .id = Id.Keyword_var},
52 KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
53 KeywordId{.bytes="while", .id = Id.Keyword_while},
54 };
55
56 fn getKeyword(bytes: []const u8) ?Id {
57 for (keywords) |kw| {
58 if (mem.eql(u8, kw.bytes, bytes)) {
59 return kw.id;
60 }
61 }
62 return null;
63 }
64
65 const StrLitKind = enum {Normal, C};
66
67 pub const Id = union(enum) {
68 Invalid,
69 Identifier,
70 StringLiteral: StrLitKind,
71 Eof,
72 Builtin,
73 Bang,
74 Equal,
75 EqualEqual,
76 BangEqual,
77 LParen,
78 RParen,
79 Semicolon,
80 Percent,
81 LBrace,
82 RBrace,
83 Period,
84 Ellipsis2,
85 Ellipsis3,
86 Minus,
87 Arrow,
88 Colon,
89 Slash,
90 Comma,
91 Ampersand,
92 AmpersandEqual,
93 IntegerLiteral,
94 FloatLiteral,
95 Keyword_align,
96 Keyword_and,
97 Keyword_asm,
98 Keyword_break,
99 Keyword_comptime,
100 Keyword_const,
101 Keyword_continue,
102 Keyword_defer,
103 Keyword_else,
104 Keyword_enum,
105 Keyword_error,
106 Keyword_export,
107 Keyword_extern,
108 Keyword_false,
109 Keyword_fn,
110 Keyword_for,
111 Keyword_goto,
112 Keyword_if,
113 Keyword_inline,
114 Keyword_nakedcc,
115 Keyword_noalias,
116 Keyword_null,
117 Keyword_or,
118 Keyword_packed,
119 Keyword_pub,
120 Keyword_return,
121 Keyword_stdcallcc,
122 Keyword_struct,
123 Keyword_switch,
124 Keyword_test,
125 Keyword_this,
126 Keyword_true,
127 Keyword_undefined,
128 Keyword_union,
129 Keyword_unreachable,
130 Keyword_use,
131 Keyword_var,
132 Keyword_volatile,
133 Keyword_while,
134 };
135};
136
137pub const Tokenizer = struct {
138 buffer: []const u8,
139 index: usize,
140 pending_invalid_token: ?Token,
141
142 pub const Location = struct {
143 line: usize,
144 column: usize,
145 line_start: usize,
146 line_end: usize,
147 };
148
149 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
150 var loc = Location {
151 .line = 0,
152 .column = 0,
153 .line_start = 0,
154 .line_end = 0,
155 };
156 for (self.buffer) |c, i| {
157 if (i == token.start) {
158 loc.line_end = i;
159 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
160 return loc;
161 }
162 if (c == '\n') {
163 loc.line += 1;
164 loc.column = 0;
165 loc.line_start = i + 1;
166 } else {
167 loc.column += 1;
168 }
169 }
170 return loc;
171 }
172
173 /// For debugging purposes
174 pub fn dump(self: &Tokenizer, token: &const Token) void {
175 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
176 }
177
178 /// buffer must end with "\n\n\n". This is so that attempting to decode
179 /// a the 3 trailing bytes of a 4-byte utf8 sequence is never a buffer overflow.
180 pub fn init(buffer: []const u8) Tokenizer {
181 std.debug.assert(buffer[buffer.len - 1] == '\n');
182 std.debug.assert(buffer[buffer.len - 2] == '\n');
183 std.debug.assert(buffer[buffer.len - 3] == '\n');
184 return Tokenizer {
185 .buffer = buffer,
186 .index = 0,
187 .pending_invalid_token = null,
188 };
189 }
190
191 const State = enum {
192 Start,
193 Identifier,
194 Builtin,
195 C,
196 StringLiteral,
197 StringLiteralBackslash,
198 Equal,
199 Bang,
200 Minus,
201 Slash,
202 LineComment,
203 Zero,
204 IntegerLiteral,
205 IntegerLiteralWithRadix,
206 NumberDot,
207 FloatFraction,
208 FloatExponentUnsigned,
209 FloatExponentNumber,
210 Ampersand,
211 Period,
212 Period2,
213 };
214
215 pub fn next(self: &Tokenizer) Token {
216 if (self.pending_invalid_token) |token| {
217 self.pending_invalid_token = null;
218 return token;
219 }
220 var state = State.Start;
221 var result = Token {
222 .id = Token.Id.Eof,
223 .start = self.index,
224 .end = undefined,
225 };
226 while (self.index < self.buffer.len) : (self.index += 1) {
227 const c = self.buffer[self.index];
228 switch (state) {
229 State.Start => switch (c) {
230 ' ', '\n' => {
231 result.start = self.index + 1;
232 },
233 'c' => {
234 state = State.C;
235 result.id = Token.Id.Identifier;
236 },
237 '"' => {
238 state = State.StringLiteral;
239 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
240 },
241 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
242 state = State.Identifier;
243 result.id = Token.Id.Identifier;
244 },
245 '@' => {
246 state = State.Builtin;
247 result.id = Token.Id.Builtin;
248 },
249 '=' => {
250 state = State.Equal;
251 },
252 '!' => {
253 state = State.Bang;
254 },
255 '(' => {
256 result.id = Token.Id.LParen;
257 self.index += 1;
258 break;
259 },
260 ')' => {
261 result.id = Token.Id.RParen;
262 self.index += 1;
263 break;
264 },
265 ';' => {
266 result.id = Token.Id.Semicolon;
267 self.index += 1;
268 break;
269 },
270 ',' => {
271 result.id = Token.Id.Comma;
272 self.index += 1;
273 break;
274 },
275 ':' => {
276 result.id = Token.Id.Colon;
277 self.index += 1;
278 break;
279 },
280 '%' => {
281 result.id = Token.Id.Percent;
282 self.index += 1;
283 break;
284 },
285 '{' => {
286 result.id = Token.Id.LBrace;
287 self.index += 1;
288 break;
289 },
290 '}' => {
291 result.id = Token.Id.RBrace;
292 self.index += 1;
293 break;
294 },
295 '.' => {
296 state = State.Period;
297 },
298 '-' => {
299 state = State.Minus;
300 },
301 '/' => {
302 state = State.Slash;
303 },
304 '&' => {
305 state = State.Ampersand;
306 },
307 '0' => {
308 state = State.Zero;
309 result.id = Token.Id.IntegerLiteral;
310 },
311 '1'...'9' => {
312 state = State.IntegerLiteral;
313 result.id = Token.Id.IntegerLiteral;
314 },
315 else => {
316 result.id = Token.Id.Invalid;
317 self.index += 1;
318 break;
319 },
320 },
321 State.Ampersand => switch (c) {
322 '=' => {
323 result.id = Token.Id.AmpersandEqual;
324 self.index += 1;
325 break;
326 },
327 else => {
328 result.id = Token.Id.Ampersand;
329 break;
330 },
331 },
332 State.Identifier => switch (c) {
333 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
334 else => {
335 if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
336 result.id = id;
337 }
338 break;
339 },
340 },
341 State.Builtin => switch (c) {
342 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
343 else => break,
344 },
345 State.C => switch (c) {
346 '\\' => @panic("TODO"),
347 '"' => {
348 state = State.StringLiteral;
349 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
350 },
351 'a'...'z', 'A'...'Z', '_', '0'...'9' => {
352 state = State.Identifier;
353 },
354 else => break,
355 },
356 State.StringLiteral => switch (c) {
357 '\\' => {
358 state = State.StringLiteralBackslash;
359 },
360 '"' => {
361 self.index += 1;
362 break;
363 },
364 '\n' => break, // Look for this error later.
365 else => self.checkLiteralCharacter(),
366 },
367
368 State.StringLiteralBackslash => switch (c) {
369 '\n' => break, // Look for this error later.
370 else => {
371 state = State.StringLiteral;
372 },
373 },
374
375 State.Bang => switch (c) {
376 '=' => {
377 result.id = Token.Id.BangEqual;
378 self.index += 1;
379 break;
380 },
381 else => {
382 result.id = Token.Id.Bang;
383 break;
384 },
385 },
386
387 State.Equal => switch (c) {
388 '=' => {
389 result.id = Token.Id.EqualEqual;
390 self.index += 1;
391 break;
392 },
393 else => {
394 result.id = Token.Id.Equal;
395 break;
396 },
397 },
398
399 State.Minus => switch (c) {
400 '>' => {
401 result.id = Token.Id.Arrow;
402 self.index += 1;
403 break;
404 },
405 else => {
406 result.id = Token.Id.Minus;
407 break;
408 },
409 },
410
411 State.Period => switch (c) {
412 '.' => {
413 state = State.Period2;
414 },
415 else => {
416 result.id = Token.Id.Period;
417 break;
418 },
419 },
420
421 State.Period2 => switch (c) {
422 '.' => {
423 result.id = Token.Id.Ellipsis3;
424 self.index += 1;
425 break;
426 },
427 else => {
428 result.id = Token.Id.Ellipsis2;
429 break;
430 },
431 },
432
433 State.Slash => switch (c) {
434 '/' => {
435 result.id = undefined;
436 state = State.LineComment;
437 },
438 else => {
439 result.id = Token.Id.Slash;
440 break;
441 },
442 },
443 State.LineComment => switch (c) {
444 '\n' => {
445 state = State.Start;
446 result = Token {
447 .id = Token.Id.Eof,
448 .start = self.index + 1,
449 .end = undefined,
450 };
451 },
452 else => self.checkLiteralCharacter(),
453 },
454 State.Zero => switch (c) {
455 'b', 'o', 'x' => {
456 state = State.IntegerLiteralWithRadix;
457 },
458 else => {
459 // reinterpret as a normal number
460 self.index -= 1;
461 state = State.IntegerLiteral;
462 },
463 },
464 State.IntegerLiteral => switch (c) {
465 '.' => {
466 state = State.NumberDot;
467 },
468 'p', 'P', 'e', 'E' => {
469 state = State.FloatExponentUnsigned;
470 },
471 '0'...'9' => {},
472 else => break,
473 },
474 State.IntegerLiteralWithRadix => switch (c) {
475 '.' => {
476 state = State.NumberDot;
477 },
478 'p', 'P' => {
479 state = State.FloatExponentUnsigned;
480 },
481 '0'...'9', 'a'...'f', 'A'...'F' => {},
482 else => break,
483 },
484 State.NumberDot => switch (c) {
485 '.' => {
486 self.index -= 1;
487 state = State.Start;
488 break;
489 },
490 else => {
491 self.index -= 1;
492 result.id = Token.Id.FloatLiteral;
493 state = State.FloatFraction;
494 },
495 },
496 State.FloatFraction => switch (c) {
497 'p', 'P' => {
498 state = State.FloatExponentUnsigned;
499 },
500 '0'...'9', 'a'...'f', 'A'...'F' => {},
501 else => break,
502 },
503 State.FloatExponentUnsigned => switch (c) {
504 '+', '-' => {
505 state = State.FloatExponentNumber;
506 },
507 else => {
508 // reinterpret as a normal exponent number
509 self.index -= 1;
510 state = State.FloatExponentNumber;
511 }
512 },
513 State.FloatExponentNumber => switch (c) {
514 '0'...'9', 'a'...'f', 'A'...'F' => {},
515 else => break,
516 },
517 }
518 }
519 result.end = self.index;
520
521 if (result.id == Token.Id.Eof) {
522 if (self.pending_invalid_token) |token| {
523 self.pending_invalid_token = null;
524 return token;
525 }
526 }
527
528 return result;
529 }
530
531 pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
532 return self.buffer[token.start..token.end];
533 }
534
535 fn checkLiteralCharacter(self: &Tokenizer) void {
536 if (self.pending_invalid_token != null) return;
537 const invalid_length = self.getInvalidCharacterLength();
538 if (invalid_length == 0) return;
539 self.pending_invalid_token = Token {
540 .id = Token.Id.Invalid,
541 .start = self.index,
542 .end = self.index + invalid_length,
543 };
544 }
545
546 fn getInvalidCharacterLength(self: &Tokenizer) u3 {
547 const c0 = self.buffer[self.index];
548 if (c0 < 0x80) {
549 if (c0 < 0x20 or c0 == 0x7f) {
550 // ascii control codes are never allowed
551 // (note that \n was checked before we got here)
552 return 1;
553 }
554 // looks fine to me.
555 return 0;
556 } else {
557 // check utf8-encoded character.
558 const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1;
559 // the last 3 bytes in the buffer are guaranteed to be '\n',
560 // which means we don't need to do any bounds checking here.
561 const bytes = self.buffer[self.index..self.index + length];
562 switch (length) {
563 2 => {
564 const value = std.unicode.utf8Decode2(bytes) catch return length;
565 if (value == 0x85) return length; // U+0085 (NEL)
566 },
567 3 => {
568 const value = std.unicode.utf8Decode3(bytes) catch return length;
569 if (value == 0x2028) return length; // U+2028 (LS)
570 if (value == 0x2029) return length; // U+2029 (PS)
571 },
572 4 => {
573 _ = std.unicode.utf8Decode4(bytes) catch return length;
574 },
575 else => unreachable,
576 }
577 self.index += length - 1;
578 return 0;
579 }
580 }
581};
582
583
584
585test "tokenizer" {
586 testTokenize("test", []Token.Id {
587 Token.Id.Keyword_test,
588 });
589}
590
591test "tokenizer - invalid token characters" {
592 testTokenize("#", []Token.Id{Token.Id.Invalid});
593 testTokenize("`", []Token.Id{Token.Id.Invalid});
594}
595
596test "tokenizer - invalid literal/comment characters" {
597 testTokenize("\"\x00\"", []Token.Id {
598 Token.Id { .StringLiteral = Token.StrLitKind.Normal },
599 Token.Id.Invalid,
600 });
601 testTokenize("//\x00", []Token.Id {
602 Token.Id.Invalid,
603 });
604 testTokenize("//\x1f", []Token.Id {
605 Token.Id.Invalid,
606 });
607 testTokenize("//\x7f", []Token.Id {
608 Token.Id.Invalid,
609 });
610}
611
612test "tokenizer - utf8" {
613 testTokenize("//\xc2\x80", []Token.Id{});
614 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
615}
616
617test "tokenizer - invalid utf8" {
618 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
619 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
620 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
621 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
622 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
623 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
624 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
625 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
626}
627
628test "tokenizer - illegal unicode codepoints" {
629 // unicode newline characters.U+0085, U+2028, U+2029
630 testTokenize("//\xc2\x84", []Token.Id{});
631 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
632 testTokenize("//\xc2\x86", []Token.Id{});
633 testTokenize("//\xe2\x80\xa7", []Token.Id{});
634 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
635 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
636 testTokenize("//\xe2\x80\xaa", []Token.Id{});
637}
638
639fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
640 // (test authors, just make this bigger if you need it)
641 var padded_source: [0x100]u8 = undefined;
642 std.mem.copy(u8, padded_source[0..source.len], source);
643 padded_source[source.len + 0] = '\n';
644 padded_source[source.len + 1] = '\n';
645 padded_source[source.len + 2] = '\n';
646
647 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
648 for (expected_tokens) |expected_token_id| {
649 const token = tokenizer.next();
650 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
651 switch (expected_token_id) {
652 Token.Id.StringLiteral => |expected_kind| {
653 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
654 },
655 else => {},
656 }
657 }
658 std.debug.assert(tokenizer.next().id == Token.Id.Eof);
659}