Expressions
An expression produces a value. Expressions nest freely, and within one precedence level they evaluate left to right. See Types for every kind of value an expression can produce, and Operators for the full precedence table.
Integer Literals
let a = 0
let b = 1000000
An int is a signed 63-bit integer, so the digits of a literal must not exceed 4611686018427387903. More than that is refused when the file is read, with a "numeric literal overflows its type" error. There is no hex, octal, binary, or underscore-separated form.
Float Literals
let pi = 3.14
let half = 0.5
Float literals require at least one digit on each side of the decimal point. 3.14 is valid; .5 and 3. are not. There is no exponent form, so write 1000.0 rather than 1e3.
A float always prints with a decimal point, so print(6.0) shows 6.0 and never 6.
Boolean Literals
let yes = true
let no = false
The keywords true and false produce bool values.
Identifiers
A variable name used in an expression evaluates to the variable's current value.
let base = 8
let doubled = base * 2
Parenthesized Expressions
Any expression can be wrapped in parentheses to override default precedence:
let a = (2 + 3) * 4
let b = -(3 + 4)
Function Calls
A function value followed by a parenthesized argument list calls that function:
fn add(a, b) {
return a + b
}
let sum = add(3, 4)
let nested = add(add(1, 2), 3)
See Functions for the full reference.
Binary Expressions
Two values combined with an operator:
let sum = 3 + 4
let diff = 10 - 3
let prod = 6 * 7
let quot = 20 / 4
let rem = 10 % 3
let bits = 255 & 15
let mask = 1 << 4
let flag = 1 < 2 && 3 > 0
Expressions associate left-to-right within the same precedence level:
let x = 10 - 3 - 2
This evaluates as (10 - 3) - 2 = 5.
Unary Expressions
Jade has three unary prefix operators:
~— bitwise NOT (integers only)!— logical NOT (booleans only)-— arithmetic negation (integers and floats)
let inv = ~0
let neg = -5
let nflag = !true
! may also be spelled not, and && and || may be spelled and and or. The word forms mean exactly the same thing and bind the same way.
String Literals
String literals may be delimited by double quotes ("…") or single quotes ('…') — both forms are identical. Triple-quoted strings ("""…""" or '''…''') span multiple lines. The + operator concatenates two strings.
let hello = "hello"
let world = 'world'
let hw = hello + " " + world
let multi = """
line one
line two
"""
let also_multi = '''
line one
line two
'''
The only escapes a string recognises are \\, \n, \t, \r, and the quote character that opened it. Any other backslash is an error rather than a literal backslash, so there is no \u or \0 form.
Indexing a string with [i] gives a char, a single Unicode scalar — not a one-character string. Indexes are zero-based and count characters, so a two-byte character still counts once. An index outside the string is a runtime error; there is no negative indexing.
let s = "café"
print(s[0]) // c
print(len(s)) // 4, not 5
print(s[0] == "c") // true — a char compares equal to the string spelling it
A string also iterates, one char per step:
for c in "café" {
print(c)
}
F-String Interpolation
An f-string is prefixed with f before the opening quote. Any expression inside { } is evaluated and its value is converted to a string and embedded in place. Both quote styles are supported.
let name = "Jade"
let n = 42
let msg = f"hello, {name}! answer is {n}"
let msg2 = f'hello, {name}! answer is {n}'
Triple-quoted f-strings are written as f"""…""" or f'''…''' and behave the same way.
To put a literal brace in an f-string, escape it with a backslash. Doubling it does not work — {{ opens a nested expression, not an escape.
print(f"a \{literal\} brace") // a {literal} brace
Array Literals
An array is written as a comma-separated list inside square brackets. Arrays may be empty and may hold values of any type.
let a = [1, 2, 3]
let empty = []
let mixed = [1, 2.0, true, "hello"]
Elements are accessed with arr[i] (zero-based). Elements can be assigned with arr[i] = expr. Arrays have reference semantics — assigning an array creates an alias that shares the same backing store.
When every element is the same type, the compiler knows the element type and can check what you do with arr[i]. When they differ it knows nothing more specific, and operations on elements are checked as the program runs instead. So a mixed array costs you compile-time errors, not correctness:
let mixed = [1, "two"]
print(mixed[0] + mixed[1]) // runs, then fails: '+' requires numeric operands
arr.contains(x) is the one place where a type mismatch is not an error. Membership asks whether any element is x, and an element of another type answers that with false:
let mixed = [1, "two", true]
print(mixed.contains("two")) // true
print(mixed.contains(9)) // false — not an error
That is deliberately different from ==, which rejects a comparison across types rather than quietly answering it. Note that 1 and 1.0 are different values to both.
in and not in ask the same question as an infix operator, and also work on a string and on a dict's keys:
print(2 in [1, 2, 3]) // true
print(4 not in [1, 2, 3]) // true
print("ell" in "hello") // true
print("k" in {"k": 1}) // true
Dict Literals
A dict is written with curly braces and string keys. See Types for the full reference.
let d = {"name": "jade", "version": 1}
print(d["name"])
Closures
|params| body builds an anonymous function value. A body without braces is an implicit return.
let double = |x| x * 2
let add = |a, b| { return a + b }
let seven = || 7
print(double(4)) // 8
See Functions for the full reference.
Pipe Operator
The |> operator passes the left-hand value as the first argument to the right-hand function. Pipes chain left-to-right.
fn double(x) { return x * 2 }
let n = 5 |> double // double(5) → 10
let m = 3 |> double |> double // double(double(3)) → 12
When the right-hand side is a call expression, the left value is inserted as the first argument before those already listed:
fn add(a, b) { return a + b }
let r = 5 |> add(3) // add(5, 3) → 8
A prompt dereference pipes like any other value. A type name as a stage constrains what the model generates and coerces the reply; a function stage after it receives the coerced value. See Operators for the full rule.
prompt p = "What is 21 + 21? Respond with only the number."
let n = ?p |> int |> double // 84