Skip to content

Latest commit

 

History

History
540 lines (414 loc) · 19 KB

File metadata and controls

540 lines (414 loc) · 19 KB

Lambda Script Language Reference

Table of Contents

  1. Introduction
  2. Language Overview
  3. Documentation Guide
  4. Modules and Imports
  5. Error Handling
  6. Examples
  7. Language Philosophy

Introduction

Lambda Script is a general-purpose, cross-platform, pure functional scripting language designed for data processing and document presentation. Built from scratch in C with JIT compilation using MIR (Medium Internal Representation) and reference counting memory management.

Key Features

  • Pure Functional: Immutable data structures and functional programming paradigms
  • JIT Compilation: Near-native performance through MIR-based compilation
  • Cross-Platform: Runs on macOS, Linux, Windows with consistent behavior
  • Rich Type System: Strong typing with type inference and advanced type constructs
  • Document Processing: Built-in support for 12+ input formats and multiple output formats
  • Unicode Support: Configurable Unicode support with ICU integration
  • Memory Safe: Reference counting and pool-based memory management

Language Overview

Lambda Script is designed around functional programming principles with modern syntax. Programs consist of expressions that evaluate to values, with support for:

  • Immutable data structures (arrays, maps, elements)
  • First-class functions and closures
  • Pattern matching and destructuring
  • Comprehensive type system with inference
  • Built-in document processing capabilities

Philosophy

  1. Data as Code: Documents and data structures are first-class citizens
  2. Type Safety: Compile-time type checking prevents runtime errors
  3. Expressiveness: Concise syntax for complex data transformations
  4. Performance: JIT compilation for production-ready performance

Documentation Guide

The Lambda language documentation is organized into focused sub-documents for easier navigation and maintenance:

Core Documentation

Document Description
Lambda_Syntax.md Syntax Fundamentals — Comments, identifiers, names, symbols, namespaces
Lambda_Data.md Literals and Collections — Primitive types, path literals, arrays, maps, elements, ranges, and data composition expressions
Lambda_Type.md Type System — First-class types, type hierarchy, union types, function types, type patterns, and string patterns
Lambda_Expr_Stam.md Expressions and Statements — Arithmetic, comparisons, logical operations, pipe expressions, query expressions (? .? [T]), control flow, and operators
Lambda_Func.md Functions — Function declarations, parameters, closures, higher-order functions, and procedural functions (fn and pn)
Lambda_Procedural.md Procedural Programming — Mutable variables, assignment, while loops, I/O module, pn functions, and main() entry point
Lambda_Error_Handling.md Error Handling — Error types, raise keyword, ^ propagation, the ^ { } handler, compile-time enforcement

Reference Documentation

Document Description
Lambda_Formal_Semantics.md Formal Semantics (normative ADR) — the core principles and decided semantics: value domain, truthiness, numerics, equality, total sort order, absence/errors, mutable value semantics, operators, metaprogramming. The authority when documentation or implementation disagree
Lambda_CLI.md CLI Reference — Commands, flags, and usage for the Lambda command-line interface
Lambda_Sys_Func.md System Functions — Complete reference for all built-in functions (type, math, string, collection, I/O, date/time)
Lambda_Validator_Guide.md Validation — Schema-based validation for data structures

Developer Documentation

Document Description
Developer_Guide.md Developer Guide — Build from source, dependencies, testing, Tree-sitter grammar, MIR JIT
Lamdba_Runtime.md Lambda Runtime — Runtime internals and architecture

Quick Reference

Data Types (see Lambda_Data.md)

Type Description Example
int Integer, float64 safe range ±(2⁵³−1) 42, -123
float 64-bit floating point 3.14, 1e-10
i8 i16 i32 Sized signed integers 42i8, 1000i16, 100i32
u8 u16 u32 Sized unsigned integers 255u8, 60000u16
i64 Alias for int64 100i64
u64 64-bit unsigned integer 1000u64
f16 f32 Sized floating point 0.5f16, 3.14f32
f64 Alias for float 2.7f64
string UTF-8 text "hello"
symbol Interned identifier 'json'
bool Boolean true, false
path File path or URL /etc.hosts, https.api.com
array Ordered collection [1, 2, 3]
int[] Typed int array var a: int[] = [1, 2]
float[] Typed float array var b: float[] = [0.1]
map Key-value mapping {name: "Alice"}
object Nominally-typed map with methods <Point x: 1, y: 2>
element Markup element <div "content">

