Language

Zy Language Reference

Read status and limitations and the migration guide. Agent methods return placeholders, security helpers only print messages, and ~ uses keyword substring matching. Checked annotations, records, enums, and try/catch are specified in types and errors. APIs are in the standard library.

Version: 0.3.0-dev (experimental development build) File extension: .zy
Paradigm: Interpreted, gradually typed, imperative

Local code reuse uses import "./policy.zy" as policy, export let, and export fn at file scope. See local modules for the full syntax, scope, resolution, and resource limits. import, export, and as are reserved keywords. CLI tooling documents formatting and dependency checks.


Table of Contents

  1. Overview
  2. Lexical Rules
  3. Types
  4. Variables
  5. Operators
  6. Control Flow
  7. Functions
  8. Lists
  9. Maps
  10. Networking Primitives
  11. AI Agents
  12. Built-in Functions
  13. String Methods
  14. List Methods
  15. Map Methods
  16. Subnet Methods
  17. Error System
  18. Scoping Rules and Closures
  19. Truthiness Rules
  20. Grammar Summary

1. Overview

Zy is a gradually typed interpreted language for scripts, readable network policies, and validation. IP addresses and subnets are built-in values. Records and annotations enforce contracts; unannotated values remain dynamic. Local modules let scripts share code. Agent values are also supported, but their AI methods are simulations.

Its syntax enforces mandatory braces { } for all blocks, which eliminates whitespace-sensitive parsing and enables precise, human-centric error messages modeled after Rust and Elm.


2. Lexical Rules

Comments

Line comments begin with // and continue to the end of the line. There are no block comments.

// This is a comment
let x = 10  // inline comment

Statement Termination

Statements are terminated by newlines. Semicolons are accepted but not required.

let a = 1
let b = 2    // preferred — no semicolons needed
let c = 3;   // also valid

Identifiers

Identifiers must start with a letter or underscore, followed by any number of letters, digits, or underscores. They are case-sensitive.

user_name    ✓
_private     ✓
Value2       ✓
2invalid     ✗  (starts with digit)

Keywords

The following words are reserved and cannot be used as identifiers:

let   fn   return   if   else   while   for   in   not   and   or
true  false  null  agent  break  continue
import export as try catch throw record enum

String Literals

Strings are enclosed in double quotes. Supported escape sequences:

SequenceMeaning
\nNewline
\tTab
\"Literal quote
\\Literal backslash
let msg = "Hello, world!"
let path = "C:\\Users\\admin"
let line = "first\nsecond"

3. Types

Zy is gradually typed. Every value carries its type at runtime; optional annotations add enforced contracts. See types and errors for records, enums, nullable values, and error handling.

TypeDescriptionExample
Int64-bit signed integer42, -7, 0
Float64-bit floating point3.14, -0.5
StringUTF-8 text"hello"
BoolBooleantrue, false
NullAbsence of a valuenull
ListOrdered, heterogeneous collection[1, "a", true]
MapKey-value dictionary{name: "Alice", age: 30}
IPIPv4/IPv6 address (native type)192.168.1.1
SubnetIPv4/IPv6 CIDR block (native type)192.168.1.0/24
AgentAI agent instancesee §11
FunctionUser-defined function (supports closures)see §7

The null Literal

null is a first-class value and keyword. It represents the deliberate absence of a value. Functions that do not return a value implicitly return null.

let empty = null
print(type(null))    // Null

if not empty {
    print("no value")   // executes — null is falsy
}

Type Inspection

print(type(42))          // Int
print(type("hello"))     // String
print(type(192.168.1.1)) // IP
print(type(10.0.0.0/8))  // Subnet
print(type(null))        // Null
print(type({}))          // Map

Type Conversion

let n = int("42")       // String → Integer: 42
let f = float(7)        // Integer → Float: 7.0
let s = str(3.14)       // any → String: "3.14"

4. Variables

Declaration

All variables must be declared with let before use.

