Skip to content

Syntax

Jiang syntax is organized around declarations, explicit types, and visible low-level operations. When reading a file, start with imports and top-level declarations, then move into statements and expressions inside function bodies.

The project uses these conventions:

  • Type names use PascalCase.
  • Function names, variable names, field names, enum members, and module aliases use snake_case.
  • Primitive types such as Int, Bool, and UInt8 are written like ordinary type names.
enum TokenKind {
kw,
string_lit,
left_paren,
}
struct SourceFile {
UInt8[] file_path;
Int start_offset;
}
UInt8[] read_source(UInt8[] file_path) {
return file_path;
}

Self is a special name in type position. self is available in instance methods and init bodies; static methods do not have self.

A source file is a sequence of top-level items:

  • import
  • alias
  • global declarations
  • functions
  • struct, record, enum, union, trait
  • extend
import math = "utils/math.jiang";
alias Byte = UInt8;
alias answer = math.answer;
public struct Pair {
Int left;
Int right;
}
public Int add(Int left, Int right) {
return left + right;
}

public marks a declaration as visible outside the module.

Blocks contain declarations, destructuring statements, assignments, call statements, control-flow statements, and defer.

Int main() {
Int! total = 0;
for i in 0..10 {
total = total + i;
}
return total;
}

Most statements end with semicolons. Control-flow forms that own a block, such as if, switch, while, and for, do not need an extra semicolon after the block.

A block has a value only when it ends with a tail expression without a semicolon:

Int value = {
Int base = 40;
base + 2
}

Without a tail expression, the block value is ().

Jiang expressions include literals, names, calls, field access, indexing, slicing, arithmetic, comparisons, if, switch, try catch, and blocks.

Int value = if flag {
1
} else {
2
}

Field literals are written with their type path:

Point origin = Point { x: 0, y: 0 }

$ enters the implicit operation layer for a value or type:

Int value = 42;
Int& ref = value$.ref();
Int copied = ref$.get();
UInt size = Int$.size();

Use parentheses when applying $ to a compound expression:

Int raw = (left + right)$.as(Int);

Prefer safe target-type initialization such as Float(value) for ordinary conversions. Do not use value$.as(Type) as the everyday conversion spelling; it is a low-level cast form for raw pointers, integer addresses, FFI, and similar code.

is performs pattern matching. Optional values use the some pattern:

if maybe is some value {
return value;
} else {
return 0;
}

Mutable inferred bindings use _!:

if maybe is some _! value {
value = value + 1;
}

switch uses the same style of patterns for optional, variant, and literal matches.