Documentation menu
Language

Variables & Types

Variables are declared with let. Types are optional — you can add them for safety or leave them out for flexibility.

Variables

Use let to declare a variable. Reassign it with plain = — no let again:

let name  = "Sergio"
let count = 20
let active = true

count = count + 1   // reassignment
name  = "Ana"       // also fine

Variables declared inside a block { } are invisible outside it. You can still mutate variables from an outer scope:

let total = 0

{
    let local = 42   // only lives inside this block
    total = local    // outer variable — allowed
}

out total   // → 42
// out local  ❌ ERROR: Variable not found: local

Constants

Use const for values that should never change. Any attempt to reassign is an error:

const PI  = 3.14159
const MAX = 100

PI = 3.0   // ❌ ERROR: Cannot reassign const 'PI'

Types

Serez Code has five primitive types and several compound types:

TypeExampleWhat it is
int42, -7, 064-bit whole number
decimal3.14, 0.5, 2.064-bit floating point
dec12.50m, 5m, 1e-7mExact base-10 decimal (28–29 digits)
booltrue, falseBoolean value
string"hello", r"raw {x}"UTF-8 text (interpolated or raw)
nullnullAbsence of a value
anyAccepts any value, skips type checks
voidReturn type for functions that return nothing

Type annotations

Add types to function parameters and return values. When present, they're enforced at every call:

fn int add(int a, int b) {
    return a + b
}

add(1, 2)        // ✅
add(1, "hello")  // ❌ TYPE ERROR: Parameter 'b' expected 'int' but received 'string'

Skip annotations when you want flexibility — the parameter accepts any value:

fn multiply(a, b) {
    return a * b
}

multiply(3, 4)       // ✅
multiply(2.5, 4.0)   // ✅

Nullable types

Append ? to any type to allow null as a valid value:

fn int? findIndex(string target, [string] list) {
    for (let i = 0; i < list.length; i++) {
        if (list[i] == target) { return i }
    }
    return null
}

let idx = findIndex("Ana", ["Bob", "Ana", "Lee"])
if (idx != null) {
    out "Found at index {idx}"   // → Found at index 1
} else {
    out "Not found"
}

Exact decimals (dec)

decimal is f64 — fast, but binary, so 0.1 + 0.2 != 0.3. For money and anything that can't tolerate rounding drift, use dec: an exact base-10 decimal written with the m suffix.

out 0.1 + 0.2 == 0.3      // false  (f64)
out 0.1m + 0.2m == 0.3m   // true   (exact)

let price = 12.50m         // inferred dec; scale preserved → "12.50"
let total = price * (1m + 0.21m)
out total                  // 15.1250

// rounding is explicit (COBOL ROUNDED == "half-up")
out (1000.00m * 0.21m).setScale(2, "half-up")   // 210.00
out Dec.fromInt(1250, 2)                          // 12.50

int mixes in exactly; mixing dec with decimal (f64) is a type error — convert with d.toDecimal() / Dec.parse. Methods: round setScale truncate scale abs floor ceil isZero sign min max toInt toDecimal toString; namespace Dec.parse / fromInt / MAX / MIN / MAX_SCALE.

String interpolation

Embed any expression directly inside a string with {}:

let name = "Sergio"
let age  = 28
out "My name is {name} and I'm {age} years old."

let result = add(3, 7)
out "3 + 7 = {result}"

// Works with method calls too
out "Upper: {name.toUpperCase()}"   // → Upper: SERGIO
Tip: Use \{ / \} for literal braces inside a string ("Empty dict: \{\}"), or a raw string to disable interpolation entirely.

Raw strings (r"…")

A r"…" string disables interpolation and escape processing — braces and backslashes are literal. Ideal for literal braces, Windows paths and regexes. It cannot contain a ".

let x = 5
out "value is {x}"     // value is 5     (interpolated)
out r"value is {x}"    // value is {x}   (raw)
out r"C:\temp\new"   // C:\temp\new  (no escapes)
out r"\d+\.\d{2}"   // \d+\.\d{2}  (regex literal)

Operators

Arithmetic

out 10 + 3    // → 13
out 10 - 3    // → 7
out 10 * 3    // → 30
out 10 / 3    // → 3  (integer division, truncates)
out 10 % 3    // → 1  (remainder)
out 2 ** 10   // → 1024 (power)

// int and decimal mix freely
out 1 + 0.5   // → 1.5

Comparison & logical

out 5 > 3      // → true
out 5 == 5     // → true
out 5 != 3     // → true

out true && false   // → false (AND)
out true || false   // → true  (OR)
out !true           // → false (NOT)

Compound assignment

let n = 10
n += 5    // n = 15
n -= 3    // n = 12
n *= 2    // n = 24
n /= 4    // n = 6
n++       // n = 7
n--       // n = 6

Ternary

let x = 10
let label = x > 5 ? "big" : "small"
out label   // → big

// Chain them
let n = 2
let name = n == 1 ? "one" : n == 2 ? "two" : "other"
out name   // → two

Null coalescing

let value = null
out value ?? "default"   // → default

let maybeNum = findIndex("Ana", names)
let safe = maybeNum ?? -1   // -1 if not found

Type check

out 42 is int        // → true
out "hi" is int      // → false
out 3.14 is decimal  // → true

// Useful for functions that accept any
fn string describe(any v) {
    if (v is int)    { return "int: {v}" }
    if (v is string) { return "string: {v}" }
    return "other"
}

Comments

// Single-line comment

/* Multi-line
   comment */

let x = /* inline */ 42

Type conversions

// String → int
out parseInt("42")      // → 42
out parseInt(3.99)      // → 3  (truncates)

// String → decimal
out parseDecimal("3.14")  // → 3.14
out parseDecimal(5)       // → 5.0

// Any type → string
out 42.toString()         // → "42"
out true.toString()       // → "true"

// Read from stdin
let input = readLine("Enter a number: ")
let num = parseInt(input)