let city = "Manila"
let port = 8080
let active = true
let data = null        // explicitly null

Optional Type Annotation

A type annotation after : enforces the declared contract on initialization and reassignment. Function parameters and -> Type results can also be annotated. Type? accepts null. See the full contract.

let zone: Subnet = 192.168.1.0/24
let count: Int = 0

Re-assignment

After declaration, a variable is re-assigned without let:

let score = 0
score = score + 10   // re-assignment

Compound Assignment

Zy supports the common shorthand operators that combine an arithmetic operation with assignment:

OperatorEquivalent ToExample
+=x = x + valuescore += 5
-=x = x - valuehealth -= 10
*=x = x * valuetotal *= 2
/=x = x / valueratio /= 100
let score = 10
score += 5     // 15
score *= 2     // 30
score -= 3     // 27
score /= 9     // 3.0
print(score)   // 3.0

Assignment Errors

Attempting to assign to a name that was never declared with let is a Name Error:

undeclared = 5
// ❌ Name Error: Cannot assign to undefined variable 'undeclared'.
// 💡 Hint: Declare the variable first with: let undeclared = ...

5. Operators

Arithmetic

OperatorOperationExampleResult
+Addition3 + 25
-Subtraction10 - 46
*Multiplication3 * 412
/Division7 / 23.5
%Modulo10 % 31
**Exponentiation2 ** 101024.0

Integer +, -, *, and % preserve exact signed 64-bit results and report overflow. Float operands promote these operations to Float. / always returns a finite Float; div(a, b) returns an integer quotient truncated toward zero. Comparisons preserve integer precision, including mixed integer/float comparisons.

Out-of-range literals, conversions, and nonfinite math results raise located errors. type(42) returns "Int". See the numeric decision.

** (power) always returns Float, even for integer operands.

Division and modulo by zero raise a Runtime Error.

Power Operator Associativity

** is right-associative: 2 ** 3 ** 2 is evaluated as 2 ** (3 ** 2) = 2 ** 9 = 512.0.

print(4 ** 2)       // 16.0
print(2 ** 3 ** 2)  // 512.0  (right-associative)

String Concatenation

+ concatenates when either operand is a String. The other operand is automatically converted to its string representation.

let msg = "Port: " + 8080     // "Port: 8080"
let info = str(99) + "% load" // "99% load"

Comparison

OperatorMeaning
==Equal
!=Not equal
<Less than
<=Less than or equal
>Greater than
>=Greater than or equal

Numeric comparisons accept both Integer and Float. Equality (==, !=) works across all types including IP, String, and Bool.

Ordering comparisons (<, <=, >, >=) also work on String values (lexicographic order).

Logical

OperatorMeaningShort-circuits
andLogical ANDYes — stops at first false
orLogical ORYes — stops at first true
notLogical NOTN/A
if active and load < 90 {
    print("All good")
}

if error or timeout {
    print("Something went wrong")
}

if not connected {
    print("Offline")
}

Membership — in / not in

Tests whether a value is contained in a list, a key exists in a map, a substring exists in a string, or an IP address is within a subnet.

// List membership
if "admin" in roles {
    print("Access granted")
}

// Map key membership
let config = {host: "localhost", port: 8080}
if "host" in config {
    print("host is set")
}

// Substring check
if "error" in log_line {
    print("Error found in log")
}

// IP-in-subnet (native)
if client_ip in 192.168.1.0/24 {
    print("Local client")
}

// Negated membership
if client_ip not in trusted_range {
    quarantine(client_ip)
}

Semantic Match — ~

The ~ operator performs a case-insensitive keyword substring check between two strings. It counts how many whitespace-separated words from the right string occur in the left string. It returns true when the count reaches max(1, floor(word_count / 2)). It does not evaluate intent or meaning.

let payload = "user tried to access account with leaked credentials"

if payload ~ "password security threat" {
    // triggers when enough right-side keywords occur as substrings
    print("Threat detected")
}