"..." creates a string; '...' creates a symbol. They are different types, so 'json' == "json" is false; convert explicitly with string(...) or symbol(...) when needed.

The empty string "" is a real string value with length 0 and is falsy. The empty symbol literal '' is invalid; use null for absence.

Type System (see Lambda_Type.md)

// Type annotations
let x: int = 42
let items: string[] = ["a", "b"]

// Sized numeric type annotations
let a: i8 = 42i8
let b: u32 = 255u32
let c: f32 = 3.14f32

// Typed array annotations
var arr: int[] = [1, 2, 3]     // Native int array
var data: float[] = [0.1, 0.2] // Native float array

// Union types
int | string           // Either int or string
int?                   // Nullable (int | null)

// Type declarations
type User = {name: string, age: int}
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"

// Object types (nominally-typed maps with methods)
type Point {
    x: float, y: float,
    fn distance(other: Point) => math.sqrt((x - other.x)**2 + (y - other.y)**2)
}
type Circle : Point { radius: float }    // Inheritance
let p = <Point x: 3.0, y: 4.0>           // Object literal
p.distance(<Point x: 0.0, y: 0.0>)       // Method call
p is Point                                // true (nominal)

Expressions (see Lambda_Expr_Stam.md)

Kind Expression Result / meaning
Pipe [1, 2, 3] | ~ * 2 [2, 4, 6]
users | ~.name that (len(~) > 3) filter and transform
Pipe/filter spread [1, [2, 3] | ~, 4, 5] [1, 2, 3, 4, 5]
[0, items that (~ > 3), 9] flattened into the enclosing array
Query — recursive html?<img> all <img> at any depth
html?<div class: string> <div> with a class attribute
data?int all int values in the tree
el.?<div> self-inclusive query
Query — child-level el[element] direct child elements
el[string] attribute values + text children
html[body]?<p> child then recursive (given type body = <body>)
For expression (for (x in [1,2,3] where x > 1 order by x desc) x * 2) clauses: let / where / group by / order by / limit / offset
(for (x in sales group by x.region into g) {region: g.region, n: len(g)}) each group g is a <group> element — keys become attributes, members children
(for (o in orders, c in customers on o.cust_id == c.id) {id: o.id, name: c.name}) equi-join; c? = left join, c is null on no match
If if (x > 0) "positive" else "negative"
if x > 0 { compute(x) } else "default" block form, expression else
// String patterns (see Lambda_Type.md § String Patterns)
type digits = \(d+)
type email = \(w+ "@" w+ "." a[2,6])
type ident = \symbol(a w*)       // \symbol(...) matches symbols, \(...) strings
"123" is digits                  // true (full-match)
"12x" is \(d+)                   // false — patterns work inline too
match input {
    case digits: "number"
    default: "other"
}

// Pattern-aware string functions
find("a1b22", digits)            // [{value: "1", index: 1}, {value: "22", index: 3}]
replace("a1b2", digits, "N")    // "aNbN"
split("a1b2", digits)           // ["a", "b", ""]

Functions (see Lambda_Func.md)

// Pure function
fn add(a: int, b: int) => a + b

// Procedural function with mutation
pn process() {
    var x = 42
    x = 3.14             // type widening (int → float)
    let obj = {a: 1}
    obj.a = "hello"       // map field type change
}

// Typed array parameters for native array access
pn advance(pos: float[], vel: float[], dt: float) {
    for i in range(0, len(pos)) {
        pos[i] = pos[i] + vel[i] * dt   // native float ops
    }
}

// Closures capture read-only snapshots; use var params for inout mutation
pn bump_first(var xs: any[]) {
    xs[0] = 42
}

