Campaign Weeks 3-6 of /Users/agra/.claude/plans/lets-plan-to-move-splendid-pumpkin.md
land in one push: the bundling pipeline that used to live in
src/target.zig (createBundle, embedFramework, extractEntitlements,
buildInfoPlist, codesign) now lives in
library/modules/platform/bundle.sx and runs in the IR interpreter
after target.link() returns.
New language-side surface:
- library/modules/fs.sx — POSIX libc bindings (open/read/write/close,
mkdir/unlink/rmdir, chmod, rename, access, basename/dirname). Variadic
open() lowers to C's varargs via the new args: ..T form. Direct libc
calls bypass *File method dispatch so they work from the post-link
IR interpreter.
- library/modules/process.sx — popen-based run(cmd) returning
ProcessResult{ exit_code, stdout }, plus env() and find_executable().
- library/modules/std.sx — xml_escape(s) and variadic path_join(parts).
- library/modules/compiler.sx — BuildOptions grows
set_post_link_callback / set_post_link_module / binary_path
accessors; bundle_path/bundle_id/codesign_identity/provisioning_profile
setters + accessors; per-target predicates is_macos/is_ios/
is_ios_device/is_ios_simulator + target_triple; framework_count /
framework_at(i) / framework_path_count / framework_path_at(i);
add_asset_dir(src, dest) + asset_dir_count / src_at / dest_at.
Compiler-side wiring:
- src/ir/compiler_hooks.zig — BuildConfig now carries post_link_callback_fn,
post_link_module, binary_path, bundle_*, target_triple,
target_frameworks, target_framework_paths, asset_dirs. Hook registry
exposes every accessor; getters return "" / 0 for unset fields so
bundle.sx can treat absent values uniformly.
- src/ir/host_ffi.zig (new) — dlsym(RTLD_DEFAULT) + arity-switched cdecl
trampolines so #foreign("c") declarations resolve through the host
libc during #run / post-link interpretation.
- src/ir/interp.zig — callForeign dispatch; build_config pointer
injection so accessor hooks see live state during re-entry.
- src/core.zig — keeps the IR module alive past generateCode; exposes
invokeByName / invokeByFuncId so main.zig can re-enter the
interpreter after linking.
- src/main.zig — wires bundle/codesign/provisioning CLI flags +
target_triple + framework lists into BuildConfig; invokes the
post-link callback (by FuncId or by <module>.bundle_main lookup) once
target.link() returns. When --bundle is set but no callback is
registered, auto-falls-back to post_link_module = "platform.bundle"
so the legacy --bundle CLI keeps working for any program that imports
modules/platform/bundle.sx.
Apple .app bundler (library/modules/platform/bundle.sx):
- Single bundle_main entry covers macOS, iOS simulator, iOS device.
Per-target Info.plist switch keys off is_ios()/is_ios_simulator() —
iOS emits UIDeviceFamily / LSRequiresIPhoneOS /
UIApplicationSceneManifest / DTPlatformName (iPhoneOS or
iPhoneSimulator); macOS emits the minimal CFBundle* set.
- iOS-only steps:
- Provisioning embed: fs.read_file + fs.write_file to
<bundle>/embedded.mobileprovision.
- Framework embed: recursive cp -R per -F search path into
<bundle>/Frameworks/<Name>.framework/ (until fs.sx grows list_dir).
- Entitlements extraction: four process.run calls (security cms -D,
plutil -extract Entitlements xml1, plutil -extract
ApplicationIdentifierPrefix.0, plutil -replace application-identifier)
resolving the wildcard <TEAM>.* -> <TEAM>.<bundle_id>.
- Real codesign with --entitlements when present.
- Asset dirs (add_asset_dir): recursive cp -R src/. into <bundle>/dest/.
Missing src is treated as "nothing to do" so projects can register
add_asset_dir("assets", "assets") unconditionally.
Parser:
- parseStmt() now accepts #import \"path\"; and #framework \"Name\"; as
statement-position tokens. Needed for top-level
inline if OS == .android { #import \"modules/platform/android.sx\"; }
blocks (issue-0042 flatten pass surfaces them); chess's
inline-if-with-#import was rejected at parse time before this fix.
Removals from src/target.zig:
- createBundle, embedFramework, extractEntitlements, buildInfoPlist,
codesign (~210 lines). main.zig no longer calls createBundle after
link(); the sx callback is the single entry point.
Tests / regression markers (all run under sx run host JIT):
- examples/115-post-link-callback.sx — callback registration round-trip.
- examples/116-fs-roundtrip.sx — fs.write_file -> fs.read_file -> exists.
- examples/117-process-roundtrip.sx — process.run + env + find_executable.
- examples/118-macos-bundle.sx — macOS .app via bundle_main callback.
- examples/119-interp-cast-ptr-cmp.sx — cast(T) val under interpreter.
- examples/120-interp-variadic-any.sx — variadic ..Any indexing in IR
interpreter.
- examples/121-ios-sim-bundle.sx — iOS-sim cross-compile + .app with
iOS-shaped Info.plist (added to tests/cross_compile.sh as the
ios-sim tuple).
- examples/122-ios-device-bundle.sx — iOS device cross-compile +
full codesign pipeline (provisioning embed + entitlements
extraction + --entitlements codesign). Manually verified end-to-end:
installed via xcrun devicectl device install app + launched
successfully on iPhone 17 Pro.
- examples/123-inline-if-import-in-body.sx — locks in the parser fix.
zig build && zig build test && bash tests/run_examples.sh => 141 passed,
0 failed; bash tests/cross_compile.sh => 7 passed, 0 failed.
594 lines
16 KiB
Zig
594 lines
16 KiB
Zig
const std = @import("std");
|
|
|
|
pub const Span = struct {
|
|
start: u32,
|
|
end: u32,
|
|
};
|
|
|
|
pub const Node = struct {
|
|
span: Span,
|
|
data: Data,
|
|
source_file: ?[]const u8 = null,
|
|
|
|
pub const Data = union(enum) {
|
|
root: Root,
|
|
fn_decl: FnDecl,
|
|
block: Block,
|
|
int_literal: IntLiteral,
|
|
float_literal: FloatLiteral,
|
|
bool_literal: BoolLiteral,
|
|
string_literal: StringLiteral,
|
|
identifier: Identifier,
|
|
enum_literal: EnumLiteral,
|
|
binary_op: BinaryOp,
|
|
chained_comparison: ChainedComparison,
|
|
unary_op: UnaryOp,
|
|
call: Call,
|
|
field_access: FieldAccess,
|
|
if_expr: IfExpr,
|
|
match_expr: MatchExpr,
|
|
match_arm: MatchArm,
|
|
const_decl: ConstDecl,
|
|
var_decl: VarDecl,
|
|
assignment: Assignment,
|
|
multi_assign: MultiAssign,
|
|
destructure_decl: DestructureDecl,
|
|
enum_decl: EnumDecl,
|
|
struct_decl: StructDecl,
|
|
struct_literal: StructLiteral,
|
|
union_decl: UnionDecl,
|
|
lambda: Lambda,
|
|
type_expr: TypeExpr,
|
|
param: Param,
|
|
defer_stmt: DeferStmt,
|
|
push_stmt: PushStmt,
|
|
comptime_expr: ComptimeExpr,
|
|
insert_expr: InsertExpr,
|
|
return_stmt: ReturnStmt,
|
|
import_decl: ImportDecl,
|
|
namespace_decl: NamespaceDecl,
|
|
array_type_expr: ArrayTypeExpr,
|
|
slice_type_expr: SliceTypeExpr,
|
|
array_literal: ArrayLiteral,
|
|
parameterized_type_expr: ParameterizedTypeExpr,
|
|
index_expr: IndexExpr,
|
|
slice_expr: SliceExpr,
|
|
pointer_type_expr: PointerTypeExpr,
|
|
many_pointer_type_expr: ManyPointerTypeExpr,
|
|
optional_type_expr: OptionalTypeExpr,
|
|
force_unwrap: ForceUnwrap,
|
|
null_coalesce: NullCoalesce,
|
|
deref_expr: DerefExpr,
|
|
null_literal: void,
|
|
while_expr: WhileExpr,
|
|
for_expr: ForExpr,
|
|
spread_expr: SpreadExpr,
|
|
break_expr: void,
|
|
continue_expr: void,
|
|
undef_literal: void,
|
|
inferred_type: void,
|
|
builtin_expr: void,
|
|
compiler_expr: void,
|
|
foreign_expr: ForeignExpr,
|
|
library_decl: LibraryDecl,
|
|
framework_decl: FrameworkDecl,
|
|
function_type_expr: FunctionTypeExpr,
|
|
closure_type_expr: ClosureTypeExpr,
|
|
tuple_type_expr: TupleTypeExpr,
|
|
tuple_literal: TupleLiteral,
|
|
ufcs_alias: UfcsAlias,
|
|
c_import_decl: CImportDecl,
|
|
protocol_decl: ProtocolDecl,
|
|
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) {
|
|
.fn_decl => |d| d.name,
|
|
.const_decl => |d| d.name,
|
|
.var_decl => |d| d.name,
|
|
.enum_decl => |d| d.name,
|
|
.struct_decl => |d| d.name,
|
|
.union_decl => |d| d.name,
|
|
.namespace_decl => |d| d.name,
|
|
.ufcs_alias => |d| d.name,
|
|
.c_import_decl => |d| d.name,
|
|
.protocol_decl => |d| d.name,
|
|
.foreign_class_decl => |d| d.name,
|
|
else => null,
|
|
};
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const Root = struct {
|
|
decls: []const *Node,
|
|
};
|
|
|
|
pub const CallingConvention = enum { default, c };
|
|
|
|
pub const FnDecl = struct {
|
|
name: []const u8,
|
|
params: []const Param,
|
|
return_type: ?*Node,
|
|
body: *Node,
|
|
type_params: []const StructTypeParam = &.{},
|
|
is_arrow: bool = false,
|
|
call_conv: CallingConvention = .default,
|
|
};
|
|
|
|
pub const Param = struct {
|
|
name: []const u8,
|
|
name_span: Span,
|
|
type_expr: *Node,
|
|
is_variadic: bool = false,
|
|
is_comptime: bool = false,
|
|
/// Optional default value expression. When the caller omits this
|
|
/// parameter, lowering substitutes this expression in its place.
|
|
default_expr: ?*Node = null,
|
|
};
|
|
|
|
pub const Block = struct {
|
|
stmts: []const *Node,
|
|
};
|
|
|
|
pub const IntLiteral = struct {
|
|
value: i64,
|
|
};
|
|
|
|
pub const FloatLiteral = struct {
|
|
value: f64,
|
|
};
|
|
|
|
pub const BoolLiteral = struct {
|
|
value: bool,
|
|
};
|
|
|
|
pub const StringLiteral = struct {
|
|
raw: []const u8,
|
|
is_raw: bool = false,
|
|
};
|
|
|
|
pub const Identifier = struct {
|
|
name: []const u8,
|
|
};
|
|
|
|
pub const EnumLiteral = struct {
|
|
name: []const u8, // without the leading dot
|
|
};
|
|
|
|
pub const BinaryOp = struct {
|
|
op: Op,
|
|
lhs: *Node,
|
|
rhs: *Node,
|
|
|
|
pub const Op = enum {
|
|
add,
|
|
sub,
|
|
mul,
|
|
div,
|
|
mod,
|
|
eq,
|
|
neq,
|
|
lt,
|
|
lte,
|
|
gt,
|
|
gte,
|
|
and_op,
|
|
or_op,
|
|
bit_and,
|
|
bit_or,
|
|
bit_xor,
|
|
shl,
|
|
shr,
|
|
in_op,
|
|
};
|
|
};
|
|
|
|
pub const ChainedComparison = struct {
|
|
operands: []const *Node,
|
|
ops: []const BinaryOp.Op,
|
|
};
|
|
|
|
pub const UnaryOp = struct {
|
|
op: Op,
|
|
operand: *Node,
|
|
|
|
pub const Op = enum {
|
|
negate,
|
|
not,
|
|
bit_not,
|
|
xx,
|
|
address_of,
|
|
};
|
|
};
|
|
|
|
pub const Call = struct {
|
|
callee: *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 {
|
|
object: *Node,
|
|
field: []const u8,
|
|
is_optional: bool = false,
|
|
};
|
|
|
|
pub const IfExpr = struct {
|
|
condition: *Node,
|
|
then_branch: *Node,
|
|
else_branch: ?*Node,
|
|
is_inline: bool, // true for `if cond then a else b`
|
|
is_comptime: bool = false, // true for `inline if` — compile-time branch elimination
|
|
binding_name: ?[]const u8 = null, // for `if val := expr { ... }` optional binding
|
|
};
|
|
|
|
pub const MatchExpr = struct {
|
|
subject: *Node,
|
|
arms: []const MatchArm,
|
|
is_comptime: bool = false,
|
|
};
|
|
|
|
pub const MatchArm = struct {
|
|
pattern: ?*Node, // null = else (default) arm
|
|
body: *Node,
|
|
is_break: bool,
|
|
capture: ?[]const u8 = null, // payload binding name: case .variant: (name) { ... }
|
|
};
|
|
|
|
pub const ConstDecl = struct {
|
|
name: []const u8,
|
|
type_annotation: ?*Node,
|
|
value: *Node,
|
|
};
|
|
|
|
pub const VarDecl = struct {
|
|
name: []const u8,
|
|
type_annotation: ?*Node,
|
|
value: ?*Node,
|
|
is_foreign: bool = false,
|
|
foreign_lib: ?[]const u8 = null,
|
|
foreign_name: ?[]const u8 = null,
|
|
};
|
|
|
|
pub const Assignment = struct {
|
|
target: *Node,
|
|
op: Op,
|
|
value: *Node,
|
|
|
|
pub const Op = enum {
|
|
assign,
|
|
add_assign,
|
|
sub_assign,
|
|
mul_assign,
|
|
div_assign,
|
|
mod_assign,
|
|
and_assign,
|
|
or_assign,
|
|
xor_assign,
|
|
shl_assign,
|
|
shr_assign,
|
|
};
|
|
};
|
|
|
|
pub const MultiAssign = struct {
|
|
targets: []const *Node,
|
|
values: []const *Node,
|
|
};
|
|
|
|
pub const DestructureDecl = struct {
|
|
names: []const []const u8,
|
|
value: *Node,
|
|
};
|
|
|
|
pub const EnumDecl = struct {
|
|
name: []const u8,
|
|
variant_names: []const []const u8,
|
|
variant_types: []const ?*Node = &.{}, // null entries = no payload; empty = payload-less enum
|
|
is_flags: bool = false,
|
|
variant_values: []const ?*Node = &.{}, // explicit value per variant (null = auto), empty = all auto
|
|
backing_type: ?*Node = null, // optional backing type: enum u8 { ... }
|
|
};
|
|
|
|
pub const UnionDecl = struct {
|
|
name: []const u8,
|
|
field_names: []const []const u8,
|
|
field_types: []const *Node,
|
|
};
|
|
|
|
pub const StructTypeParam = struct {
|
|
name: []const u8, // e.g. "N" or "T" (without $)
|
|
constraint: *Node, // type_expr: "u32" for value param, "Type" for type param
|
|
protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable
|
|
};
|
|
|
|
pub const UsingEntry = struct {
|
|
insert_index: u32, // position in field_names where used fields are spliced
|
|
type_name: []const u8, // struct type to inline
|
|
};
|
|
|
|
pub const StructDecl = struct {
|
|
name: []const u8,
|
|
field_names: []const []const u8,
|
|
field_types: []const *Node, // type_expr nodes
|
|
field_defaults: []const ?*Node, // default value per field, null if none
|
|
type_params: []const StructTypeParam = &.{},
|
|
using_entries: []const UsingEntry = &.{},
|
|
methods: []const *Node = &.{}, // fn_decl nodes for struct methods
|
|
constants: []const *Node = &.{}, // const_decl nodes for struct-level constants
|
|
};
|
|
|
|
pub const StructFieldInit = struct {
|
|
name: ?[]const u8, // null for positional, non-null for named/shorthand
|
|
value: *Node,
|
|
};
|
|
|
|
pub const StructLiteral = struct {
|
|
struct_name: ?[]const u8, // null for anonymous `.{ ... }`
|
|
type_expr: ?*Node = null, // for GenericType(args).{ ... }
|
|
field_inits: []const StructFieldInit,
|
|
init_block: ?*Node = null, // optional `{ stmts }` block after struct literal
|
|
};
|
|
|
|
pub const Lambda = struct {
|
|
params: []const Param,
|
|
return_type: ?*Node,
|
|
body: *Node,
|
|
type_params: []const StructTypeParam = &.{},
|
|
call_conv: CallingConvention = .default,
|
|
};
|
|
|
|
pub const TypeExpr = struct {
|
|
name: []const u8,
|
|
is_generic: bool = false,
|
|
protocol_constraints: []const []const u8 = &.{}, // e.g. ["Eq", "Hashable"] for $T/Eq/Hashable
|
|
};
|
|
|
|
pub const DeferStmt = struct {
|
|
expr: *Node,
|
|
};
|
|
|
|
pub const PushStmt = struct {
|
|
context_expr: *Node,
|
|
body: *Node,
|
|
};
|
|
|
|
pub const ComptimeExpr = struct {
|
|
expr: *Node,
|
|
};
|
|
|
|
pub const InsertExpr = struct {
|
|
expr: *Node,
|
|
};
|
|
|
|
pub const ReturnStmt = struct {
|
|
value: ?*Node,
|
|
};
|
|
|
|
pub const ImportDecl = struct {
|
|
path: []const u8,
|
|
name: ?[]const u8,
|
|
};
|
|
|
|
pub const ArrayTypeExpr = struct {
|
|
length: *Node, // int_literal for the size
|
|
element_type: *Node, // type_expr for the element type
|
|
};
|
|
|
|
pub const SliceTypeExpr = struct {
|
|
element_type: *Node, // type_expr for the element type
|
|
};
|
|
|
|
pub const ArrayLiteral = struct {
|
|
elements: []const *Node,
|
|
type_expr: ?*Node = null,
|
|
};
|
|
|
|
pub const ParameterizedTypeExpr = struct {
|
|
name: []const u8, // e.g. "Vector", or later generic struct names
|
|
args: []const *Node, // e.g. [int_literal(3), type_expr("f32")]
|
|
};
|
|
|
|
pub const IndexExpr = struct {
|
|
object: *Node,
|
|
index: *Node,
|
|
};
|
|
|
|
pub const SliceExpr = struct {
|
|
object: *Node,
|
|
start: ?*Node = null,
|
|
end: ?*Node = null,
|
|
};
|
|
|
|
pub const PointerTypeExpr = struct {
|
|
pointee_type: *Node,
|
|
};
|
|
|
|
pub const ManyPointerTypeExpr = struct {
|
|
element_type: *Node,
|
|
};
|
|
|
|
pub const OptionalTypeExpr = struct {
|
|
inner_type: *Node,
|
|
};
|
|
|
|
pub const ForceUnwrap = struct {
|
|
operand: *Node,
|
|
};
|
|
|
|
pub const NullCoalesce = struct {
|
|
lhs: *Node,
|
|
rhs: *Node,
|
|
};
|
|
|
|
pub const DerefExpr = struct {
|
|
operand: *Node,
|
|
};
|
|
|
|
pub const WhileExpr = struct {
|
|
condition: *Node,
|
|
body: *Node,
|
|
binding_name: ?[]const u8 = null, // for `while val := expr { ... }` optional binding
|
|
};
|
|
|
|
pub const ForExpr = struct {
|
|
iterable: *Node,
|
|
body: *Node,
|
|
capture_name: []const u8,
|
|
index_name: ?[]const u8 = null,
|
|
};
|
|
|
|
pub const SpreadExpr = struct {
|
|
operand: *Node,
|
|
};
|
|
|
|
pub const NamespaceDecl = struct {
|
|
name: []const u8,
|
|
decls: []const *Node,
|
|
};
|
|
|
|
pub const ForeignExpr = struct {
|
|
library_ref: ?[]const u8 = null, // identifier name of library constant
|
|
c_name: ?[]const u8 = null, // C symbol name override
|
|
};
|
|
|
|
pub const LibraryDecl = struct {
|
|
lib_name: []const u8,
|
|
name: []const u8, // sx-side constant name
|
|
};
|
|
|
|
pub const FrameworkDecl = struct {
|
|
name: []const u8, // framework name, e.g. "Foundation"
|
|
};
|
|
|
|
pub const FunctionTypeExpr = struct {
|
|
param_types: []const *Node,
|
|
param_names: ?[]const ?[]const u8 = null, // optional documentation names
|
|
return_type: ?*Node, // null = void return
|
|
call_conv: CallingConvention = .default,
|
|
};
|
|
|
|
pub const ClosureTypeExpr = struct {
|
|
param_types: []const *Node,
|
|
param_names: ?[]const ?[]const u8 = null, // optional documentation names
|
|
return_type: ?*Node, // null = void return
|
|
};
|
|
|
|
pub const TupleTypeExpr = struct {
|
|
field_types: []const *Node,
|
|
field_names: ?[]const []const u8, // null for positional
|
|
};
|
|
|
|
pub const TupleLiteral = struct {
|
|
elements: []const TupleElement,
|
|
};
|
|
|
|
pub const TupleElement = struct {
|
|
name: ?[]const u8, // null for positional
|
|
value: *Node,
|
|
};
|
|
|
|
pub const UfcsAlias = struct {
|
|
name: []const u8,
|
|
target: []const u8,
|
|
};
|
|
|
|
pub const CImportDecl = struct {
|
|
includes: []const []const u8,
|
|
sources: []const []const u8,
|
|
defines: []const []const u8,
|
|
flags: []const []const u8,
|
|
name: ?[]const u8 = null,
|
|
bitcode_paths: []const []const u8 = &.{}, // populated during import resolution
|
|
};
|
|
|
|
pub const ProtocolMethodDecl = struct {
|
|
name: []const u8,
|
|
params: []const *Node, // type_expr nodes for parameter types (excluding implicit self)
|
|
param_names: []const []const u8, // parameter names (excluding implicit self)
|
|
return_type: ?*Node, // null = void return
|
|
default_body: ?*Node, // null = required method, non-null = default implementation
|
|
};
|
|
|
|
pub const ProtocolDecl = struct {
|
|
name: []const u8,
|
|
methods: []const ProtocolMethodDecl,
|
|
is_inline: bool = false, // #inline — embedded fn ptrs instead of vtable pointer
|
|
type_params: []const StructTypeParam = &.{}, // for `protocol(Target: Type) { ... }`
|
|
};
|
|
|
|
pub const ForeignRuntime = enum {
|
|
jni_class,
|
|
jni_interface,
|
|
objc_class,
|
|
objc_protocol,
|
|
swift_class,
|
|
swift_struct,
|
|
swift_protocol,
|
|
};
|
|
|
|
pub const ForeignMethodDecl = struct {
|
|
name: []const u8,
|
|
params: []const *Node, // type_expr nodes — first is `*Self` for instance methods
|
|
param_names: []const []const u8,
|
|
return_type: ?*Node, // null = void
|
|
is_static: bool = false, // true for `static name :: ...`
|
|
jni_descriptor_override: ?[]const u8 = null, // `#jni_method_descriptor("(Sig)Ret")` — JNI runtime only
|
|
body: ?*Node = null, // sx-side implementation (defined-class only). null = `;`-terminated decl referencing inherited / external method.
|
|
};
|
|
|
|
pub const ForeignFieldDecl = struct {
|
|
name: []const u8,
|
|
field_type: *Node, // type_expr node
|
|
};
|
|
|
|
pub const ForeignClassMember = union(enum) {
|
|
method: ForeignMethodDecl,
|
|
field: ForeignFieldDecl, // JNI runtime only (sema-checked in later step)
|
|
extends: []const u8, // sx-side alias name (right of `#extends`)
|
|
implements: []const u8, // sx-side alias name (right of `#implements`)
|
|
};
|
|
|
|
pub const ForeignClassDecl = struct {
|
|
name: []const u8, // sx-side alias (left of `::`)
|
|
foreign_path: []const u8, // directive arg: "java/path/Foo" / "NSString" / "Foundation.URL"
|
|
runtime: ForeignRuntime,
|
|
members: []const ForeignClassMember = &.{},
|
|
is_foreign: bool = false, // `#foreign #...` prefix — class is provided by the foreign runtime; we only reference it
|
|
is_main: bool = false, // `#jni_main` / `#objc_main` — class is the launchable entry (Activity / UIApplicationDelegate / ...)
|
|
};
|
|
|
|
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,
|
|
target_type_params: []const StructTypeParam = &.{}, // for `impl P for List($T)`
|
|
methods: []const *Node, // fn_decl nodes
|
|
protocol_type_args: []const *Node = &.{}, // for `impl Into(Block) for Source` — type args on the protocol side
|
|
target_type_expr: ?*Node = null, // populated for parameterised-protocol impls; carries non-identifier source spellings (e.g. `Closure() -> void`)
|
|
};
|