Both sides must be String. A Type Error is raised otherwise.

Embedding-based similarity is not implemented. Any future AI similarity API is a separate design decision; see the roadmap.

Operator Precedence

From highest to lowest:

LevelOperators
1- (unary negation), not (unary)
2** (right-associative)
3* / %
4+ -
5== != < <= > >= ~ in not in
6not
7and
8or

Use parentheses to override precedence:

let result = (a + b) * c

6. Control Flow

if / else if / else

Parentheses around the condition are not required. Braces { } are mandatory.

if temperature > 80 {
    print("Overheating")
} else {
    print("Normal temperature")
}

else if chains are fully supported and may be nested to any depth:

let score = 85

if score >= 90 {
    print("A")
} else if score >= 80 {
    print("B")
} else if score >= 70 {
    print("C")
} else {
    print("F")
}

while

Repeats the body as long as the condition is truthy.

let retries = 0
while retries < 5 {
    print("Attempting connection...")
    retries += 1
}

for / in

Iterates over every element of a List. The loop variable is scoped to the loop body.

let nodes = ["web-01", "web-02", "db-01"]
for node in nodes {
    print("Checking node: " + node)
}

Combined with range() for numeric iteration:

for i in range(5) {
    print("Step " + str(i))   // 0 1 2 3 4
}

for i in range(1, 6) {
    print(str(i))              // 1 2 3 4 5
}

break

Immediately exits the nearest enclosing while or for loop.

let i = 0
while i < 100 {
    i += 1
    if i > 10 {
        break     // stops the loop when i reaches 11
    }
}
print(i)   // 11

Using break outside a loop raises a Runtime Error.

continue

Skips the rest of the current loop iteration and jumps to the next one.

for i in range(10) {
    if i % 2 == 0 {
        continue   // skip even numbers
    }
    print(i)       // prints 1, 3, 5, 7, 9
}

Using continue outside a loop raises a Runtime Error.


7. Functions

Declaration

fn function_name(param1, param2) {
    // body
    return value
}
  • Parameters are positional; no default values.
  • return exits the function and optionally produces a value.
  • A function without return returns null.
  • Functions are values — they can be stored in variables and passed as arguments.
fn add(a, b) {
    return a + b
}

let result = add(10, 20)
print(result)   // 30

Recursion

fn factorial(n) {
    if n <= 1 {
        return 1
    }
    return n * factorial(n - 1)
}

print(factorial(6))   // 720

Functions as Values

fn double(x) {
    return x * 2
}

let transform = double
print(transform(7))   // 14

Closures and Nested Functions

Functions in Zy use lexical scoping — they capture the environment at the point of their definition. This makes closures possible:

fn make_counter(start) {
    let count = start

    fn increment() {
        count += 1
        return count
    }

    return increment
}

let counter = make_counter(0)
print(counter())   // 1
print(counter())   // 2
print(counter())   // 3

Each call to make_counter() creates an independent closure with its own count variable. The inner function increment captures count from its enclosing scope and can both read and update it.

Higher-Order Functions

fn apply(f, value) {
    return f(value)
}

fn square(x) {
    return x ** 2
}

print(apply(square, 5))   // 25.0

Argument Count

Calling a function with the wrong number of arguments raises a Runtime Error with a clear message:

fn greet(name) { return "Hello " + name }
greet()
// ❌ Runtime Error: Function 'greet' expects 1 argument(s), but got 0.
// 💡 Hint: Pass exactly 1 argument(s) to 'greet'.

8. Lists

Lists are ordered, zero-indexed, and can hold values of mixed types.

Literal Syntax

let numbers = [1, 2, 3, 4, 5]
let mixed   = [42, "hello", true, 192.168.1.1]
let empty   = []

Indexing

let first = numbers[0]    // 1
let last  = numbers[-1]   // 5  (negative indexing from end)

Out-of-bounds access raises a Runtime Error:

