01 / Structure

Modules and imports

An entry .zi file starts a module. #load "relative/file.zi"; adds another file to that module. Extensionless imports load Ziran modules transitively; pass --module-path DIR for library directories. Use Alias :: #import "module";, Alias :: #import, file "relative/path.zi";, or Alias :: #import, dir "relative/directory"; to access public declarations as Alias.Name. Directory modules use module.zi. #import, string "..."; compiles embedded source, including raw #string bodies; its named form exposes declarations as Alias.Name. C headers are not Ziran modules.

#import "text"

Answer :: () -> bool {
    return StartsWithFoldASCII("Ziran", "zi");
}

Use #scope_file, #scope_module, or #scope_export to set visibility for following declarations. Loaded files share module-scoped declarations while file-private declarations stay in their source file.

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 such as Child :: #type (s32) -> (); 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, 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. Pure procedures with bounded local control flow can run during translation with integer, boolean, string, or floating-point values. Earlier imports and #run values can feed #if; selected results are stored in checked IR. Aggregate results, build-time I/O, and general metaprogramming remain unsupported.

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 #enum, 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.