ffi 2.16a green: parser + AST + sema for #jni_env(env) { body }
New `hash_jni_env` lexer token; `parsePrimary` dispatches to a small
`parseJniEnvBlock` that consumes `(env) { body }` and returns a new
`JniEnvBlock` AST node (env_expr + body block).
Sema's analyzeNode arm recurses into env + body inside a pushed
scope; findNodeAtOffset descends through both children for go-to-
definition.
Lowering treats it as a syntactic wrapper around the block: env is
evaluated for side effects, body lowers as a normal block. The TL
push/pop semantics (synthesizing the env stack so `#jni_call`'s env
arg can become optional) land in 2.16b.
`expectSemicolonAfter` recognises `jni_env_block` as block-form so
statement-position uses don't need a trailing `;` — matches `if` /
`while` / `for` / bare blocks.
Test runs through the block body and prints expected output; xfail
snapshot flips to green. 127/127 examples green.
This commit is contained in:
@@ -82,6 +82,7 @@ pub const Node = struct {
|
||||
impl_block: ImplBlock,
|
||||
ffi_intrinsic_call: FfiIntrinsicCall,
|
||||
foreign_class_decl: ForeignClassDecl,
|
||||
jni_env_block: JniEnvBlock,
|
||||
|
||||
pub fn declName(self: Data) ?[]const u8 {
|
||||
return switch (self) {
|
||||
@@ -571,6 +572,11 @@ pub const ForeignClassDecl = struct {
|
||||
members: []const ForeignClassMember = &.{},
|
||||
};
|
||||
|
||||
pub const JniEnvBlock = struct {
|
||||
env: *Node, // expression yielding the *JNIEnv for this scope
|
||||
body: *Node, // block (or expression) — runs with `env` scoped via TL push/pop
|
||||
};
|
||||
|
||||
pub const ImplBlock = struct {
|
||||
protocol_name: []const u8,
|
||||
target_type: []const u8,
|
||||
|
||||
@@ -1050,6 +1050,13 @@ pub const Lowering = struct {
|
||||
.destructure_decl => |dd| self.lowerDestructureDecl(&dd),
|
||||
.insert_expr => |ins| self.lowerInsertExpr(ins.expr),
|
||||
.block => self.lowerBlock(node),
|
||||
.jni_env_block => |eb| {
|
||||
// 2.16a: evaluate env for side effects, lower body as a normal block.
|
||||
// TL push/pop semantics land in 2.16b; until then `#jni_env` is a
|
||||
// syntactic marker that doesn't affect codegen.
|
||||
_ = self.lowerExpr(eb.env);
|
||||
self.lowerBlock(eb.body);
|
||||
},
|
||||
// Block-local type declarations
|
||||
.struct_decl => |sd| self.registerStructDecl(&sd),
|
||||
.enum_decl, .union_decl => {
|
||||
|
||||
@@ -92,6 +92,7 @@ pub const Lexer = struct {
|
||||
.{ "#extends", Tag.hash_extends },
|
||||
.{ "#implements", Tag.hash_implements },
|
||||
.{ "#jni_method_descriptor", Tag.hash_jni_method_descriptor },
|
||||
.{ "#jni_env", Tag.hash_jni_env },
|
||||
};
|
||||
inline for (directives) |d| {
|
||||
const keyword = d[0];
|
||||
|
||||
@@ -1508,6 +1508,7 @@ pub const Server = struct {
|
||||
.hash_extends,
|
||||
.hash_implements,
|
||||
.hash_jni_method_descriptor,
|
||||
.hash_jni_env,
|
||||
=> ST.keyword,
|
||||
|
||||
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
||||
|
||||
@@ -1558,6 +1558,7 @@ pub const Parser = struct {
|
||||
.while_expr => false,
|
||||
.for_expr => false,
|
||||
.block => false,
|
||||
.jni_env_block => false,
|
||||
else => true,
|
||||
};
|
||||
if (needs_semi) {
|
||||
@@ -2187,6 +2188,9 @@ pub const Parser = struct {
|
||||
.hash_objc_call, .hash_jni_call, .hash_jni_static_call => {
|
||||
return try self.parseFfiIntrinsicCall();
|
||||
},
|
||||
.hash_jni_env => {
|
||||
return try self.parseJniEnvBlock();
|
||||
},
|
||||
else => {
|
||||
return self.fail("unexpected token in expression");
|
||||
},
|
||||
@@ -2198,6 +2202,27 @@ pub const Parser = struct {
|
||||
/// `#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 parseJniEnvBlock(self: *Parser) anyerror!*Node {
|
||||
const start = self.current.loc.start;
|
||||
self.advance(); // skip `#jni_env`
|
||||
|
||||
try self.expect(.l_paren);
|
||||
const env_expr = try self.parseExpr();
|
||||
try self.expect(.r_paren);
|
||||
|
||||
// Body is a brace-delimited block. The `-> ?T` annotation for
|
||||
// exception bubbling lands with step 2.15 / 2.16 follow-ups.
|
||||
if (self.current.tag != .l_brace) {
|
||||
return self.fail("expected '{' after '#jni_env(env)'");
|
||||
}
|
||||
const body = try self.parseBlock();
|
||||
|
||||
return try self.createNode(start, .{ .jni_env_block = .{
|
||||
.env = env_expr,
|
||||
.body = body,
|
||||
} });
|
||||
}
|
||||
|
||||
fn parseFfiIntrinsicCall(self: *Parser) anyerror!*Node {
|
||||
const start = self.current.loc.start;
|
||||
const kind: ast.FfiIntrinsicKind = switch (self.current.tag) {
|
||||
|
||||
10
src/sema.zig
10
src/sema.zig
@@ -910,6 +910,12 @@ pub const Analyzer = struct {
|
||||
.foreign_class_decl => |fd| {
|
||||
try self.addSymbol(fd.name, .type_alias, null, node.span);
|
||||
},
|
||||
.jni_env_block => |eb| {
|
||||
try self.analyzeNode(eb.env);
|
||||
try self.pushScope();
|
||||
try self.analyzeNode(eb.body);
|
||||
self.popScope();
|
||||
},
|
||||
.impl_block => |ib| {
|
||||
// Each impl block gets its own scope so methods don't conflict across impls
|
||||
try self.pushScope();
|
||||
@@ -1311,6 +1317,10 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.closure_type_expr,
|
||||
.foreign_class_decl,
|
||||
=> {},
|
||||
.jni_env_block => |eb| {
|
||||
if (findNodeAtOffset(eb.env, offset)) |found| return found;
|
||||
if (findNodeAtOffset(eb.body, offset)) |found| return found;
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
for (sd.methods) |method_node| {
|
||||
if (findNodeAtOffset(method_node, offset)) |found| return found;
|
||||
|
||||
@@ -125,6 +125,7 @@ pub const Tag = enum {
|
||||
hash_extends, // `#extends Alias;` inside a foreign-class body
|
||||
hash_implements, // `#implements Alias;` inside a foreign-class body
|
||||
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
||||
hash_jni_env, // `#jni_env(env) { body }` block-form env-scoping intrinsic
|
||||
triple_minus, // ---
|
||||
|
||||
// Special
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
@@ -1 +1 @@
|
||||
/Users/agra/projects/sx/examples/ffi-jni-env-01-block.sx:17:5: error: unexpected token in expression
|
||||
inside #jni_env scope
|
||||
|
||||
Reference in New Issue
Block a user