grammar


Notation

Notation Meaning
elementopt Element is optional
element-list One or more elements
element... Zero or more elements (repetition)
identifier A valid Jai identifier name
expression Any valid Jai expression
statement Any valid Jai statement
Indented lines Alternative productions for the rule above

Table of Contents


Statements

Statement

statement:
    variable-declaration
    expression ;
    for-statement
    while-statement
    if-statement
    if-case-statement
    jump-statement
    defer-statement
    using-statement
    push-context-statement
    #insert-directive
    #if-directive
    { statement-listopt }

statement-list:
    statement
    statement-list statement

For Loop

The for loop iterates over iterables including ranges, arrays, and custom types with iterator support. See also: named loops, jump statements.

for-statement:
    for for-modifiersopt for-iteratoropt iterable statement
    for for-modifiersopt for-iteratoropt iterable { statement-listopt }

For Modifiers

Modifiers that alter for loop behavior. < reverses iteration order, * dereferences the iterator to get a pointer to each element.

for-modifiers:
    <
    *
    < *
    * <

For Iterator

Optional explicit naming of loop variables. If omitted, it and it_index are used implicitly.

for-iterator:
    identifier :
    identifier : identifier :

Iterable

Values that can be iterated over in a for loop.

iterable:
    range-expression
    expression

Range Expression

Defines an integer range for iteration. Used in for loops.

range-expression:
    expression .. expression

While Loop

A condition-based loop. See also: named loops, jump statements.

while-statement:
    while expression statement
    while expression { statement-listopt }

Named Loop

A named loop allows break and continue statements to target specific outer loops.

named-loop-statement:
    identifier : for-statement
    identifier : while-statement

Jump Statements

Control flow statements for breaking out of loops, continuing to next iteration, or returning from functions.

jump-statement:
    break-statement
    continue-statement
    return-statement

break-statement:
    break ;
    break identifier ;

continue-statement:
    continue ;
    continue identifier ;

return-statement:
    return ;
    return expression ;
    return expression-list ;

expression-list:
    expression
    expression-list , expression

If Statement

Conditional execution. For compile-time conditionals, see #if directive. For switch-like behavior, see if-case statement.

if-statement:
    if expression statement
    if expression statement else statement
    if expression { statement-listopt }
    if expression { statement-listopt } else { statement-listopt }
    if expression { statement-listopt } else if-statement
    if expression then expression else expression

If Case Statement

A switch-like construct using if with case. Supports #complete for exhaustiveness checking with enums. Use #through for fallthrough behavior.

if-case-statement:
    if expression == if-case-modifiersopt { case-list }

if-case-modifiers:
    #complete

case-list:
    case-clause
    case-list case-clause

case-clause:
    case expression ; case-body
    case expression ; #through ;
    case ; case-body

case-body:
    statement-listopt

Defer Statement

Defers execution of a statement until the end of the current scope. Useful for cleanup operations.

defer-statement:
    defer statement
    defer { statement-listopt }

Using Statement

Brings members of a struct or namespace into the current scope. Also used in struct member declarations and function parameters.

using-statement:
    using expression ;
    using identifier ;

Push Context

Temporarily modifies the implicit context for a block of code. The context contains allocator, logger, and other runtime settings.

push-context-statement:
    push_context expression { statement-listopt }

Declarations

Variable Declaration

Declares variables with various combinations of explicit types, initial values, and constness. Uses : for type annotation, = for runtime initialization, and :: for compile-time constants.

variable-declaration:
    identifier : type-specifier ;
    identifier : type-specifier = expression ;
    identifier := expression ;
    identifier : type-specifier : expression ;
    identifier :: expression ;
    identifier : type-specifier = --- ;
    identifier-list : type-specifier ;
    identifier-list := expression ;
    identifier-list : type-specifier = expression-list ;

identifier-list:
    identifier
    identifier-list , identifier

Function Declaration

Declares a procedure with optional parameters and return types. See also: function modifiers, parameter modifiers, lambda expressions.

function-declaration:
    identifier :: function-signature function-body
    identifier :: function-signature function-modifiers function-body
    identifier :: function-signature function-modifiers

Function Signature

The parameter and return type specification of a function. Also used in procedure types.

