authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-15 21:12:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 17:20:02-07:00
log6c4a104822679e92e094b166cc11b56b43f84a33
tree144422948210c4bf9c58279c7bad52bd5c3539d8
parent1a20b467ea3f480306317a6145d910d8c44a9b48

std.zig.Ast: update to new I/O API


1 files changed, 89 insertions(+), 97 deletions(-)

lib/std/zig/Ast.zig+89-97
......@@ -4,6 +4,16 @@
44//! For Zon syntax, the root node is at nodes[0] and contains lhs as the node
55//! index of the main expression.
66
7const std = @import("../std.zig");
8const assert = std.debug.assert;
9const testing = std.testing;
10const mem = std.mem;
11const Token = std.zig.Token;
12const Ast = @This();
13const Allocator = std.mem.Allocator;
14const Parse = @import("Parse.zig");
15const Writer = std.Io.Writer;
16
717/// Reference to externally-owned data.
818source: [:0]const u8,
919
......@@ -128,12 +138,6 @@ pub fn deinit(tree: *Ast, gpa: Allocator) void {
128138 tree.* = undefined;
129139}
130140
131pub const RenderError = error{
132 /// Ran out of memory allocating call stack frames to complete rendering, or
133 /// ran out of memory allocating space in the output buffer.
134 OutOfMemory,
135};
136
137141pub const Mode = enum { zig, zon };
138142
139143/// Result should be freed with tree.deinit() when there are
......@@ -199,27 +203,25 @@ pub fn parse(gpa: Allocator, source: [:0]const u8, mode: Mode) Allocator.Error!A
199203
200204/// `gpa` is used for allocating the resulting formatted source code.
201205/// Caller owns the returned slice of bytes, allocated with `gpa`.
202pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {
203 var buffer = std.ArrayList(u8).init(gpa);
204 defer buffer.deinit();
205
206 try tree.renderToArrayList(&buffer, .{});
207 return buffer.toOwnedSlice();
206pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {
207 var aw: std.io.Writer.Allocating = .init(gpa);
208 defer aw.deinit();
209 render(tree, gpa, &aw.writer, .{}) catch |err| switch (err) {
210 error.WriteFailed => return error.OutOfMemory,
211 };
212 return aw.toOwnedSlice();
208213}
209214
210pub const Fixups = private_render.Fixups;
215pub const Render = @import("Ast/Render.zig");
211216
212pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8), fixups: Fixups) RenderError!void {
213 return @import("./render.zig").renderTree(buffer, tree, fixups);
217pub fn render(tree: Ast, gpa: Allocator, w: *Writer, fixups: Render.Fixups) Render.Error!void {
218 return Render.tree(gpa, w, tree, fixups);
214219}
215220
216221/// Returns an extra offset for column and byte offset of errors that
217222/// should point after the token in the error message.
218223pub fn errorOffset(tree: Ast, parse_error: Error) u32 {
219 return if (parse_error.token_is_prev)
220 @as(u32, @intCast(tree.tokenSlice(parse_error.token).len))
221 else
222 0;
224 return if (parse_error.token_is_prev) @intCast(tree.tokenSlice(parse_error.token).len) else 0;
223225}
224226
225227pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location {
......@@ -318,254 +320,254 @@ pub fn rootDecls(tree: Ast) []const Node.Index {
318320 }
319321}
320322
321pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
323pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void {
322324 switch (parse_error.tag) {
323325 .asterisk_after_ptr_deref => {
324326 // Note that the token will point at the `.*` but ideally the source
325327 // location would point to the `*` after the `.*`.
326 return stream.writeAll("'.*' cannot be followed by '*'; are you missing a space?");
328 return w.writeAll("'.*' cannot be followed by '*'; are you missing a space?");
327329 },
328330 .chained_comparison_operators => {
329 return stream.writeAll("comparison operators cannot be chained");
331 return w.writeAll("comparison operators cannot be chained");
330332 },
331333 .decl_between_fields => {
332 return stream.writeAll("declarations are not allowed between container fields");
334 return w.writeAll("declarations are not allowed between container fields");
333335 },
334336 .expected_block => {
335 return stream.print("expected block, found '{s}'", .{
337 return w.print("expected block, found '{s}'", .{
336338 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
337339 });
338340 },
339341 .expected_block_or_assignment => {
340 return stream.print("expected block or assignment, found '{s}'", .{
342 return w.print("expected block or assignment, found '{s}'", .{
341343 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
342344 });
343345 },
344346 .expected_block_or_expr => {
345 return stream.print("expected block or expression, found '{s}'", .{
347 return w.print("expected block or expression, found '{s}'", .{
346348 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
347349 });
348350 },
349351 .expected_block_or_field => {
350 return stream.print("expected block or field, found '{s}'", .{
352 return w.print("expected block or field, found '{s}'", .{
351353 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
352354 });
353355 },
354356 .expected_container_members => {
355 return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{
357 return w.print("expected test, comptime, var decl, or container field, found '{s}'", .{
356358 tree.tokenTag(parse_error.token).symbol(),
357359 });
358360 },
359361 .expected_expr => {
360 return stream.print("expected expression, found '{s}'", .{
362 return w.print("expected expression, found '{s}'", .{
361363 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
362364 });
363365 },
364366 .expected_expr_or_assignment => {
365 return stream.print("expected expression or assignment, found '{s}'", .{
367 return w.print("expected expression or assignment, found '{s}'", .{
366368 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
367369 });
368370 },
369371 .expected_expr_or_var_decl => {
370 return stream.print("expected expression or var decl, found '{s}'", .{
372 return w.print("expected expression or var decl, found '{s}'", .{
371373 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
372374 });
373375 },
374376 .expected_fn => {
375 return stream.print("expected function, found '{s}'", .{
377 return w.print("expected function, found '{s}'", .{
376378 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
377379 });
378380 },
379381 .expected_inlinable => {
380 return stream.print("expected 'while' or 'for', found '{s}'", .{
382 return w.print("expected 'while' or 'for', found '{s}'", .{
381383 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
382384 });
383385 },
384386 .expected_labelable => {
385 return stream.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{
387 return w.print("expected 'while', 'for', 'inline', or '{{', found '{s}'", .{
386388 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
387389 });
388390 },
389391 .expected_param_list => {
390 return stream.print("expected parameter list, found '{s}'", .{
392 return w.print("expected parameter list, found '{s}'", .{
391393 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
392394 });
393395 },
394396 .expected_prefix_expr => {
395 return stream.print("expected prefix expression, found '{s}'", .{
397 return w.print("expected prefix expression, found '{s}'", .{
396398 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
397399 });
398400 },
399401 .expected_primary_type_expr => {
400 return stream.print("expected primary type expression, found '{s}'", .{
402 return w.print("expected primary type expression, found '{s}'", .{
401403 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
402404 });
403405 },
404406 .expected_pub_item => {
405 return stream.writeAll("expected function or variable declaration after pub");
407 return w.writeAll("expected function or variable declaration after pub");
406408 },
407409 .expected_return_type => {
408 return stream.print("expected return type expression, found '{s}'", .{
410 return w.print("expected return type expression, found '{s}'", .{
409411 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
410412 });
411413 },
412414 .expected_semi_or_else => {
413 return stream.writeAll("expected ';' or 'else' after statement");
415 return w.writeAll("expected ';' or 'else' after statement");
414416 },
415417 .expected_semi_or_lbrace => {
416 return stream.writeAll("expected ';' or block after function prototype");
418 return w.writeAll("expected ';' or block after function prototype");
417419 },
418420 .expected_statement => {
419 return stream.print("expected statement, found '{s}'", .{
421 return w.print("expected statement, found '{s}'", .{
420422 tree.tokenTag(parse_error.token).symbol(),
421423 });
422424 },
423425 .expected_suffix_op => {
424 return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
426 return w.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
425427 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
426428 });
427429 },
428430 .expected_type_expr => {
429 return stream.print("expected type expression, found '{s}'", .{
431 return w.print("expected type expression, found '{s}'", .{
430432 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
431433 });
432434 },
433435 .expected_var_decl => {
434 return stream.print("expected variable declaration, found '{s}'", .{
436 return w.print("expected variable declaration, found '{s}'", .{
435437 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
436438 });
437439 },
438440 .expected_var_decl_or_fn => {
439 return stream.print("expected variable declaration or function, found '{s}'", .{
441 return w.print("expected variable declaration or function, found '{s}'", .{
440442 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
441443 });
442444 },
443445 .expected_loop_payload => {
444 return stream.print("expected loop payload, found '{s}'", .{
446 return w.print("expected loop payload, found '{s}'", .{
445447 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
446448 });
447449 },
448450 .expected_container => {
449 return stream.print("expected a struct, enum or union, found '{s}'", .{
451 return w.print("expected a struct, enum or union, found '{s}'", .{
450452 tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev)).symbol(),
451453 });
452454 },
453455 .extern_fn_body => {
454 return stream.writeAll("extern functions have no body");
456 return w.writeAll("extern functions have no body");
455457 },
456458 .extra_addrspace_qualifier => {
457 return stream.writeAll("extra addrspace qualifier");
459 return w.writeAll("extra addrspace qualifier");
458460 },
459461 .extra_align_qualifier => {
460 return stream.writeAll("extra align qualifier");
462 return w.writeAll("extra align qualifier");
461463 },
462464 .extra_allowzero_qualifier => {
463 return stream.writeAll("extra allowzero qualifier");
465 return w.writeAll("extra allowzero qualifier");
464466 },
465467 .extra_const_qualifier => {
466 return stream.writeAll("extra const qualifier");
468 return w.writeAll("extra const qualifier");
467469 },
468470 .extra_volatile_qualifier => {
469 return stream.writeAll("extra volatile qualifier");
471 return w.writeAll("extra volatile qualifier");
470472 },
471473 .ptr_mod_on_array_child_type => {
472 return stream.print("pointer modifier '{s}' not allowed on array child type", .{
474 return w.print("pointer modifier '{s}' not allowed on array child type", .{
473475 tree.tokenTag(parse_error.token).symbol(),
474476 });
475477 },
476478 .invalid_bit_range => {
477 return stream.writeAll("bit range not allowed on slices and arrays");
479 return w.writeAll("bit range not allowed on slices and arrays");
478480 },
479481 .same_line_doc_comment => {
480 return stream.writeAll("same line documentation comment");
482 return w.writeAll("same line documentation comment");
481483 },
482484 .unattached_doc_comment => {
483 return stream.writeAll("unattached documentation comment");
485 return w.writeAll("unattached documentation comment");
484486 },
485487 .test_doc_comment => {
486 return stream.writeAll("documentation comments cannot be attached to tests");
488 return w.writeAll("documentation comments cannot be attached to tests");
487489 },
488490 .comptime_doc_comment => {
489 return stream.writeAll("documentation comments cannot be attached to comptime blocks");
491 return w.writeAll("documentation comments cannot be attached to comptime blocks");
490492 },
491493 .varargs_nonfinal => {
492 return stream.writeAll("function prototype has parameter after varargs");
494 return w.writeAll("function prototype has parameter after varargs");
493495 },
494496 .expected_continue_expr => {
495 return stream.writeAll("expected ':' before while continue expression");
497 return w.writeAll("expected ':' before while continue expression");
496498 },
497499
498500 .expected_semi_after_decl => {
499 return stream.writeAll("expected ';' after declaration");
501 return w.writeAll("expected ';' after declaration");
500502 },
501503 .expected_semi_after_stmt => {
502 return stream.writeAll("expected ';' after statement");
504 return w.writeAll("expected ';' after statement");
503505 },
504506 .expected_comma_after_field => {
505 return stream.writeAll("expected ',' after field");
507 return w.writeAll("expected ',' after field");
506508 },
507509 .expected_comma_after_arg => {
508 return stream.writeAll("expected ',' after argument");
510 return w.writeAll("expected ',' after argument");
509511 },
510512 .expected_comma_after_param => {
511 return stream.writeAll("expected ',' after parameter");
513 return w.writeAll("expected ',' after parameter");
512514 },
513515 .expected_comma_after_initializer => {
514 return stream.writeAll("expected ',' after initializer");
516 return w.writeAll("expected ',' after initializer");
515517 },
516518 .expected_comma_after_switch_prong => {
517 return stream.writeAll("expected ',' after switch prong");
519 return w.writeAll("expected ',' after switch prong");
518520 },
519521 .expected_comma_after_for_operand => {
520 return stream.writeAll("expected ',' after for operand");
522 return w.writeAll("expected ',' after for operand");
521523 },
522524 .expected_comma_after_capture => {
523 return stream.writeAll("expected ',' after for capture");
525 return w.writeAll("expected ',' after for capture");
524526 },
525527 .expected_initializer => {
526 return stream.writeAll("expected field initializer");
528 return w.writeAll("expected field initializer");
527529 },
528530 .mismatched_binary_op_whitespace => {
529 return stream.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?});
531 return w.print("binary operator '{s}' has whitespace on one side, but not the other", .{tree.tokenTag(parse_error.token).lexeme().?});
530532 },
531533 .invalid_ampersand_ampersand => {
532 return stream.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND");
534 return w.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND");
533535 },
534536 .c_style_container => {
535 return stream.print("'{s} {s}' is invalid", .{
537 return w.print("'{s} {s}' is invalid", .{
536538 parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token),
537539 });
538540 },
539541 .zig_style_container => {
540 return stream.print("to declare a container do 'const {s} = {s}'", .{
542 return w.print("to declare a container do 'const {s} = {s}'", .{
541543 tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(),
542544 });
543545 },
544546 .previous_field => {
545 return stream.writeAll("field before declarations here");
547 return w.writeAll("field before declarations here");
546548 },
547549 .next_field => {
548 return stream.writeAll("field after declarations here");
550 return w.writeAll("field after declarations here");
549551 },
550552 .expected_var_const => {
551 return stream.writeAll("expected 'var' or 'const' before variable declaration");
553 return w.writeAll("expected 'var' or 'const' before variable declaration");
552554 },
553555 .wrong_equal_var_decl => {
554 return stream.writeAll("variable initialized with '==' instead of '='");
556 return w.writeAll("variable initialized with '==' instead of '='");
555557 },
556558 .var_const_decl => {
557 return stream.writeAll("use 'var' or 'const' to declare variable");
559 return w.writeAll("use 'var' or 'const' to declare variable");
558560 },
559561 .extra_for_capture => {
560 return stream.writeAll("extra capture in for loop");
562 return w.writeAll("extra capture in for loop");
561563 },
562564 .for_input_not_captured => {
563 return stream.writeAll("for input is not captured");
565 return w.writeAll("for input is not captured");
564566 },
565567
566568 .invalid_byte => {
567569 const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..];
568 return stream.print("{s} contains invalid byte: '{f}'", .{
570 return w.print("{s} contains invalid byte: '{f}'", .{
569571 switch (tok_slice[0]) {
570572 '\'' => "character literal",
571573 '"', '\\' => "string literal",
......@@ -580,10 +582,10 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
580582 const found_tag = tree.tokenTag(parse_error.token + @intFromBool(parse_error.token_is_prev));
581583 const expected_symbol = parse_error.extra.expected_tag.symbol();
582584 switch (found_tag) {
583 .invalid => return stream.print("expected '{s}', found invalid bytes", .{
585 .invalid => return w.print("expected '{s}', found invalid bytes", .{
584586 expected_symbol,
585587 }),
586 else => return stream.print("expected '{s}', found '{s}'", .{
588 else => return w.print("expected '{s}', found '{s}'", .{
587589 expected_symbol, found_tag.symbol(),
588590 }),
589591 }
......@@ -4136,17 +4138,7 @@ pub fn tokensToSpan(tree: *const Ast, start: Ast.TokenIndex, end: Ast.TokenIndex
41364138 return Span{ .start = start_off, .end = end_off, .main = tree.tokenStart(main) };
41374139}
41384140
4139const std = @import("../std.zig");
4140const assert = std.debug.assert;
4141const testing = std.testing;
4142const mem = std.mem;
4143const Token = std.zig.Token;
4144const Ast = @This();
4145const Allocator = std.mem.Allocator;
4146const Parse = @import("Parse.zig");
4147const private_render = @import("./render.zig");
4148
41494141test {
41504142 _ = Parse;
4151 _ = private_render;
4143 _ = Render;
41524144}