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 }