lib/chant/src/parse/external.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const ast = @import("../ast/root.zig");
3 const assertion = @import("assert.zig");
4 const auto_mod = @import("auto.zig");
5 const constexpr = @import("constexpr.zig");
6 const ctype = @import("type/root.zig");
7 const expression = @import("expression.zig");
8 const initializer_mod = @import("initializer.zig");
9 const state = @import("state/root.zig");
10 const thread = @import("thread.zig");
11 const cursor = state.cursor;
12 const diagnostic = state.diagnostic;
13 const object = state.object;
14 const statement = @import("statement.zig");
15 const typedef = state.typedef;
16 const Error = @import("error.zig").Error;
17
18 const Parser = state.Parser;
19
20 pub fn parseExternal(parser: *Parser) Error![]ast.Decl {
21 if (try assertion.consume(parser)) {
22 return &.{};
23 }
24 const specifiers = try ctype.parseSpecifiers(parser);
25
26 var declarations = std.ArrayListUnmanaged(ast.Decl).empty;
27 if (cursor.consume(parser, .semicolon)) {
28 return declarations.toOwnedSlice(parser.arena);
29 }
30 if (specifiers.requires_standalone_declaration) {
31 return diagnostic.fail(parser, error.UnsupportedConstruct, "fixed enum forward declaration must be standalone");
32 }
33
34 var first = true;
35 while (true) {
36 try ctype.skipAttributes(parser);
37 const declarator = try ctype.parseDeclarator(parser, specifiers.type, expression.parseAssignment);
38 try ctype.skipAttributes(parser);
39 const name = declarator.name orelse return diagnostic.fail(parser, error.ExpectedIdentifier, "declaration requires a name");
40
41 if (specifiers.storage == .typedef_storage) {
42 try thread.validateTypedef(parser, specifiers.is_thread_local);
43 if (specifiers.is_constexpr) {
44 return diagnostic.fail(parser, error.UnsupportedConstruct, "typedef cannot be constexpr");
45 }
46 if (auto_mod.contains(declarator.type)) {
47 return diagnostic.fail(parser, error.ExpectedType, "typedef cannot infer auto");
48 }
49 try typedef.register(parser, name, declarator.type);
50 } else if (declarator.type.kind == .function) {
51 if (auto_mod.contains(declarator.type)) {
52 return diagnostic.fail(parser, error.ExpectedType, "function declarations cannot infer auto");
53 }
54 if (specifiers.alignment != null) {
55 return diagnostic.fail(parser, error.UnsupportedConstruct, "alignas on function declarations is not supported");
56 }
57 if (specifiers.is_constexpr) {
58 return diagnostic.fail(parser, error.UnsupportedConstruct, "function declarations cannot be constexpr");
59 }
60 try thread.validateFunction(parser, specifiers.is_thread_local);
61 try object.register(parser, name, declarator.type);
62 if (first and cursor.peek(parser).kind == .lbrace) {
63 try object.push(parser);
64 defer object.pop(parser);
65 for (declarator.type.params) |param| {
66 if (param.name.len != 0) try object.register(parser, param.name, param.type);
67 }
68 const body = try statement.parseCompound(parser);
69 try declarations.append(parser.arena, .{ .function = .{
70 .name = name,
71 .type = declarator.type,
72 .storage = specifiers.storage,
73 .body = body,
74 } });
75 return declarations.toOwnedSlice(parser.arena);
76 }
77 try declarations.append(parser.arena, .{ .function = .{
78 .name = name,
79 .type = declarator.type,
80 .storage = specifiers.storage,
81 .body = null,
82 } });
83 } else {
84 var initializer: ?*ast.Expr = null;
85 if (cursor.consume(parser, .assign)) {
86 initializer = try initializer_mod.parse(parser, declarator.type);
87 }
88 const completed_type = try initializer_mod.completeType(parser, declarator.type, initializer);
89 const variable_type = try auto_mod.resolve(parser, completed_type, initializer);
90 const constant_value = if (specifiers.is_constexpr)
91 try constexpr.validateObject(parser, variable_type, initializer)
92 else
93 null;
94 try thread.validateObject(parser, .file, specifiers.storage, variable_type, initializer, specifiers.is_thread_local);
95 try declarations.append(parser.arena, .{ .variable = .{
96 .name = name,
97 .type = variable_type,
98 .storage = specifiers.storage,
99 .alignment = specifiers.alignment,
100 .is_constexpr = specifiers.is_constexpr,
101 .is_thread_local = specifiers.is_thread_local,
102 .initializer = initializer,
103 } });
104 if (specifiers.is_constexpr) {
105 try constexpr.registerObject(parser, name, variable_type, constant_value);
106 } else {
107 try object.register(parser, name, variable_type);
108 }
109 }
110
111 first = false;
112 if (cursor.consume(parser, .comma)) continue;
113 _ = try cursor.expect(parser, .semicolon);
114 break;
115 }
116 return declarations.toOwnedSlice(parser.arena);
117 }
118
119 test "parameter arrays decay to pointers" {
120 const lexer = @import("../lexer/root.zig");
121 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
122 defer arena_state.deinit();
123 const arena = arena_state.allocator();
124
125 const source = "void f(double a[10], double b[]);";
126 const token_survey = try lexer.survey(source, "decay.c");
127 const capacity = try lexer.Capacity.derive(token_survey.limits);
128 const bytes = try arena.alignedAlloc(
129 u8,
130 .fromByteUnits(lexer.Storage.storage_alignment),
131 capacity.storage_bytes,
132 );
133 var storage = try lexer.Storage.init(bytes, token_survey.limits);
134 storage.activate();
135 defer _ = storage.deinit();
136 const tokens = try storage.fill(token_survey, source, "decay.c");
137 var parser = try @import("state/test.zig").initParser(arena, tokens);
138 const declarations = try parseExternal(&parser);
139 const fn_type = declarations[0].function.type;
140 try std.testing.expectEqual(ast.types.Kind.pointer, fn_type.params[0].type.kind);
141 try std.testing.expectEqual(ast.types.Kind.double_type, fn_type.params[0].type.child.?.kind);
142 try std.testing.expectEqual(ast.types.Kind.pointer, fn_type.params[1].type.kind);
143 }