numbers[99]
// ❌ Runtime Error: List index 99 is out of range (length 5).
// 💡 Hint: Valid indices are 0 to 4.

Index Assignment

let fruits = ["apple", "banana", "cherry"]
fruits[1] = "blueberry"
print(fruits)   // [apple, blueberry, cherry]

Iterating

for item in numbers {
    print(item)
}

List Methods

See §14 List Methods for the full method reference.


9. Maps

Maps are ordered key-value dictionaries. Keys are always stored as strings; values can be any type. Insertion order is preserved.

Literal Syntax

let user = {
    name: "Alice",
    age: 30,
    active: true
}

Bare identifiers (name:) are treated as string keys. String literals can also be used:

let config = {
    "host": "localhost",
    "port": 8080
}

An empty map is {}.

Reading Values

Bracket notation (dynamic key):

let name = user["name"]    // "Alice"

Dot notation (static key — must be a valid identifier):

let age = user.age         // 30

If the key does not exist, both notations return null.

Writing Values

user["city"] = "Nairobi"   // bracket notation
user.score = 100            // dot notation

Key Membership

The in operator checks whether a key exists in a map:

if "name" in user {
    print("name is set")
}

if "email" not in user {
    print("no email on file")
}

Map Methods

See §15 Map Methods for the full method reference.

Maps and len()

let m = {a: 1, b: 2, c: 3}
print(len(m))   // 3

10. Networking Primitives

Zy treats IPv4 addresses and CIDR subnets as distinct native types, not strings. This prevents accidental misuse and enables type-safe network operations.

IP Literals

Any dotted-quad address is automatically parsed as an IP type:

let gateway    = 192.168.1.1
let dns_server = 8.8.8.8
let loopback   = 127.0.0.1

An invalid address causes a Type Error at runtime.

Subnet Literals

Any CIDR notation is automatically parsed as a Subnet type:

let lan       = 192.168.0.0/16
let dmz       = 10.10.10.0/24
let host_only = 172.16.0.1/32

IP Membership

The in / not in operators work natively between IP and Subnet:

let zone = 10.0.0.0/8
let host = 10.5.3.2

if host in zone {
    print("Host is inside the zone")
}

if 172.16.0.1 not in zone {
    print("External address detected")
}

Subnet Properties

PropertyTypeDescription
subnet.networkStringNetwork address as a string
subnet.maskIntegerPrefix length (e.g. 24)
let net = 192.168.1.0/24
print(net.network)   // 192.168.1.0
print(net.mask)      // 24

Subnet Methods

See §16 Subnet Methods.


11. AI Agents

Agents are first-class values in Zy. They store configuration and expose simulated query and vision methods. The current runtime does not contact model providers or process images.

Declaration

agent AgentName {
    model: "model-identifier",
    temperature: 0.2,
    instructions: "System prompt for the agent."
}

All properties are optional. Recognized properties:

PropertyTypeDescription
modelStringAI model identifier
temperatureFloatSampling temperature (0.0 – 1.0)
instructionsStringSystem-level instruction prompt
modeStringOperational mode (e.g. "computer-vision")
agent SecurityMonitor {
    model: "gemini-2.0-pro",
    temperature: 0.1,
    instructions: "You are a security analyst. Be terse and precise."
}

agent VisionCore {
    model: "vision-pro",
    mode: "computer-vision"
}

Agent Methods

.ask(prompt)

Currently prints a query message and returns a simulated response String. No provider request is made.

let verdict = SecurityMonitor.ask("Is this payload malicious: " + payload)
print(verdict)

.detect_objects(frame)

Currently prints a simulation message and returns ["unknown_object"]. The input is not processed by a vision model.

let found = VisionCore.detect_objects(frame)
if found.contains("unauthorized_person") {
    trigger_alert(location: "Server Room B")
}

Accessing Agent Properties

Properties declared in the agent block are readable as normal member access:

