authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-14 15:30:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 16:27:38-07:00
log1e785409bb77a4ae1c8a496acae64d94c34431b9
tree2d1c00e45239ed55181359aa2d3962fb71b2cf67
parent028a9a10549e59e84105515ffb50c491f487c91a

move link.Elf.LdScript to link.LdScript


4 files changed, 452 insertions(+), 452 deletions(-)

CMakeLists.txt+1-1
...@@ -601,7 +601,6 @@ set(ZIG_STAGE2_SOURCES...@@ -601,7 +601,6 @@ set(ZIG_STAGE2_SOURCES
601 src/link/Elf/Archive.zig601 src/link/Elf/Archive.zig
602 src/link/Elf/Atom.zig602 src/link/Elf/Atom.zig
603 src/link/Elf/AtomList.zig603 src/link/Elf/AtomList.zig
604 src/link/Elf/LdScript.zig
605 src/link/Elf/LinkerDefined.zig604 src/link/Elf/LinkerDefined.zig
606 src/link/Elf/Merge.zig605 src/link/Elf/Merge.zig
607 src/link/Elf/Object.zig606 src/link/Elf/Object.zig
...@@ -615,6 +614,7 @@ set(ZIG_STAGE2_SOURCES...@@ -615,6 +614,7 @@ set(ZIG_STAGE2_SOURCES
615 src/link/Elf/relocatable.zig614 src/link/Elf/relocatable.zig
616 src/link/Elf/relocation.zig615 src/link/Elf/relocation.zig
617 src/link/Elf/synthetic_sections.zig616 src/link/Elf/synthetic_sections.zig
617 src/link/LdScript.zig
618 src/link/MachO.zig618 src/link/MachO.zig
619 src/link/MachO/Archive.zig619 src/link/MachO/Archive.zig
620 src/link/MachO/Atom.zig620 src/link/MachO/Atom.zig
src/link/Elf.zig+1-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1pub const Atom = @import("Elf/Atom.zig");1pub const Atom = @import("Elf/Atom.zig");
2pub const LdScript = @import("LdScript.zig");
23
3base: link.File,4base: link.File,
4rpath_table: std.StringArrayHashMapUnmanaged(void),5rpath_table: std.StringArrayHashMapUnmanaged(void),
...@@ -5625,7 +5626,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection;...@@ -5625,7 +5626,6 @@ const GnuHashSection = synthetic_sections.GnuHashSection;
5625const GotSection = synthetic_sections.GotSection;5626const GotSection = synthetic_sections.GotSection;
5626const GotPltSection = synthetic_sections.GotPltSection;5627const GotPltSection = synthetic_sections.GotPltSection;
5627const HashSection = synthetic_sections.HashSection;5628const HashSection = synthetic_sections.HashSection;
5628const LdScript = @import("Elf/LdScript.zig");
5629const LinkerDefined = @import("Elf/LinkerDefined.zig");5629const LinkerDefined = @import("Elf/LinkerDefined.zig");
5630const Liveness = @import("../Liveness.zig");5630const Liveness = @import("../Liveness.zig");
5631const LlvmObject = @import("../codegen/llvm.zig").Object;5631const LlvmObject = @import("../codegen/llvm.zig").Object;
src/link/Elf/LdScript.zig deleted-450
...@@ -1,450 +0,0 @@
1path: Path,
2cpu_arch: ?std.Target.Cpu.Arch,
3args: []const Arg,
4
5pub const Arg = struct {
6 needed: bool = false,
7 path: []const u8,
8};
9
10pub fn deinit(ls: *LdScript, gpa: Allocator) void {
11 gpa.free(ls.args);
12 ls.* = undefined;
13}
14
15pub const Error = error{
16 LinkFailure,
17 UnexpectedToken,
18 UnknownCpuArch,
19 OutOfMemory,
20};
21
22pub fn parse(
23 gpa: Allocator,
24 diags: *Diags,
25 /// For error reporting.
26 path: Path,
27 data: []const u8,
28) Error!LdScript {
29 var tokenizer = Tokenizer{ .source = data };
30 var tokens: std.ArrayListUnmanaged(Token) = .empty;
31 defer tokens.deinit(gpa);
32 var line_col: std.ArrayListUnmanaged(LineColumn) = .empty;
33 defer line_col.deinit(gpa);
34
35 var line: usize = 0;
36 var prev_line_last_col: usize = 0;
37
38 while (true) {
39 const tok = tokenizer.next();
40 try tokens.append(gpa, tok);
41 const column = tok.start - prev_line_last_col;
42 try line_col.append(gpa, .{ .line = line, .column = column });
43 switch (tok.id) {
44 .invalid => {
45 return diags.failParse(path, "invalid token in LD script: '{s}' ({d}:{d})", .{
46 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
47 });
48 },
49 .new_line => {
50 line += 1;
51 prev_line_last_col = tok.end;
52 },
53 .eof => break,
54 else => {},
55 }
56 }
57
58 var it: TokenIterator = .{ .tokens = tokens.items };
59 var parser: Parser = .{
60 .gpa = gpa,
61 .source = data,
62 .it = &it,
63 .args = .empty,
64 .cpu_arch = null,
65 };
66 defer parser.args.deinit(gpa);
67
68 parser.start() catch |err| switch (err) {
69 error.UnexpectedToken => {
70 const last_token_id = parser.it.pos - 1;
71 const last_token = parser.it.get(last_token_id);
72 const lcol = line_col.items[last_token_id];
73 return diags.failParse(path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
74 @tagName(last_token.id),
75 last_token.get(data),
76 lcol.line,
77 lcol.column,
78 });
79 },
80 else => |e| return e,
81 };
82 return .{
83 .path = path,
84 .cpu_arch = parser.cpu_arch,
85 .args = try parser.args.toOwnedSlice(gpa),
86 };
87}
88
89const LineColumn = struct {
90 line: usize,
91 column: usize,
92};
93
94const Command = enum {
95 output_format,
96 input,
97 group,
98 as_needed,
99
100 fn fromString(s: []const u8) ?Command {
101 inline for (@typeInfo(Command).@"enum".fields) |field| {
102 const upper_name = n: {
103 comptime var buf: [field.name.len]u8 = undefined;
104 inline for (field.name, 0..) |c, i| {
105 buf[i] = comptime std.ascii.toUpper(c);
106 }
107 break :n buf;
108 };
109 if (std.mem.eql(u8, &upper_name, s)) return @field(Command, field.name);
110 }
111 return null;
112 }
113};
114
115const Parser = struct {
116 gpa: Allocator,
117 source: []const u8,
118 it: *TokenIterator,
119
120 cpu_arch: ?std.Target.Cpu.Arch,
121 args: std.ArrayListUnmanaged(Arg),
122
123 fn start(parser: *Parser) !void {
124 while (true) {
125 parser.skipAny(&.{ .comment, .new_line });
126
127 if (parser.maybe(.command)) |cmd_id| {
128 const cmd = parser.getCommand(cmd_id);
129 switch (cmd) {
130 .output_format => parser.cpu_arch = try parser.outputFormat(),
131 // TODO we should verify that group only contains libraries
132 .input, .group => try parser.group(),
133 else => return error.UnexpectedToken,
134 }
135 } else break;
136 }
137
138 if (parser.it.next()) |tok| switch (tok.id) {
139 .eof => {},
140 else => return error.UnexpectedToken,
141 };
142 }
143
144 fn outputFormat(p: *Parser) !std.Target.Cpu.Arch {
145 const value = value: {
146 if (p.skip(&.{.lparen})) {
147 const value_id = try p.require(.literal);
148 const value = p.it.get(value_id);
149 _ = try p.require(.rparen);
150 break :value value.get(p.source);
151 } else if (p.skip(&.{ .new_line, .lbrace })) {
152 const value_id = try p.require(.literal);
153 const value = p.it.get(value_id);
154 _ = p.skip(&.{.new_line});
155 _ = try p.require(.rbrace);
156 break :value value.get(p.source);
157 } else return error.UnexpectedToken;
158 };
159 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
160 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
161 return error.UnknownCpuArch;
162 }
163
164 fn group(p: *Parser) !void {
165 const gpa = p.gpa;
166 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
167
168 while (true) {
169 if (p.maybe(.literal)) |tok_id| {
170 const tok = p.it.get(tok_id);
171 const path = tok.get(p.source);
172 try p.args.append(gpa, .{ .path = path, .needed = true });
173 } else if (p.maybe(.command)) |cmd_id| {
174 const cmd = p.getCommand(cmd_id);
175 switch (cmd) {
176 .as_needed => try p.asNeeded(),
177 else => return error.UnexpectedToken,
178 }
179 } else break;
180 }
181
182 _ = try p.require(.rparen);
183 }
184
185 fn asNeeded(p: *Parser) !void {
186 const gpa = p.gpa;
187 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
188
189 while (p.maybe(.literal)) |tok_id| {
190 const tok = p.it.get(tok_id);
191 const path = tok.get(p.source);
192 try p.args.append(gpa, .{ .path = path, .needed = false });
193 }
194
195 _ = try p.require(.rparen);
196 }
197
198 fn skip(p: *Parser, comptime ids: []const Token.Id) bool {
199 const pos = p.it.pos;
200 inline for (ids) |id| {
201 const tok = p.it.next() orelse return false;
202 if (tok.id != id) {
203 p.it.seekTo(pos);
204 return false;
205 }
206 }
207 return true;
208 }
209
210 fn skipAny(p: *Parser, comptime ids: []const Token.Id) void {
211 outer: while (p.it.next()) |tok| {
212 inline for (ids) |id| {
213 if (id == tok.id) continue :outer;
214 }
215 break p.it.seekBy(-1);
216 }
217 }
218
219 fn maybe(p: *Parser, comptime id: Token.Id) ?Token.Index {
220 const pos = p.it.pos;
221 const tok = p.it.next() orelse return null;
222 if (tok.id == id) return pos;
223 p.it.seekBy(-1);
224 return null;
225 }
226
227 fn require(p: *Parser, comptime id: Token.Id) !Token.Index {
228 return p.maybe(id) orelse return error.UnexpectedToken;
229 }
230
231 fn getCommand(p: *Parser, index: Token.Index) Command {
232 const tok = p.it.get(index);
233 assert(tok.id == .command);
234 return Command.fromString(tok.get(p.source)).?;
235 }
236};
237
238const Token = struct {
239 id: Id,
240 start: usize,
241 end: usize,
242
243 const Id = enum {
244 eof,
245 invalid,
246
247 new_line,
248 lparen, // (
249 rparen, // )
250 lbrace, // {
251 rbrace, // }
252
253 comment, // /* */
254
255 command, // literal with special meaning, see Command
256 literal,
257 };
258
259 const Index = usize;
260
261 fn get(tok: Token, source: []const u8) []const u8 {
262 return source[tok.start..tok.end];
263 }
264};
265
266const Tokenizer = struct {
267 source: []const u8,
268 index: usize = 0,
269
270 fn matchesPattern(comptime pattern: []const u8, slice: []const u8) bool {
271 comptime var count: usize = 0;
272 inline while (count < pattern.len) : (count += 1) {
273 if (count >= slice.len) return false;
274 const c = slice[count];
275 if (pattern[count] != c) return false;
276 }
277 return true;
278 }
279
280 fn matches(tok: Tokenizer, comptime pattern: []const u8) bool {
281 return matchesPattern(pattern, tok.source[tok.index..]);
282 }
283
284 fn isCommand(tok: Tokenizer, start: usize, end: usize) bool {
285 return if (Command.fromString(tok.source[start..end]) == null) false else true;
286 }
287
288 fn next(tok: *Tokenizer) Token {
289 var result = Token{
290 .id = .eof,
291 .start = tok.index,
292 .end = undefined,
293 };
294
295 var state: enum {
296 start,
297 comment,
298 literal,
299 } = .start;
300
301 while (tok.index < tok.source.len) : (tok.index += 1) {
302 const c = tok.source[tok.index];
303 switch (state) {
304 .start => switch (c) {
305 ' ', '\t' => result.start += 1,
306
307 '\n' => {
308 result.id = .new_line;
309 tok.index += 1;
310 break;
311 },
312
313 '\r' => {
314 if (tok.matches("\r\n")) {
315 result.id = .new_line;
316 tok.index += "\r\n".len;
317 } else {
318 result.id = .invalid;
319 tok.index += 1;
320 }
321 break;
322 },
323
324 '/' => if (tok.matches("/*")) {
325 state = .comment;
326 tok.index += "/*".len;
327 } else {
328 state = .literal;
329 },
330
331 '(' => {
332 result.id = .lparen;
333 tok.index += 1;
334 break;
335 },
336
337 ')' => {
338 result.id = .rparen;
339 tok.index += 1;
340 break;
341 },
342
343 '{' => {
344 result.id = .lbrace;
345 tok.index += 1;
346 break;
347 },
348
349 '}' => {
350 result.id = .rbrace;
351 tok.index += 1;
352 break;
353 },
354
355 else => state = .literal,
356 },
357
358 .comment => switch (c) {
359 '*' => if (tok.matches("*/")) {
360 result.id = .comment;
361 tok.index += "*/".len;
362 break;
363 },
364 else => {},
365 },
366
367 .literal => switch (c) {
368 ' ', '(', '\n' => {
369 if (tok.isCommand(result.start, tok.index)) {
370 result.id = .command;
371 } else {
372 result.id = .literal;
373 }
374 break;
375 },
376
377 ')' => {
378 result.id = .literal;
379 break;
380 },
381
382 '\r' => {
383 if (tok.matches("\r\n")) {
384 if (tok.isCommand(result.start, tok.index)) {
385 result.id = .command;
386 } else {
387 result.id = .literal;
388 }
389 } else {
390 result.id = .invalid;
391 tok.index += 1;
392 }
393 break;
394 },
395
396 else => {},
397 },
398 }
399 }
400
401 result.end = tok.index;
402 return result;
403 }
404};
405
406const TokenIterator = struct {
407 tokens: []const Token,
408 pos: Token.Index = 0,
409
410 fn next(it: *TokenIterator) ?Token {
411 const token = it.peek() orelse return null;
412 it.pos += 1;
413 return token;
414 }
415
416 fn peek(it: TokenIterator) ?Token {
417 if (it.pos >= it.tokens.len) return null;
418 return it.tokens[it.pos];
419 }
420
421 fn reset(it: *TokenIterator) void {
422 it.pos = 0;
423 }
424
425 fn seekTo(it: *TokenIterator, pos: Token.Index) void {
426 it.pos = pos;
427 }
428
429 fn seekBy(it: *TokenIterator, offset: isize) void {
430 const new_pos = @as(isize, @bitCast(it.pos)) + offset;
431 if (new_pos < 0) {
432 it.pos = 0;
433 } else {
434 it.pos = @as(usize, @intCast(new_pos));
435 }
436 }
437
438 fn get(it: *TokenIterator, pos: Token.Index) Token {
439 assert(pos < it.tokens.len);
440 return it.tokens[pos];
441 }
442};
443
444const LdScript = @This();
445const Diags = @import("../../link.zig").Diags;
446
447const std = @import("std");
448const assert = std.debug.assert;
449const Path = std.Build.Cache.Path;
450const Allocator = std.mem.Allocator;
src/link/LdScript.zig created+450
...@@ -0,0 +1,450 @@
1path: Path,
2cpu_arch: ?std.Target.Cpu.Arch,
3args: []const Arg,
4
5pub const Arg = struct {
6 needed: bool = false,
7 path: []const u8,
8};
9
10pub fn deinit(ls: *LdScript, gpa: Allocator) void {
11 gpa.free(ls.args);
12 ls.* = undefined;
13}
14
15pub const Error = error{
16 LinkFailure,
17 UnexpectedToken,
18 UnknownCpuArch,
19 OutOfMemory,
20};
21
22pub fn parse(
23 gpa: Allocator,
24 diags: *Diags,
25 /// For error reporting.
26 path: Path,
27 data: []const u8,
28) Error!LdScript {
29 var tokenizer = Tokenizer{ .source = data };
30 var tokens: std.ArrayListUnmanaged(Token) = .empty;
31 defer tokens.deinit(gpa);
32 var line_col: std.ArrayListUnmanaged(LineColumn) = .empty;
33 defer line_col.deinit(gpa);
34
35 var line: usize = 0;
36 var prev_line_last_col: usize = 0;
37
38 while (true) {
39 const tok = tokenizer.next();
40 try tokens.append(gpa, tok);
41 const column = tok.start - prev_line_last_col;
42 try line_col.append(gpa, .{ .line = line, .column = column });
43 switch (tok.id) {
44 .invalid => {
45 return diags.failParse(path, "invalid token in LD script: '{s}' ({d}:{d})", .{
46 std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column,
47 });
48 },
49 .new_line => {
50 line += 1;
51 prev_line_last_col = tok.end;
52 },
53 .eof => break,
54 else => {},
55 }
56 }
57
58 var it: TokenIterator = .{ .tokens = tokens.items };
59 var parser: Parser = .{
60 .gpa = gpa,
61 .source = data,
62 .it = &it,
63 .args = .empty,
64 .cpu_arch = null,
65 };
66 defer parser.args.deinit(gpa);
67
68 parser.start() catch |err| switch (err) {
69 error.UnexpectedToken => {
70 const last_token_id = parser.it.pos - 1;
71 const last_token = parser.it.get(last_token_id);
72 const lcol = line_col.items[last_token_id];
73 return diags.failParse(path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
74 @tagName(last_token.id),
75 last_token.get(data),
76 lcol.line,
77 lcol.column,
78 });
79 },
80 else => |e| return e,
81 };
82 return .{
83 .path = path,
84 .cpu_arch = parser.cpu_arch,
85 .args = try parser.args.toOwnedSlice(gpa),
86 };
87}
88
89const LineColumn = struct {
90 line: usize,
91 column: usize,
92};
93
94const Command = enum {
95 output_format,
96 input,
97 group,
98 as_needed,
99
100 fn fromString(s: []const u8) ?Command {
101 inline for (@typeInfo(Command).@"enum".fields) |field| {
102 const upper_name = n: {
103 comptime var buf: [field.name.len]u8 = undefined;
104 inline for (field.name, 0..) |c, i| {
105 buf[i] = comptime std.ascii.toUpper(c);
106 }
107 break :n buf;
108 };
109 if (std.mem.eql(u8, &upper_name, s)) return @field(Command, field.name);
110 }
111 return null;
112 }
113};
114
115const Parser = struct {
116 gpa: Allocator,
117 source: []const u8,
118 it: *TokenIterator,
119
120 cpu_arch: ?std.Target.Cpu.Arch,
121 args: std.ArrayListUnmanaged(Arg),
122
123 fn start(parser: *Parser) !void {
124 while (true) {
125 parser.skipAny(&.{ .comment, .new_line });
126
127 if (parser.maybe(.command)) |cmd_id| {
128 const cmd = parser.getCommand(cmd_id);
129 switch (cmd) {
130 .output_format => parser.cpu_arch = try parser.outputFormat(),
131 // TODO we should verify that group only contains libraries
132 .input, .group => try parser.group(),
133 else => return error.UnexpectedToken,
134 }
135 } else break;
136 }
137
138 if (parser.it.next()) |tok| switch (tok.id) {
139 .eof => {},
140 else => return error.UnexpectedToken,
141 };
142 }
143
144 fn outputFormat(p: *Parser) !std.Target.Cpu.Arch {
145 const value = value: {
146 if (p.skip(&.{.lparen})) {
147 const value_id = try p.require(.literal);
148 const value = p.it.get(value_id);
149 _ = try p.require(.rparen);
150 break :value value.get(p.source);
151 } else if (p.skip(&.{ .new_line, .lbrace })) {
152 const value_id = try p.require(.literal);
153 const value = p.it.get(value_id);
154 _ = p.skip(&.{.new_line});
155 _ = try p.require(.rbrace);
156 break :value value.get(p.source);
157 } else return error.UnexpectedToken;
158 };
159 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
160 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
161 return error.UnknownCpuArch;
162 }
163
164 fn group(p: *Parser) !void {
165 const gpa = p.gpa;
166 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
167
168 while (true) {
169 if (p.maybe(.literal)) |tok_id| {
170 const tok = p.it.get(tok_id);
171 const path = tok.get(p.source);
172 try p.args.append(gpa, .{ .path = path, .needed = true });
173 } else if (p.maybe(.command)) |cmd_id| {
174 const cmd = p.getCommand(cmd_id);
175 switch (cmd) {
176 .as_needed => try p.asNeeded(),
177 else => return error.UnexpectedToken,
178 }
179 } else break;
180 }
181
182 _ = try p.require(.rparen);
183 }
184
185 fn asNeeded(p: *Parser) !void {
186 const gpa = p.gpa;
187 if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
188
189 while (p.maybe(.literal)) |tok_id| {
190 const tok = p.it.get(tok_id);
191 const path = tok.get(p.source);
192 try p.args.append(gpa, .{ .path = path, .needed = false });
193 }
194
195 _ = try p.require(.rparen);
196 }
197
198 fn skip(p: *Parser, comptime ids: []const Token.Id) bool {
199 const pos = p.it.pos;
200 inline for (ids) |id| {
201 const tok = p.it.next() orelse return false;
202 if (tok.id != id) {
203 p.it.seekTo(pos);
204 return false;
205 }
206 }
207 return true;
208 }
209
210 fn skipAny(p: *Parser, comptime ids: []const Token.Id) void {
211 outer: while (p.it.next()) |tok| {
212 inline for (ids) |id| {
213 if (id == tok.id) continue :outer;
214 }
215 break p.it.seekBy(-1);
216 }
217 }
218
219 fn maybe(p: *Parser, comptime id: Token.Id) ?Token.Index {
220 const pos = p.it.pos;
221 const tok = p.it.next() orelse return null;
222 if (tok.id == id) return pos;
223 p.it.seekBy(-1);
224 return null;
225 }
226
227 fn require(p: *Parser, comptime id: Token.Id) !Token.Index {
228 return p.maybe(id) orelse return error.UnexpectedToken;
229 }
230
231 fn getCommand(p: *Parser, index: Token.Index) Command {
232 const tok = p.it.get(index);
233 assert(tok.id == .command);
234 return Command.fromString(tok.get(p.source)).?;
235 }
236};
237
238const Token = struct {
239 id: Id,
240 start: usize,
241 end: usize,
242
243 const Id = enum {
244 eof,
245 invalid,
246
247 new_line,
248 lparen, // (
249 rparen, // )
250 lbrace, // {
251 rbrace, // }
252
253 comment, // /* */
254
255 command, // literal with special meaning, see Command
256 literal,
257 };
258
259 const Index = usize;
260
261 fn get(tok: Token, source: []const u8) []const u8 {
262 return source[tok.start..tok.end];
263 }
264};
265
266const Tokenizer = struct {
267 source: []const u8,
268 index: usize = 0,
269
270 fn matchesPattern(comptime pattern: []const u8, slice: []const u8) bool {
271 comptime var count: usize = 0;
272 inline while (count < pattern.len) : (count += 1) {
273 if (count >= slice.len) return false;
274 const c = slice[count];
275 if (pattern[count] != c) return false;
276 }
277 return true;
278 }
279
280 fn matches(tok: Tokenizer, comptime pattern: []const u8) bool {
281 return matchesPattern(pattern, tok.source[tok.index..]);
282 }
283
284 fn isCommand(tok: Tokenizer, start: usize, end: usize) bool {
285 return if (Command.fromString(tok.source[start..end]) == null) false else true;
286 }
287
288 fn next(tok: *Tokenizer) Token {
289 var result = Token{
290 .id = .eof,
291 .start = tok.index,
292 .end = undefined,
293 };
294
295 var state: enum {
296 start,
297 comment,
298 literal,
299 } = .start;
300
301 while (tok.index < tok.source.len) : (tok.index += 1) {
302 const c = tok.source[tok.index];
303 switch (state) {
304 .start => switch (c) {
305 ' ', '\t' => result.start += 1,
306
307 '\n' => {
308 result.id = .new_line;
309 tok.index += 1;
310 break;
311 },
312
313 '\r' => {
314 if (tok.matches("\r\n")) {
315 result.id = .new_line;
316 tok.index += "\r\n".len;
317 } else {
318 result.id = .invalid;
319 tok.index += 1;
320 }
321 break;
322 },
323
324 '/' => if (tok.matches("/*")) {
325 state = .comment;
326 tok.index += "/*".len;
327 } else {
328 state = .literal;
329 },
330
331 '(' => {
332 result.id = .lparen;
333 tok.index += 1;
334 break;
335 },
336
337 ')' => {
338 result.id = .rparen;
339 tok.index += 1;
340 break;
341 },
342
343 '{' => {
344 result.id = .lbrace;
345 tok.index += 1;
346 break;
347 },
348
349 '}' => {
350 result.id = .rbrace;
351 tok.index += 1;
352 break;
353 },
354
355 else => state = .literal,
356 },
357
358 .comment => switch (c) {
359 '*' => if (tok.matches("*/")) {
360 result.id = .comment;
361 tok.index += "*/".len;
362 break;
363 },
364 else => {},
365 },
366
367 .literal => switch (c) {
368 ' ', '(', '\n' => {
369 if (tok.isCommand(result.start, tok.index)) {
370 result.id = .command;
371 } else {
372 result.id = .literal;
373 }
374 break;
375 },
376
377 ')' => {
378 result.id = .literal;
379 break;
380 },
381
382 '\r' => {
383 if (tok.matches("\r\n")) {
384 if (tok.isCommand(result.start, tok.index)) {
385 result.id = .command;
386 } else {
387 result.id = .literal;
388 }
389 } else {
390 result.id = .invalid;
391 tok.index += 1;
392 }
393 break;
394 },
395
396 else => {},
397 },
398 }
399 }
400
401 result.end = tok.index;
402 return result;
403 }
404};
405
406const TokenIterator = struct {
407 tokens: []const Token,
408 pos: Token.Index = 0,
409
410 fn next(it: *TokenIterator) ?Token {
411 const token = it.peek() orelse return null;
412 it.pos += 1;
413 return token;
414 }
415
416 fn peek(it: TokenIterator) ?Token {
417 if (it.pos >= it.tokens.len) return null;
418 return it.tokens[it.pos];
419 }
420
421 fn reset(it: *TokenIterator) void {
422 it.pos = 0;
423 }
424
425 fn seekTo(it: *TokenIterator, pos: Token.Index) void {
426 it.pos = pos;
427 }
428
429 fn seekBy(it: *TokenIterator, offset: isize) void {
430 const new_pos = @as(isize, @bitCast(it.pos)) + offset;
431 if (new_pos < 0) {
432 it.pos = 0;
433 } else {
434 it.pos = @as(usize, @intCast(new_pos));
435 }
436 }
437
438 fn get(it: *TokenIterator, pos: Token.Index) Token {
439 assert(pos < it.tokens.len);
440 return it.tokens[pos];
441 }
442};
443
444const LdScript = @This();
445const Diags = @import("../link.zig").Diags;
446
447const std = @import("std");
448const assert = std.debug.assert;
449const Path = std.Build.Cache.Path;
450const Allocator = std.mem.Allocator;