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 }