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
- Declarations
- Expressions
- Types
- Compile-Time Directives
- Modifiers and Annotations
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 :
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