ffi 1.1: parser accepts #objc_call / #jni_call / #jni_static_call
98/98 regression tests pass; ffi-objc-call-01-parse flips from
parse-error xfail to passing.
Shape: `#<intrinsic>(ReturnT)(args...)`. The return-type generic
sits in the first parens, the actual call args in the second. All
three intrinsics share the same parse rule; only the kind tag and
the downstream lowering differ.
token.zig | three new hash_* tags
lexer.zig | matches the directive keywords with the same
isIdentContinue boundary check as the rest
ast.zig | FfiIntrinsicCall node with `kind`, `return_type`,
and `args` fields; FfiIntrinsicKind enum
parser.zig | parseFfiIntrinsicCall — same call-arg loop shape
as Call, with the leading return-type slot
sema.zig | analyzeNode + findNodeAtOffset arms walk the args
+ return-type child nodes
lsp/server.zig | classify the new tokens as ST.keyword
Codegen for the new intrinsic isn't wired yet — examples that
reach the body of a non-suppressed call would fail at lowering.
The current parse test uses `inline if false { ... }` to suppress
the dead branch, so sema/codegen don't see the node. Phase 1.3+
adds the lowering and the gate comes off.
Chess Android + iOS-sim builds clean — no regression on the
existing `objc_msgSend` cast pattern or the JNI helper.
This commit is contained in:
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user