ffi 2.7 green: parser accepts all seven type-introducer directive forms

Six new lexer tokens (`hash_jni_interface`, `hash_objc_class`,
`hash_objc_protocol`, `hash_swift_class`, `hash_swift_struct`,
`hash_swift_protocol`) join the existing `hash_jni_class`. All seven
share the body grammar from Phases 2.1–2.6.

AST refactored: `JniClassDecl` → `ForeignClassDecl` with a
`runtime: ForeignRuntime` enum discriminator; `JniMethodDecl` →
`ForeignMethodDecl` (with `jni_descriptor_override` renamed for
clarity since it's JNI-only); `JniFieldDecl` → `ForeignFieldDecl`;
`JniClassMember` → `ForeignClassMember`. AST variant renamed
`jni_class_decl` → `foreign_class_decl`.

`parseForeignClassDecl` takes the runtime as a parameter; the
`parseConstBinding` dispatch table now maps each of the seven
directive tokens to its `ForeignRuntime` variant via
`foreignRuntimeForCurrent`. No codegen yet — Phase 3 picks up Obj-C
runtime, Phase 4 picks up Swift. Runtime-specific body items (fields,
descriptor override) are validated at sema time in later steps.

126/126 examples green.
This commit is contained in:
agra
2026-05-20 10:15:10 +03:00
parent dc3821aeb0
commit 5fd8e0fbbe
8 changed files with 73 additions and 29 deletions

View File

@@ -209,9 +209,10 @@ pub const Parser = struct {
return self.parseProtocolDecl(name, start_pos);
}
// JNI class binding: name :: #jni_class("java/path/Foo") { ...body... }
if (self.current.tag == .hash_jni_class) {
return self.parseJniClassDecl(name, start_pos);
// Foreign-type binding: name :: #jni_class / #jni_interface / #objc_class /
// #objc_protocol / #swift_class / #swift_struct / #swift_protocol ("path") { body }
if (self.foreignRuntimeForCurrent()) |runtime| {
return self.parseForeignClassDecl(name, start_pos, runtime);
}
// C-style union declaration
@@ -1027,21 +1028,34 @@ pub const Parser = struct {
} });
}
fn parseJniClassDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
self.advance(); // skip '#jni_class'
fn foreignRuntimeForCurrent(self: *Parser) ?ast.ForeignRuntime {
return switch (self.current.tag) {
.hash_jni_class => .jni_class,
.hash_jni_interface => .jni_interface,
.hash_objc_class => .objc_class,
.hash_objc_protocol => .objc_protocol,
.hash_swift_class => .swift_class,
.hash_swift_struct => .swift_struct,
.hash_swift_protocol => .swift_protocol,
else => null,
};
}
fn parseForeignClassDecl(self: *Parser, name: []const u8, start_pos: u32, runtime: ast.ForeignRuntime) anyerror!*Node {
self.advance(); // skip directive token
try self.expect(.l_paren);
if (self.current.tag != .string_literal) {
return self.fail("expected string literal Java class path after '#jni_class('");
return self.fail("expected string literal foreign-type path after directive");
}
const raw = self.tokenSlice(self.current);
const java_path = raw[1 .. raw.len - 1];
const foreign_path = raw[1 .. raw.len - 1];
self.advance();
try self.expect(.r_paren);
try self.expect(.l_brace);
var members = std.ArrayList(ast.JniClassMember).empty;
var members = std.ArrayList(ast.ForeignClassMember).empty;
while (self.current.tag != .r_brace and self.current.tag != .eof) {
// #extends Alias; or #implements Alias;
if (self.current.tag == .hash_extends or self.current.tag == .hash_implements) {
@@ -1143,14 +1157,15 @@ pub const Parser = struct {
.param_names = try param_names.toOwnedSlice(self.allocator),
.return_type = return_type,
.is_static = is_static,
.desc_override = desc_override,
.jni_descriptor_override = desc_override,
} });
}
try self.expect(.r_brace);
return try self.createNode(start_pos, .{ .jni_class_decl = .{
return try self.createNode(start_pos, .{ .foreign_class_decl = .{
.name = name,
.java_path = java_path,
.foreign_path = foreign_path,
.runtime = runtime,
.members = try members.toOwnedSlice(self.allocator),
} });
}