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:
19
src/ast.zig
19
src/ast.zig
@@ -80,6 +80,7 @@ pub const Node = struct {
|
|||||||
c_import_decl: CImportDecl,
|
c_import_decl: CImportDecl,
|
||||||
protocol_decl: ProtocolDecl,
|
protocol_decl: ProtocolDecl,
|
||||||
impl_block: ImplBlock,
|
impl_block: ImplBlock,
|
||||||
|
ffi_intrinsic_call: FfiIntrinsicCall,
|
||||||
|
|
||||||
pub fn declName(self: Data) ?[]const u8 {
|
pub fn declName(self: Data) ?[]const u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
@@ -203,6 +204,24 @@ pub const Call = struct {
|
|||||||
args: []const *Node,
|
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 {
|
pub const FieldAccess = struct {
|
||||||
object: *Node,
|
object: *Node,
|
||||||
field: []const u8,
|
field: []const u8,
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ pub const Lexer = struct {
|
|||||||
.{ "#define", Tag.hash_define },
|
.{ "#define", Tag.hash_define },
|
||||||
.{ "#flags", Tag.hash_flags },
|
.{ "#flags", Tag.hash_flags },
|
||||||
.{ "#inline", Tag.hash_inline },
|
.{ "#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| {
|
inline for (directives) |d| {
|
||||||
const keyword = d[0];
|
const keyword = d[0];
|
||||||
|
|||||||
@@ -1495,6 +1495,9 @@ pub const Server = struct {
|
|||||||
.hash_define,
|
.hash_define,
|
||||||
.hash_flags,
|
.hash_flags,
|
||||||
.hash_inline,
|
.hash_inline,
|
||||||
|
.hash_objc_call,
|
||||||
|
.hash_jni_call,
|
||||||
|
.hash_jni_static_call,
|
||||||
=> ST.keyword,
|
=> ST.keyword,
|
||||||
|
|
||||||
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
||||||
|
|||||||
@@ -2036,12 +2036,54 @@ pub const Parser = struct {
|
|||||||
const inner = try self.parseExpr();
|
const inner = try self.parseExpr();
|
||||||
return try self.createNode(start, .{ .comptime_expr = .{ .expr = inner } });
|
return try self.createNode(start, .{ .comptime_expr = .{ .expr = inner } });
|
||||||
},
|
},
|
||||||
|
.hash_objc_call, .hash_jni_call, .hash_jni_static_call => {
|
||||||
|
return try self.parseFfiIntrinsicCall();
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
return self.fail("unexpected token in expression");
|
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 {
|
fn parseIfExpr(self: *Parser) anyerror!*Node {
|
||||||
const start = self.current.loc.start;
|
const start = self.current.loc.start;
|
||||||
self.advance(); // skip 'if'
|
self.advance(); // skip 'if'
|
||||||
|
|||||||
12
src/sema.zig
12
src/sema.zig
@@ -743,6 +743,12 @@ pub const Analyzer = struct {
|
|||||||
try self.analyzeNode(arg);
|
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| {
|
.field_access => |fa| {
|
||||||
try self.analyzeNode(fa.object);
|
try self.analyzeNode(fa.object);
|
||||||
},
|
},
|
||||||
@@ -1187,6 +1193,12 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
|||||||
if (findNodeAtOffset(arg, offset)) |found| return found;
|
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| {
|
.field_access => |fa| {
|
||||||
if (findNodeAtOffset(fa.object, offset)) |found| return found;
|
if (findNodeAtOffset(fa.object, offset)) |found| return found;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ pub const Tag = enum {
|
|||||||
hash_define, // #define (inside #import c { ... })
|
hash_define, // #define (inside #import c { ... })
|
||||||
hash_flags, // #flags (inside #import c { ... })
|
hash_flags, // #flags (inside #import c { ... })
|
||||||
hash_inline, // #inline (protocol layout modifier)
|
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, // ---
|
triple_minus, // ---
|
||||||
|
|
||||||
// Special
|
// Special
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1
|
0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
/Users/agra/projects/sx/examples/ffi-objc-call-01-parse.sx:21:9: error: unexpected token in expression
|
parse-only ok
|
||||||
|
|||||||
Reference in New Issue
Block a user