pn main() {
    var xs: any[] = [1, 2, 3]
    bump_first(xs)
    print(xs[0])   // 42
}

Concurrency

pn concurrency is colorless: suspending callees do not add syntax to their callers. Under S13.1.1v2, the builtin start(target, args = [], options = {}) launches a scoped child and returns a task handle; args is an array and options.mode is 'task', 'thread', or 'process' (only task mode is implemented currently).

pn worker() {
    let message = receive()^
    return "done: " ++ message
}

pn main() {
    let handle = start(worker)
    send(handle, "job")^
    print(wait(handle)^)
}

The task operations are send, receive, wait, select, sleep, self, and cancel. Mailboxes are bounded FIFO queues. A normal lexical-block exit joins non-escaped children; an error exit cancels and joins them. A started procedure cannot capture an outer mutable var; use an immutable let snapshot or messages. wait(handle, timeout: ms) does not cancel the target.

JavaScript Promises and Lambda task handles share the same libuv loop. Lambda can wait on an imported Promise; JavaScript sees every exported Lambda pn as a Promise-returning function. toPromise(handle) is the explicit handle adapter.


Modules and Imports

Import Statements

// Relative import — resolved relative to the importing script's directory
import .relative_module
import .path.to.module

// Absolute import — resolved relative to CWD/project root
import module_name

// Import with alias
import alias: .module
import my_utils: .utilities

Import resolution:

  • .module (dot prefix) — resolved relative to the importing script's directory. Nested imports work correctly: if A.ls imports B.ls and B.ls imports C.ls, C.ls resolves relative to B.ls's directory.
  • module (no dot) — resolved relative to the current working directory / project root.
  • Paths are normalized via realpath() to prevent redundant compilation when the same file is imported through different relative paths.

Module Structure

Each Lambda Script file is a module that can export public declarations — variables, functions, procedures, type aliases, and object types:

// math_utils.ls

// Public values
pub let PI = 3.14159
pub let E = 2.71828

// Public functions
pub fn square(x: float) => x * x
pub fn cube(x: float) => x * x * x

// Public type alias
pub type Angle = float

// Public object type with methods
pub type Vec2 {
    x: float = 0.0, y: float = 0.0,
    fn len() => math.sqrt(x**2 + y**2)
    fn scale(f) => <Vec2 x: x*f, y: y*f>
}

// Public, with the error handled at the binding
pub let config = input("config.json", 'json') ^ { {} }

// Private (not exported)
let v = 123
fn helper(x: float) => x + 1
type Internal = {a: int, b: int}

Using Imported Modules

// main.ls
import .math_utils

// Imported values and functions are available directly
let area = PI * square(radius)

// Imported types can be used for annotations, construction, and type checks
let angle: Angle = 1.57
let v = <Vec2 x: 3.0, y: 4.0>
v.len()          // 5.0
v is Vec2        // true

Export Visibility

Declaration Visibility
pub x = ... Public (exported)
pub fn f() / pub pn p() Public function/procedure
pub type T = ... Public type alias
pub type T { ... } Public object type
let x = ... Private (module-local)
fn f() / pn p() Private function/procedure
type T = ... / type T { ... } Private type

Built-in Module Imports

Lambda provides built-in modules (math, io) for mathematical functions and file system operations. These modules support three import styles:

// 1. No import — use full module prefix (default, always available)
math.sqrt(16)         // 4
math.pi               // 3.1415926536
io.copy(\.a, \.b)

// 2. Global import — all functions available without prefix
import math;
sqrt(16)              // 4
pi                    // 3.1415926536
sin(0)                // 0

// 3. Aliased import — use a custom prefix
import m:math;
m.sqrt(16)            // 4
m.pi                  // 3.1415926536
m.sin(0)              // 0
Style Syntax Usage Best For
No import (none) math.sqrt(x) Clarity, avoiding name conflicts
Global import import math; sqrt(x) Math-heavy scripts, brevity
Aliased import import m:math; m.sqrt(x) Short prefix, avoiding conflicts

Error Handling

