Language Reference - ThornLang/JavaThorn GitHub Wiki

ThornLang Language Reference

This is the complete reference for the ThornLang programming language syntax and features.

Table of Contents

  1. Lexical Structure
  2. Variables and Constants
  3. Data Types
  4. Operators
  5. Functions
  6. Classes and Objects
  7. Control Flow
  8. Pattern Matching
  9. Arrays and Collections
  10. Modules
  11. Comments
  12. Reserved Keywords

Lexical Structure

Identifiers

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

Line Termination

Statements are terminated by semicolons.

x = 42;
y = "hello";
print(x);

Variables and Constants

Variable Declaration

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]

Immutable Variables

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!

Variable Scope

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

Data Types

Primitive Types

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

Composite Types

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")

Type Checking

// Runtime type checking
x: number = 42      // OK
y: string = 42      // Runtime error!

// Type inference
z = 42              // Inferred as number
w = "hello"         // Inferred as string

Operators

Arithmetic Operators

Operator Description Example
+ Addition/Concatenation 5 + 3, "a" + "b"
- Subtraction 10 - 4
* Multiplication 6 * 7
/ Division 15 / 3
% Modulo 10 % 3
** Exponentiation 2 ** 8

Comparison Operators

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

Logical Operators

Operator Description Example
&& Logical AND x && y
|| Logical OR x || y
! Logical NOT !x
?? Null coalescing x ?? "default"

Assignment Operators

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

Operator Precedence

From highest to lowest:

  1. Member access (.), calls (()), indexing ([])
  2. Unary (-x, !x)
  3. Exponentiation (**)
  4. Multiplication, division, modulo (*, /, %)
  5. Addition, subtraction (+, -)
  6. Comparison (<, >, <=, >=)
  7. Equality (==, !=)
  8. Logical AND (&&)
  9. Logical OR (||)
  10. Null coalescing (??)
  11. Assignment (=, +=, etc.)

Functions

Function Declaration

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
}

Lambda Functions

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

Function Types

// 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) }

Higher-Order Functions

// 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]

Classes and Objects

Class Definition

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 + ")"
    }
}

Creating Instances

// 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

Object Literals

// 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

Property Access

// Dot notation (for identifiers)
person.name
person.age

// Bracket notation (for any expression)
person["name"]
key = "age"
person[key]

// Nested access
config["server"]["port"]

Control Flow

If-Else Statements

// 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"

While Loops

// 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
}

For Loops

// 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
}

Pattern Matching

Basic Match

// 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"
}

Guards

// 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"
}

Type Patterns (Future Feature)

// Planned for future releases
// response = match value {
//     String s => "String: " + s,
//     Number n => "Number: " + n,
//     Array a => "Array of " + a.length + " items",
//     _ => "Unknown type"
// }

Arrays and Collections

Array Creation

// 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]]

Array Methods

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

Array Iteration

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) })

Dictionaries/Objects

// 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()) { ... }

Modules

Import Statements

// 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 Statements

// 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

Module Best Practices

// 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)

Comments

// 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

Reserved Keywords

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

Special Considerations

Truthiness

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

Type Coercion

// 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

Error Handling

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)
// }

See Also

⚠️ **GitHub.com Fallback** ⚠️