Commit 9f8d938d38

rpkak <rpkak@users.noreply.github.com>
2025-03-14 12:18:10
DepTokenizer: allow space between target and colon
1 parent 677b2d6
Changed files (1)
lib
std
Build
lib/std/Build/Cache/DepTokenizer.zig
@@ -25,7 +25,7 @@ pub fn next(self: *Tokenizer) ?Token {
                 },
             },
             .target => switch (char) {
-                '\t', '\n', '\r', ' ' => {
+                '\n', '\r' => {
                     return errorIllegalChar(.invalid_target, self.index, char);
                 },
                 '$' => {
@@ -40,6 +40,15 @@ pub fn next(self: *Tokenizer) ?Token {
                     self.state = .target_colon;
                     self.index += 1;
                 },
+                '\t', ' ' => {
+                    self.state = .target_space;
+
+                    const bytes = self.bytes[start..self.index];
+                    std.debug.assert(bytes.len != 0);
+                    self.index += 1;
+
+                    return finishTarget(must_resolve, bytes);
+                },
                 else => {
                     self.index += 1;
                 },
@@ -110,6 +119,19 @@ pub fn next(self: *Tokenizer) ?Token {
                     self.state = .target;
                 },
             },
+            .target_space => switch (char) {
+                '\t', ' ' => {
+                    // silently ignore additional horizontal whitespace
+                    self.index += 1;
+                },
+                ':' => {
+                    self.state = .rhs;
+                    self.index += 1;
+                },
+                else => {
+                    return errorIllegalChar(.expected_colon, self.index, char);
+                },
+            },
             .rhs => switch (char) {
                 '\t', ' ' => {
                     // silently ignore horizontal whitespace
@@ -256,6 +278,10 @@ pub fn next(self: *Tokenizer) ?Token {
                 self.state = .lhs;
                 return null;
             },
+            .target_space => {
+                const idx = self.index - 1;
+                return errorIllegalChar(.expected_colon, idx, self.bytes[idx]);
+            },
             .prereq_quote => {
                 return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
             },
@@ -299,6 +325,7 @@ const State = enum {
     target_dollar_sign,
     target_colon,
     target_colon_reverse_solidus,
+    target_space,
     rhs,
     rhs_continuation,
     rhs_continuation_linefeed,
@@ -322,6 +349,7 @@ pub const Token = union(enum) {
     expected_dollar_sign: IndexAndChar,
     continuation_eol: IndexAndChar,
     incomplete_escape: IndexAndChar,
+    expected_colon: IndexAndChar,
 
     pub const IndexAndChar = struct {
         index: usize,
@@ -420,6 +448,7 @@ pub const Token = union(enum) {
             .expected_dollar_sign,
             .continuation_eol,
             .incomplete_escape,
+            .expected_colon,
             => |index_and_char| {
                 try writer.writeAll("illegal char ");
                 try printUnderstandableChar(writer, index_and_char.char);
@@ -438,6 +467,7 @@ pub const Token = union(enum) {
             .expected_dollar_sign => "expecting '$'",
             .continuation_eol => "continuation expecting end-of-line",
             .incomplete_escape => "incomplete escape",
+            .expected_colon => "expecting ':'",
         };
     }
 };
@@ -545,6 +575,16 @@ test "empty target linefeeds + hspace + continuations" {
     , expect);
 }
 
+test "empty target + hspace + colon" {
+    const expect = "target = {foo.o}";
+
+    try depTokenizer("foo.o :", expect);
+    try depTokenizer("foo.o\t\t\t:", expect);
+    try depTokenizer("foo.o \t \t :", expect);
+    try depTokenizer("\r\nfoo.o :", expect);
+    try depTokenizer(" foo.o :", expect);
+}
+
 test "prereq" {
     const expect =
         \\target = {foo.o}
@@ -923,9 +963,6 @@ test "error illegal char at position - expecting dollar_sign" {
 }
 
 test "error illegal char at position - invalid target" {
-    try depTokenizer("foo\t.o",
-        \\ERROR: illegal char \x09 at position 3: invalid target
-    );
     try depTokenizer("foo\n.o",
         \\ERROR: illegal char \x0A at position 3: invalid target
     );
@@ -963,6 +1000,25 @@ test "error prereq - continuation expecting end-of-line" {
     );
 }
 
+test "error illegal char at position - expecting colon" {
+    try depTokenizer("foo\t.o:",
+        \\target = {foo}
+        \\ERROR: illegal char '.' at position 4: expecting ':'
+    );
+    try depTokenizer("foo .o:",
+        \\target = {foo}
+        \\ERROR: illegal char '.' at position 4: expecting ':'
+    );
+    try depTokenizer("foo \n.o:",
+        \\target = {foo}
+        \\ERROR: illegal char \x0A at position 4: expecting ':'
+    );
+    try depTokenizer("foo.o\t\n:",
+        \\target = {foo.o}
+        \\ERROR: illegal char \x0A at position 6: expecting ':'
+    );
+}
+
 // - tokenize input, emit textual representation, and compare to expect
 fn depTokenizer(input: []const u8, expect: []const u8) !void {
     var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);