lib/chant/src/parse/initializer.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const ast = @import("../ast/root.zig");
  3 const expression = @import("expression.zig");
  4 const state = @import("state/root.zig");
  5 
  6 const cursor = state.cursor;
  7 const diagnostic = state.diagnostic;
  8 const memory = state.memory;
  9 const Error = @import("error.zig").Error;
 10 const Parser = state.Parser;
 11 
 12 pub fn parse(parser: *Parser, c_type: *const ast.Type) Error!*ast.Expr {
 13     if (cursor.consume(parser, .lbrace)) {
 14         var items = std.ArrayListUnmanaged(ast.expr.InitializerItem).empty;
 15         if (!cursor.consume(parser, .rbrace)) {
 16             while (true) {
 17                 const path = try parseDesignator(parser);
 18                 const value_type = if (path.len == 0)
 19                     ast.types.element(c_type) orelse c_type
 20                 else blk: {
 21                     _ = try cursor.expect(parser, .assign);
 22                     break :blk try designatorType(parser, c_type, path);
 23                 };
 24                 try items.append(parser.arena, .{ .designator = path, .value = try parse(parser, value_type) });
 25                 if (cursor.consume(parser, .comma)) {
 26                     if (cursor.consume(parser, .rbrace)) break;
 27                     continue;
 28                 }
 29                 _ = try cursor.expect(parser, .rbrace);
 30                 break;
 31             }
 32         }
 33         const slice = try items.toOwnedSlice(parser.arena);
 34         if (ast.types.isArithmetic(c_type)) {
 35             if (slice.len == 0) {
 36                 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = 0, .type = &ast.types.int_type } });
 37             }
 38             if (slice.len == 1 and slice[0].designator.len == 0) return slice[0].value;
 39             return diagnostic.fail(parser, error.UnsupportedConstruct, "scalar initializer list has too many values");
 40         }
 41         return memory.create(parser, ast.Expr, .{ .initializer_list = .{ .type = c_type, .items = slice } });
 42     }
 43     return expression.parseAssignment(parser);
 44 }
 45 
 46 pub fn completeType(parser: *Parser, declared: *const ast.Type, initializer: ?*ast.Expr) Error!*const ast.Type {
 47     const value = initializer orelse return declared;
 48     const resolved = try completeArrayType(parser, declared, value);
 49     if (value.* == .initializer_list) value.initializer_list.type = resolved;
 50     return resolved;
 51 }
 52 
 53 fn parseDesignator(parser: *Parser) Error![]const u64 {
 54     var designator = std.ArrayListUnmanaged(u64).empty;
 55     while (true) {
 56         if (cursor.consume(parser, .lbracket)) {
 57             const index_expr = try expression.parseAssignment(parser);
 58             const index_value = expression.evaluateIntegerConstant(parser, index_expr) orelse
 59                 return diagnostic.fail(parser, error.InvalidConstant, "array designator is not an integer constant");
 60             const index = std.math.cast(u64, index_value) orelse
 61                 return diagnostic.fail(parser, error.InvalidConstant, "array designator is negative");
 62             try designator.append(parser.arena, index);
 63             _ = try cursor.expect(parser, .rbracket);
 64             continue;
 65         }
 66         if (cursor.consume(parser, .dot)) {
 67             return diagnostic.fail(parser, error.UnsupportedConstruct, "struct initializer designators are not supported");
 68         }
 69         break;
 70     }
 71     return designator.toOwnedSlice(parser.arena);
 72 }
 73 
 74 fn designatorType(parser: *Parser, c_type: *const ast.Type, path: []const u64) Error!*const ast.Type {
 75     var current = c_type;
 76     for (path) |index| {
 77         if (current.kind != .array) {
 78             return diagnostic.fail(parser, error.UnsupportedConstruct, "array designator does not name an array element");
 79         }
 80         if (current.array_len) |len| {
 81             if (index >= len) return diagnostic.fail(parser, error.InvalidConstant, "array designator exceeds array bound");
 82         }
 83         current = current.child orelse return diagnostic.fail(parser, error.UnsupportedConstruct, "array designator names an incomplete element");
 84     }
 85     return current;
 86 }
 87 
 88 fn completeArrayType(parser: *Parser, declared: *const ast.Type, initializer: *ast.Expr) Error!*const ast.Type {
 89     if (declared.kind != .array or declared.array_len != null) return declared;
 90     const len = try inferArrayLength(parser, declared, initializer);
 91     const type_origin = parser.nodes.typeOrigin(declared) orelse unreachable;
 92     const resolved = try memory.createType(
 93         parser,
 94         declared.*,
 95         memory.derivedType(type_origin, true),
 96     );
 97     resolved.array_len = len;
 98     return resolved;
 99 }