print(SecurityMonitor.model)        // gemini-2.0-pro
print(SecurityMonitor.temperature)  // 0.1

The Semantic Match Operator with Agents

A common pattern combines the ~ operator with an agent for layered threat detection:

let payload = "GET /admin?token=leaked_token HTTP/1.1"

if payload ~ "authentication bypass credential theft" {
    let analysis = SecurityMonitor.ask("Classify threat level: " + payload)
    print(analysis)
}

12. Built-in Functions

These functions are available globally in every .zy program.


print(value, ...)

Prints one or more values to standard output, separated by spaces, followed by a newline.

print("Hello")               // Hello
print("Load:", 72, "%")      // Load: 72 %
print(192.168.1.1)           // 192.168.1.1

len(value)

Returns the length of a String (character count), List (element count), or Map (key count).

len("hello")         // 5
len([1, 2, 3])       // 3
len({a: 1, b: 2})    // 2

type(value)

Returns the type name of a value as a String.

type(42)             // "Int"
type(3.14)           // "Float"
type("hi")           // "String"
type(true)           // "Bool"
type(null)           // "Null"
type([1, 2])         // "List"
type({})             // "Map"
type(10.0.0.0/8)     // "Subnet"
type(192.168.1.1)    // "IP"

str(value)

Converts any value to its String representation.

str(100)             // "100"
str(3.14)            // "3.14"
str(true)            // "true"
str(null)            // "null"
str(192.168.1.1)     // "192.168.1.1"

int(value)

Converts a Float or numeric String to an Integer.

int(3.9)             // 3  (truncates)
int("42")            // 42

float(value)

Converts an Integer or numeric String to a Float.

float(7)             // 7.0
float("3.14")        // 3.14

range(stop) / range(start, stop) / range(start, stop, step)

Generates a List of integers with an exclusive stop. Results are limited to 65,536 entries; step/bound arithmetic cannot wrap. Zero steps or oversized results raise an error.

range(5)             // [0, 1, 2, 3, 4]
range(2, 6)          // [2, 3, 4, 5]
range(0, 10, 2)      // [0, 2, 4, 6, 8]
range(5, 0, -1)      // [5, 4, 3, 2, 1]

abs(n)

Returns the absolute value of a number.

abs(-7)    // 7
abs(3.5)   // 3.5

max(a, b, ...) / min(a, b, ...)

Returns the largest or smallest value among the arguments.

max(3, 7, 2)   // 7
min(3, 7, 2)   // 2

sqrt(n)

Returns the square root of n as a Float.

sqrt(16)    // 4.0
sqrt(2)     // 1.4142135...

pow(base, exp)

Returns base ** exp as a Float.

pow(2, 10)   // 1024.0
pow(3, 3)    // 27.0

floor(n) / ceil(n) / round(n)

Standard rounding functions. floor and ceil return an Integer; round returns an Integer.

floor(3.9)   // 3
ceil(3.1)    // 4
round(3.5)   // 4
round(3.4)   // 3

input(prompt?)

Reads a line from standard input and returns it as a String. An optional prompt string is printed before reading.

let name = input("Enter your name: ")
print("Hello, " + name)

assert(condition, message?)

Asserts that condition is truthy. If it is not, a Runtime Error is raised with the optional message.

assert(1 + 1 == 2)
assert(len("hello") == 5, "String length is wrong")

// Failure:
assert(false, "This always fails")
// ❌ Runtime Error: Assertion failed: This always fails

format(template, args...)

Returns a formatted string by replacing each {} placeholder with the corresponding argument (converted via str()).

let msg = format("Hello, {}! You are {} years old.", "Alice", 30)
print(msg)   // Hello, Alice! You are 30 years old.

let log = format("[{}] ERROR: {}", "2026-01-01", "Disk full")
print(log)   // [2026-01-01] ERROR: Disk full

Placeholders are filled left-to-right. Extra arguments are ignored; unmatched {} remain as-is.