function-signature:
    ( parameter-listopt )
    ( parameter-listopt ) -> return-specification

return-specification:
    type-specifier
    ( return-list )

Parameter List

Function parameters. See parameter modifiers for using, $ (compile-time), and other parameter options.

parameter-list:
    parameter
    parameter-list , parameter

parameter:
    parameter-modifiersopt identifier : type-specifier
    parameter-modifiersopt identifier : type-specifier = expression
    parameter-modifiersopt identifier := expression
    parameter-modifiersopt $ identifier : type-specifier
    parameter-modifiersopt $$ identifier : type-specifier
    parameter-modifiersopt identifier : .. type-specifier

Return List

Multiple return values for functions. Values can be optionally named.

return-list:
    return-item
    return-list , return-item

return-item:
    type-specifier
    identifier : type-specifier
    identifier : type-specifier = expression

Function Body

The implementation of a function. May contain #insert for code injection.

function-body:
    { statement-listopt }
    #no_context { statement-listopt }

Struct Declaration

Declares a compound data type. Supports using for member promotion, #place for memory layout control, and modifiers like #packed.

struct-declaration:
    identifier :: struct struct-modifiersopt { member-listopt }
    identifier :: struct struct-parameters struct-modifiersopt { member-listopt }

Struct Parameters

Compile-time parameters for polymorphic structs. Similar to generics in other languages.

struct-parameters:
    ( parameter-list )

Member List

Members of a struct. May include using declarations, #place directives, and #insert code.

member-list:
    member
    member-list member

member:
    identifier : type-specifier ;
    identifier : type-specifier = expression ;
    identifier := expression ;
    identifier : type-specifier = --- ;
    using-member
    place-directive
    #insert-directive
    #if-directive
    noteopt identifier : type-specifier ;

Using in Structs

Promotes members of an embedded struct to the parent scope, enabling inheritance-like patterns. See also: using statement.

using-member:
    using identifier : type-specifier ;
    using identifier : type-specifier = expression ;
    using , identifier : type-specifier ;

Place Directive in Structs

Controls memory layout by placing a member at a specific offset or overlapping with another member. Useful for unions-within-structs and memory-mapped I/O.

place-directive:
    #place identifier ;

Struct Modifiers

Modifiers that change struct behavior and layout.

struct-modifiers:
    struct-modifier
    struct-modifiers struct-modifier

struct-modifier:
    #packed
    #align expression
    #type_info_none
    #type_info_procedures_are_void_pointers
    #no_padding

Enum Declaration

Declares an enumeration type. Can specify underlying type. See also: enum_flags for bitfield enums.

enum-declaration:
    identifier :: enum type-specifieropt { enumerator-listopt }
    identifier :: enum type-specifieropt #specified { enumerator-listopt }

Enumerator List

The values in an enum or enum_flags.

enumerator-list:
    enumerator
    enumerator-list enumerator

enumerator:
    identifier ;
    identifier :: expression ;
    noteopt identifier ;

Enum Flags Declaration

Declares a bitfield enumeration where each value is automatically a power of two. Supports bitwise operations. See also: enum.

enum-flags-declaration:
    identifier :: enum_flags type-specifieropt { enumerator-listopt }

Union Declaration

Declares a union type where all members share the same memory location.

union-declaration:
    identifier :: union { member-listopt }

Expressions

Expression

The general expression hierarchy following operator precedence.

expression:
    assignment-expression
    lambda-expression
    #run-expression
    #code-expression

Primary Expression

The most basic expressions. it and it_index are implicit loop variables in for loops.

primary-expression:
    identifier
    literal
    ( expression )
    it
    it_index
    struct-literal
    array-literal
    type-specifier
    type_of ( expression )
    size_of ( type-specifier )
    #type
    #char string-literal
    #string
    context
    #caller_location
    #location ( identifier )

Literal

Constant values in source code.

literal:
    integer-literal
    float-literal
    string-literal
    character-literal
    true
    false
    null

integer-literal:
    decimal-literal
    0x hexadecimal-literal
    0b binary-literal
    0o octal-literal

string-literal:
    " character-sequenceopt "
    #string delimiter

Postfix Expression

Array indexing, function calls, and member access.

