feat(ffi-linkage): lower extern data globals (Phase 1.2d) — Phase 1 complete

Parser: a 'kw_extern' branch in the var-decl-with-type-annotation path
(beside #foreign) parses 'name : type extern [LIB] ["csym"];' into
VarDecl.is_extern/extern_lib/extern_name; the trailing diagnostic now
lists 'extern'. Lowering: registerTopLevelGlobal uses
extern_name orelse foreign_name orelse name for the C symbol and sets
is_extern = is_foreign or is_extern; globalInitValue returns null (no
initializer) for extern globals too.

examples/1225 green: '__stdinp : *void extern;' lowers to
'@__stdinp = external global ptr'; @__stdinp reads non-null. Suite
green (636 corpus / 443 unit).

Phase 1 done: extern functions (bare + rename) and data globals (bare +
rename) all work, behavior-equivalent to the matching #foreign form.
export (Phase 2), aggregates (Phase 3), docs + A->B gate (Phase 4)
remain. green commit.
This commit is contained in:
agra
2026-06-14 13:39:05 +03:00
parent 235f74a8c9
commit 6932426c41
3 changed files with 78 additions and 40 deletions

View File

@@ -449,7 +449,35 @@ pub const Parser = struct {
} });
}
return self.fail("expected ':', '=', ';' or '#foreign' after type annotation");
if (self.current.tag == .kw_extern) {
// name : type extern [LIB] ["csym"]; (extern data global — the
// extern-named counterpart of `#foreign`; resolved at link time)
self.advance();
var ext_lib: ?[]const u8 = null;
if (self.current.tag == .identifier) {
ext_lib = self.tokenSlice(self.current);
self.advance();
}
var ext_name: ?[]const u8 = null;
if (self.current.tag == .string_literal) {
const raw = self.tokenSlice(self.current);
ext_name = raw[1 .. raw.len - 1];
self.advance();
}
try self.expect(.semicolon);
return try self.createNode(start_pos, .{ .var_decl = .{
.name = name,
.name_span = name_span,
.type_annotation = type_node,
.value = null,
.is_extern = true,
.extern_lib = ext_lib,
.extern_name = ext_name,
.is_raw = name_is_raw,
} });
}
return self.fail("expected ':', '=', ';', '#foreign', or 'extern' after type annotation");
}
fn parseTypeExpr(self: *Parser) anyerror!*Node {