quarantine(ip)

Security primitive. Prints a quarantine notice for the given IP. In a production runtime, this hooks into firewall or network isolation APIs.

quarantine(suspicious_ip)
// 🔒 Quarantined: 10.0.0.99

trigger_alert(location:)

Security primitive. Fires an alert for the given location string.

trigger_alert(location: "Server Room B")
// 🚨 ALERT triggered at: Server Room B

13. String Methods

Methods are called with dot notation followed by parentheses: value.method(args).

length is a property (no parentheses); all others are methods (require ()).

Method / PropertyReturnsDescription
s.lengthIntegerNumber of characters (Unicode-aware)
s.upper()StringUppercase copy
s.lower()StringLowercase copy
s.trim()StringRemoves leading and trailing whitespace
s.contains(sub)BoolWhether the string contains the substring
s.split(sep)ListSplits on an explicit String separator
s.starts_with(prefix)BoolWhether the string starts with the given prefix
s.ends_with(suffix)BoolWhether the string ends with the given suffix
s.replace(old, new)StringReplaces all occurrences of old with new
s.index_of(sub)IntegerIndex of first occurrence, or -1 if not found
let name = "  Zy Language  "

print(name.trim())                      // "Zy Language"
print(name.trim().upper())              // "Zy LANGUAGE"
print(name.trim().lower())              // "Zy language"
print(name.trim().contains("Lang"))     // true
print(name.trim().split(" "))           // [Zy, Language]
print(name.trim().starts_with("Zy"))   // true
print(name.trim().ends_with("e"))       // true
print(name.trim().replace("Zy", "Go")) // "Go Language"
print(name.trim().index_of("L"))        // 4
print(name.length)                      // 16 (includes spaces)

Method calls can be chained:

let clean = raw_input.trim().lower().replace("-", "_")

14. List Methods

Method arity is checked. Sorting accepts all numbers or all strings and returns a shallow copy. Slicing uses integer bounds, clamps out-of-range values, and returns an empty list for reversed bounds. Collection aliases compare equal by identity; separate collections do not use structural equality.

length is a property (no parentheses); all others are methods.

Method / PropertyReturnsDescription
list.lengthIntegerNumber of elements
list.contains(val)BoolWhether the list contains an equal value
list.append(val)ListAppends val in-place; returns the same list
list.pop()anyRemoves and returns the last element
list.pop(i)anyRemoves and returns the element at index i
list.slice(start, end)ListReturns a new sub-list from start (inclusive) to end (exclusive)
list.reverse()ListReturns a new reversed copy (does not mutate)
list.sort()ListReturns a new sorted copy (does not mutate)
list.join(sep)StringJoins elements into a string using sep as separator
list.index_of(val)IntegerIndex of first occurrence, or -1 if not found
let nums = [5, 3, 8, 1, 9, 2]

print(nums.sort())           // [1, 2, 3, 5, 8, 9]
print(nums.reverse())        // [2, 9, 1, 8, 3, 5]
print(nums.slice(1, 4))      // [9, 1, 8]  (of reversed list)
print(nums.length)           // 6

let words = ["hello", "world", "foo"]
words.append("bar")
let last = words.pop()       // "bar" — removes last element
print(words.join(", "))      // hello, world, foo
print(words.index_of("world"))  // 1

Negative indices in pop() count from the end:

let items = [10, 20, 30, 40]
print(items.pop(-1))   // 40
print(items)           // [10, 20, 30]

15. Map Methods

length is a property; all others are methods.

Method / PropertyReturnsDescription
map.lengthIntegerNumber of key-value pairs
map.keys()ListList of all keys (in insertion order)
map.values()ListList of all values (in insertion order)
map.has(key)BoolWhether the given key exists
map.remove(key)nullRemoves the key-value pair; no-op if key is missing
let config = {host: "localhost", port: 8080, debug: true}