postfix-expression:
    primary-expression
    postfix-expression [ expression ]
    postfix-expression ( argument-listopt )
    postfix-expression . identifier
    postfix-expression . type-specifier

Argument List

Arguments to a function call. Supports named arguments and spread operator.

argument-list:
    argument
    argument-list , argument

argument:
    expression
    identifier = expression
    .. expression

Unary Expression

Prefix operators. * dereferences pointers, << is an alternate dereference syntax, xx is auto-cast.

unary-expression:
    postfix-expression
    * unary-expression
    << unary-expression
    & unary-expression
    - unary-expression
    + unary-expression
    ! unary-expression
    ~ unary-expression
    xx unary-expression
    xx , unary-expression
    cast-expression

Cast Expression

Explicit type conversion. Various cast modes control overflow and bounds checking. See also: xx (auto-cast) in unary expressions.

cast-expression:
    cast ( type-specifier ) unary-expression
    cast , cast-modifier ( type-specifier ) unary-expression
    cast ( ) unary-expression

cast-modifier:
    no_check
    trunc

Binary Expression

Binary operators with standard precedence rules. From lowest to highest: logical OR, logical AND, equality, relational, additive, multiplicative.

multiplicative-expression:
    unary-expression
    multiplicative-expression * unary-expression
    multiplicative-expression / unary-expression
    multiplicative-expression % unary-expression

additive-expression:
    multiplicative-expression
    additive-expression + multiplicative-expression
    additive-expression - multiplicative-expression

shift-expression:
    additive-expression
    shift-expression << additive-expression
    shift-expression >> additive-expression
    shift-expression >>> additive-expression

bitwise-and-expression:
    shift-expression
    bitwise-and-expression & shift-expression

bitwise-xor-expression:
    bitwise-and-expression
    bitwise-xor-expression ^ bitwise-and-expression

bitwise-or-expression:
    bitwise-xor-expression
    bitwise-or-expression | bitwise-xor-expression

relational-expression:
    bitwise-or-expression
    relational-expression < bitwise-or-expression
    relational-expression > bitwise-or-expression
    relational-expression <= bitwise-or-expression
    relational-expression >= bitwise-or-expression

equality-expression:
    relational-expression
    equality-expression == relational-expression
    equality-expression != relational-expression

logical-and-expression:
    equality-expression
    logical-and-expression && equality-expression

logical-or-expression:
    logical-and-expression
    logical-or-expression || logical-and-expression

Assignment Expression

Assignment and compound assignment operators.

assignment-expression:
    logical-or-expression
    unary-expression assignment-operator assignment-expression

assignment-operator:
    =
    +=
    -=
    *=
    /=
    %=
    &=
    |=
    ^=
    <<=
    >>=

Lambda Expression

Anonymous functions. Can capture context or be marked #no_context. See also: function declarations.

lambda-expression:
    ( parameter-listopt ) function-body
    ( parameter-listopt ) -> return-specification function-body
    ( parameter-listopt ) function-modifiersopt => expression
    identifier => expression

Struct Literal

Initializes a struct with values. Fields can be named or positional.

struct-literal:
    type-specifier . { field-initializer-listopt }
    . { field-initializer-listopt }

field-initializer-list:
    field-initializer
    field-initializer-list , field-initializer

field-initializer:
    expression
    identifier = expression

Array Literal

Initializes an array with values.

array-literal:
    type-specifier . [ expression-listopt ]
    . [ expression-listopt ]
    array-type . [ expression-listopt ]

Types

Type Specifier

Specifies the type of a value. Includes basic types, pointers, arrays, and procedure types.

type-specifier:
    basic-type
    pointer-type
    array-type
    procedure-type
    identifier
    $ identifier
    $$ identifier
    #type-expression

Basic Type

Built-in primitive types.

basic-type:
    int
    s8
    s16
    s32
    s64
    u8
    u16
    u32
    u64
    float
    float32
    float64
    bool
    string
    void
    Any
    Type

Pointer Type

A pointer to another type. See also: dereference operators.

pointer-type:
    * type-specifier
    *void

Array Type

Fixed-size arrays, dynamic arrays, and slices (views into arrays).

array-type:
    [ expression ] type-specifier
    [ .. ] type-specifier
    [ ] type-specifier
    [..] type-specifier

