authorgravatar for michael.dusan@gmail.comMichael Dusan <michael.dusan@gmail.com> 2019-05-27 19:47:10-04:00
committergravatar for michael.dusan@gmail.comMichael Dusan <michael.dusan@gmail.com> 2019-05-27 19:47:10-04:00
logd4b241c14e7e9eb8f0c5fcb767c6021e8651c93f
tree787f361be1fe37035b6df0fa451cf3c165b98a20
parentf68d8060ec2c65e3062007348c0e331ffbe86f37
signaturelock-open Commit is signed but in an unrecognized format.

new .d file parser for C compilation

- wip for #2046 - clang .d output must be created with `clang -MV` switch - implemented in Zig - hybridized for zig stage0 and stage1 - zig test src-self-hosted/dep_tokenizer.zig

6 files changed, 1231 insertions(+), 55 deletions(-)

CMakeLists.txt+1
......@@ -6726,6 +6726,7 @@ add_custom_command(
67266726 "-Doutput-dir=${CMAKE_BINARY_DIR}"
67276727 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
67286728 DEPENDS
6729 "${CMAKE_SOURCE_DIR}/src-self-hosted/dep_tokenizer.zig"
67296730 "${CMAKE_SOURCE_DIR}/src-self-hosted/stage1.zig"
67306731 "${CMAKE_SOURCE_DIR}/src-self-hosted/translate_c.zig"
67316732 "${CMAKE_SOURCE_DIR}/build.zig"
src-self-hosted/dep_tokenizer.zig created+1131
......@@ -0,0 +1,1131 @@
1const std = @import("std");
2const testing = std.testing;
3
4pub const Tokenizer = struct {
5 arena: std.heap.ArenaAllocator,
6 index: usize,
7 bytes: []const u8,
8 error_text: []const u8,
9 state: State,
10
11 pub fn init(allocator: *std.mem.Allocator, bytes: []const u8) Tokenizer {
12 return Tokenizer{
13 .arena = std.heap.ArenaAllocator.init(allocator),
14 .index = 0,
15 .bytes = bytes,
16 .error_text = "",
17 .state = State{ .lhs = {} },
18 };
19 }
20
21 pub fn deinit(self: *Tokenizer) void {
22 self.arena.deinit();
23 }
24
25 pub fn next(self: *Tokenizer) Error!?Token {
26 while (self.index < self.bytes.len) {
27 const char = self.bytes[self.index];
28 while (true) {
29 switch (self.state) {
30 .lhs => switch (char) {
31 '\t', '\n', '\r', ' ' => {
32 // silently ignore whitespace
33 break; // advance
34 },
35 else => {
36 self.state = State{ .target = try std.Buffer.initSize(&self.arena.allocator, 0) };
37 },
38 },
39 .target => |*target| switch (char) {
40 '\t', '\n', '\r', ' ' => {
41 return self.errorIllegalChar(self.index, char, "invalid target");
42 },
43 '$' => {
44 self.state = State{ .target_dollar_sign = target.* };
45 break; // advance
46 },
47 '\\' => {
48 self.state = State{ .target_reverse_solidus = target.* };
49 break; // advance
50 },
51 ':' => {
52 self.state = State{ .target_colon = target.* };
53 break; // advance
54 },
55 else => {
56 try target.appendByte(char);
57 break; // advance
58 },
59 },
60 .target_reverse_solidus => |*target| switch (char) {
61 '\t', '\n', '\r' => {
62 return self.errorIllegalChar(self.index, char, "bad target escape");
63 },
64 ' ', '#', '\\' => {
65 try target.appendByte(char);
66 self.state = State{ .target = target.* };
67 break; // advance
68 },
69 '$' => {
70 try target.append(self.bytes[self.index - 1 .. self.index]);
71 self.state = State{ .target_dollar_sign = target.* };
72 break; // advance
73 },
74 else => {
75 try target.append(self.bytes[self.index - 1 .. self.index + 1]);
76 self.state = State{ .target = target.* };
77 break; // advance
78 },
79 },
80 .target_dollar_sign => |*target| switch (char) {
81 '$' => {
82 try target.appendByte(char);
83 self.state = State{ .target = target.* };
84 break; // advance
85 },
86 else => {
87 return self.errorIllegalChar(self.index, char, "expecting '$'");
88 },
89 },
90 .target_colon => |*target| switch (char) {
91 '\n', '\r' => {
92 const bytes = target.toSlice();
93 if (bytes.len != 0) {
94 self.state = State{ .lhs = {} };
95 return Token{ .id = .target, .bytes = bytes };
96 }
97 // silently ignore null target
98 self.state = State{ .lhs = {} };
99 continue;
100 },
101 '\\' => {
102 self.state = State{ .target_colon_reverse_solidus = target.* };
103 break; // advance
104 },
105 else => {
106 const bytes = target.toSlice();
107 if (bytes.len != 0) {
108 self.state = State{ .rhs = {} };
109 return Token{ .id = .target, .bytes = bytes };
110 }
111 // silently ignore null target
112 self.state = State{ .lhs = {} };
113 continue;
114 },
115 },
116 .target_colon_reverse_solidus => |*target| switch (char) {
117 '\n', '\r' => {
118 const bytes = target.toSlice();
119 if (bytes.len != 0) {
120 self.state = State{ .lhs = {} };
121 return Token{ .id = .target, .bytes = bytes };
122 }
123 // silently ignore null target
124 self.state = State{ .lhs = {} };
125 continue;
126 },
127 else => {
128 try target.append(self.bytes[self.index - 2 .. self.index + 1]);
129 self.state = State{ .target = target.* };
130 break;
131 },
132 },
133 .rhs => switch (char) {
134 '\t', ' ' => {
135 // silently ignore horizontal whitespace
136 break; // advance
137 },
138 '\n', '\r' => {
139 self.state = State{ .lhs = {} };
140 continue;
141 },
142 '\\' => {
143 self.state = State{ .rhs_continuation = {} };
144 break; // advance
145 },
146 '"' => {
147 self.state = State{ .prereq_quote = try std.Buffer.initSize(&self.arena.allocator, 0) };
148 break; // advance
149 },
150 else => {
151 self.state = State{ .prereq = try std.Buffer.initSize(&self.arena.allocator, 0) };
152 },
153 },
154 .rhs_continuation => switch (char) {
155 '\n' => {
156 self.state = State{ .rhs = {} };
157 break; // advance
158 },
159 '\r' => {
160 self.state = State{ .rhs_continuation_linefeed = {} };
161 break; // advance
162 },
163 else => {
164 return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line");
165 },
166 },
167 .rhs_continuation_linefeed => switch (char) {
168 '\n' => {
169 self.state = State{ .rhs = {} };
170 break; // advance
171 },
172 else => {
173 return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line");
174 },
175 },
176 .prereq_quote => |*prereq| switch (char) {
177 '"' => {
178 const bytes = prereq.toSlice();
179 self.index += 1;
180 self.state = State{ .rhs = {} };
181 return Token{ .id = .prereq, .bytes = bytes };
182 },
183 else => {
184 try prereq.appendByte(char);
185 break; // advance
186 },
187 },
188 .prereq => |*prereq| switch (char) {
189 '\t', ' ' => {
190 const bytes = prereq.toSlice();
191 self.state = State{ .rhs = {} };
192 return Token{ .id = .prereq, .bytes = bytes };
193 },
194 '\n', '\r' => {
195 const bytes = prereq.toSlice();
196 self.state = State{ .lhs = {} };
197 return Token{ .id = .prereq, .bytes = bytes };
198 },
199 '\\' => {
200 self.state = State{ .prereq_continuation = prereq.* };
201 break; // advance
202 },
203 else => {
204 try prereq.appendByte(char);
205 break; // advance
206 },
207 },
208 .prereq_continuation => |*prereq| switch (char) {
209 '\n' => {
210 const bytes = prereq.toSlice();
211 self.index += 1;
212 self.state = State{ .rhs = {} };
213 return Token{ .id = .prereq, .bytes = bytes };
214 },
215 '\r' => {
216 self.state = State{ .prereq_continuation_linefeed = prereq.* };
217 break; // advance
218 },
219 else => {
220 // not continuation
221 try prereq.append(self.bytes[self.index - 1 .. self.index + 1]);
222 self.state = State{ .prereq = prereq.* };
223 break; // advance
224 },
225 },
226 .prereq_continuation_linefeed => |prereq| switch (char) {
227 '\n' => {
228 const bytes = prereq.toSlice();
229 self.index += 1;
230 self.state = State{ .rhs = {} };
231 return Token{ .id = .prereq, .bytes = bytes };
232 },
233 else => {
234 return self.errorIllegalChar(self.index, char, "continuation expecting end-of-line");
235 },
236 },
237 }
238 }
239 self.index += 1;
240 }
241
242 // eof, handle maybe incomplete token
243 if (self.index == 0) return null;
244 const idx = self.index - 1;
245 switch (self.state) {
246 .lhs,
247 .rhs,
248 .rhs_continuation,
249 .rhs_continuation_linefeed,
250 => {},
251 .target => |target| {
252 return self.errorPosition(idx, target.toSlice(), "incomplete target");
253 },
254 .target_reverse_solidus,
255 .target_dollar_sign,
256 => {
257 const index = self.index - 1;
258 return self.errorIllegalChar(idx, self.bytes[idx], "incomplete escape");
259 },
260 .target_colon => |target| {
261 const bytes = target.toSlice();
262 if (bytes.len != 0) {
263 self.index += 1;
264 self.state = State{ .rhs = {} };
265 return Token{ .id = .target, .bytes = bytes };
266 }
267 // silently ignore null target
268 self.state = State{ .lhs = {} };
269 },
270 .target_colon_reverse_solidus => |target| {
271 const bytes = target.toSlice();
272 if (bytes.len != 0) {
273 self.index += 1;
274 self.state = State{ .rhs = {} };
275 return Token{ .id = .target, .bytes = bytes };
276 }
277 // silently ignore null target
278 self.state = State{ .lhs = {} };
279 },
280 .prereq_quote => |prereq| {
281 return self.errorPosition(idx, prereq.toSlice(), "incomplete quoted prerequisite");
282 },
283 .prereq => |prereq| {
284 const bytes = prereq.toSlice();
285 self.state = State{ .lhs = {} };
286 return Token{ .id = .prereq, .bytes = bytes };
287 },
288 .prereq_continuation => |prereq| {
289 const bytes = prereq.toSlice();
290 self.state = State{ .lhs = {} };
291 return Token{ .id = .prereq, .bytes = bytes };
292 },
293 .prereq_continuation_linefeed => |prereq| {
294 const bytes = prereq.toSlice();
295 self.state = State{ .lhs = {} };
296 return Token{ .id = .prereq, .bytes = bytes };
297 },
298 }
299 return null;
300 }
301
302 fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: ...) Error {
303 self.error_text = (try std.Buffer.allocPrint(&self.arena.allocator, fmt, args)).toSlice();
304 return Error.InvalidInput;
305 }
306
307 fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: ...) Error {
308 var buffer = try std.Buffer.initSize(&self.arena.allocator, 0);
309 std.fmt.format(&buffer, anyerror, std.Buffer.append, fmt, args) catch {};
310 try buffer.append(" '");
311 var out = makeOutput(std.Buffer.append, &buffer);
312 try printCharValues(&out, bytes);
313 try buffer.append("'");
314 std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", position - (bytes.len - 1)) catch {};
315 self.error_text = buffer.toSlice();
316 return Error.InvalidInput;
317 }
318
319 fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: ...) Error {
320 var buffer = try std.Buffer.initSize(&self.arena.allocator, 0);
321 try buffer.append("illegal char ");
322 var out = makeOutput(std.Buffer.append, &buffer);
323 try printUnderstandableChar(&out, char);
324 std.fmt.format(&buffer, anyerror, std.Buffer.append, " at position {}", position) catch {};
325 if (fmt.len != 0) std.fmt.format(&buffer, anyerror, std.Buffer.append, ": " ++ fmt, args) catch {};
326 self.error_text = buffer.toSlice();
327 return Error.InvalidInput;
328 }
329
330 const Error = error{
331 OutOfMemory,
332 InvalidInput,
333 };
334
335 const State = union(enum) {
336 lhs: void,
337 target: std.Buffer,
338 target_reverse_solidus: std.Buffer,
339 target_dollar_sign: std.Buffer,
340 target_colon: std.Buffer,
341 target_colon_reverse_solidus: std.Buffer,
342 rhs: void,
343 rhs_continuation: void,
344 rhs_continuation_linefeed: void,
345 prereq_quote: std.Buffer,
346 prereq: std.Buffer,
347 prereq_continuation: std.Buffer,
348 prereq_continuation_linefeed: std.Buffer,
349 };
350
351 const Token = struct {
352 id: ID,
353 bytes: []const u8,
354
355 const ID = enum {
356 target,
357 prereq,
358 };
359 };
360};
361
362// stage1 compiler support
363var stage2_da = std.heap.DirectAllocator.init();
364
365export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer {
366 const t = stage2_da.allocator.create(Tokenizer) catch unreachable;
367 t.* = Tokenizer.init(&stage2_da.allocator, input[0..len]);
368 return stage2_DepTokenizer{
369 .handle = t,
370 };
371}
372
373export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void {
374 self.handle.deinit();
375}
376
377export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult {
378 const otoken = self.handle.next() catch {
379 return stage2_DepNextResult{
380 .ent = 0,
381 .textz = (std.Buffer.init(&self.handle.arena.allocator, self.handle.error_text) catch unreachable).toSlice().ptr,
382 };
383 };
384 const token = otoken orelse {
385 return stage2_DepNextResult{
386 .ent = 1,
387 .textz = undefined,
388 };
389 };
390 return stage2_DepNextResult{
391 .ent = @enumToInt(token.id) + u8(2),
392 .textz = (std.Buffer.init(&self.handle.arena.allocator, token.bytes) catch unreachable).toSlice().ptr,
393 };
394}
395
396export const stage2_DepTokenizer = extern struct {
397 handle: *Tokenizer,
398};
399
400export const stage2_DepNextResult = extern struct {
401 // 0=error, 1=null, 2=token=target, 3=token=prereq
402 ent: u8,
403 // ent=0 -- error text
404 // ent=1 -- NEVER
405 // ent=2 -- token text value
406 // ent=3 -- token text value
407 textz: [*]const u8,
408};
409
410test "empty file" {
411 try depTokenizer("", "");
412}
413
414test "empty whitespace" {
415 try depTokenizer("\n", "");
416 try depTokenizer("\r", "");
417 try depTokenizer("\r\n", "");
418 try depTokenizer(" ", "");
419}
420
421test "empty colon" {
422 try depTokenizer(":", "");
423 try depTokenizer("\n:", "");
424 try depTokenizer("\r:", "");
425 try depTokenizer("\r\n:", "");
426 try depTokenizer(" :", "");
427}
428
429test "empty target" {
430 try depTokenizer("foo.o:", "target = {foo.o}");
431 try depTokenizer(
432 \\foo.o:
433 \\bar.o:
434 \\abcd.o:
435 ,
436 \\target = {foo.o}
437 \\target = {bar.o}
438 \\target = {abcd.o}
439 );
440}
441
442test "whitespace empty target" {
443 try depTokenizer("\nfoo.o:", "target = {foo.o}");
444 try depTokenizer("\rfoo.o:", "target = {foo.o}");
445 try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
446 try depTokenizer(" foo.o:", "target = {foo.o}");
447}
448
449test "escape empty target" {
450 try depTokenizer("\\ foo.o:", "target = { foo.o}");
451 try depTokenizer("\\#foo.o:", "target = {#foo.o}");
452 try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
453 try depTokenizer("$$foo.o:", "target = {$foo.o}");
454}
455
456test "empty target linefeeds" {
457 try depTokenizer("\n", "");
458 try depTokenizer("\r\n", "");
459
460 const expect = "target = {foo.o}";
461 try depTokenizer(
462 \\foo.o:
463 ,
464 expect
465 );
466 try depTokenizer(
467 \\foo.o:
468 \\
469 ,
470 expect
471 );
472 try depTokenizer(
473 \\foo.o:
474 ,
475 expect
476 );
477 try depTokenizer(
478 \\foo.o:
479 \\
480 ,
481 expect
482 );
483}
484
485test "empty target linefeeds + continuations" {
486 const expect = "target = {foo.o}";
487 try depTokenizer(
488 \\foo.o:\
489 ,
490 expect
491 );
492 try depTokenizer(
493 \\foo.o:\
494 \\
495 ,
496 expect
497 );
498 try depTokenizer(
499 \\foo.o:\
500 ,
501 expect
502 );
503 try depTokenizer(
504 \\foo.o:\
505 \\
506 ,
507 expect
508 );
509}
510
511test "empty target linefeeds + hspace + continuations" {
512 const expect = "target = {foo.o}";
513 try depTokenizer(
514 \\foo.o: \
515 ,
516 expect
517 );
518 try depTokenizer(
519 \\foo.o: \
520 \\
521 ,
522 expect
523 );
524 try depTokenizer(
525 \\foo.o: \
526 ,
527 expect
528 );
529 try depTokenizer(
530 \\foo.o: \
531 \\
532 ,
533 expect
534 );
535}
536
537test "prereq" {
538 const expect =
539 \\target = {foo.o}
540 \\prereq = {foo.c}
541 ;
542 try depTokenizer("foo.o: foo.c", expect);
543 try depTokenizer(
544 \\foo.o: \
545 \\foo.c
546 , expect);
547 try depTokenizer(
548 \\foo.o: \
549 \\ foo.c
550 , expect);
551 try depTokenizer(
552 \\foo.o: \
553 \\ foo.c
554 , expect);
555}
556
557test "prereq continuation" {
558 const expect =
559 \\target = {foo.o}
560 \\prereq = {foo.h}
561 \\prereq = {bar.h}
562 ;
563 try depTokenizer(
564 \\foo.o: foo.h\
565 \\bar.h
566 ,
567 expect
568 );
569 try depTokenizer(
570 \\foo.o: foo.h\
571 \\bar.h
572 ,
573 expect
574 );
575}
576
577test "multiple prereqs" {
578 const expect =
579 \\target = {foo.o}
580 \\prereq = {foo.c}
581 \\prereq = {foo.h}
582 \\prereq = {bar.h}
583 ;
584 try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
585 try depTokenizer(
586 \\foo.o: \
587 \\foo.c foo.h bar.h
588 , expect);
589 try depTokenizer(
590 \\foo.o: foo.c foo.h bar.h\
591 , expect);
592 try depTokenizer(
593 \\foo.o: foo.c foo.h bar.h\
594 \\
595 , expect);
596 try depTokenizer(
597 \\foo.o: \
598 \\foo.c \
599 \\ foo.h\
600 \\bar.h
601 \\
602 , expect);
603 try depTokenizer(
604 \\foo.o: \
605 \\foo.c \
606 \\ foo.h\
607 \\bar.h\
608 \\
609 , expect);
610 try depTokenizer(
611 \\foo.o: \
612 \\foo.c \
613 \\ foo.h\
614 \\bar.h\
615 , expect);
616}
617
618test "multiple targets and prereqs" {
619 try depTokenizer(
620 \\foo.o: foo.c
621 \\bar.o: bar.c a.h b.h c.h
622 \\abc.o: abc.c \
623 \\ one.h two.h \
624 \\ three.h four.h
625 ,
626 \\target = {foo.o}
627 \\prereq = {foo.c}
628 \\target = {bar.o}
629 \\prereq = {bar.c}
630 \\prereq = {a.h}
631 \\prereq = {b.h}
632 \\prereq = {c.h}
633 \\target = {abc.o}
634 \\prereq = {abc.c}
635 \\prereq = {one.h}
636 \\prereq = {two.h}
637 \\prereq = {three.h}
638 \\prereq = {four.h}
639 );
640 try depTokenizer(
641 \\ascii.o: ascii.c
642 \\base64.o: base64.c stdio.h
643 \\elf.o: elf.c a.h b.h c.h
644 \\macho.o: \
645 \\ macho.c\
646 \\ a.h b.h c.h
647 ,
648 \\target = {ascii.o}
649 \\prereq = {ascii.c}
650 \\target = {base64.o}
651 \\prereq = {base64.c}
652 \\prereq = {stdio.h}
653 \\target = {elf.o}
654 \\prereq = {elf.c}
655 \\prereq = {a.h}
656 \\prereq = {b.h}
657 \\prereq = {c.h}
658 \\target = {macho.o}
659 \\prereq = {macho.c}
660 \\prereq = {a.h}
661 \\prereq = {b.h}
662 \\prereq = {c.h}
663 );
664 try depTokenizer(
665 \\a$$scii.o: ascii.c
666 \\\\base64.o: "\base64.c" "s t#dio.h"
667 \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
668 \\macho.o: \
669 \\ "macho!.c" \
670 \\ a.h b.h c.h
671 ,
672 \\target = {a$scii.o}
673 \\prereq = {ascii.c}
674 \\target = {\base64.o}
675 \\prereq = {\base64.c}
676 \\prereq = {s t#dio.h}
677 \\target = {e\lf.o}
678 \\prereq = {e\lf.c}
679 \\prereq = {a.h$$}
680 \\prereq = {$$b.h c.h$$}
681 \\target = {macho.o}
682 \\prereq = {macho!.c}
683 \\prereq = {a.h}
684 \\prereq = {b.h}
685 \\prereq = {c.h}
686 );
687}
688
689test "windows quoted prereqs" {
690 try depTokenizer(
691 \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
692 \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
693 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
694 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
695 ,
696 \\target = {c:\foo.o}
697 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
698 \\target = {c:\foo2.o}
699 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
700 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
701 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
702 );
703}
704
705test "windows mixed prereqs" {
706 try depTokenizer(
707 \\cimport.o: \
708 \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
709 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
710 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
711 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
712 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
713 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
714 \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
715 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
716 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
717 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
718 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
719 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
720 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
721 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
722 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
723 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
724 ,
725 \\target = {cimport.o}
726 \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
727 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
728 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
729 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
730 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
731 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
732 \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
733 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
734 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
735 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
736 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
737 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
738 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
739 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
740 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
741 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
742 );
743}
744
745test "funky targets" {
746 try depTokenizer(
747 \\C:\Users\anon\foo.o:
748 \\C:\Users\anon\foo\ .o:
749 \\C:\Users\anon\foo\#.o:
750 \\C:\Users\anon\foo$$.o:
751 \\C:\Users\anon\\\ foo.o:
752 \\C:\Users\anon\\#foo.o:
753 \\C:\Users\anon\$$foo.o:
754 \\C:\Users\anon\\\ \ \ \ \ foo.o:
755 ,
756 \\target = {C:\Users\anon\foo.o}
757 \\target = {C:\Users\anon\foo .o}
758 \\target = {C:\Users\anon\foo#.o}
759 \\target = {C:\Users\anon\foo$.o}
760 \\target = {C:\Users\anon\ foo.o}
761 \\target = {C:\Users\anon\#foo.o}
762 \\target = {C:\Users\anon\$foo.o}
763 \\target = {C:\Users\anon\ foo.o}
764 );
765}
766
767test "error incomplete escape - reverse_solidus" {
768 try depTokenizer("\\",
769 \\ERROR: illegal char '\' at position 0: incomplete escape
770 );
771 try depTokenizer("\t\\",
772 \\ERROR: illegal char '\' at position 1: incomplete escape
773 );
774 try depTokenizer("\n\\",
775 \\ERROR: illegal char '\' at position 1: incomplete escape
776 );
777 try depTokenizer("\r\\",
778 \\ERROR: illegal char '\' at position 1: incomplete escape
779 );
780 try depTokenizer("\r\n\\",
781 \\ERROR: illegal char '\' at position 2: incomplete escape
782 );
783 try depTokenizer(" \\",
784 \\ERROR: illegal char '\' at position 1: incomplete escape
785 );
786}
787
788test "error incomplete escape - dollar_sign" {
789 try depTokenizer("$",
790 \\ERROR: illegal char '$' at position 0: incomplete escape
791 );
792 try depTokenizer("\t$",
793 \\ERROR: illegal char '$' at position 1: incomplete escape
794 );
795 try depTokenizer("\n$",
796 \\ERROR: illegal char '$' at position 1: incomplete escape
797 );
798 try depTokenizer("\r$",
799 \\ERROR: illegal char '$' at position 1: incomplete escape
800 );
801 try depTokenizer("\r\n$",
802 \\ERROR: illegal char '$' at position 2: incomplete escape
803 );
804 try depTokenizer(" $",
805 \\ERROR: illegal char '$' at position 1: incomplete escape
806 );
807}
808
809test "error incomplete target" {
810 try depTokenizer("foo.o",
811 \\ERROR: incomplete target 'foo.o' at position 0
812 );
813 try depTokenizer("\tfoo.o",
814 \\ERROR: incomplete target 'foo.o' at position 1
815 );
816 try depTokenizer("\nfoo.o",
817 \\ERROR: incomplete target 'foo.o' at position 1
818 );
819 try depTokenizer("\rfoo.o",
820 \\ERROR: incomplete target 'foo.o' at position 1
821 );
822 try depTokenizer("\r\nfoo.o",
823 \\ERROR: incomplete target 'foo.o' at position 2
824 );
825 try depTokenizer(" foo.o",
826 \\ERROR: incomplete target 'foo.o' at position 1
827 );
828
829 try depTokenizer("\\ foo.o",
830 \\ERROR: incomplete target ' foo.o' at position 1
831 );
832 try depTokenizer("\\#foo.o",
833 \\ERROR: incomplete target '#foo.o' at position 1
834 );
835 try depTokenizer("\\\\foo.o",
836 \\ERROR: incomplete target '\foo.o' at position 1
837 );
838 try depTokenizer("$$foo.o",
839 \\ERROR: incomplete target '$foo.o' at position 1
840 );
841}
842
843test "error illegal char at position - bad target escape" {
844 try depTokenizer("\\\t",
845 \\ERROR: illegal char \x09 at position 1: bad target escape
846 );
847 try depTokenizer("\\\n",
848 \\ERROR: illegal char \x0A at position 1: bad target escape
849 );
850 try depTokenizer("\\\r",
851 \\ERROR: illegal char \x0D at position 1: bad target escape
852 );
853 try depTokenizer("\\\r\n",
854 \\ERROR: illegal char \x0D at position 1: bad target escape
855 );
856}
857
858test "error illegal char at position - execting dollar_sign" {
859 try depTokenizer("$\t",
860 \\ERROR: illegal char \x09 at position 1: expecting '$'
861 );
862 try depTokenizer("$\n",
863 \\ERROR: illegal char \x0A at position 1: expecting '$'
864 );
865 try depTokenizer("$\r",
866 \\ERROR: illegal char \x0D at position 1: expecting '$'
867 );
868 try depTokenizer("$\r\n",
869 \\ERROR: illegal char \x0D at position 1: expecting '$'
870 );
871}
872
873test "error illegal char at position - invalid target" {
874 try depTokenizer("foo\t.o",
875 \\ERROR: illegal char \x09 at position 3: invalid target
876 );
877 try depTokenizer("foo\n.o",
878 \\ERROR: illegal char \x0A at position 3: invalid target
879 );
880 try depTokenizer("foo\r.o",
881 \\ERROR: illegal char \x0D at position 3: invalid target
882 );
883 try depTokenizer("foo\r\n.o",
884 \\ERROR: illegal char \x0D at position 3: invalid target
885 );
886}
887
888test "error target - continuation expecting end-of-line" {
889 try depTokenizer("foo.o: \\\t",
890 \\target = {foo.o}
891 \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
892 );
893 try depTokenizer("foo.o: \\ ",
894 \\target = {foo.o}
895 \\ERROR: illegal char \x20 at position 8: continuation expecting end-of-line
896 );
897 try depTokenizer("foo.o: \\x",
898 \\target = {foo.o}
899 \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
900 );
901 try depTokenizer("foo.o: \\ x",
902 \\target = {foo.o}
903 \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
904 );
905}
906
907test "error prereq - continuation expecting end-of-line" {
908 try depTokenizer("foo.o: foo.h\\ x",
909 \\target = {foo.o}
910 \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line
911 );
912}
913
914// - tokenize input, emit textual representation, and compare to expect
915fn depTokenizer(input: []const u8, expect: []const u8) !void {
916 var direct_allocator = std.heap.DirectAllocator.init();
917 var arena_allocator = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
918 const arena = &arena_allocator.allocator;
919 defer arena_allocator.deinit();
920
921 var it = Tokenizer.init(&direct_allocator.allocator, input);
922 var buffer = try std.Buffer.initSize(arena, 0);
923 var i: usize = 0;
924 while (true) {
925 const r = it.next() catch |err| {
926 switch (err) {
927 Tokenizer.Error.InvalidInput => {
928 if (i != 0) try buffer.append("\n");
929 try buffer.append("ERROR: ");
930 try buffer.append(it.error_text);
931 },
932 else => return err,
933 }
934 break;
935 };
936 const token = r orelse break;
937 if (i != 0) try buffer.append("\n");
938 try buffer.append(@tagName(token.id));
939 try buffer.append(" = {");
940 for (token.bytes) |b| {
941 try buffer.appendByte(printable_char_tab[b]);
942 }
943 try buffer.append("}");
944 i += 1;
945 }
946 const got: []const u8 = buffer.toSlice();
947
948 if (std.mem.eql(u8, expect, got)) {
949 testing.expect(true);
950 return;
951 }
952
953 var out = makeOutput(std.fs.File.write, try std.io.getStdErr());
954
955 try out.write("\n");
956 try printSection(&out, "<<<< input", input);
957 try printSection(&out, "==== expect", expect);
958 try printSection(&out, ">>>> got", got);
959 try printRuler(&out);
960
961 testing.expect(false);
962}
963
964fn printSection(out: var, label: []const u8, bytes: []const u8) !void {
965 try printLabel(out, label, bytes);
966 try hexDump(out, bytes);
967 try printRuler(out);
968 try out.write(bytes);
969 try out.write("\n");
970}
971
972fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {
973 var buf: [80]u8 = undefined;
974 var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", label, bytes.len);
975 try out.write(text);
976 var i: usize = text.len;
977 const end = 79;
978 while (i < 79) : (i += 1) {
979 try out.write([]const u8{label[0]});
980 }
981 try out.write("\n");
982}
983
984fn printRuler(out: var) !void {
985 var i: usize = 0;
986 const end = 79;
987 while (i < 79) : (i += 1) {
988 try out.write("-");
989 }
990 try out.write("\n");
991}
992
993fn hexDump(out: var, bytes: []const u8) !void {
994 const n16 = bytes.len >> 4;
995 var line: usize = 0;
996 var offset: usize = 0;
997 while (line < n16) : (line += 1) {
998 try hexDump16(out, offset, bytes[offset .. offset + 16]);
999 offset += 16;
1000 }
1001
1002 const n = bytes.len & 0x0f;
1003 if (n > 0) {
1004 try printDecValue(out, offset, 8);
1005 try out.write(":");
1006 try out.write(" ");
1007 var end1 = std.math.min(offset + n, offset + 8);
1008 for (bytes[offset..end1]) |b| {
1009 try out.write(" ");
1010 try printHexValue(out, b, 2);
1011 }
1012 var end2 = offset + n;
1013 if (end2 > end1) {
1014 try out.write(" ");
1015 for (bytes[end1..end2]) |b| {
1016 try out.write(" ");
1017 try printHexValue(out, b, 2);
1018 }
1019 }
1020 const short = 16 - n;
1021 var i: usize = 0;
1022 while (i < short) : (i += 1) {
1023 try out.write(" ");
1024 }
1025 if (end2 > end1) {
1026 try out.write(" |");
1027 } else {
1028 try out.write(" |");
1029 }
1030 try printCharValues(out, bytes[offset..end2]);
1031 try out.write("|\n");
1032 offset += n;
1033 }
1034
1035 try printDecValue(out, offset, 8);
1036 try out.write(":");
1037 try out.write("\n");
1038}
1039
1040fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {
1041 try printDecValue(out, offset, 8);
1042 try out.write(":");
1043 try out.write(" ");
1044 for (bytes[0..8]) |b| {
1045 try out.write(" ");
1046 try printHexValue(out, b, 2);
1047 }
1048 try out.write(" ");
1049 for (bytes[8..16]) |b| {
1050 try out.write(" ");
1051 try printHexValue(out, b, 2);
1052 }
1053 try out.write(" |");
1054 try printCharValues(out, bytes);
1055 try out.write("|\n");
1056}
1057
1058fn printDecValue(out: var, value: u64, width: u8) !void {
1059 var buffer: [20]u8 = undefined;
1060 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width);
1061 try out.write(buffer[0..len]);
1062}
1063
1064fn printHexValue(out: var, value: u64, width: u8) !void {
1065 var buffer: [16]u8 = undefined;
1066 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width);
1067 try out.write(buffer[0..len]);
1068}
1069
1070fn printCharValues(out: var, bytes: []const u8) !void {
1071 for (bytes) |b| {
1072 try out.write([]const u8{printable_char_tab[b]});
1073 }
1074}
1075
1076fn printUnderstandableChar(out: var, char: u8) !void {
1077 if (!std.ascii.isPrint(char) or char == ' ') {
1078 std.fmt.format(out.context, anyerror, out.output, "\\x{X2}", char) catch {};
1079 } else {
1080 try out.write("'");
1081 try out.write([]const u8{printable_char_tab[char]});
1082 try out.write("'");
1083 }
1084}
1085
1086// zig fmt: off
1087const printable_char_tab: []const u8 =
1088 "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
1089 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
1090 "................................................................" ++
1091 "................................................................";
1092// zig fmt: on
1093comptime {
1094 std.debug.assert(printable_char_tab.len == 256);
1095}
1096
1097// Make an output var that wraps a context and output function.
1098// output: must be a function that takes a `self` idiom parameter
1099// and a bytes parameter
1100// context: must be that self
1101fn makeOutput(output: var, context: var) Output(@typeOf(output)) {
1102 return Output(@typeOf(output)){
1103 .output = output,
1104 .context = context,
1105 };
1106}
1107
1108fn Output(comptime T: type) type {
1109 const args = switch (@typeInfo(T)) {
1110 .Fn => |f| f.args,
1111 else => @compileError("output parameter is not a function"),
1112 };
1113 if (args.len != 2) {
1114 @compileError("output function must take 2 arguments");
1115 }
1116 const at0 = args[0].arg_type orelse @compileError("output arg[0] does not have a type");
1117 const at1 = args[1].arg_type orelse @compileError("output arg[1] does not have a type");
1118 const arg1p = switch (@typeInfo(at1)) {
1119 .Pointer => |p| p,
1120 else => @compileError("output arg[1] is not a slice"),
1121 };
1122 if (arg1p.child != u8) @compileError("output arg[1] is not a u8 slice");
1123 return struct {
1124 output: T,
1125 context: at0,
1126
1127 fn write(self: *@This(), bytes: []const u8) !void {
1128 try self.output(self.context, bytes);
1129 }
1130 };
1131}
src-self-hosted/stage1.zig+4
......@@ -20,6 +20,10 @@ var stderr_file: fs.File = undefined;
2020var stderr: *io.OutStream(fs.File.WriteError) = undefined;
2121var stdout: *io.OutStream(fs.File.WriteError) = undefined;
2222
23comptime {
24 _ = @import("dep_tokenizer.zig");
25}
26
2327// ABI warning
2428export fn stage2_zen(ptr: *[*]const u8, len: *usize) void {
2529 const info_zen = @import("main.zig").info_zen;
src/cache_hash.cpp+47-55
......@@ -5,6 +5,7 @@
55 * See http://opensource.org/licenses/MIT
66 */
77
8#include "userland.h"
89#include "cache_hash.hpp"
910#include "all_types.hpp"
1011#include "buffer.hpp"
......@@ -473,71 +474,62 @@ Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) {
473474 if (err == ErrorFileNotFound)
474475 return err;
475476 if (verbose) {
476 fprintf(stderr, "unable to read .d file: %s\n", err_str(err));
477 fprintf(stderr, "%s: unable to read .d file: %s\n", err_str(err), buf_ptr(dep_file_path));
477478 }
478479 return ErrorReadingDepFile;
479480 }
480 SplitIterator it = memSplit(buf_to_slice(contents), str("\r\n"));
481 // skip first line
482 SplitIterator_next(&it);
483 for (;;) {
484 Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&it);
485 if (!opt_line.is_some)
486 break;
487 if (opt_line.value.len == 0)
488 continue;
489 // skip over indentation
490 while (opt_line.value.len != 0 && (opt_line.value.ptr[0] == ' ' || opt_line.value.ptr[0] == '\t')) {
491 opt_line.value.ptr += 1;
492 opt_line.value.len -= 1;
493 }
494 if (opt_line.value.len == 0)
495 continue;
496
497 if (opt_line.value.ptr[0] == '"') {
498 if (opt_line.value.len < 2) {
481 auto it = stage2_DepTokenizer_init(buf_ptr(contents), buf_len(contents));
482 // skip first token: target
483 {
484 auto result = stage2_DepTokenizer_next(&it);
485 switch (result.ent) {
486 case stage2_DepNextResult::error:
499487 if (verbose) {
500 fprintf(stderr, "unable to process invalid .d file %s: line too short\n", buf_ptr(dep_file_path));
488 fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
501489 }
502 return ErrorInvalidDepFile;
503 }
504 opt_line.value.ptr += 1;
505 opt_line.value.len -= 2;
506 while (opt_line.value.len != 0 && opt_line.value.ptr[opt_line.value.len] != '"') {
507 opt_line.value.len -= 1;
508 }
509 if (opt_line.value.len == 0) {
510 if (verbose) {
511 fprintf(stderr, "unable to process invalid .d file %s: missing double quote\n", buf_ptr(dep_file_path));
512 }
513 return ErrorInvalidDepFile;
514 }
515 Buf *filename_buf = buf_create_from_slice(opt_line.value);
516 if ((err = cache_add_file(ch, filename_buf))) {
490 err = ErrorInvalidDepFile;
491 goto finish;
492 case stage2_DepNextResult::null:
493 err = ErrorNone;
494 goto finish;
495 case stage2_DepNextResult::target:
496 case stage2_DepNextResult::prereq:
497 err = ErrorNone;
498 break;
499 }
500 }
501 // Process 0+ preqreqs.
502 // clang is invoked in single-source mode so we never get more targets.
503 for (;;) {
504 auto result = stage2_DepTokenizer_next(&it);
505 switch (result.ent) {
506 case stage2_DepNextResult::error:
517507 if (verbose) {
518 fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(filename_buf), err_str(err));
519 fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path));
520 }
521 return err;
522 }
523 } else {
524 // sometimes there are multiple files on the same line; we actually need space tokenization.
525 SplitIterator line_it = memSplit(opt_line.value, str(" \t"));
526 Slice<uint8_t> filename;
527 while (SplitIterator_next(&line_it).unwrap(&filename)) {
528 Buf *filename_buf = buf_create_from_slice(filename);
529 if (buf_eql_str(filename_buf, "\\")) continue;
530 if ((err = cache_add_file(ch, filename_buf))) {
531 if (verbose) {
532 fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(filename_buf), err_str(err));
533 fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path));
534 }
535 return err;
508 fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path));
536509 }
510 err = ErrorInvalidDepFile;
511 goto finish;
512 case stage2_DepNextResult::null:
513 case stage2_DepNextResult::target:
514 err = ErrorNone;
515 goto finish;
516 case stage2_DepNextResult::prereq:
517 break;
518 }
519 auto textbuf = buf_alloc();
520 buf_init_from_str(textbuf, result.textz);
521 if ((err = cache_add_file(ch, textbuf))) {
522 if (verbose) {
523 fprintf(stderr, "unable to add %s to cache: %s\n", result.textz, err_str(err));
524 fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path));
537525 }
526 goto finish;
538527 }
539528 }
540 return ErrorNone;
529
530 finish:
531 stage2_DepTokenizer_deinit(&it);
532 return err;
541533}
542534
543535static Error write_manifest_file(CacheHash *ch) {
src/userland.cpp+15
......@@ -42,3 +42,18 @@ int stage2_fmt(int argc, char **argv) {
4242 const char *msg = "stage0 called stage2_fmt";
4343 stage2_panic(msg, strlen(msg));
4444}
45
46stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) {
47 const char *msg = "stage0 called stage2_DepTokenizer_init";
48 stage2_panic(msg, strlen(msg));
49}
50
51void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) {
52 const char *msg = "stage0 called stage2_DepTokenizer_deinit";
53 stage2_panic(msg, strlen(msg));
54}
55
56stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) {
57 const char *msg = "stage0 called stage2_DepTokenizer_next";
58 stage2_panic(msg, strlen(msg));
59}
src/userland.h+33
......@@ -9,6 +9,7 @@
99#define ZIG_USERLAND_H
1010
1111#include <stddef.h>
12#include <stdint.h>
1213#include <stdio.h>
1314
1415#ifdef __cplusplus
......@@ -118,4 +119,36 @@ ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t le
118119// ABI warning
119120ZIG_EXTERN_C int stage2_fmt(int argc, char **argv);
120121
122// ABI warning
123struct stage2_DepTokenizer {
124 void *handle;
125};
126
127// ABI warning
128struct stage2_DepNextResult {
129 enum Ent : uint8_t {
130 error,
131 null,
132 target,
133 prereq,
134 };
135
136 Ent ent;
137
138 // when ent == error --> error text
139 // when ent == null --> undefined
140 // when ent == target --> target pathname
141 // when ent == prereq --> prereq pathname
142 const char *textz;
143};
144
145// ABI warning
146ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len);
147
148// ABI warning
149ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self);
150
151// ABI warning
152ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self);
153
121154#endif