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:
@@ -541,10 +541,16 @@ pub const JniMethodDecl = struct {
|
|||||||
is_static: bool = false, // true for `static name :: ...`
|
is_static: bool = false, // true for `static name :: ...`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const JniClassMember = union(enum) {
|
||||||
|
method: JniMethodDecl,
|
||||||
|
extends: []const u8, // sx-side alias name (right of `#extends`)
|
||||||
|
implements: []const u8, // sx-side alias name (right of `#implements`)
|
||||||
|
};
|
||||||
|
|
||||||
pub const JniClassDecl = struct {
|
pub const JniClassDecl = struct {
|
||||||
name: []const u8, // sx-side alias (left of `::`)
|
name: []const u8, // sx-side alias (left of `::`)
|
||||||
java_path: []const u8, // directive arg, e.g. "java/path/Foo"
|
java_path: []const u8, // directive arg, e.g. "java/path/Foo"
|
||||||
methods: []const JniMethodDecl = &.{}, // instance methods (static/fields/extends land in later steps)
|
members: []const JniClassMember = &.{}, // methods, #extends, #implements (fields/#desc land in 2.5+)
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ImplBlock = struct {
|
pub const ImplBlock = struct {
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ pub const Lexer = struct {
|
|||||||
.{ "#jni_call", Tag.hash_jni_call },
|
.{ "#jni_call", Tag.hash_jni_call },
|
||||||
.{ "#jni_static_call", Tag.hash_jni_static_call },
|
.{ "#jni_static_call", Tag.hash_jni_static_call },
|
||||||
.{ "#jni_class", Tag.hash_jni_class },
|
.{ "#jni_class", Tag.hash_jni_class },
|
||||||
|
.{ "#extends", Tag.hash_extends },
|
||||||
|
.{ "#implements", Tag.hash_implements },
|
||||||
};
|
};
|
||||||
inline for (directives) |d| {
|
inline for (directives) |d| {
|
||||||
const keyword = d[0];
|
const keyword = d[0];
|
||||||
|
|||||||
@@ -1499,6 +1499,8 @@ pub const Server = struct {
|
|||||||
.hash_jni_call,
|
.hash_jni_call,
|
||||||
.hash_jni_static_call,
|
.hash_jni_static_call,
|
||||||
.hash_jni_class,
|
.hash_jni_class,
|
||||||
|
.hash_extends,
|
||||||
|
.hash_implements,
|
||||||
=> ST.keyword,
|
=> ST.keyword,
|
||||||
|
|
||||||
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
||||||
|
|||||||
@@ -1041,8 +1041,25 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
try self.expect(.l_brace);
|
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) {
|
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`).
|
// Optional `static` prefix → class method (no implicit `self`).
|
||||||
// Context-sensitive — `static` is a plain identifier elsewhere.
|
// Context-sensitive — `static` is a plain identifier elsewhere.
|
||||||
var is_static = false;
|
var is_static = false;
|
||||||
@@ -1055,7 +1072,7 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Method: name :: (args...) -> Ret;
|
// Method: name :: (args...) -> Ret;
|
||||||
// (Fields, #extends, #implements land in later steps.)
|
// (Fields, #desc override land in later steps.)
|
||||||
if (self.current.tag != .identifier) {
|
if (self.current.tag != .identifier) {
|
||||||
return self.fail("expected method name in '#jni_class' body");
|
return self.fail("expected method name in '#jni_class' body");
|
||||||
}
|
}
|
||||||
@@ -1091,20 +1108,20 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
try self.expect(.semicolon);
|
try self.expect(.semicolon);
|
||||||
|
|
||||||
try methods.append(self.allocator, .{
|
try members.append(self.allocator, .{ .method = .{
|
||||||
.name = method_name,
|
.name = method_name,
|
||||||
.params = try param_types.toOwnedSlice(self.allocator),
|
.params = try param_types.toOwnedSlice(self.allocator),
|
||||||
.param_names = try param_names.toOwnedSlice(self.allocator),
|
.param_names = try param_names.toOwnedSlice(self.allocator),
|
||||||
.return_type = return_type,
|
.return_type = return_type,
|
||||||
.is_static = is_static,
|
.is_static = is_static,
|
||||||
});
|
} });
|
||||||
}
|
}
|
||||||
try self.expect(.r_brace);
|
try self.expect(.r_brace);
|
||||||
|
|
||||||
return try self.createNode(start_pos, .{ .jni_class_decl = .{
|
return try self.createNode(start_pos, .{ .jni_class_decl = .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.java_path = java_path,
|
.java_path = java_path,
|
||||||
.methods = try methods.toOwnedSlice(self.allocator),
|
.members = try members.toOwnedSlice(self.allocator),
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ pub const Tag = enum {
|
|||||||
hash_jni_call, // #jni_call(T)(env, target, "name", "(Sig)R", args...)
|
hash_jni_call, // #jni_call(T)(env, target, "name", "(Sig)R", args...)
|
||||||
hash_jni_static_call, // #jni_static_call(T)(class, "name", "(Sig)R", args...)
|
hash_jni_static_call, // #jni_static_call(T)(class, "name", "(Sig)R", args...)
|
||||||
hash_jni_class, // Foo :: #jni_class("java/path/Foo") { ...body... }
|
hash_jni_class, // Foo :: #jni_class("java/path/Foo") { ...body... }
|
||||||
|
hash_extends, // `#extends Alias;` inside a #jni_class / #objc_class body
|
||||||
|
hash_implements, // `#implements Alias;` inside a #jni_class / #objc_class body
|
||||||
triple_minus, // ---
|
triple_minus, // ---
|
||||||
|
|
||||||
// Special
|
// Special
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1
|
0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
/Users/agra/projects/sx/examples/ffi-jni-class-04-extends.sx:18:5: error: expected method name in '#jni_class' body
|
parse-only ok
|
||||||
|
|||||||
Reference in New Issue
Block a user