Lambda uses an error-as-return-value paradigm — no try/throw/catch exceptions. Functions declare error return types with T^E syntax, raise errors with the raise keyword, and callers must explicitly handle errors with the ^ propagation operator or the ^ { … } handler. Ignoring an error is a compile-time error.

// Function that may fail
fn divide(a, b) int^ {
    if (b == 0) raise error("division by zero")
    else a / b
}

// Propagate error with ^
let result = divide(10, x)^

// Or handle it locally — `~` is the error
let result = divide(10, x) ^ {
    print("error: " ++ ^.message)
    0
}

Full documentation: See Lambda_Error_Handling.md for the complete guide — error types, raise, ^ operator, the ^ { } handler, enforcement rules, error codes, and examples.


Examples

Basic Data Processing

// Read and process JSON data
let data = input("sales.json", 'json');

// Calculate total sales
let total = data.sales |> ~.amount |> sum;

// Filter high-value sales
let high_value = data.sales where ~.amount > 1000;

// Summarize by region — each group `g` is a <group> element
// (grouping key becomes an attribute, members become children)
let by_region = for (s in data.sales group by s.region into g)
    {region: g.region, total: sum(g |> ~.amount), count: len(g)};

// Generate report
let report = {
    total_sales: total,
    high_value_count: len(high_value),
    by_region: by_region,
    average: total / len(data.sales),
    timestamp: datetime()
};

print(format(report, 'json'))

Document Processing

// Parse Markdown document
let doc = input("article.md", 'markdown');

// Query for all headings using type-based search
let headings = doc?(h1 | h2) | ~.content;

// Generate table of contents
let toc = <div class: "toc",
    <h2 "Table of Contents">
    <ul
        for (heading in headings) <li <a href: "#" ++ heading, heading>>
    >
>;

print(format(toc, 'html'))

Mathematical Computation

// Recursive functions
fn factorial(n: int) int {
    if (n <= 1) 1
    else n * factorial(n - 1)
}

fn fibonacci(n: int) int {
    if (n <= 1) n
    else fibonacci(n - 1) + fibonacci(n - 2)
}

// Generate sequences
let factorials = (for (i in 1 to 10) factorial(i));
let fibs = (for (i in 1 to 15) fibonacci(i));

print("Factorials:", factorials)
print("Fibonacci:", fibs)

Procedural Script with Main

// script.ls - Run with: lambda.exe run script.ls

pn main() {
    print("Starting processing...")

    // Load configuration
    let config = if exists(\.config.json) {
        input(\.config.json, 'json')
    } else {
        {default: true}
    }

    // Process data
    var count = 0
    for item in config.items or [] {
        process_item(item)
        count = count + 1
    }

    // Save results
    output({processed: count, time: now()}, "./output/summary.json")

    print("Done! Processed", count, "items")
}

pn process_item(item) {
    // Processing logic here
    print("Processing:", item.name)
}

Language Philosophy and Design Principles

Functional Programming

Lambda Script embraces functional programming principles:

  1. Immutability: Data structures are immutable by default
  2. Pure Functions: Functions have no side effects (except I/O functions)
  3. Expression-Oriented: Everything is an expression that returns a value
  4. Higher-Order Functions: Functions are first-class values

Type Safety

Strong typing prevents runtime errors:

  1. Static Type Checking: Types are checked at compile time
  2. Type Inference: Types are automatically inferred when possible
  3. Explicit Types: Optional type annotations for clarity
  4. Error Types: Errors are explicit values, not exceptions

Performance

JIT compilation provides excellent performance:

  1. MIR Backend: Uses MIR for efficient code generation
  2. Memory Pools: Efficient memory allocation strategies
  3. Reference Counting: Automatic memory management without GC pauses
  4. Structural Sharing: Efficient copying of immutable data

Expressiveness

Concise syntax for complex operations:

  1. Collection Comprehensions: Powerful for-expressions for data processing
  2. Pipe Expressions: Fluent data transformation pipelines
  3. Query Expressions: jQuery-style search with ? (descendants), .? (self-inclusive), and [T] (child-level)
  4. Pattern Matching: Type-based pattern matching with is
  5. Document Processing: Built-in support for markup and data formats