01 / Structure
Modules and imports
Each .zi file is a module named after its filename. Extensionless imports load Ziran modules transitively; pass --module-path DIR for library directories. A dotted import such as stdio.h names a host header.
#import "text"
Answer :: () -> bool {
return StartsWithFoldASCII("Ziran", "zi");
}Use #scope_file, #scope_module, or #scope_export to set visibility for following declarations. The current one-file-per-module model gives file and module scope the same reach.
02 / Bindings
Declarations and calls
Name :: value defines a compile-time value or a named type. Name :: (args) -> Result { ... } defines a procedure. File-scope variables use name: Type; local values use typed declarations or inferred :=. An inferred integer local uses s64 storage.
Limit :: 8;
counter: s32 = 0;
Add :: (a: s32, b: s32) -> s32 {
result := a + b;
return cast(s32)result;
}A standalone #program_export before a function preserves its native symbol name. A named procedure type can pass a named function as a callback; capturing closures are not supported.
03 / Values
Types, records, and enums
The tested surface includes bool, signed and unsigned integers, float32, float64, char, immutable string, records, enums, fixed arrays, and borrowed slices. int aliases s64. Native targets also support checked raw pointer forms; raw pointers are outside portable .zib.
Box :: struct($T: Type) {
value: T;
}
NumberBox :: Box(s32);
box: NumberBox = NumberBox.{value = 42};
Mode :: enum u8 {
Idle :: 0;
Running :: 1;
}Generic records apply as Box(s32) and can nest in fields and signatures. enum_flags assigns successive bits by default. #specified requires explicit member values. Fixed arrays use a checked capacity; slices borrow array storage and obey lifetime limits.
04 / Execution
Control flow and expressions
Functions use if/else, while, lexical blocks, break, continue, return, and unreachable in the tested subset. ifx condition then a else b is a runtime conditional expression. if value == { case ... } is the Jai-style enum case form; #complete requires all members.
ClampLow :: (value: s32) -> s32 {
return ifx value < 0 then 0 else value;
}Top-level && and || preserve short-circuit evaluation in supported statements and loop conditions. Lazy expressions embedded inside outer calls are still a documented gap.
05 / Compile time
Selected constant forms
#if selects declarations or statements before checking. #ifx selects a constant, global initializer, or function expression arm. Conditions accept the current constant evaluator, including compiler-host OS comparisons with .WINDOWS, .MACOS, and .LINUX. Unselected arms do not enter saved IR.
size_of(Type) folds for supported scalars, pointers, fixed arrays, and nonempty plain records. In function bodies, size_of(type_of(expression)) uses the inferred type without evaluating the expression. Broader type_of use and compile-time procedures remain unfinished.
06 / Effects
Foreign and host calls
Foreign procedures use a Jai-style library declaration followed by #foreign. The special host_api system library denotes an explicit portable host capability, available only when the host supplies a matching binding.
libc :: #system_library "libc";
Abs :: (value: s32) -> s32 #foreign libc "abs";
host_api :: #system_library "host_api";
EchoHost :: (value: s32) -> s32 #foreign host_api;The portable runner checks required capabilities before execution. Supported host values are narrower than native FFI; see the C host API.
07 / Boundaries
What to check before building
Ziran rejects the former Kryon-specific #ui modifier and app/route syntax. It also rejects C-style switch, goto labels, raw C statements, variant, payload match, postfix ?, and capturing slot bodies. Jai compatibility is still being audited.
Native and portable backends do not yet accept the same full feature set. Read the status summary, then use implementation status for the exact current boundary. The intended syntax and future contracts are described in language direction.