ffi 2.4 green: #extends and #implements body items

Two new lexer tokens `hash_extends` / `hash_implements` (global tokens,
context-meaningful inside #jni_class bodies — same pattern as #using).
`JniClassDecl.methods` refactored into `members: []const JniClassMember`,
a tagged union with `method` / `extends` / `implements` variants.
Body loop dispatches on the leading token: `#extends Alias;` /
`#implements Alias;` consume the alias name and push a non-method
member; everything else falls through to the existing method path.

The alias on the right of `#extends` is the sx-side name (resolved
to the corresponding #jni_class at sema time in a later step), not
the foreign Java path — the path lives only in the alias's own
directive arg.

123/123 examples green.
This commit is contained in:
agra
2026-05-20 10:02:56 +03:00
parent e225adbd1c
commit a5c6f754a8
7 changed files with 37 additions and 8 deletions

View File

@@ -1041,8 +1041,25 @@ pub const Parser = struct {
try self.expect(.l_brace);
var methods = std.ArrayList(ast.JniMethodDecl).empty;
var members = std.ArrayList(ast.JniClassMember).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) {
const is_extends = self.current.tag == .hash_extends;
self.advance();
if (self.current.tag != .identifier) {
return self.fail(if (is_extends) "expected superclass alias after '#extends'" else "expected interface alias after '#implements'");
}
const alias = self.tokenSlice(self.current);
self.advance();
try self.expect(.semicolon);
try members.append(self.allocator, if (is_extends)
.{ .extends = alias }
else
.{ .implements = alias });
continue;
}
// Optional `static` prefix → class method (no implicit `self`).
// Context-sensitive — `static` is a plain identifier elsewhere.
var is_static = false;
@@ -1055,7 +1072,7 @@ pub const Parser = struct {
}
// Method: name :: (args...) -> Ret;
// (Fields, #extends, #implements land in later steps.)
// (Fields, #desc override land in later steps.)
if (self.current.tag != .identifier) {
return self.fail("expected method name in '#jni_class' body");
}
@@ -1091,20 +1108,20 @@ pub const Parser = struct {
try self.expect(.semicolon);
try methods.append(self.allocator, .{
try members.append(self.allocator, .{ .method = .{
.name = method_name,
.params = try param_types.toOwnedSlice(self.allocator),
.param_names = try param_names.toOwnedSlice(self.allocator),
.return_type = return_type,
.is_static = is_static,
});
} });
}
try self.expect(.r_brace);
return try self.createNode(start_pos, .{ .jni_class_decl = .{
.name = name,
.java_path = java_path,
.methods = try methods.toOwnedSlice(self.allocator),
.members = try members.toOwnedSlice(self.allocator),
} });
}