Procedure Type

The type of a function or lambda. Can be used for function pointers and callbacks.

procedure-type:
    #type ( parameter-listopt )
    #type ( parameter-listopt ) -> return-specification
    #type ( parameter-listopt ) function-modifiers
    #type ( parameter-listopt ) -> return-specification function-modifiers

Compile-Time Directives

#run Directive

Executes code at compile time. The result can be used as a compile-time constant. See also: #code, #insert.

#run-directive:
    #run expression
    #run { statement-listopt }
    #run , expression

#code Directive

Captures code as a first-class value of type Code. Used with #insert for metaprogramming. See also: #run.

#code-directive:
    #code statement
    #code { statement-listopt }
    #code , expression

#insert Directive

Inserts code (from a Code value or string) at the current location. Can be used in function bodies and struct declarations. See also: #code.

#insert-directive:
    #insert expression
    #insert , expression
    #insert_internal expression

#if Directive (Compile-Time If)

Compile-time conditional compilation. The condition must be evaluable at compile time. Unlike runtime if statements, excluded branches are not compiled.

#if-directive:
    #if expression statement
    #if expression { statement-listopt }
    #if expression { statement-listopt } else { statement-listopt }
    #if expression { statement-listopt } else #if-directive

#import Directive

Imports a module into the current compilation. Modules are searched in the modules path. See also: #load for loading individual files.

#import-directive:
    #import string-literal ;
    #import string-literal ( argument-listopt ) ;
    #import , identifier string-literal ;
    identifier :: #import string-literal ;
    identifier :: #import string-literal ( argument-listopt ) ;

#load Directive

Loads and compiles another source file as if its contents were included at this location. See also: #import for module imports.

#load-directive:
    #load string-literal ;

#type Directive

Used to construct procedure types and perform type operations.

#type-directive:
    #type procedure-type-body
    #type , function-modifiers procedure-type-body

procedure-type-body:
    ( parameter-listopt )
    ( parameter-listopt ) -> return-specification

#char Directive

Converts a single-character string literal to its integer character code value.

#char-directive:
    #char string-literal

#string Directive

Creates a multi-line string literal with a custom delimiter. The string continues until the delimiter is encountered again.

#string-directive:
    #string identifier
        ... content ...
    identifier

Scope Directives

Control the visibility and export behavior of declarations.

scope-directive:
    #scope_file
    #scope_module
    #scope_export
    #add_context identifier : type-specifier ;
    #add_context identifier : type-specifier = expression ;

#assert Directive

Compile-time assertion. The expression must evaluate to true at compile time, otherwise compilation fails with an error.

#assert-directive:
    #assert expression
    #assert expression string-literal

Foreign Directives

Interface with external libraries and system calls. Used for FFI (Foreign Function Interface).

foreign-directive:
    #foreign identifier
    #foreign_library string-literal
    #foreign_system_library string-literal

foreign-function-declaration:
    identifier :: ( parameter-listopt ) -> return-specificationopt #foreign identifier ;
    identifier :: ( parameter-listopt ) -> return-specificationopt #foreign identifier string-literal ;

#bake Directive

Creates a specialized version of a polymorphic function with some compile-time parameters fixed. See also: parameter modifiers for $ parameters.

#bake-directive:
    #bake expression ( argument-listopt )
    #bake_arguments expression ( argument-listopt )
    #bake_constants expression ( argument-listopt )

#modify Directive

Used with polymorphic functions to constrain or transform type parameters during instantiation.

#modify-directive:
    #modify expression
    #modify { statement-listopt }

#placeholder Directive

Declares a placeholder that must be filled in by another compilation unit. Used for dependency injection patterns.

#placeholder-directive:
    identifier :: #placeholder;

#through Directive

Used in if-case statements to fall through to the next case, similar to C switch fallthrough.

#through-directive:
    #through

#complete Directive

Used in if-case statements to require that all enum values are handled. Causes a compile error if a case is missing.

#complete-directive:
    #complete

#must Directive

Marks a return value that must not be ignored. The compiler will error if the return value is discarded.

#must-directive:
    #must

#caller_location

Returns the source location of the caller. Used for debugging and error reporting.

#caller_location-expression:
    #caller_location

