lib/chant/src/parse/expression.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const chant = @import("../root.zig");
3 const ast = @import("../ast/root.zig");
4 const ctype = @import("type/root.zig");
5 const state = @import("state/root.zig");
6 const cursor = state.cursor;
7 const diagnostic = state.diagnostic;
8 const memory = state.memory;
9 const object = state.object;
10 const start_mod = state.start;
11 const Error = @import("error.zig").Error;
12
13 const Parser = state.Parser;
14 const token = chant.token;
15 const Kind = token.Kind;
16
17 pub fn parseExpression(parser: *Parser) Error!*ast.Expr {
18 return parseAssignment(parser);
19 }
20
21 pub fn parseAssignment(parser: *Parser) Error!*ast.Expr {
22 const target = try parseConditional(parser);
23 const op: ?ast.expr.BinaryOp = switch (cursor.peek(parser).kind) {
24 .assign => null,
25 .plus_assign => .add,
26 .minus_assign => .sub,
27 .star_assign => .mul,
28 .slash_assign => .div,
29 .percent_assign => .rem,
30 .amp_assign => .bit_and,
31 .pipe_assign => .bit_or,
32 .caret_assign => .bit_xor,
33 .shl_assign => .shl,
34 .shr_assign => .shr,
35 else => return target,
36 };
37 _ = cursor.advance(parser);
38 const value = try parseAssignment(parser);
39 return memory.create(parser, ast.Expr, .{ .assign = .{ .op = op, .target = target, .value = value } });
40 }
41
42 fn parseConditional(parser: *Parser) Error!*ast.Expr {
43 const condition = try parseBinary(parser, 0);
44 if (!cursor.consume(parser, .question)) return condition;
45 const then_value = try parseExpression(parser);
46 _ = try cursor.expect(parser, .colon);
47 const else_value = try parseConditional(parser);
48 return memory.create(parser, ast.Expr, .{ .conditional = .{
49 .condition = condition,
50 .then_value = then_value,
51 .else_value = else_value,
52 } });
53 }
54
55 const Level = struct {
56 kind: Kind,
57 op: ast.expr.BinaryOp,
58 precedence: u8,
59 };
60
61 const levels = [_]Level{
62 .{ .kind = .pipe_pipe, .op = .logical_or, .precedence = 1 },
63 .{ .kind = .amp_amp, .op = .logical_and, .precedence = 2 },
64 .{ .kind = .pipe, .op = .bit_or, .precedence = 3 },
65 .{ .kind = .caret, .op = .bit_xor, .precedence = 4 },
66 .{ .kind = .amp, .op = .bit_and, .precedence = 5 },
67 .{ .kind = .eq, .op = .eq, .precedence = 6 },
68 .{ .kind = .ne, .op = .ne, .precedence = 6 },
69 .{ .kind = .lt, .op = .lt, .precedence = 7 },
70 .{ .kind = .gt, .op = .gt, .precedence = 7 },
71 .{ .kind = .le, .op = .le, .precedence = 7 },
72 .{ .kind = .ge, .op = .ge, .precedence = 7 },
73 .{ .kind = .shl, .op = .shl, .precedence = 8 },
74 .{ .kind = .shr, .op = .shr, .precedence = 8 },
75 .{ .kind = .plus, .op = .add, .precedence = 9 },
76 .{ .kind = .minus, .op = .sub, .precedence = 9 },
77 .{ .kind = .star, .op = .mul, .precedence = 10 },
78 .{ .kind = .slash, .op = .div, .precedence = 10 },
79 .{ .kind = .percent, .op = .rem, .precedence = 10 },
80 };
81
82 fn binaryLevel(kind: Kind) ?Level {
83 for (levels) |level| {
84 if (level.kind == kind) return level;
85 }
86 return null;
87 }
88
89 fn parseBinary(parser: *Parser, min_precedence: u8) Error!*ast.Expr {
90 var lhs = try parseUnary(parser);
91 while (true) {
92 const level = binaryLevel(cursor.peek(parser).kind) orelse break;
93 if (level.precedence < min_precedence) break;
94 _ = cursor.advance(parser);
95 const rhs = try parseBinary(parser, level.precedence + 1);
96 lhs = try memory.create(parser, ast.Expr, .{ .binary = .{ .op = level.op, .lhs = lhs, .rhs = rhs } });
97 }
98 return lhs;
99 }
100
101 fn parseUnary(parser: *Parser) Error!*ast.Expr {
102 switch (cursor.peek(parser).kind) {
103 .plus => {
104 _ = cursor.advance(parser);
105 return parseUnary(parser);
106 },
107 .minus => {
108 _ = cursor.advance(parser);
109 const operand = try parseUnary(parser);
110 return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .negate, .operand = operand } });
111 },
112 .bang => {
113 _ = cursor.advance(parser);
114 const operand = try parseUnary(parser);
115 return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .logical_not, .operand = operand } });
116 },
117 .tilde => {
118 _ = cursor.advance(parser);
119 const operand = try parseUnary(parser);
120 return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .bit_not, .operand = operand } });
121 },
122 .star => {
123 _ = cursor.advance(parser);
124 const operand = try parseUnary(parser);
125 return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .deref, .operand = operand } });
126 },
127 .amp => {
128 const amp_index = parser.index;
129 _ = cursor.advance(parser);
130 const operand = try parseUnary(parser);
131 return memory.createExprAt(
132 parser,
133 .{ .unary = .{ .op = .address_of, .operand = operand } },
134 amp_index,
135 );
136 },
137 .plus_plus => {
138 _ = cursor.advance(parser);
139 return desugarIncrement(parser, try parseUnary(parser), .add);
140 },
141 .minus_minus => {
142 _ = cursor.advance(parser);
143 return desugarIncrement(parser, try parseUnary(parser), .sub);
144 },
145 .kw_sizeof => {
146 _ = cursor.advance(parser);
147 return parseSizeof(parser);
148 },
149 .kw_alignof => {
150 _ = cursor.advance(parser);
151 return parseAlignof(parser);
152 },
153 .lparen => {
154 if (start_mod.typename(parser, 1)) {
155 _ = cursor.advance(parser);
156 const specifiers = try ctype.parseSpecifiers(parser);
157 if (specifiers.requires_standalone_declaration) {
158 return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");
159 }
160 const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);
161 if (declarator.name != null) {
162 return diagnostic.fail(parser, error.UnexpectedToken, "cast names a declarator");
163 }
164 _ = try cursor.expect(parser, .rparen);
165 const operand = try parseUnary(parser);
166 return memory.create(parser, ast.Expr, .{ .cast = .{ .target = declarator.type, .operand = operand } });
167 }
168 return parsePostfixChain(parser, try parsePrimary(parser));
169 },
170 else => return parsePostfixChain(parser, try parsePrimary(parser)),
171 }
172 }
173
174 fn desugarIncrement(parser: *Parser, target: *ast.Expr, op: ast.expr.BinaryOp) Error!*ast.Expr {
175 const one = try memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = 1, .type = &ast.types.int_type } });
176 return memory.create(parser, ast.Expr, .{ .assign = .{ .op = op, .target = target, .value = one } });
177 }
178
179 fn integerConstantExpression(parser: *Parser, value: i128, c_type: *const ast.Type) Error!*ast.Expr {
180 if (value >= 0) {
181 const unsigned: u64 = std.math.cast(u64, value) orelse return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too large");
182 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = unsigned, .type = c_type } });
183 }
184 if (value == std.math.minInt(i128)) return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too small");
185 const magnitude: u64 = std.math.cast(u64, -value) orelse return diagnostic.fail(parser, error.InvalidConstant, "integer constant is too small");
186 const operand = try memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = magnitude, .type = c_type } });
187 return memory.create(parser, ast.Expr, .{ .unary = .{ .op = .negate, .operand = operand } });
188 }
189
190 pub fn evaluateIntegerConstant(parser: *const Parser, expr: *ast.Expr) ?i128 {
191 switch (expr.*) {
192 .integer_literal => |literal| return @intCast(literal.value),
193 .identifier => |identifier| return object.constantValue(parser, identifier.name),
194 .float_literal, .string_literal, .call, .index, .initializer_list => return null,
195 .unary => |unary| {
196 const operand = evaluateIntegerConstant(parser, unary.operand) orelse return null;
197 return switch (unary.op) {
198 .negate => -operand,
199 .logical_not => if (operand == 0) 1 else 0,
200 .bit_not => ~operand,
201 .address_of, .deref => null,
202 };
203 },
204 .binary => |binary| {
205 const lhs = evaluateIntegerConstant(parser, binary.lhs) orelse return null;
206 const rhs = evaluateIntegerConstant(parser, binary.rhs) orelse return null;
207 return switch (binary.op) {
208 .add => lhs + rhs,
209 .sub => lhs - rhs,
210 .mul => lhs * rhs,
211 .div => if (rhs == 0) null else @divTrunc(lhs, rhs),
212 .rem => if (rhs == 0) null else @rem(lhs, rhs),
213 .lt => if (lhs < rhs) 1 else 0,
214 .gt => if (lhs > rhs) 1 else 0,
215 .le => if (lhs <= rhs) 1 else 0,
216 .ge => if (lhs >= rhs) 1 else 0,
217 .eq => if (lhs == rhs) 1 else 0,
218 .ne => if (lhs != rhs) 1 else 0,
219 .logical_and => if (lhs != 0 and rhs != 0) 1 else 0,
220 .logical_or => if (lhs != 0 or rhs != 0) 1 else 0,
221 .bit_and => lhs & rhs,
222 .bit_or => lhs | rhs,
223 .bit_xor => lhs ^ rhs,
224 .shl => if (rhs < 0 or rhs >= 128) null else lhs << @intCast(rhs),
225 .shr => if (rhs < 0 or rhs >= 128) null else lhs >> @intCast(rhs),
226 };
227 },
228 .assign => return null,
229 .conditional => |conditional| {
230 const condition = evaluateIntegerConstant(parser, conditional.condition) orelse return null;
231 return evaluateIntegerConstant(parser, if (condition != 0) conditional.then_value else conditional.else_value);
232 },
233 .cast => |cast| return evaluateIntegerConstant(parser, cast.operand),
234 }
235 }
236
237 pub fn inferType(parser: *Parser, expr: *ast.Expr) Error!?*const ast.Type {
238 switch (expr.*) {
239 .integer_literal => |literal| return literal.type,
240 .float_literal => |literal| return literal.type,
241 .string_literal => return makePointerType(
242 parser,
243 expr,
244 &ast.types.char_type,
245 ),
246 .identifier => |identifier| return state.object.lookup(parser, identifier.name),
247 .call => |call| {
248 const callee_type = state.object.lookup(parser, call.callee) orelse return null;
249 if (callee_type.kind != .function) return null;
250 return callee_type.child;
251 },
252 .index => |index| {
253 const base = try inferType(parser, index.base) orelse return null;
254 return ast.types.element(base);
255 },
256 .initializer_list => |list| return list.type,
257 .unary => |unary| {
258 const operand = try inferType(parser, unary.operand) orelse return null;
259 return switch (unary.op) {
260 .negate, .bit_not => if (ast.types.isArithmetic(operand)) operand else null,
261 .logical_not => &ast.types.int_type,
262 .address_of => makePointerType(parser, expr, operand),
263 .deref => ast.types.element(operand),
264 };
265 },
266 .binary => |binary| {
267 const lhs = try inferType(parser, binary.lhs) orelse return null;
268 const rhs = try inferType(parser, binary.rhs) orelse return null;
269 return switch (binary.op) {
270 .lt, .gt, .le, .ge, .eq, .ne, .logical_and, .logical_or => &ast.types.int_type,
271 .shl, .shr => if (ast.types.isInteger(lhs) and ast.types.isInteger(rhs)) lhs else null,
272 .add, .sub, .mul, .div, .rem, .bit_and, .bit_or, .bit_xor => if (ast.types.isArithmetic(lhs) and ast.types.isArithmetic(rhs))
273 ast.types.commonArithmetic(lhs, rhs)
274 else
275 null,
276 };
277 },
278 .assign => |assign| return inferType(parser, assign.target),
279 .conditional => |conditional| {
280 const then_type = try inferType(parser, conditional.then_value) orelse return null;
281 const else_type = try inferType(parser, conditional.else_value) orelse return null;
282 if (ast.types.isArithmetic(then_type) and ast.types.isArithmetic(else_type)) {
283 return ast.types.commonArithmetic(then_type, else_type);
284 }
285 if (then_type == else_type) return then_type;
286 if (then_type.kind == .nullptr_type and ast.types.isPointerLike(else_type)) return else_type;
287 if (else_type.kind == .nullptr_type and ast.types.isPointerLike(then_type)) return then_type;
288 return null;
289 },
290 .cast => |cast| return cast.target,
291 }
292 }
293
294 fn makePointerType(
295 parser: *Parser,
296 expr: *ast.Expr,
297 child: *const ast.Type,
298 ) Error!*const ast.Type {
299 if (parser.nodes.inferredType(expr)) |cached| return cached;
300 const token_index = parser.nodes.expressionOrigin(expr);
301 const inferred = try memory.createType(
302 parser,
303 .{ .kind = .pointer, .child = child },
304 memory.directType(token_index, null),
305 );
306 parser.nodes.setInferredType(expr, inferred);
307 return inferred;
308 }
309
310 fn parseSizeof(parser: *Parser) Error!*ast.Expr {
311 if (cursor.peek(parser).kind == .lparen and start_mod.typename(parser, 1)) {
312 _ = cursor.advance(parser);
313 const specifiers = try ctype.parseSpecifiers(parser);
314 if (specifiers.requires_standalone_declaration) {
315 return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");
316 }
317 const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);
318 _ = try cursor.expect(parser, .rparen);
319 const size = ast.types.byteSize(declarator.type) orelse
320 return diagnostic.fail(parser, error.UnsupportedConstruct, "sizeof of an incomplete type");
321 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = size, .type = &ast.types.ulong_type } });
322 }
323 const operand = try parseUnary(parser);
324 _ = operand;
325 return diagnostic.fail(parser, error.UnsupportedConstruct, "sizeof of an expression");
326 }
327
328 fn parseAlignof(parser: *Parser) Error!*ast.Expr {
329 if (cursor.peek(parser).kind == .lparen and start_mod.typename(parser, 1)) {
330 _ = cursor.advance(parser);
331 const specifiers = try ctype.parseSpecifiers(parser);
332 if (specifiers.requires_standalone_declaration) {
333 return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");
334 }
335 const declarator = try ctype.parseDeclarator(parser, specifiers.type, parseAssignment);
336 _ = try cursor.expect(parser, .rparen);
337 const alignment = ast.types.byteAlign(declarator.type) orelse
338 return diagnostic.fail(parser, error.UnsupportedConstruct, "alignof of an incomplete type");
339 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = alignment, .type = &ast.types.ulong_type } });
340 }
341 const operand = try parseUnary(parser);
342 _ = operand;
343 return diagnostic.fail(parser, error.UnsupportedConstruct, "alignof of an expression");
344 }
345
346 fn parsePostfixChain(parser: *Parser, start: *ast.Expr) Error!*ast.Expr {
347 var current = start;
348 while (true) {
349 switch (cursor.peek(parser).kind) {
350 .lbracket => {
351 _ = cursor.advance(parser);
352 const subscript = try parseExpression(parser);
353 _ = try cursor.expect(parser, .rbracket);
354 current = try memory.create(parser, ast.Expr, .{ .index = .{ .base = current, .subscript = subscript } });
355 },
356 .plus_plus => {
357 _ = cursor.advance(parser);
358 current = try desugarIncrement(parser, current, .add);
359 },
360 .minus_minus => {
361 _ = cursor.advance(parser);
362 current = try desugarIncrement(parser, current, .sub);
363 },
364 else => return current,
365 }
366 }
367 }
368
369 fn parsePrimary(parser: *Parser) Error!*ast.Expr {
370 switch (cursor.peek(parser).kind) {
371 .integer => {
372 const token_index = parser.index;
373 const tok = cursor.advance(parser);
374 const decoded = token.decodeInteger(tok.text) orelse
375 return diagnostic.fail(parser, error.InvalidConstant, "invalid integer constant");
376 const literal_type: *const ast.Type = if (decoded.bit_width) |width|
377 try memory.createType(
378 parser,
379 .{
380 .kind = .bitint_type,
381 .is_unsigned = decoded.is_unsigned,
382 .bit_width = width,
383 },
384 memory.directType(token_index, null),
385 )
386 else if (decoded.is_unsigned)
387 (if (decoded.is_long) &ast.types.ulong_type else &ast.types.uint_type)
388 else
389 (if (decoded.is_long) &ast.types.long_type else &ast.types.int_type);
390 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = decoded.value, .type = literal_type } });
391 },
392 .floating => {
393 const tok = cursor.advance(parser);
394 const decoded = token.decodeFloat(parser.arena, tok.text) orelse
395 return diagnostic.fail(parser, error.InvalidConstant, "invalid float constant");
396 const literal_type: *const ast.Type = switch (decoded.kind) {
397 .float => &ast.types.float_type,
398 .double => &ast.types.double_type,
399 .decimal32 => &ast.types.decimal32_type,
400 .decimal64 => &ast.types.decimal64_type,
401 .decimal128 => &ast.types.decimal128_type,
402 };
403 return memory.create(parser, ast.Expr, .{ .float_literal = .{ .value = decoded.value, .type = literal_type } });
404 },
405 .kw_true, .kw_false => {
406 const tok = cursor.advance(parser);
407 const value: u64 = if (tok.kind == .kw_true) 1 else 0;
408 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = value, .type = &ast.types.uchar_type } });
409 },
410 .kw_nullptr => {
411 _ = cursor.advance(parser);
412 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = 0, .type = &ast.types.nullptr_type } });
413 },
414 .character => {
415 const tok = cursor.advance(parser);
416 const value = token.decodeCharacter(tok.text) orelse
417 return diagnostic.fail(parser, error.InvalidConstant, "invalid character constant");
418 return memory.create(parser, ast.Expr, .{ .integer_literal = .{ .value = value, .type = &ast.types.int_type } });
419 },
420 .string => {
421 const token_index = parser.index;
422 const tok = cursor.advance(parser);
423 const text = token.decodeString(parser.arena, tok.text) orelse
424 return diagnostic.fail(parser, error.InvalidConstant, "invalid string constant");
425 return memory.createExprAt(
426 parser,
427 .{ .string_literal = .{ .text = text } },
428 token_index,
429 );
430 },
431 .identifier => {
432 const tok = cursor.advance(parser);
433 if (cursor.peek(parser).kind == .lparen) {
434 _ = cursor.advance(parser);
435 var arguments = std.ArrayListUnmanaged(*ast.Expr).empty;
436 if (!cursor.consume(parser, .rparen)) {
437 while (true) {
438 try arguments.append(parser.arena, try parseAssignment(parser));
439 if (!cursor.consume(parser, .comma)) break;
440 }
441 _ = try cursor.expect(parser, .rparen);
442 }
443 return memory.create(parser, ast.Expr, .{ .call = .{
444 .callee = tok.text,
445 .arguments = try arguments.toOwnedSlice(parser.arena),
446 } });
447 }
448 if (object.constantValue(parser, tok.text)) |value| {
449 const constant_type = object.lookup(parser, tok.text) orelse &ast.types.int_type;
450 return integerConstantExpression(parser, value, constant_type);
451 }
452 return memory.create(parser, ast.Expr, .{ .identifier = .{ .name = tok.text } });
453 },
454 .lparen => {
455 _ = cursor.advance(parser);
456 const inner = try parseExpression(parser);
457 _ = try cursor.expect(parser, .rparen);
458 return inner;
459 },
460 else => return diagnostic.fail(parser, error.UnexpectedToken, "expected an expression"),
461 }
462 }
463
464 fn parseSource(arena: std.mem.Allocator, source: []const u8) !*ast.Expr {
465 const lexer = @import("../lexer/root.zig");
466 const token_survey = try lexer.survey(source, "expr.c");
467 const capacity = try lexer.Capacity.derive(token_survey.limits);
468 const bytes = try arena.alignedAlloc(
469 u8,
470 .fromByteUnits(lexer.Storage.storage_alignment),
471 capacity.storage_bytes,
472 );
473 var storage = try lexer.Storage.init(bytes, token_survey.limits);
474 storage.activate();
475 defer _ = storage.deinit();
476 const tokens = try storage.fill(token_survey, source, "expr.c");
477 var parser = try @import("state/test.zig").initParser(arena, tokens);
478 return parseExpression(&parser);
479 }
480
481 test "precedence binds multiplication over addition" {
482 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
483 defer arena_state.deinit();
484 const arena = arena_state.allocator();
485
486 const sum = try parseSource(arena, "a + b * c");
487 try std.testing.expectEqual(ast.expr.BinaryOp.add, sum.binary.op);
488 try std.testing.expectEqual(ast.expr.BinaryOp.mul, sum.binary.rhs.binary.op);
489
490 const comparison = try parseSource(arena, "i * n + j < bound == 0");
491 try std.testing.expectEqual(ast.expr.BinaryOp.eq, comparison.binary.op);
492 try std.testing.expectEqual(ast.expr.BinaryOp.lt, comparison.binary.lhs.binary.op);
493 }
494
495 test "assignments nest right and compound ops decode" {
496 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
497 defer arena_state.deinit();
498 const arena = arena_state.allocator();
499
500 const chain = try parseSource(arena, "a = b = 1");
501 try std.testing.expect(chain.assign.op == null);
502 try std.testing.expect(chain.assign.value.assign.op == null);
503
504 const compound = try parseSource(arena, "C[i][j] += alpha * A[i][k]");
505 try std.testing.expectEqual(ast.expr.BinaryOp.add, compound.assign.op.?);
506 try std.testing.expect(compound.assign.target.* == .index);
507 try std.testing.expect(compound.assign.target.index.base.* == .index);
508 }
509
510 test "unary postfix and casts parse" {
511 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
512 defer arena_state.deinit();
513 const arena = arena_state.allocator();
514
515 const increment = try parseSource(arena, "i++");
516 try std.testing.expectEqual(ast.expr.BinaryOp.add, increment.assign.op.?);
517
518 const cast = try parseSource(arena, "(double)(i % n) / n");
519 try std.testing.expectEqual(ast.expr.BinaryOp.div, cast.binary.op);
520 try std.testing.expect(cast.binary.lhs.* == .cast);
521
522 const ternary = try parseSource(arena, "x > 0 ? f(x, 1) : -x");
523 try std.testing.expect(ternary.* == .conditional);
524 try std.testing.expectEqual(@as(usize, 2), ternary.conditional.then_value.call.arguments.len);
525
526 const size = try parseSource(arena, "sizeof(double)");
527 try std.testing.expectEqual(@as(u64, 8), size.integer_literal.value);
528
529 const alignment = try parseSource(arena, "alignof(double)");
530 try std.testing.expectEqual(@as(u64, 8), alignment.integer_literal.value);
531 }
532
533 test "c23 constants parse as existing scalar expressions" {
534 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
535 defer arena_state.deinit();
536 const arena = arena_state.allocator();
537
538 const binary = try parseSource(arena, "0b1010'0101");
539 try std.testing.expectEqual(@as(u64, 0xa5), binary.integer_literal.value);
540
541 const signed_bitint = try parseSource(arena, "3wb");
542 try std.testing.expectEqual(ast.types.Kind.bitint_type, signed_bitint.integer_literal.type.kind);
543 try std.testing.expectEqual(@as(u16, 3), signed_bitint.integer_literal.type.bit_width);
544
545 const unsigned_bitint = try parseSource(arena, "3uwb");
546 try std.testing.expectEqual(ast.types.Kind.bitint_type, unsigned_bitint.integer_literal.type.kind);
547 try std.testing.expect(unsigned_bitint.integer_literal.type.is_unsigned);
548 try std.testing.expectEqual(@as(u16, 2), unsigned_bitint.integer_literal.type.bit_width);
549
550 const separated = try parseSource(arena, "1.25'5");
551 try std.testing.expectEqual(@as(f64, 1.255), separated.float_literal.value);
552
553 const decimal32 = try parseSource(arena, "1.25df");
554 try std.testing.expectEqual(ast.types.Kind.decimal32_type, decimal32.float_literal.type.kind);
555
556 const decimal64 = try parseSource(arena, "1.25DD");
557 try std.testing.expectEqual(ast.types.Kind.decimal64_type, decimal64.float_literal.type.kind);
558
559 const decimal128 = try parseSource(arena, "1.25DL");
560 try std.testing.expectEqual(ast.types.Kind.decimal128_type, decimal128.float_literal.type.kind);
561
562 const truth = try parseSource(arena, "true ? 299'792'458 : false");
563 try std.testing.expect(truth.* == .conditional);
564 try std.testing.expectEqual(@as(u64, 1), truth.conditional.condition.integer_literal.value);
565 try std.testing.expectEqual(@as(u64, 0), truth.conditional.else_value.integer_literal.value);
566
567 const null_expr = try parseSource(arena, "nullptr");
568 try std.testing.expectEqual(ast.types.Kind.nullptr_type, null_expr.integer_literal.type.kind);
569 }
570
571 test "integer constant expressions evaluate" {
572 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
573 defer arena_state.deinit();
574 const arena = arena_state.allocator();
575
576 const expr = try parseSource(arena, "alignof(double) == 8 && (1 + 2 * 3) == 7");
577 var parser = try @import("state/test.zig").initParser(arena, &.{});
578 try std.testing.expectEqual(@as(i128, 1), evaluateIntegerConstant(&parser, expr).?);
579 }
580
581 test "expression types infer from parser objects" {
582 const lexer = @import("../lexer/root.zig");
583 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
584 defer arena_state.deinit();
585 const arena = arena_state.allocator();
586
587 const source = "x + 1.0";
588 const token_survey = try lexer.survey(source, "expr.c");
589 const capacity = try lexer.Capacity.derive(token_survey.limits);
590 const bytes = try arena.alignedAlloc(
591 u8,
592 .fromByteUnits(lexer.Storage.storage_alignment),
593 capacity.storage_bytes,
594 );
595 var storage = try lexer.Storage.init(bytes, token_survey.limits);
596 storage.activate();
597 defer _ = storage.deinit();
598 const tokens = try storage.fill(token_survey, source, "expr.c");
599 var parser = try @import("state/test.zig").initParser(arena, tokens);
600 try state.object.register(&parser, "x", &ast.types.int_type);
601
602 const expr = try parseExpression(&parser);
603 const inferred = (try inferType(&parser, expr)).?;
604 try std.testing.expectEqual(ast.types.Kind.double_type, inferred.kind);
605 }
606
607 test "pointer inference reuses one charged type" {
608 comptime {
609 @stardustClaim(
610 @import("alloc_phase").capacity.witness(@import("./state/root.zig").Storage, "chant_parser_type_cache"),
611 null,
612 null,
613 null,
614 null,
615 null,
616 null,
617 );
618 }
619
620 const lexer = @import("../lexer/root.zig");
621 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
622 defer arena_state.deinit();
623 const arena = arena_state.allocator();
624
625 const source = "&x";
626 const token_survey = try lexer.survey(source, "infer.c");
627 const capacity = try lexer.Capacity.derive(token_survey.limits);
628 const bytes = try arena.alignedAlloc(
629 u8,
630 .fromByteUnits(lexer.Storage.storage_alignment),
631 capacity.storage_bytes,
632 );
633 var storage = try lexer.Storage.init(bytes, token_survey.limits);
634 storage.activate();
635 defer _ = storage.deinit();
636 const tokens = try storage.fill(token_survey, source, "infer.c");
637 var parser = try @import("state/test.zig").initParser(arena, tokens);
638 try state.object.register(&parser, "x", &ast.types.int_type);
639 const expr = try parseExpression(&parser);
640
641 const first = (try inferType(&parser, expr)).?;
642 const after_first = parser.nodes.status();
643 const second = (try inferType(&parser, expr)).?;
644 try std.testing.expectEqual(first, second);
645 try std.testing.expectEqual(after_first, parser.nodes.status());
646 }