| ... | @@ -454,7 +454,7 @@ pub const Payload = struct { | ... | @@ -454,7 +454,7 @@ pub const Payload = struct { |
| 454 | data: struct { | 454 | data: struct { |
| 455 | cond: Node, | 455 | cond: Node, |
| 456 | body: Node, | 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,7 +568,7 @@ pub const Payload = struct { |
| 568 | base: Payload, | 568 | base: Payload, |
| 569 | data: struct { | 569 | data: struct { |
| 570 | label: ?[]const u8, | 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,6 +640,182 @@ pub const Payload = struct { |
| 640 | }; | 640 | }; |
| 641 | | 641 | |
| 642 | /// Converts the nodes into a Zig ast. | 642 | /// Converts the nodes into a Zig ast. |
| 643 | pub fn render(allocator: *Allocator, nodes: []const Node) !std.zig.ast.Tree { | 643 | /// Caller must free the source slice. |
| 644 | @panic("TODO"); | 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 | } |