#location Directive

Returns the source location of an identifier's declaration.

#location-directive:
    #location ( identifier )

#filepath Directive

Returns the file path of the current source file as a string.

#filepath-directive:
    #filepath

#line Directive

Returns the current line number in the source file.

#line-directive:
    #line

#procedure_name Directive

Returns the name of the current procedure as a string.

#procedure_name-directive:
    #procedure_name

Modifiers and Annotations

Function Modifiers

Modifiers that change function behavior, calling convention, or compilation. Multiple modifiers can be combined.

function-modifiers:
    function-modifier
    function-modifiers function-modifier

function-modifier:
    #inline
    #no_inline
    #c_call
    #no_context
    #compiler
    #intrinsic
    #runtime_support
    #symmetric
    #expand
    #no_abc
    #deprecated
    #deprecated string-literal
    #must
    #modify-directive
    #foreign identifier

Parameter Modifiers

Modifiers for function parameters. using brings struct members into scope, $ makes parameters compile-time.

parameter-modifiers:
    parameter-modifier
    parameter-modifiers parameter-modifier

parameter-modifier:
    using
    $
    $$
    *
    #no_alias
    #must

Notes

User-defined annotations attached to declarations. Can be queried at compile time using type introspection. Used for custom metaprogramming.

note:
    @identifier
    @identifier ( expression )

notes:
    note
    notes note

Special Constructs

Inline Assembly

Embeds assembly code directly in Jai source.

inline-assembly:
    #asm { assembly-instructionsopt }
    #asm type-specifier { assembly-instructionsopt }
    #bytes expression

Uninitialized Values

The --- token explicitly marks a variable as uninitialized, bypassing default initialization. Used in variable declarations and struct members.

uninitialized:
    ---

Type Introspection

Built-in operations for examining types at compile time and runtime.

type-introspection:
    type_of ( expression )
    size_of ( type-specifier )
    type_info ( type-specifier )
    is_constant ( expression )
    initializer_of ( type-specifier )

Context

The implicit context passed to procedures. Contains allocator, logger, and other runtime settings. Modified with push_context.

context-expression:
    context
    context . identifier

New and Delete

Memory allocation using the context allocator.

new-expression:
    New ( type-specifier )
    New ( type-specifier , expression )

delete-expression:
    free ( expression )

Autocast

The xx operator automatically casts a value to the expected type. Less explicit than cast but more convenient.

autocast-expression:
    xx unary-expression
    xx , unary-expression

Ifx Expression (Ternary)

Conditional expression using ifx or if then else syntax. See also: if statement.

ifx-expression:
    ifx expression then expression else expression
    ifx expression expression else expression

#no_check Modifier

Disables runtime bounds checking and overflow checking for the following statement or expression.

#no_check:
    #no_check statement

Memory and Pointers

Pointer Operations

Operations on pointers.

pointer-operations:
    * expression
    << expression
    & expression

pointer-arithmetic:
    pointer-expression + integer-expression
    pointer-expression - integer-expression
    pointer-expression - pointer-expression

Relative Pointers

Pointers stored as offsets from their own location. Useful for serialization and memory-mapped structures.

relative-pointer-type:
    *~ type-specifier
    *~integer-type type-specifier

Operator Overloading

Operator Overloading

Define custom behavior for operators on user-defined types like structs.

operator-overload:
    operator overloadable-operator :: function-signature function-body

overloadable-operator:
    +
    -
    *
    /
    %
    ==
    !=
    <
    >
    <=
    >=
    []
    []=
    <<
    >>

For Expansion

Define custom iteration behavior for user-defined types in for loops.

for-expansion:
    for_expansion :: ( body : Code , obj : * type-specifier , flags : For_Flags ) -> Code function-body

Compilation Unit

A complete Jai source file or module.

compilation-unit:
    top-level-declaration-listopt

top-level-declaration-list:
    top-level-declaration
    top-level-declaration-list top-level-declaration

top-level-declaration:
    variable-declaration
    function-declaration
    struct-declaration
    enum-declaration
    enum-flags-declaration
    union-declaration
    operator-overload
    #import-directive
    #load-directive
    #run-directive
    scope-directive
    #if-directive
    #assert-directive
    #placeholder-directive