| ... | ... | @@ -398,7 +398,9 @@ pub fn fromZoirNodeAlloc( |
| 398 | 398 | .diag = diag, |
| 399 | 399 | }; |
| 400 | 400 | |
| 401 | | return parser.parseExpr(T, node); |
| 401 | var out: T = undefined; |
| 402 | try parser.parseExprInto(node, &out); |
| 403 | return out; |
| 402 | 404 | } |
| 403 | 405 | |
| 404 | 406 | /// Frees ZON values. |
| ... | ... | @@ -488,34 +490,36 @@ const Parser = struct { |
| 488 | 490 | |
| 489 | 491 | const ParseExprError = error{ ParseZon, OutOfMemory }; |
| 490 | 492 | |
| 491 | | fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T { |
| 492 | | return self.parseExprInner(T, node) catch |err| switch (err) { |
| 493 | | error.WrongType => return self.failExpectedType(T, node), |
| 493 | fn parseExprInto(self: *@This(), node: Zoir.Node.Index, out: anytype) ParseExprError!void { |
| 494 | return self.parseExprInnerInto(node, out) catch |err| switch (err) { |
| 495 | error.WrongType => return self.failExpectedType(@TypeOf(out.*), node), |
| 494 | 496 | else => |e| return e, |
| 495 | 497 | }; |
| 496 | 498 | } |
| 497 | 499 | |
| 498 | 500 | const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType }; |
| 499 | 501 | |
| 500 | | fn parseExprInner( |
| 502 | fn parseExprInnerInto( |
| 501 | 503 | self: *@This(), |
| 502 | | T: type, |
| 503 | 504 | node: Zoir.Node.Index, |
| 504 | | ) ParseExprInnerError!T { |
| 505 | | if (T == Zoir.Node.Index) { |
| 506 | | return node; |
| 505 | out: anytype, |
| 506 | ) ParseExprInnerError!void { |
| 507 | if (@TypeOf(out.*) == Zoir.Node.Index) { |
| 508 | out.* = node; |
| 509 | return; |
| 507 | 510 | } |
| 508 | 511 | |
| 509 | | switch (@typeInfo(T)) { |
| 512 | switch (@typeInfo(@TypeOf(out.*))) { |
| 510 | 513 | .optional => |optional| if (node.get(self.zoir) == .null) { |
| 511 | | return null; |
| 514 | out.* = null; |
| 512 | 515 | } else { |
| 513 | | return try self.parseExprInner(optional.child, node); |
| 516 | out.* = @as(optional.child, undefined); // Make non-null |
| 517 | try self.parseExprInnerInto(node, &out.*.?); // Parse value into non-null pointer |
| 514 | 518 | }, |
| 515 | | .bool => return self.parseBool(node), |
| 516 | | .int => return self.parseInt(T, node), |
| 517 | | .float => return self.parseFloat(T, node), |
| 518 | | .@"enum" => return self.parseEnumLiteral(T, node), |
| 519 | .bool => out.* = try self.parseBool(node), |
| 520 | .int => out.* = try self.parseInt(@TypeOf(out.*), node), |
| 521 | .float => out.* = try self.parseFloat(@TypeOf(out.*), node), |
| 522 | .@"enum" => out.* = try self.parseEnumLiteral(@TypeOf(out.*), node), |
| 519 | 523 | .pointer => |pointer| switch (pointer.size) { |
| 520 | 524 | .one => { |
| 521 | 525 | const slice = try self.gpa.alignedAlloc( |
| ... | ... | @@ -524,23 +528,20 @@ const Parser = struct { |
| 524 | 528 | 1, |
| 525 | 529 | ); |
| 526 | 530 | errdefer self.gpa.free(slice); |
| 527 | | const result = &slice[0]; |
| 528 | | result.* = try self.parseExprInner(pointer.child, node); |
| 529 | | return result; |
| 531 | const ptr = &slice[0]; |
| 532 | try self.parseExprInnerInto(node, ptr); |
| 533 | out.* = ptr; |
| 530 | 534 | }, |
| 531 | | .slice => return self.parseSlicePointer(T, node), |
| 535 | .slice => out.* = try self.parseSlicePointer(@TypeOf(out.*), node), |
| 532 | 536 | else => comptime unreachable, |
| 533 | 537 | }, |
| 534 | | .array => return self.parseArray(T, node), |
| 535 | | .vector => |vector| { |
| 536 | | const A = [vector.len]vector.child; |
| 537 | | return try self.parseArray(A, node); |
| 538 | | }, |
| 538 | .array => try self.parseArrayInto(node, out), |
| 539 | .vector => try self.parseVectorInto(node, out), |
| 539 | 540 | .@"struct" => |@"struct"| if (@"struct".is_tuple) |
| 540 | | return self.parseTuple(T, node) |
| 541 | try self.parseTupleInto(node, out) |
| 541 | 542 | else |
| 542 | | return self.parseStruct(T, node), |
| 543 | | .@"union" => return self.parseUnion(T, node), |
| 543 | try self.parseStructInto(node, out), |
| 544 | .@"union" => try self.parseUnionInto(node, out), |
| 544 | 545 | |
| 545 | 546 | else => comptime unreachable, |
| 546 | 547 | } |
| ... | ... | @@ -758,53 +759,61 @@ const Parser = struct { |
| 758 | 759 | free(self.gpa, item); |
| 759 | 760 | } |
| 760 | 761 | }; |
| 761 | | elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i))); |
| 762 | try self.parseExprInto(nodes.at(@intCast(i)), elem); |
| 762 | 763 | } |
| 763 | 764 | |
| 764 | 765 | return slice; |
| 765 | 766 | } |
| 766 | 767 | |
| 767 | | fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T { |
| 768 | fn parseArrayInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { |
| 768 | 769 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { |
| 769 | 770 | .array_literal => |nodes| nodes, |
| 770 | 771 | .empty_literal => .{ .start = node, .len = 0 }, |
| 771 | 772 | else => return error.WrongType, |
| 772 | 773 | }; |
| 773 | 774 | |
| 774 | | const array_info = @typeInfo(T).array; |
| 775 | const array = @typeInfo(@TypeOf(out.*)).array; |
| 775 | 776 | |
| 776 | 777 | // Check if the size matches |
| 777 | | if (nodes.len < array_info.len) { |
| 778 | if (nodes.len < array.len) { |
| 778 | 779 | return self.failNodeFmt( |
| 779 | 780 | node, |
| 780 | 781 | "expected {} array elements; found {}", |
| 781 | | .{ array_info.len, nodes.len }, |
| 782 | .{ array.len, nodes.len }, |
| 782 | 783 | ); |
| 783 | | } else if (nodes.len > array_info.len) { |
| 784 | } else if (nodes.len > array.len) { |
| 784 | 785 | return self.failNodeFmt( |
| 785 | | nodes.at(array_info.len), |
| 786 | nodes.at(array.len), |
| 786 | 787 | "index {} outside of array of length {}", |
| 787 | | .{ array_info.len, array_info.len }, |
| 788 | .{ array.len, array.len }, |
| 788 | 789 | ); |
| 789 | 790 | } |
| 790 | 791 | |
| 791 | 792 | // Parse the elements and return the array |
| 792 | | var result: T = undefined; |
| 793 | | for (&result, 0..) |*elem, i| { |
| 793 | inline for (out, 0..) |*elem, i| { |
| 794 | 794 | // If we fail to parse this field, free all fields before it |
| 795 | 795 | errdefer if (self.options.free_on_error) { |
| 796 | | for (result[0..i]) |item| { |
| 796 | for (out[0..i]) |item| { |
| 797 | 797 | free(self.gpa, item); |
| 798 | 798 | } |
| 799 | 799 | }; |
| 800 | 800 | |
| 801 | | elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i))); |
| 801 | try self.parseExprInto(nodes.at(@intCast(i)), elem); |
| 802 | } |
| 803 | |
| 804 | if (array.sentinel()) |s| { |
| 805 | out[array.len] = s; |
| 802 | 806 | } |
| 803 | | if (array_info.sentinel()) |s| result[result.len] = s; |
| 804 | | return result; |
| 805 | 807 | } |
| 806 | 808 | |
| 807 | | fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T { |
| 809 | fn parseVectorInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { |
| 810 | const vector = @typeInfo(@TypeOf(out.*)).vector; |
| 811 | var array: [vector.len]vector.child = undefined; |
| 812 | try self.parseArrayInto(node, &array); |
| 813 | out.* = array; |
| 814 | } |
| 815 | |
| 816 | fn parseStructInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { |
| 808 | 817 | const repr = node.get(self.zoir); |
| 809 | 818 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { |
| 810 | 819 | .struct_literal => |nodes| nodes, |
| ... | ... | @@ -812,7 +821,7 @@ const Parser = struct { |
| 812 | 821 | else => return error.WrongType, |
| 813 | 822 | }; |
| 814 | 823 | |
| 815 | | const info = @typeInfo(T).@"struct"; |
| 824 | const info = @typeInfo(@TypeOf(out.*)).@"struct"; |
| 816 | 825 | |
| 817 | 826 | // Build a map from field name to index. |
| 818 | 827 | // The special value `comptime_field` indicates that this is actually a comptime field. |
| ... | ... | @@ -826,7 +835,6 @@ const Parser = struct { |
| 826 | 835 | }; |
| 827 | 836 | |
| 828 | 837 | // Parse the struct |
| 829 | | var result: T = undefined; |
| 830 | 838 | var field_found: [info.field_names.len]bool = @splat(false); |
| 831 | 839 | |
| 832 | 840 | // If we fail partway through, free all already initialized fields |
| ... | ... | @@ -836,7 +844,7 @@ const Parser = struct { |
| 836 | 844 | switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) { |
| 837 | 845 | inline 0...(info.field_names.len - 1) => |name_index| { |
| 838 | 846 | const name = info.field_names[name_index]; |
| 839 | | free(self.gpa, @field(result, name)); |
| 847 | free(self.gpa, @field(out, name)); |
| 840 | 848 | }, |
| 841 | 849 | else => unreachable, // Can't be out of bounds |
| 842 | 850 | } |
| ... | ... | @@ -848,7 +856,7 @@ const Parser = struct { |
| 848 | 856 | const name = fields.names[i].get(self.zoir); |
| 849 | 857 | const field_index = field_indices.get(name) orelse { |
| 850 | 858 | if (self.options.ignore_unknown_fields) continue; |
| 851 | | return self.failUnexpected(T, "field", node, i, name); |
| 859 | return self.failUnexpected(@TypeOf(out.*), "field", node, i, name); |
| 852 | 860 | }; |
| 853 | 861 | if (field_index == comptime_field) { |
| 854 | 862 | return self.failComptimeField(node, i); |
| ... | ... | @@ -863,9 +871,9 @@ const Parser = struct { |
| 863 | 871 | inline 0...(info.field_names.len - 1) => |j| { |
| 864 | 872 | if (info.field_attrs[j].@"comptime") unreachable; |
| 865 | 873 | |
| 866 | | @field(result, info.field_names[j]) = try self.parseExpr( |
| 867 | | info.field_types[j], |
| 874 | try self.parseExprInto( |
| 868 | 875 | fields.vals.at(@intCast(i)), |
| 876 | &@field(out, info.field_names[j]), |
| 869 | 877 | ); |
| 870 | 878 | }, |
| 871 | 879 | else => unreachable, // Can't be out of bounds |
| ... | ... | @@ -879,7 +887,7 @@ const Parser = struct { |
| 879 | 887 | if (!found) { |
| 880 | 888 | const field_attrs = info.field_attrs[i]; |
| 881 | 889 | if (field_attrs.defaultValue(info.field_types[i])) |default| { |
| 882 | | @field(result, info.field_names[i]) = default; |
| 890 | @field(out, info.field_names[i]) = default; |
| 883 | 891 | } else { |
| 884 | 892 | return self.failNodeFmt( |
| 885 | 893 | node, |
| ... | ... | @@ -889,19 +897,16 @@ const Parser = struct { |
| 889 | 897 | } |
| 890 | 898 | } |
| 891 | 899 | } |
| 892 | | |
| 893 | | return result; |
| 894 | 900 | } |
| 895 | 901 | |
| 896 | | fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T { |
| 902 | fn parseTupleInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { |
| 897 | 903 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { |
| 898 | 904 | .array_literal => |nodes| nodes, |
| 899 | 905 | .empty_literal => .{ .start = node, .len = 0 }, |
| 900 | 906 | else => return error.WrongType, |
| 901 | 907 | }; |
| 902 | 908 | |
| 903 | | var result: T = undefined; |
| 904 | | const info = @typeInfo(T).@"struct"; |
| 909 | const info = @typeInfo(@TypeOf(out.*)).@"struct"; |
| 905 | 910 | |
| 906 | 911 | if (nodes.len > info.field_names.len) { |
| 907 | 912 | return self.failNodeFmt( |
| ... | ... | @@ -915,7 +920,7 @@ const Parser = struct { |
| 915 | 920 | // Check if we're out of bounds |
| 916 | 921 | if (i >= nodes.len) { |
| 917 | 922 | if (info.field_attrs[i].defaultValue(info.field_types[i])) |default| { |
| 918 | | @field(result, info.field_names[i]) = default; |
| 923 | @field(out.*, info.field_names[i]) = default; |
| 919 | 924 | } else { |
| 920 | 925 | return self.failNodeFmt(node, "missing tuple field with index {}", .{i}); |
| 921 | 926 | } |
| ... | ... | @@ -924,23 +929,21 @@ const Parser = struct { |
| 924 | 929 | errdefer if (self.options.free_on_error) { |
| 925 | 930 | inline for (0..i) |j| { |
| 926 | 931 | if (j >= i) break; |
| 927 | | free(self.gpa, result[j]); |
| 932 | free(self.gpa, out[j]); |
| 928 | 933 | } |
| 929 | 934 | }; |
| 930 | 935 | |
| 931 | 936 | if (info.field_attrs[i].@"comptime") { |
| 932 | 937 | return self.failComptimeField(node, i); |
| 933 | 938 | } else { |
| 934 | | result[i] = try self.parseExpr(info.field_types[i], nodes.at(i)); |
| 939 | try self.parseExprInto(nodes.at(i), &out[i]); |
| 935 | 940 | } |
| 936 | 941 | } |
| 937 | 942 | } |
| 938 | | |
| 939 | | return result; |
| 940 | 943 | } |
| 941 | 944 | |
| 942 | | fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T { |
| 943 | | const @"union" = @typeInfo(T).@"union"; |
| 945 | fn parseUnionInto(self: *@This(), node: Zoir.Node.Index, out: anytype) !void { |
| 946 | const @"union" = @typeInfo(@TypeOf(out.*)).@"union"; |
| 944 | 947 | |
| 945 | 948 | if (@"union".field_names.len == 0) comptime unreachable; |
| 946 | 949 | |
| ... | ... | @@ -966,7 +969,7 @@ const Parser = struct { |
| 966 | 969 | const field_index = b: { |
| 967 | 970 | const field_name_str = field_name.get(self.zoir); |
| 968 | 971 | break :b field_indices.get(field_name_str) orelse |
| 969 | | return self.failUnexpected(T, "field", node, null, field_name_str); |
| 972 | return self.failUnexpected(@TypeOf(out.*), "field", node, null, field_name_str); |
| 970 | 973 | }; |
| 971 | 974 | |
| 972 | 975 | // Initialize the union from the given field. |
| ... | ... | @@ -977,7 +980,7 @@ const Parser = struct { |
| 977 | 980 | return self.failNode(node, "expected union"); |
| 978 | 981 | |
| 979 | 982 | // Instantiate the union |
| 980 | | return @unionInit(T, @"union".field_names[i], {}); |
| 983 | out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], {}); |
| 981 | 984 | }, |
| 982 | 985 | else => unreachable, // Can't be out of bounds |
| 983 | 986 | } |
| ... | ... | @@ -992,15 +995,15 @@ const Parser = struct { |
| 992 | 995 | const field_name_str = field_name.get(self.zoir); |
| 993 | 996 | const field_val = struct_fields.vals.at(0); |
| 994 | 997 | const field_index = field_indices.get(field_name_str) orelse |
| 995 | | return self.failUnexpected(T, "field", node, 0, field_name_str); |
| 998 | return self.failUnexpected(@TypeOf(out.*), "field", node, 0, field_name_str); |
| 996 | 999 | |
| 997 | 1000 | switch (field_index) { |
| 998 | 1001 | inline 0...@"union".field_names.len - 1 => |i| { |
| 999 | 1002 | if (@"union".field_types[i] == void) { |
| 1000 | 1003 | return self.failNode(field_val, "expected type 'void'"); |
| 1001 | 1004 | } else { |
| 1002 | | const value = try self.parseExpr(@"union".field_types[i], field_val); |
| 1003 | | return @unionInit(T, @"union".field_names[i], value); |
| 1005 | out.* = @unionInit(@TypeOf(out.*), @"union".field_names[i], undefined); |
| 1006 | try self.parseExprInto(field_val, &@field(out.*, @"union".field_names[i])); |
| 1004 | 1007 | } |
| 1005 | 1008 | }, |
| 1006 | 1009 | else => unreachable, // Can't be out of bounds |