print(config.length)           // 3
print(config.keys())           // [host, port, debug]
print(config.values())         // [localhost, 8080, true]
print(config.has("port"))      // true
print(config.has("timeout"))   // false

config.remove("debug")
print(config)                  // {host: localhost, port: 8080}

16. Subnet Methods

Splits require parent.mask < prefix <= 32 and produce at most 65,536 results. /0 boundaries terminate correctly. net.allocate() supports variable-size planning; see the standard library.

Method / PropertyReturnsDescription
subnet.networkStringNetwork address string (e.g. "192.168.1.0")
subnet.maskIntegerPrefix length (e.g. 24)
subnet.split(prefix)ListSplits into all equal /prefix sub-networks
subnet.contains(ip)BoolWhether the given IP is inside the subnet
let zone = 10.0.0.0/8

print(zone.network)          // 10.0.0.0
print(zone.mask)             // 8

// Split into /10 subnets
let quarters = zone.split(10)
print(quarters[0])           // 10.0.0.0/10
print(quarters[1])           // 10.64.0.0/10

// Membership via method
print(zone.contains(10.5.1.2))   // true

split() Type Safety

The prefix argument must be an Integer. Passing a String raises a Type Error with a hint:

zone.split("10")
// ❌ Type Error: subnet.split() requires an Integer CIDR prefix (e.g. split(28)).
// 💡 Hint: Remove quotes if you used a string: split(28) not split("28").

17. Error System

Zy produces structured, human-centric error messages. Every error includes:

  • The error kind (Syntax, Type, Runtime, Name)
  • A plain-language description of what went wrong
  • A file reference with line and column numbers
  • A source excerpt with a ^ caret pointing at the exact position
  • A 💡 Hint suggesting how to fix it

Error Format

❌ <Kind> Error: <message>
  --> <file>:<line>:<column>
   |
 N | <source line>
   |   ^
   |   <message repeated>

💡 Hint: <actionable fix>

Error Kinds

KindWhen it occurs
Syntax ErrorUnrecognized character, missing brace, malformed token
Type ErrorWrong type for operation (e.g. string CIDR, non-numeric math)
Runtime ErrorDivision by zero, out-of-bounds index, wrong argument count
Name ErrorReading or assigning an undeclared variable

Examples

Missing opening brace:

if load > 90
    print("High load")

// ❌ Syntax Error: Expected '{' to start a block, but found 'print'.
//   --> monitor.zy:2:5
//    |
//  2 |     print("High load")
//    |     ^
//    |     Add a '{' here to open the block.

String used as subnet mask:

let zone = 192.168.0.0/16
let sub = zone.split("20")

// ❌ Type Error: subnet.split() requires an Integer CIDR prefix (e.g. split(28)).
//   --> config.zy:2:21
//    |
//  2 | let sub = zone.split("20")
//    |                     ^
// 💡 Hint: Remove quotes if you used a string: split(28) not split("28").

Undefined variable:

print(user_count)

// ❌ Name Error: 'user_count' is not defined.
//   --> app.zy:1:7
//    |
//  1 | print(user_count)
//    |       ^
// 💡 Hint: Declare it first: let user_count = ...

Division by zero:

let ratio = total / active

// ❌ Runtime Error: Division by zero.
//   --> calc.zy:1:17
// 💡 Hint: Ensure the divisor is not zero before dividing.

18. Scoping Rules and Closures

  • Every { } block creates a new inner scope.
  • Inner scopes can read variables from outer scopes.
  • Inner scopes can re-assign variables declared in outer scopes.
  • Variables declared with let inside a block are not visible outside it.
let x = 10

fn show() {
    let y = 20
    print(x)   // ✓ reads outer x
    print(y)   // ✓ reads local y
}

show()
print(x)       // ✓ still 10
// print(y)    // ✗ Name Error — y is not in scope here

Closure Semantics

Functions use lexical scoping: they capture the environment at the point of their definition, not at the point of their call.

