Language Reference - ThornLang/JavaThorn GitHub Wiki
This is the complete reference for the ThornLang programming language syntax and features.
- Lexical Structure
- Variables and Constants
- Data Types
- Operators
- Functions
- Classes and Objects
- Control Flow
- Pattern Matching
- Arrays and Collections
- Modules
- Comments
- Reserved Keywords
Identifiers must:
- Start with a letter or underscore
- Contain only letters, digits, and underscores
- Not be a reserved keyword
valid_name
_private
userName123
MAX_VALUE
Statements are terminated by semicolons.
x = 42;
y = "hello";
print(x);
Variables are dynamically typed and declared with =
:
// Simple assignment
name = "Alice"
age = 25
isActive = true
// With type annotation
username: string = "bob123"
score: number = 98.5
flags: Array[boolean] = [true, false, true]
Use @immut
to declare constants:
@immut PI = 3.14159
@immut MAX_SIZE: number = 1000
@immut CONFIG = {
"host": "localhost",
"port": 8080
}
// Error: Cannot reassign
// PI = 3.14 // Runtime error!
Variables are block-scoped:
x = 10 // Global scope
if (true) {
y = 20 // Block scope
x = 15 // Modifies global x
}
// print(y) // Error: y not in scope
print(x) // Prints: 15
Type | Description | Examples |
---|---|---|
number |
64-bit floating point |
42 , 3.14 , -0.5
|
string |
Text data |
"hello" , 'world'
|
boolean |
True or false |
true , false
|
null |
Absence of value | null |
Type | Description | Example |
---|---|---|
Array[T] |
Ordered collection | [1, 2, 3] |
Object |
Key-value pairs | {"key": "value"} |
Function |
Callable object | $(x) => x * 2 |
Class instances | User-defined types | Person("Alice") |
// Runtime type checking
x: number = 42 // OK
y: string = 42 // Runtime error!
// Type inference
z = 42 // Inferred as number
w = "hello" // Inferred as string
Operator | Description | Example |
---|---|---|
+ |
Addition/Concatenation |
5 + 3 , "a" + "b"
|
- |
Subtraction | 10 - 4 |
* |
Multiplication | 6 * 7 |
/ |
Division | 15 / 3 |
% |
Modulo | 10 % 3 |
** |
Exponentiation | 2 ** 8 |
Operator | Description | Example |
---|---|---|
== |
Equality | x == y |
!= |
Inequality | x != y |
< |
Less than | x < y |
> |
Greater than | x > y |
<= |
Less than or equal | x <= y |
>= |
Greater than or equal | x >= y |
Operator | Description | Example |
---|---|---|
&& |
Logical AND | x && y |
|| |
Logical OR | x || y |
! |
Logical NOT | !x |
?? |
Null coalescing | x ?? "default" |
Operator | Description | Example |
---|---|---|
= |
Assignment | x = 5 |
+= |
Add and assign | x += 3 |
-= |
Subtract and assign | x -= 2 |
*= |
Multiply and assign | x *= 4 |
/= |
Divide and assign | x /= 2 |
%= |
Modulo and assign | x %= 3 |
From highest to lowest:
- Member access (
.
), calls (()
), indexing ([]
) - Unary (
-x
,!x
) - Exponentiation (
**
) - Multiplication, division, modulo (
*
,/
,%
) - Addition, subtraction (
+
,-
) - Comparison (
<
,>
,<=
,>=
) - Equality (
==
,!=
) - Logical AND (
&&
) - Logical OR (
||
) - Null coalescing (
??
) - Assignment (
=
,+=
, etc.)
Functions are declared with the $
prefix:
// Simple function
$ greet(name) {
print("Hello, " + name)
}
// With type annotations
$ add(a: number, b: number): number {
return a + b
}
// With default return (null)
$ doSomething() {
x = 5
// Implicitly returns null
}
Anonymous functions using arrow syntax:
// Simple lambda
double = $(x) => x * 2
// Multi-line lambda
processArray = $(arr) => {
result = []
for (item in arr) {
result.push(item * 2)
}
return result
}
// Type annotated lambda
validator: Function[(string), boolean] = $(s) => s.length > 0
// Old syntax (return type only)
callback: Function[void] = $() => { print("Done") }
// New syntax (parameters and return)
processor: Function[(number, number), number] = $(a, b) => a + b
handler: Function[(string), void] = $(msg) => { print(msg) }
// Function that returns a function
$ makeAdder(x: number): Function[(number), number] {
return $(y) => x + y
}
add5 = makeAdder(5)
print(add5(3)) // 8
// Function that takes a function
$ map(arr: Array[number], fn: Function[(number), number]): Array[number] {
result = []
for (item in arr) {
result.push(fn(item))
}
return result
}
doubled = map([1, 2, 3], $(x) => x * 2) // [2, 4, 6]
class Person {
// Constructor (always named 'init')
$ init(name: string, age: number) {
// Direct property initialization (no 'this.' needed)
name = name
age = age
// Or with explicit this
this.id = generateId()
}
// Methods
$ greet(): string {
return "Hi, I'm " + this.name
}
$ haveBirthday(): void {
this.age += 1
}
$ toString(): string {
return "Person(" + this.name + ", " + this.age + ")"
}
}
// Create instance
person = Person("Alice", 25)
bob: Person = Person("Bob", 30)
// Access properties
print(person.name) // "Alice"
print(person.age) // 25
// Call methods
greeting = person.greet()
person.haveBirthday()
print(person.age) // 26
// Simple object
point = {
"x": 10,
"y": 20
}
// Nested object
config = {
"server": {
"host": "localhost",
"port": 8080
},
"debug": true,
"timeout": 30
}
// Access properties
print(point["x"]) // 10
print(config["server"]) // Object
print(config["debug"]) // true
// Modify properties
point["x"] = 15
config["timeout"] = 60
// Dot notation (for identifiers)
person.name
person.age
// Bracket notation (for any expression)
person["name"]
key = "age"
person[key]
// Nested access
config["server"]["port"]
// Simple if
if (x > 0) {
print("Positive")
}
// If-else
if (score >= 60) {
print("Pass")
} else {
print("Fail")
}
// If-else-if chain
if (score >= 90) {
grade = "A"
} else if (score >= 80) {
grade = "B"
} else if (score >= 70) {
grade = "C"
} else if (score >= 60) {
grade = "D"
} else {
grade = "F"
}
// Single expression
status = if (age >= 18) "adult" else "minor"
// Basic while
i = 0
while (i < 10) {
print(i)
i += 1
}
// While with break
while (true) {
input = readLine()
if (input == "quit") {
break
}
print("You said: " + input)
}
// While with continue
i = 0
while (i < 10) {
i += 1
if (i % 2 == 0) {
continue
}
print(i) // Only odd numbers
}
// C-style for loop
for (i = 0; i < 10; i += 1) {
print(i)
}
// For with complex increment
for (i = 1; i <= 100; i *= 2) {
print(i) // 1, 2, 4, 8, 16, 32, 64
}
// For-in loop (arrays)
numbers = [1, 2, 3, 4, 5]
for (num in numbers) {
print(num * num)
}
// For-in loop (strings)
for (char in "hello") {
print(char) // h, e, l, l, o
}
// Simple pattern matching
result = match value {
0 => "zero",
1 => "one",
2 => "two",
_ => "other"
}
// Match with blocks
message = match errorCode {
404 => {
log("Not found")
"Resource not found"
},
500 => {
log("Server error")
"Internal server error"
},
_ => "Unknown error"
}
// Pattern matching with guards
category = match age {
n if n < 13 => "child",
n if n < 20 => "teenager",
n if n < 60 => "adult",
_ => "senior"
}
// Complex guards
result = match score {
s if s == 100 => "Perfect!",
s if s >= 90 && s < 100 => "Excellent",
s if s >= 80 => "Good",
s if s >= 60 => "Pass",
_ => "Fail"
}
// Planned for future releases
// response = match value {
// String s => "String: " + s,
// Number n => "Number: " + n,
// Array a => "Array of " + a.length + " items",
// _ => "Unknown type"
// }
// Array literal
numbers = [1, 2, 3, 4, 5]
mixed = ["hello", 42, true, null]
empty = []
// Typed arrays
scores: Array[number] = [95, 87, 92]
names: Array[string] = ["Alice", "Bob"]
matrix: Array[Array[number]] = [[1, 2], [3, 4]]
arr = [1, 2, 3]
// Adding elements
arr.push(4) // Add to end: [1, 2, 3, 4]
arr.unshift(0) // Add to start: [0, 1, 2, 3, 4]
// Removing elements
last = arr.pop() // Remove from end: 4
first = arr.shift() // Remove from start: 0
// Searching
found = arr.includes(2) // true
index = arr.indexOf(2) // 1
notFound = arr.indexOf(5) // -1
// Slicing
subset = arr.slice(1, 3) // [2, 3]
all = arr.slice(0) // Copy entire array
// Length
size = arr.length // Property, not method
numbers = [1, 2, 3, 4, 5]
// For-in loop
for (num in numbers) {
print(num)
}
// Manual iteration
for (i = 0; i < numbers.length; i += 1) {
print("Index " + i + ": " + numbers[i])
}
// Functional style (with custom forEach)
$ forEach(arr, fn) {
for (item in arr) {
fn(item)
}
}
forEach(numbers, $(n) => { print(n * n) })
// Creation
person = {
"name": "Alice",
"age": 30,
"city": "Seattle"
}
// Access
name = person["name"]
age = person["age"]
// Modification
person["age"] = 31
person["email"] = "[email protected]"
// Check existence (manual)
hasPhone = person["phone"] != null
// Iteration (planned feature)
// for (key in person.keys()) { ... }
// Import specific items
import { calculate, format } from "./utils"
import { Person, Company } from "./models"
// Import with different path styles
import { helper } from "../lib/helper"
import { config } from "./config"
// Export variables
export PI = 3.14159
export @immut VERSION = "1.0.0"
// Export functions
export $ calculate(x: number, y: number): number {
return x * y + y
}
// Export classes
export class User {
$ init(name: string) {
name = name
}
}
// Re-export imported items
import { someFunction } from "./other"
export someFunction
// math.thorn
export @immut PI = 3.14159
export @immut E = 2.71828
export $ square(x: number): number {
return x * x
}
export $ cube(x: number): number {
return x * x * x
}
// main.thorn
import { PI, square } from "./math"
area = PI * square(5)
print("Area: " + area)
// Single-line comment
x = 42 // End-of-line comment
/*
* Multi-line comment
* Can span multiple lines
* Useful for documentation
*/
/* Inline multi-line */ x = 42
// TODO: Add feature
// FIXME: Handle edge case
// NOTE: This is important
The following words are reserved and cannot be used as identifiers:
break |
class |
continue |
else |
export |
false |
for |
if |
import |
match |
null |
return |
this |
true |
while |
@immut |
In ThornLang, only null
and false
are falsy:
// Falsy values
if (null) { } // Won't execute
if (false) { } // Won't execute
// Truthy values (different from JavaScript!)
if (0) { } // WILL execute
if ("") { } // WILL execute
if ([]) { } // WILL execute
if ({}) { } // WILL execute
// String concatenation with +
"Hello " + 123 // "Hello 123"
"Value: " + true // "Value: true"
// No implicit numeric coercion
// "5" - 3 // Error: Operands must be numbers
// "5" * 2 // Error: Operands must be numbers
Currently, ThornLang uses runtime exceptions. Future versions will support Result types:
// Current approach
$ safeDivide(a: number, b: number): number {
if (b == 0) {
print("Error: Division by zero")
return null
}
return a / b
}
// Future approach (Issue #38)
// $ divide(a: number, b: number): Result[number, string] {
// if (b == 0) {
// return Err("Division by zero")
// }
// return Ok(a / b)
// }
- Type System - Detailed type system documentation
- Built-in Functions - Complete list of built-in functions
- Performance Guide - Writing efficient Thorn code
- Examples - Sample programs demonstrating features