diff --git a/src/ast.zig b/src/ast.zig index 2d63d57..5f85553 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -80,6 +80,7 @@ pub const Node = struct { c_import_decl: CImportDecl, protocol_decl: ProtocolDecl, impl_block: ImplBlock, + ffi_intrinsic_call: FfiIntrinsicCall, pub fn declName(self: Data) ?[]const u8 { return switch (self) { @@ -203,6 +204,24 @@ pub const Call = struct { args: []const *Node, }; +/// `#objc_call(T)(recv, "sel:", args...)`, +/// `#jni_call(T)(env, target, "name", "(Sig)R", args...)`, +/// `#jni_static_call(T)(class, "name", "(Sig)R", args...)`. +/// The return-type T sits in the first parens; the actual call args +/// follow in the second parens. Codegen branches on `kind` to pick +/// the lowering (objc_msgSend / CallXxxMethod / CallStaticXxxMethod). +pub const FfiIntrinsicKind = enum { + objc_call, + jni_call, + jni_static_call, +}; + +pub const FfiIntrinsicCall = struct { + kind: FfiIntrinsicKind, + return_type: *Node, + args: []const *Node, +}; + pub const FieldAccess = struct { object: *Node, field: []const u8, diff --git a/src/lexer.zig b/src/lexer.zig index c1d178c..2f78712 100644 --- a/src/lexer.zig +++ b/src/lexer.zig @@ -79,6 +79,9 @@ pub const Lexer = struct { .{ "#define", Tag.hash_define }, .{ "#flags", Tag.hash_flags }, .{ "#inline", Tag.hash_inline }, + .{ "#objc_call", Tag.hash_objc_call }, + .{ "#jni_call", Tag.hash_jni_call }, + .{ "#jni_static_call", Tag.hash_jni_static_call }, }; inline for (directives) |d| { const keyword = d[0]; diff --git a/src/lsp/server.zig b/src/lsp/server.zig index b51d2a7..baca0bf 100644 --- a/src/lsp/server.zig +++ b/src/lsp/server.zig @@ -1495,6 +1495,9 @@ pub const Server = struct { .hash_define, .hash_flags, .hash_inline, + .hash_objc_call, + .hash_jni_call, + .hash_jni_static_call, => ST.keyword, .kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_, diff --git a/src/parser.zig b/src/parser.zig index 86511d3..4168ab1 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -2036,12 +2036,54 @@ pub const Parser = struct { const inner = try self.parseExpr(); return try self.createNode(start, .{ .comptime_expr = .{ .expr = inner } }); }, + .hash_objc_call, .hash_jni_call, .hash_jni_static_call => { + return try self.parseFfiIntrinsicCall(); + }, else => { return self.fail("unexpected token in expression"); }, } } + /// Parse `#objc_call(T)(recv, "sel:", args...)`, + /// `#jni_call(T)(env, target, "name", "(Sig)R", args...)`, or + /// `#jni_static_call(T)(class, "name", "(Sig)R", args...)`. The + /// return type sits in the first parens; the actual call args + /// follow in the second. + fn parseFfiIntrinsicCall(self: *Parser) anyerror!*Node { + const start = self.current.loc.start; + const kind: ast.FfiIntrinsicKind = switch (self.current.tag) { + .hash_objc_call => .objc_call, + .hash_jni_call => .jni_call, + .hash_jni_static_call => .jni_static_call, + else => unreachable, + }; + self.advance(); // skip the directive + + try self.expect(.l_paren); + const ret_type = try self.parseTypeExpr(); + try self.expect(.r_paren); + + try self.expect(.l_paren); + var args = std.ArrayList(*Node).empty; + while (self.current.tag != .r_paren and self.current.tag != .eof) { + const arg = try self.parseExpr(); + try args.append(self.allocator, arg); + if (self.current.tag == .comma) { + self.advance(); + } else { + break; + } + } + try self.expect(.r_paren); + + return try self.createNode(start, .{ .ffi_intrinsic_call = .{ + .kind = kind, + .return_type = ret_type, + .args = try args.toOwnedSlice(self.allocator), + } }); + } + fn parseIfExpr(self: *Parser) anyerror!*Node { const start = self.current.loc.start; self.advance(); // skip 'if' diff --git a/src/sema.zig b/src/sema.zig index 6182eb9..8072831 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -743,6 +743,12 @@ pub const Analyzer = struct { try self.analyzeNode(arg); } }, + .ffi_intrinsic_call => |fic| { + try self.analyzeNode(fic.return_type); + for (fic.args) |arg| { + try self.analyzeNode(arg); + } + }, .field_access => |fa| { try self.analyzeNode(fa.object); }, @@ -1187,6 +1193,12 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node { if (findNodeAtOffset(arg, offset)) |found| return found; } }, + .ffi_intrinsic_call => |fic| { + if (findNodeAtOffset(fic.return_type, offset)) |found| return found; + for (fic.args) |arg| { + if (findNodeAtOffset(arg, offset)) |found| return found; + } + }, .field_access => |fa| { if (findNodeAtOffset(fa.object, offset)) |found| return found; }, diff --git a/src/token.zig b/src/token.zig index 33ceedf..44c5cdf 100644 --- a/src/token.zig +++ b/src/token.zig @@ -112,6 +112,9 @@ pub const Tag = enum { hash_define, // #define (inside #import c { ... }) hash_flags, // #flags (inside #import c { ... }) hash_inline, // #inline (protocol layout modifier) + hash_objc_call, // #objc_call(T)(recv, "sel:", 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...) triple_minus, // --- // Special diff --git a/tests/expected/ffi-objc-call-01-parse.exit b/tests/expected/ffi-objc-call-01-parse.exit index d00491f..573541a 100644 --- a/tests/expected/ffi-objc-call-01-parse.exit +++ b/tests/expected/ffi-objc-call-01-parse.exit @@ -1 +1 @@ -1 +0 diff --git a/tests/expected/ffi-objc-call-01-parse.txt b/tests/expected/ffi-objc-call-01-parse.txt index ccd4580..2ef3b99 100644 --- a/tests/expected/ffi-objc-call-01-parse.txt +++ b/tests/expected/ffi-objc-call-01-parse.txt @@ -1 +1 @@ -/Users/agra/projects/sx/examples/ffi-objc-call-01-parse.sx:21:9: error: unexpected token in expression +parse-only ok