fn make_adder(n) {
    fn adder(x) {
        return x + n    // captures n from make_adder's scope
    }
    return adder
}

let add5 = make_adder(5)
let add10 = make_adder(10)

print(add5(3))    // 8
print(add10(3))   // 13

Each closure has independent state:

fn make_counter(start) {
    let count = start
    fn next() {
        count += 1
        return count
    }
    return next
}

let a = make_counter(0)
let b = make_counter(100)

print(a())   // 1
print(a())   // 2
print(b())   // 101   — independent from a

19. Truthiness Rules

Every value has an inherent boolean interpretation used in if, while, and logical operators.

ValueTruthy?
nullfalse
falsefalse
0false
0.0false
"" (empty string)false
[] (empty list)false
{} (empty map)false
Any other valuetrue
let items = []
if items {
    print("Has items")
} else {
    print("Empty list")  // this branch executes
}

let config = {}
if not config {
    print("Config is empty")   // executes
}

20. Grammar Summary

program     → statement* EOF

statement   → let_stmt
            | assign_stmt
            | aug_assign_stmt
            | index_assign_stmt
            | fn_decl
            | type_decl
            | try_stmt
            | throw_stmt
            | import_stmt
            | export_stmt
            | agent_decl
            | return_stmt
            | break_stmt
            | continue_stmt
            | if_stmt
            | while_stmt
            | for_stmt
            | expr_stmt

let_stmt        → "let" IDENT (":" type_ref)? "=" expression
assign_stmt     → IDENT "=" expression
aug_assign_stmt → IDENT ("+=" | "-=" | "*=" | "/=") expression
index_assign_stmt → postfix "[" expression "]" "=" expression
fn_decl         → "fn" IDENT "(" params? ")" ("->" type_ref)? block
type_ref        → IDENT ("." IDENT)? "?"?
type_decl       → "record" IDENT "{" (IDENT ":" type_ref ","?)* "}"
                | "enum" IDENT "{" (IDENT ","?)+ "}"
try_stmt        → "try" block "catch" IDENT block
throw_stmt      → "throw" expression
import_stmt     → "import" STRING "as" IDENT
export_stmt     → "export" (let_stmt | fn_decl | type_decl)
agent_decl      → "agent" IDENT "{" (IDENT ":" expression ","?)* "}"
return_stmt     → "return" expression?
break_stmt      → "break"
continue_stmt   → "continue"
if_stmt         → "if" expression block ("else" ("if" expression block)* ("else" block)?)?
while_stmt      → "while" expression block
for_stmt        → "for" IDENT "in" expression block
expr_stmt       → expression

block       → "{" statement* "}"
params      → IDENT (":" type_ref)? ("," IDENT (":" type_ref)?)*

expression  → or_expr
or_expr     → and_expr ("or" and_expr)*
and_expr    → not_expr ("and" not_expr)*
not_expr    → "not" not_expr | comparison
comparison  → add_sub (("==" | "!=" | "<" | "<=" | ">" | ">="
                        | "~" | "in" | "not" "in") add_sub)*
add_sub     → mul_div (("+" | "-") mul_div)*
mul_div     → power (("*" | "/" | "%") power)*
power       → unary ("**" power)?          // right-associative
unary       → "-" unary | postfix
postfix     → primary (("." IDENT ("(" args? ")")?
                        | "[" expression "]"
                        | "(" args? ")"))*
primary     → INT | FLOAT | STRING | BOOL | NULL | IP | SUBNET
            | IDENT | "(" expression ")" | list_literal | map_literal
list_literal → "[" (expression ("," expression)*)? "]"
map_literal  → "{" (map_pair ("," map_pair)*)? "}"
map_pair     → (IDENT | STRING) ":" expression
args         → (IDENT ":")? expression ("," (IDENT ":")? expression)*
Found something to improve?Edit this page on GitHub ↗
Search guides, language features, and APIs ↵ to open first result