| ... | ... | @@ -454,7 +454,7 @@ pub const Payload = struct { |
| 454 | 454 | data: struct { |
| 455 | 455 | cond: Node, |
| 456 | 456 | body: Node, |
| 457 | | cont_expr: ?Node |
| 457 | cont_expr: ?Node, |
| 458 | 458 | }, |
| 459 | 459 | }; |
| 460 | 460 | |
| ... | ... | @@ -568,7 +568,7 @@ pub const Payload = struct { |
| 568 | 568 | base: Payload, |
| 569 | 569 | data: struct { |
| 570 | 570 | label: ?[]const u8, |
| 571 | | stmts: []Node |
| 571 | stmts: []Node, |
| 572 | 572 | }, |
| 573 | 573 | }; |
| 574 | 574 | |
| ... | ... | @@ -640,6 +640,182 @@ pub const Payload = struct { |
| 640 | 640 | }; |
| 641 | 641 | |
| 642 | 642 | /// Converts the nodes into a Zig ast. |
| 643 | | pub fn render(allocator: *Allocator, nodes: []const Node) !std.zig.ast.Tree { |
| 644 | | @panic("TODO"); |
| 643 | /// Caller must free the source slice. |
| 644 | pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree { |
| 645 | var ctx = Context{ |
| 646 | .gpa = gpa, |
| 647 | .buf = std.ArrayList(u8).init(gpa), |
| 648 | }; |
| 649 | defer ctx.buf.deinit(); |
| 650 | defer ctx.nodes.deinit(gpa); |
| 651 | defer ctx.extra_data.deinit(gpa); |
| 652 | defer ctx.tokens.deinit(gpa); |
| 653 | |
| 654 | // Estimate that each top level node has 25 child nodes. |
| 655 | const estimated_node_count = nodes.len * 25; |
| 656 | try ctx.nodes.ensureCapacity(gpa, estimated_node_count); |
| 657 | |
| 658 | ctx.nodes.appendAssumeCapacity(.{ |
| 659 | .tag = .root, |
| 660 | .main_token = 0, |
| 661 | .data = .{ |
| 662 | .lhs = undefined, |
| 663 | .rhs = undefined, |
| 664 | }, |
| 665 | }); |
| 666 | const root_members = try renderNodes(&ctx, nodes); |
| 667 | ctx.nodes.items(.data)[0] = .{ |
| 668 | .lhs = root_members.start, |
| 669 | .rhs = root_members.end, |
| 670 | }; |
| 671 | |
| 672 | try ctx.tokens.append(gpa, .{ |
| 673 | .tag = .eof, |
| 674 | .start = @intCast(u32, ctx.buf.items.len), |
| 675 | }); |
| 676 | |
| 677 | return std.zig.ast.Tree{ |
| 678 | .source = ctx.buf.toOwnedSlice(), |
| 679 | .tokens = ctx.tokens.toOwnedSlice(), |
| 680 | .nodes = ctx.nodes.toOwnedSlice(), |
| 681 | .extra_data = ctx.extra_data.toOwnedSlice(gpa), |
| 682 | .errors = &.{}, |
| 683 | }; |
| 684 | } |
| 685 | |
| 686 | const NodeIndex = std.zig.ast.Node.Index; |
| 687 | const NodeSubRange = std.zig.ast.Node.SubRange; |
| 688 | const TokenIndex = std.zig.ast.TokenIndex; |
| 689 | const TokenTag = std.zig.Token.Tag; |
| 690 | |
| 691 | const Context = struct { |
| 692 | gpa: *Allocator, |
| 693 | buf: std.ArrayList(u8) = .{}, |
| 694 | nodes: std.zig.ast.NodeList = .{}, |
| 695 | extra_data: std.ArrayListUnmanaged(std.zig.ast.Node.Index) = .{}, |
| 696 | tokens: std.zig.ast.TokenList = .{}, |
| 697 | |
| 698 | fn appendTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex { |
| 699 | const start_index = c.buf.items.len; |
| 700 | try c.buf.writer().print(format ++ " ", args); |
| 701 | |
| 702 | try c.tokens.append(c.gpa, .{ |
| 703 | .tag = tag, |
| 704 | .start = @intCast(u32, start_index), |
| 705 | }); |
| 706 | |
| 707 | return @intCast(u32, c.tokens.len - 1); |
| 708 | } |
| 709 | |
| 710 | fn appendToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { |
| 711 | std.debug.assert(tag != .identifier); // use appendIdentifier |
| 712 | return appendTokenFmt(c, tag, "{s}", .{bytes}); |
| 713 | } |
| 714 | |
| 715 | fn appendIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { |
| 716 | return appendTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)}); |
| 717 | } |
| 718 | |
| 719 | fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { |
| 720 | try c.extra_data.appendSlice(c.gpa, list); |
| 721 | return NodeSubRange{ |
| 722 | .start = @intCast(NodeIndex, c.extra_data.items.len - list.len), |
| 723 | .end = @intCast(NodeIndex, c.extra_data.items.len), |
| 724 | }; |
| 725 | } |
| 726 | |
| 727 | fn appendNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex { |
| 728 | const result = @intCast(NodeIndex, c.nodes.len); |
| 729 | try c.nodes.append(c.gpa, elem); |
| 730 | return result; |
| 731 | } |
| 732 | }; |
| 733 | |
| 734 | fn renderNodes(c: *Context, nodes: []const Node) !NodeSubRange { |
| 735 | var result = std.ArrayList(NodeIndex).init(c.gpa); |
| 736 | defer result.deinit(); |
| 737 | |
| 738 | for (nodes) |node| { |
| 739 | const res = try renderNode(c, node); |
| 740 | if (res == 0) continue; |
| 741 | try result.append(res); |
| 742 | } |
| 743 | |
| 744 | return try c.listToSpan(result.items); |
| 745 | } |
| 746 | |
| 747 | fn renderNode(c: *Context, node: Node) !NodeIndex { |
| 748 | switch (node.tag()) { |
| 749 | .warning => { |
| 750 | const payload = node.castTag(.warning).?; |
| 751 | try c.buf.appendSlice(payload.data); |
| 752 | try c.buf.append('\n'); |
| 753 | return 0; |
| 754 | }, |
| 755 | .usingnamespace_builtins => { |
| 756 | // pub usingnamespace @import("std").c.builtins; |
| 757 | _ = try c.appendToken(.keyword_pub, "pub"); |
| 758 | const usingnamespace_token = try c.appendToken(.keyword_usingnamespace, "usingnamespace"); |
| 759 | const import_node = try renderStdImport(c, "c", "builtins"); |
| 760 | _ = try c.appendToken(.semicolon, ";"); |
| 761 | |
| 762 | return c.appendNode(.{ |
| 763 | .tag = .@"usingnamespace", |
| 764 | .main_token = usingnamespace_token, |
| 765 | .data = .{ |
| 766 | .lhs = import_node, |
| 767 | .rhs = undefined, |
| 768 | }, |
| 769 | }); |
| 770 | }, |
| 771 | else => { |
| 772 | try c.buf.writer().print("// TODO renderNode {}\n", .{node.tag()}); |
| 773 | return @as(u32, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' |
| 774 | }, |
| 775 | } |
| 776 | } |
| 777 | |
| 778 | fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { |
| 779 | const import_tok = try c.appendToken(.builtin, "@import"); |
| 780 | _ = try c.appendToken(.l_paren, "("); |
| 781 | |
| 782 | const std_tok = try c.appendToken(.string_literal, "\"std\""); |
| 783 | const std_node = try c.appendNode(.{ |
| 784 | .tag = .string_literal, |
| 785 | .main_token = std_tok, |
| 786 | .data = .{ |
| 787 | .lhs = std_tok, |
| 788 | .rhs = std_tok, |
| 789 | }, |
| 790 | }); |
| 791 | |
| 792 | _ = try c.appendToken(.r_paren, ")"); |
| 793 | |
| 794 | const import_node = try c.appendNode(.{ |
| 795 | .tag = .builtin_call_two, |
| 796 | .main_token = import_tok, |
| 797 | .data = .{ |
| 798 | .lhs = std_node, |
| 799 | .rhs = 0, |
| 800 | }, |
| 801 | }); |
| 802 | |
| 803 | var access_chain = import_node; |
| 804 | access_chain = try c.appendNode(.{ |
| 805 | .tag = .field_access, |
| 806 | .main_token = try c.appendToken(.period, "."), |
| 807 | .data = .{ |
| 808 | .lhs = access_chain, |
| 809 | .rhs = try c.appendIdentifier(first), |
| 810 | }, |
| 811 | }); |
| 812 | access_chain = try c.appendNode(.{ |
| 813 | .tag = .field_access, |
| 814 | .main_token = try c.appendToken(.period, "."), |
| 815 | .data = .{ |
| 816 | .lhs = access_chain, |
| 817 | .rhs = try c.appendIdentifier(second), |
| 818 | }, |
| 819 | }); |
| 820 | return access_chain; |
| 645 | 821 | } |