New `hash_jni_class` token + lexer entry, `JniClassDecl` AST node
(alias + java path; body deferred to 2.2+), `parseJniClassDecl`
consuming `("...") { }` and rejecting non-empty bodies for now.
Sema registers the alias as a type_alias symbol; LSP classifies
the directive as a keyword. The 2.0 xfail snapshot flips to
`parse-only ok`, exit 0.
120/120 examples green; zig test clean.
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.
Android (toolchain):
--target android / --target android-arm64 → aarch64-linux-android21.
target.zig discovers $ANDROID_NDK_HOME (or scans
~/Library/Android/sdk/ndk/* for the newest), invokes the NDK clang
with -shared -fPIC and links libsxhello.so against -llog -landroid
-lEGL -lGLESv3 -lm -ldl. native_app_glue.c from the NDK is compiled
and linked alongside the sx .o so apps can use the conventional
android_main(struct android_app*) shape; -u ANativeActivity_onCreate
keeps glue's symbol live.
Android (APK):
--apk <out> wraps the .so into a debug-signed installable APK.
target.zig discovers the SDK at $ANDROID_HOME (or
~/Library/Android/sdk), picks the newest build-tools + platforms,
generates a NativeActivity AndroidManifest.xml from --bundle-id,
packages via aapt2 link, appends the lib/ tree, zipalign, then
apksigner against ~/.android/debug.keystore (auto-generated via
keytool on first use). One command end-to-end:
sx build --target android --apk out.apk \\
--bundle-id co.swipelab.foo main.sx
Verified on Pixel 7 Pro: install + launch reaches android_main.
Compiler (entry-point linkage):
Top-level fn defs default to LLVM internal linkage and are lazily
lowered (only `main` was eagerly lowered before). Added
isExportedEntryName() — a small allowlist for names the OS loader
calls: `main`, `android_main`, `ANativeActivity_onCreate`,
`JNI_OnLoad`. These get eagerly lowered AND keep external linkage,
so they actually land in .dynsym.
LSP (imports):
DocumentStore now takes the install-discovered stdlib_paths and
forwards them into resolveImportPath, mirroring the compiler. Before
this, every `#import "modules/..."` resolved through the stdlib path
failed silently inside the LSP and identifiers from those modules
showed as `undefined variable`. Repro on label.sx: 1 false positive
before, 0 after.
- examples/modules/ -> library/modules/ (top-level, no more
symlink hacks in consumer projects)
- compiler discovers stdlib via _NSGetExecutablePath / readlink
/proc/self/exe; searches dev layout (../../library), install
layout (../library), and alongside-binary fallback
- SX_STDLIB_PATH env var overrides for tests / dev convenience
- SX_DEBUG_STDLIB env var dumps the discovery results
- build.zig installs library/ alongside the binary
- Compilation gains stdlib_paths field threaded through resolveImports
- 50 tests pass; consumer projects can now build from any cwd