Language Reference
Complete syntax and semantics reference for TinyLang. Every language feature is documented here with examples.
Data Types
TinyLang has six fundamental data types:
| Type | Description | Examples |
|---|---|---|
number | 64-bit floating point numbers | 42, 3.14, -7, 1e6 |
string | Unicode text (double or single quotes) | "hello", 'world' |
boolean | Logical true or false | true, false |
null | Absence of value | null |
array | Ordered collection of values | [1, 2, 3] |
object | Key-value pairs | {name: "Alice", age: 30} |
Numbers
let integer = 42
let float = 3.14159
let negative = -7
let scientific = 1e6 // 1000000
Strings
let double = "Hello, World!"
let single = 'Also valid'
let concat = "Hello" + ", " + "World" // String concatenation with +
let length = len("hello") // 5
Arrays
let empty = []
let nums = [1, 2, 3, 4, 5]
let mixed = [1, "two", true, null]
let nested = [[1, 2], [3, 4]]
// Access by index (zero-based)
print(nums[0]) // 1
print(nums[4]) // 5
Objects
let person = {
name: "Alice",
age: 30,
city: "NYC"
}
// Property access
print(person.name) // "Alice"
print(person["age"]) // 30
// Modify properties
person.age = 31
Variables
let - Mutable Variables
let x = 10
x = 20 // OK - can reassign
let name = "Alice"
name = "Bob" // OK
const - Immutable Variables
const PI = 3.14159
// PI = 3.0 // Error! Cannot reassign a constant
const MAX_SIZE = 100
const APP_NAME = "TinyLang"
Compound Assignment
let x = 10
x += 5 // x = 15
x -= 3 // x = 12
x *= 2 // x = 24
x /= 4 // x = 6
Operators
Arithmetic
| Operator | Description | Example |
|---|---|---|
+ | Addition / String concatenation | 3 + 4 = 7 |
- | Subtraction | 10 - 3 = 7 |
* | Multiplication | 4 * 5 = 20 |
/ | Division | 15 / 4 = 3.75 |
% | Modulo (remainder) | 17 % 5 = 2 |
** | Exponentiation | 2 ** 10 = 1024 |
- (unary) | Negation | -x |
Comparison
| Operator | Description | Example |
|---|---|---|
== | Equal to | 5 == 5 = true |
!= | Not equal to | 5 != 3 = true |
< | Less than | 3 < 5 = true |
<= | Less than or equal | 5 <= 5 = true |
> | Greater than | 7 > 3 = true |
>= | Greater than or equal | 5 >= 5 = true |
Logical
| Operator | Description | Example |
|---|---|---|
and | Logical AND | true and false = false |
or | Logical OR | true or false = true |
not | Logical NOT | not true = false |
Conditional (Ternary)
cond ? whenTrue : whenFalse is an expression, so it can appear anywhere a value can.
Only the arm that is selected is evaluated.
let n = 5
let label = n > 0 ? "positive" : "not positive"
print(items.length() == 0 ? "empty" : "has items")
let config = {retries: isProd ? 5 : 1}
It binds looser than every operator except assignment, so a comparison forms the condition without parentheses, and it is right-associative, so a chain reads top to bottom:
let grade = score > 90 ? "A" : score > 80 ? "B" : "C"
// groups as: score > 90 ? "A" : (score > 80 ? "B" : "C")
Control Flow
if / else
let age = 18
if age >= 18 {
print("Adult")
} else if age >= 13 {
print("Teenager")
} else {
print("Child")
}
while Loop
let i = 0
while i < 5 {
print(i)
i += 1
}
// Output: 0 1 2 3 4
for..in Loop
Iterate over arrays, strings, or ranges:
// Iterate over an array
let fruits = ["apple", "banana", "cherry"]
for fruit in fruits {
print(fruit)
}
// Iterate over a range (0 to 4)
for i in 0..5 {
print(i)
}
// Iterate over characters of a string
for ch in "HELLO" {
print(ch)
}
Ranges
The .. operator creates a range (exclusive upper bound):
// Range from 0 to 9
for i in 0..10 {
print(i)
}
// Ranges work with variables
let n = 5
for i in 0..n {
print(i)
}
break and continue
// break - exit loop early
for i in 0..100 {
if i > 5 { break }
print(i)
}
// continue - skip to next iteration
for i in 0..10 {
if i % 2 == 0 { continue }
print(i) // Only odd numbers
}
match / when
Pattern matching for multi-way branching:
fn describe(code) {
match code {
when 200 => return "OK"
when 404 => return "Not Found"
when 500 => return "Server Error"
else => return "Unknown"
}
}
print(describe(200)) // "OK"
print(describe(404)) // "Not Found"
Match with blocks:
match op {
when "+" => return a + b
when "-" => return a - b
when "/" => {
if b == 0 {
print("Error: division by zero")
return null
}
return a / b
}
else => return null
}
Functions
Named Functions
fn add(a, b) {
return a + b
}
print(add(3, 4)) // 7
Default Parameters
fn greet(name, greeting = "Hello") {
return greeting + ", " + name + "!"
}
print(greet("Alice")) // "Hello, Alice!"
print(greet("Bob", "Hi")) // "Hi, Bob!"
Arrow Functions
let double = (x) => x * 2
let square = (x) => x * x
let isEven = (n) => n % 2 == 0
print(double(5)) // 10
print(square(4)) // 16
print(isEven(6)) // true
Closures
Functions capture variables from their enclosing scope:
fn makeCounter(start = 0) {
let count = start
return fn() {
count += 1
return count
}
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2
print(counter()) // 3
Higher-Order Functions
fn apply(f, value) {
return f(value)
}
let double = (x) => x * 2
print(apply(double, 5)) // 10
// Arrays support map, filter, reduce
let nums = [1, 2, 3, 4, 5]
let doubled = nums.map((x) => x * 2) // [2, 4, 6, 8, 10]
let evens = nums.filter((x) => x % 2 == 0) // [2, 4]
let sum = nums.reduce((a, x) => a + x, 0) // 15
Classes
Basic Classes
class Animal {
let name = ""
let sound = ""
fn init(name, sound) {
this.name = name
this.sound = sound
}
fn speak() {
print(this.name + " says " + this.sound)
}
}
let dog = new Animal("Dog", "Woof")
dog.speak() // "Dog says Woof"
Inheritance
class Dog extends Animal {
let tricks = []
fn init(name) {
this.name = name
this.sound = "Woof"
this.tricks = []
}
fn learn(trick) {
this.tricks.push(trick)
}
}
let buddy = new Dog("Buddy")
buddy.speak() // "Buddy says Woof" (inherited)
buddy.learn("sit") // Dog-specific method
Class Features
- Properties - Declared with
letinside the class body - Constructor - The
init()method is called onnew - Methods - Declared with
fninside the class body - this - Refers to the current instance
- Inheritance - Use
extendsto inherit from a parent class - Instantiation - Use
new ClassName(args)
Modules
Importing
// Import specific items from a module
import {greet, add} from "./lib"
greet("World")
print(add(2, 3))
Module paths are relative to the importing file. The .tiny extension is optional.
Test Blocks
TinyLang has built-in testing support:
fn add(a, b) {
return a + b
}
test "addition works" {
expectToBe(add(2, 3), 5)
expectToBe(add(-1, 1), 0)
}
test "string concatenation" {
let result = "hello" + " world"
expectToBe(result, "hello world")
}
Run tests with:
tinylang test main.test.tiny
Comments
// Single-line comment
// Multi-line comments use multiple single-line comments
// There is no block comment syntax in TinyLang
Array Methods
Arrays support method-style calls for common operations:
let nums = [1, 2, 3, 4, 5]
// Transform
nums.map((x) => x * 2) // [2, 4, 6, 8, 10]
nums.filter((x) => x > 2) // [3, 4, 5]
nums.reduce((a, x) => a + x, 0) // 15
// Search
nums.find((x) => x > 3) // 4
nums.includes(3) // true
// Iterate
nums.forEach((x, i) => {
print(i, x)
})
// Chaining
[1, 2, 3, 4, 5]
.filter((x) => x % 2 == 0)
.map((x) => x * x)
// [4, 16]
Scoping Rules
TinyLang uses lexical (block) scoping:
let x = 1
if true {
let x = 2 // New variable in inner scope
print(x) // 2
}
print(x) // 1 (outer scope unchanged)
String Interpolation (F-Strings)
Prefix a string with f to embed expressions inside curly braces. The expressions are evaluated and converted to strings automatically.
let name = "World"
let x = 7
print(f"Hello, {name}!") // "Hello, World!"
print(f"{x} * {x} = {x * x}") // "7 * 7 = 49"
print(f"Items: {[1, 2, 3]}") // "Items: [1, 2, 3]"
// Works with any expression
let arr = [10, 20, 30]
print(f"Length: {len(arr)}, First: {arr[0]}")
Error Handling (Try/Catch/Throw)
TinyLang provides structured error handling with try, catch, and throw.
Throwing Errors
fn divide(a, b) {
if b == 0 {
throw "Division by zero!"
}
return a / b
}
Catching Errors
try {
let result = divide(10, 0)
print(result)
} catch err {
print(f"Error: {err.message}") // "Error: Division by zero!"
}
Error Propagation
Errors propagate up through function calls until caught by a try/catch block. Uncaught errors terminate the program with an error message.
fn validateAge(age) {
if age < 0 { throw "Age cannot be negative" }
return age
}
fn createUser(name, age) {
let valid = validateAge(age) // may throw
return {name: name, age: valid}
}
try {
let user = createUser("Bob", -5)
} catch err {
print(err.message) // "Age cannot be negative"
}
Destructuring
Destructuring lets you unpack values from arrays and objects into distinct variables.
Array Destructuring
let [a, b, c] = [1, 2, 3]
print(a) // 1
print(b) // 2
print(c) // 3
// From function returns
fn getPoint() { return [3, 4] }
let [x, y] = getPoint()
Object Destructuring
let config = {host: "localhost", port: 8080}
let {host, port} = config
print(host) // "localhost"
print(port) // 8080
Spread Operator
The spread operator (...) expands an array or string into individual elements within another array.
Array Spread
let a = [1, 2, 3]
let b = [4, 5, 6]
let combined = [...a, ...b] // [1, 2, 3, 4, 5, 6]
let withExtra = [0, ...a, 99] // [0, 1, 2, 3, 99]
String Spread
let chars = [..."hello"] // ["h", "e", "l", "l", "o"]