diff --git a/src/ast.zig b/src/ast.zig index d13f38a..99a8650 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -541,10 +541,16 @@ pub const JniMethodDecl = struct { 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 { name: []const u8, // sx-side alias (left of `::`) 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 { diff --git a/src/lexer.zig b/src/lexer.zig index 5dc7689..4aabf86 100644 --- a/src/lexer.zig +++ b/src/lexer.zig @@ -83,6 +83,8 @@ pub const Lexer = struct { .{ "#jni_call", Tag.hash_jni_call }, .{ "#jni_static_call", Tag.hash_jni_static_call }, .{ "#jni_class", Tag.hash_jni_class }, + .{ "#extends", Tag.hash_extends }, + .{ "#implements", Tag.hash_implements }, }; inline for (directives) |d| { const keyword = d[0]; diff --git a/src/lsp/server.zig b/src/lsp/server.zig index 644c64e..f17d30a 100644 --- a/src/lsp/server.zig +++ b/src/lsp/server.zig @@ -1499,6 +1499,8 @@ pub const Server = struct { .hash_jni_call, .hash_jni_static_call, .hash_jni_class, + .hash_extends, + .hash_implements, => ST.keyword, .kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_, diff --git a/src/parser.zig b/src/parser.zig index 2034301..2efd1cc 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -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), } }); } diff --git a/src/token.zig b/src/token.zig index e1d8845..d2d4b4c 100644 --- a/src/token.zig +++ b/src/token.zig @@ -116,6 +116,8 @@ pub const Tag = enum { 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_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, // --- // Special diff --git a/tests/expected/ffi-jni-class-04-extends.exit b/tests/expected/ffi-jni-class-04-extends.exit index d00491f..573541a 100644 --- a/tests/expected/ffi-jni-class-04-extends.exit +++ b/tests/expected/ffi-jni-class-04-extends.exit @@ -1 +1 @@ -1 +0 diff --git a/tests/expected/ffi-jni-class-04-extends.txt b/tests/expected/ffi-jni-class-04-extends.txt index f830242..2ef3b99 100644 --- a/tests/expected/ffi-jni-class-04-extends.txt +++ b/tests/expected/ffi-jni-class-04-extends.txt @@ -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