100 
101 fn inferArrayLength(parser: *Parser, declared: *const ast.Type, initializer: *ast.Expr) Error!u64 {
102     const child = declared.child orelse return diagnostic.fail(parser, error.UnsupportedConstruct, "array initializer has no element type");
103     switch (initializer.*) {
104         .string_literal => |literal| {
105             if (!isCharType(child)) return diagnostic.fail(parser, error.UnsupportedConstruct, "string initializer requires a character array");
106             return std.math.add(u64, literal.text.len, 1) catch return diagnostic.fail(parser, error.InvalidConstant, "string initializer is too large");
107         },
108         .initializer_list => |list| {
109             const child_count = try staticElementCount(parser, child);
110             var next_scalar: u64 = 0;
111             var max_len: u64 = 0;
112             for (list.items) |item| {
113                 if (item.designator.len == 0) {
114                     const consumed = try initializerScalarCount(parser, child, item.value);
115                     next_scalar = std.math.add(u64, next_scalar, consumed) catch return diagnostic.fail(parser, error.InvalidConstant, "array initializer is too large");
116                 } else {
117                     const target = try designatorType(parser, declared, item.designator);
118                     const offset = try designatorOffset(parser, declared, item.designator);
119                     const consumed = try initializerScalarCount(parser, target, item.value);
120                     next_scalar = std.math.add(u64, offset, consumed) catch return diagnostic.fail(parser, error.InvalidConstant, "array initializer is too large");
121                 }
122                 const len = std.math.divCeil(u64, next_scalar, child_count) catch return diagnostic.fail(parser, error.InvalidConstant, "array initializer is too large");
123                 max_len = @max(max_len, len);
124             }
125             if (max_len == 0) return diagnostic.fail(parser, error.UnsupportedConstruct, "incomplete array initializer has no size");
126             return max_len;
127         },
128         else => return diagnostic.fail(parser, error.UnsupportedConstruct, "array initializer does not determine array size"),
129     }
130 }
131 
132 fn isCharType(c_type: *const ast.Type) bool {
133     return c_type.kind == .char_type;
134 }
135 
136 fn isCharArray(c_type: *const ast.Type) bool {
137     if (c_type.kind != .array) return false;
138     var current = c_type;
139     while (current.kind == .array) {
140         current = current.child orelse return false;
141     }
142     return isCharType(current);
143 }
144 
145 fn initializerScalarCount(parser: *Parser, target: *const ast.Type, initializer: *ast.Expr) Error!u64 {
146     switch (initializer.*) {
147         .initializer_list => |list| return staticElementCount(parser, list.type),
148         .string_literal => {
149             if (!isCharArray(target)) return 1;
150             return staticElementCount(parser, target);
151         },
152         else => return 1,
153     }
154 }
155 
156 fn designatorOffset(parser: *Parser, c_type: *const ast.Type, path: []const u64) Error!u64 {
157     var current = c_type;
158     var offset: u64 = 0;
159     for (path) |index| {
160         if (current.kind != .array) return diagnostic.fail(parser, error.UnsupportedConstruct, "array designator does not name an array element");
161         if (current.array_len) |len| {
162             if (index >= len) return diagnostic.fail(parser, error.InvalidConstant, "array designator exceeds array bound");
163         }
164         const child = current.child orelse return diagnostic.fail(parser, error.UnsupportedConstruct, "array designator names an incomplete element");
165         const stride = try staticElementCount(parser, child);
166         const scaled = std.math.mul(u64, index, stride) catch return diagnostic.fail(parser, error.InvalidConstant, "array designator is too large");
167         offset = std.math.add(u64, offset, scaled) catch return diagnostic.fail(parser, error.InvalidConstant, "array designator is too large");
168         current = child;
169     }
170     return offset;
171 }
172 
173 fn staticElementCount(parser: *Parser, c_type: *const ast.Type) Error!u64 {
174     if (c_type.kind != .array) return 1;
175     const len = c_type.array_len orelse return diagnostic.fail(parser, error.UnsupportedConstruct, "nested incomplete array initializer is unsupported");
176     const child = c_type.child orelse return diagnostic.fail(parser, error.UnsupportedConstruct, "array initializer has no element type");
177     const child_count = try staticElementCount(parser, child);
178     return std.math.mul(u64, len, child_count) catch return diagnostic.fail(parser, error.InvalidConstant, "array initializer is too large");
179 }