# Langoost — Full documentation > Langoost is a statically-typed scripting language that compiles to bytecode and runs on a fast stack-based virtual machine. Built for backend scripting — like PHP, but a long-running process serves every request with no cold start, and a batteries-included standard library is built in. Status: Open source · in active development Canonical site: https://langoost.dev File extension: .goost · Toolchain: langoost This file concatenates the full Langoost documentation and examples for use by language models and other automated readers. ================================================================ DOCUMENTATION ================================================================ # What is Langoost? **Langoost** is a statically-typed scripting language that compiles to bytecode and runs on a fast, stack-based virtual machine. It is designed for **backend scripting** — similar in spirit to PHP, but without the per-request cold-start cost. A single long-running process handles every request, and each request executes in its own isolated VM with a private stack. Source files use the **`.goost`** extension. ```goost let name: string = "world" print("Hello, " + name + "!") ``` ``` $ langoost run hello.goost Hello, world! ``` ## Why Langoost - **No cold start.** The process stays warm. Compiled modules are cached, so imports after the first are a map lookup — not a recompile. - **Familiar, optional typing.** Python-like simplicity with TypeScript-style type annotations (`let x: int = 1`). Annotations are documentation today and enforced incrementally. - **Batteries included.** A large standard library ships in the box: `strings`, `math`, `json`, `yaml`, `xml`, `http`, `net`, `crypto`, `io`, `exec`, `collections`, `thread`, and more. - **Safe concurrency by construction.** Each HTTP request runs in its own VM with a private stack and output buffer. Scripts from different requests never share mutable state. - **Inspectable.** Compile to bytecode and disassemble it with one command to see exactly what runs. ## How it runs Langoost is a bytecode-compiled interpreter. Each script flows through a small, predictable pipeline: ``` Source → Lexer → Parser → Compiler → Bytecode → VM → Result ``` See **[architecture](/docs/architecture)** for the bytecode format, stack model, and module cache. ## Where to go next - **[Install & run](/docs/installation)** — build from source and run your first script. - **[Language tour](/docs/language-tour)** — the syntax in a few minutes. - **[Standard library](/docs/stdlib)** — what's available out of the box. - **[HTTP server](/docs/http-server)** — serve requests with a handler function. - **[Examples](/examples)** — complete, runnable programs. --- # Install & run Langoost is in active development. Today you build it from source with the Go toolchain; prebuilt binaries are planned for a later release. ## Prerequisites - [Go](https://go.dev/dl/) 1.21 or newer ## Build from source ``` $ git clone $ cd langoost $ go build -o langoost . ``` This produces a single `langoost` binary in the current directory. > During development you can skip the build step and run directly with > `go run . run script.goost`. ## Your first script Create **hello.goost**: ```goost let name: string = "world" print("Hello, " + name + "!") ``` Run it: ``` $ ./langoost run hello.goost Hello, world! ``` ## The REPL Start an interactive session to experiment line by line: ``` $ ./langoost repl > let x = 21 > print(x * 2) 42 ``` Press `Ctrl+D` to exit. ## Server mode Langoost can run as a persistent execution server — the model it's designed for. The process stays warm and serves requests with no cold start: ``` $ ./langoost server --port 8080 ``` | Flag | Default | Description | | --- | --- | --- | | `--port` | `8080` | Port to listen on | | `--timeout` | `30s` | Max execution time per request | | `--dir` | `.` | Directory to search for `.goost` files | To build an HTTP API directly from a script instead, see the **[HTTP server](/docs/http-server)** guide. ## Inspect the bytecode Curious what the compiler produces? Disassemble any script: ``` $ ./langoost disasm hello.goost ``` Next: take the **[language tour](/docs/language-tour)**. --- # Language tour A quick pass over the core syntax. Each topic has a dedicated page with more detail; follow the links as you go. ## Comments & statements Single-line comments use `//` (there are no block comments). Statements are separated by newlines or semicolons; newlines inside `(...)` and `[...]` are ignored, so expressions can span lines. ```goost // this is a comment ``` ## Variables Declare with `let`; the type annotation is optional and inferred from the value: ```goost let name = "alice" // inferred string let age: int = 30 // explicit annotation let active: bool = true ``` Reassign with `=`, `+=`, or `-=`. Variables are block-scoped. See **[types & values](/docs/types)** for the full type list and number literals (`0xFF`, `0b1010`, `1_000`). ## Functions ```goost fn greet(name: string): string { return "Hello, " + name + "!" } let double = fn(x) { return x * 2 } // anonymous function ``` Top-level functions are hoisted, support recursion and variadics (`...args`), and are first-class values. See **[functions](/docs/functions)**. ## Strings Concatenate with `+`, or interpolate with backtick **template strings**: ```goost let user = "Ada" println("Hi, " + user) println(`Hi, ${user} — ${3 + 4} new messages`) ``` Strings and arrays carry methods like `.upper()`, `.split()`, `.map()` — see **[value methods](/docs/methods)**. ## Operators ```goost 1 + 2 - 3 * 4 / 2 % 3 // arithmetic (+ also concatenates strings) a == b a != b a < b // comparison a && b a || b !a // logical, short-circuiting a & b a | b a ^ b // bitwise and / or / xor a << n a >> n ~a // shift left / right, bitwise not ``` ## Control flow `if / else if / else`, `while`, and `for..in` (over arrays or `range`), with `break` and `continue`: ```goost for i in range(10) { if i == 3 { continue } if i == 7 { break } println(i) } ``` More in **[control flow](/docs/control-flow)**. ## Arrays & objects ```goost let nums = [1, 2, 3] nums = append(nums, 4) let evens = nums.filter(fn(n) { return n % 2 == 0 }) let user = {name: "alice", age: 30} println(user.name) ``` See **[arrays](/docs/arrays)** and **[classes & types](/docs/classes)**. ## Classes & errors Langoost has classes with single inheritance, runtime-checked interfaces (`is` / `as`), and `try` / `catch` / `throw` / `finally` / `defer` for error handling: ```goost class Dog extends Animal { fn speak() { return "woof" } } try { risky() } catch err { println("failed: " + toString(err)) } finally { cleanup() } ``` See **[classes & types](/docs/classes)** and **[error handling](/docs/error-handling)**. ## Imports ```goost import strings import { abs, sqrt } from "math" strings.upper("hi") // "HI" println(sqrt(16.0)) // 4 ``` See **[modules & imports](/docs/modules)** and the **[standard library](/docs/stdlib)** — strings, http, json, crypto, sql, and more. ## Built-in functions Always in scope without an import: `print`, `println`, `len`, `toString`, `toInt`, `toFloat`, `range`, `typeof`, `append`, `read`, `exit`. --- # Types & values Langoost has a small set of value types. Type annotations are **optional** and serve as documentation and editor hints while the static checker matures; values themselves are strongly typed at runtime. ## Value types | Type | Description | Example | | --- | --- | --- | | `int` | 64-bit signed integer | `42`, `-7`, `0` | | `float` | 64-bit IEEE-754 double | `3.14`, `-0.5`, `1e10` | | `bool` | Boolean | `true`, `false` | | `string` | UTF-8 text | `"hello"`, `'world'` | | `array` | Ordered list of any values | `[1, "two", true]` | | `object` | Key/value structure | `{x: 10, y: 20}` | | `void` | Absence of a value (`nil`) | `nil` | | `any` | Accepts any type, unchecked | — | | `json` / `yaml` / `xml` | String annotated as that data format | `{key: "val"}` | Array types use a `[]` suffix on the element type: `int[]`, `string[]`. ## Number literals Integers can be written in several bases, with `_` as a digit separator; floats support scientific notation: ```goost let dec = 1_000_000 let hex = 0xDEAD_BEEF let bin = 0b1010 let oct = 0o755 let f = 1.5e-3 ``` ## Strings Single and double quotes are both accepted, with escapes `\n`, `\t`, `\r`, `\"`, `\'`, `\\`, `\0`: ```goost let a = "hello" let b = 'world' ``` ### Template strings Backtick strings interpolate embedded expressions with `${...}`: ```goost let name = "Ada" println(`Hello, ${name} — you have ${3 + 4} messages`) ``` ## Objects Object literals map keys to values. Keys can be read with dot or bracket access; brackets also allow dynamic keys: ```goost let user = {name: "alice", age: 30} println(user.name) // "alice" println(user["age"]) // 30 user["role"] = "admin" // add / update a field ``` Objects double as maps. To attach a named type, see **[classes & types](/docs/classes)**. ## nil and negative indexing `nil` is the empty/void value. Arrays and strings support Python-style negative indices: ```goost let xs = [10, 20, 30] println(xs[-1]) // 30 (last element) let missing = nil ``` ## Conversion Coercion is explicit: ```goost let n: int = toInt("42") let f: float = toFloat(n) let s: string = toString(n) println(typeof(n)) // "int" ``` The `json`, `yaml`, and `xml` types are strings with special meaning — see the **[stdlib `json` module](/docs/stdlib/json)** and the `: json` inline-literal syntax. --- # Functions ## Declaration Functions are declared with `fn`. Parameter and return-type annotations are optional but recommended: ```goost fn greet(name: string): string { return "Hello, " + name + "!" } fn add(a, b) { // annotations omitted return a + b } ``` A function with no return type returns `void`. `return` with no value, or falling off the end, returns `void`. ## Anonymous functions & closures Functions are first-class values. Write an anonymous function with `fn(...)` and assign it, pass it, or return it. Closures capture variables from the enclosing scope: ```goost let double = fn(x) { return x * 2 } println(double(21)) // 42 fn makeAdder(n: int) { return fn(x) { return x + n } // captures n } let add10 = makeAdder(10) println(add10(5)) // 15 ``` This is the form you pass to array methods and to APIs like `thread.core.spawn`: ```goost let evens = [1, 2, 3, 4].filter(fn(n) { return n % 2 == 0 }) ``` ## Variadic parameters & spread A final parameter prefixed with `...` collects any extra arguments into an array: ```goost fn sum(...nums) { let total = 0 for n in nums { total += n } return total } println(sum(1, 2, 3, 4)) // 10 ``` The `...` spread operator also expands an array into positional arguments at a call site: ```goost let args = [1, 2, 3] println(sum(...args)) // 10 ``` ## Hoisting & recursion Top-level function declarations are hoisted, so a function can be called before it appears in the file, which also makes mutual recursion straightforward: ```goost println(fib(10)) // works — fib is defined below fn fib(n: int): int { if n <= 1 { return n } return fib(n - 1) + fib(n - 2) } ``` Functions can also be nested inside other functions. ## Notes - There are **no default or named parameters** — pass every argument positionally (or use a variadic / an object). - Functions return a single value; to return several, return an array or object. Continue to **[value methods](/docs/methods)** for the methods available on strings, arrays, and numbers. --- # Value methods Langoost values carry built-in methods, called with dot syntax — `value.method(args)`. This page lists the methods on each primitive type. (Array methods are covered in full on the **[arrays](/docs/arrays)** page.) ## String methods ```goost let s = " Hello, World " println(s.trim().lower()) // "hello, world" ``` | Method | Description | | --- | --- | | `len` | Length in bytes | | `upper()` / `lower()` | Change case | | `trim([cutset])` / `trimLeft([cutset])` / `trimRight([cutset])` | Trim whitespace or a cutset | | `split(sep)` | Split into a `string[]` | | `contains(sub)` | Whether `sub` occurs | | `hasPrefix(p)` / `hasSuffix(s)` | Prefix / suffix test | | `startsWith(p)` / `endsWith(s)` | Aliases for the above | | `replace(old, new)` | Replace all occurrences | | `replaceFirst(old, new)` | Replace the first occurrence | | `indexOf(sub)` / `lastIndexOf(sub)` | First / last index (−1 if absent) | | `slice(start, end)` | Substring `[start, end)` (negative indices allowed) | | `repeat(n)` | Repeat `n` times | | `padLeft(width, fill?)` / `padRight(width, fill?)` | Pad to a width | | `format(...args)` | sprintf-style (`%s`, `%d`, `%f`, `%v`) | | `toInt()` / `toFloat()` | Parse to a number (0 on failure) | | `isEmpty()` | Whether the string is empty | ## Array methods Arrays support `push`, `pop`, `first`, `last`, `slice`, `contains`, `indexOf`, `join`, `reverse`, `sort`, `flatten`, and the higher-order `map`, `filter`, `reduce`, `forEach`, `find`, `findIndex`, `every`, and `some`. See **[arrays](/docs/arrays)** for signatures and examples. ## Number methods ```goost println((255).toBase(16)) // "ff" println((-3).abs()) // 3 println((3.9).toInt()) // 3 ``` **Int:** `toString()`, `toFloat()`, `toBase(base)`, `abs()` **Float:** `toString()`, `toInt()` (truncates), `abs()` **Bool:** `toString()` Many of these overlap with the global built-ins (`toString`, `toInt`, `toFloat`) and the **[`strings`](/docs/stdlib/strings)** and **[`types`](/docs/stdlib/types)** modules — use whichever reads best. --- # Classes & types Langoost supports objects, named types, and classes with single inheritance. ## Objects The simplest structured value is an object literal — keys mapped to values, accessed with dot or bracket notation: ```goost let point = {x: 3, y: 4} println(point.x) // 3 point["y"] = 5 // update, or add a new key ``` Keys may also be written as string literals, which is handy for keys that aren't identifiers (e.g. building queries): ```goost let update = {"$set": {status: "active"}} ``` Objects are also Langoost's map type. See **[types & values](/docs/types)**. ## Type declarations Give an object shape a name with `type`. This documents the structure and lets you construct values with `Name{...}`: ```goost type User = { id: int, name: string, email: string, } let u = User{id: 1, name: "alice", email: "alice@example.com"} println(u.name) // "alice" ``` Types describe structure; fields are not enforced at runtime (duck typing). ## Classes A `class` groups methods. Construct an instance with `ClassName{...}` (setting any fields), then call its methods with dot syntax: ```goost class Counter { fn increment() { self.count += 1 return self.count } fn value() { return self.count } } let c = Counter{count: 0} c.increment() c.increment() println(c.value()) // 2 ``` Inside a method, `self` refers to the instance. ## Inheritance A class can `extend` one parent and call the parent's implementation with `super`: ```goost class Animal { fn speak() { return "..." } fn describe() { return "I say " + self.speak() } } class Dog extends Animal { fn speak() { return "woof" } } let d = Dog{} println(d.describe()) // "I say woof" ``` ## Interfaces An `interface` names a set of methods a value must provide. Interfaces are checked at runtime with the `is` and `as` operators: ```goost interface Reader { fn read() fn close() } fn consume(x) { if x is Reader { // → bool let r = x as Reader // → x, or throws if it doesn't satisfy Reader process(r.read()) r.close() } } ``` `x is Reader` returns a bool; `x as Reader` returns the value if it satisfies the interface, or throws `{kind: "InterfaceMismatch", message, want, got}` otherwise. The check walks a class's parent chain. (Generics are deferred — without static type checking they would be cosmetic.) ## Notes Langoost classes are intentionally small. There are **no constructors** (initialize fields in the `Name{...}` literal), and no static members or visibility modifiers. For error types and recoverable failures, see **[error handling](/docs/error-handling)**. --- # Control flow ## if / else if / else ```goost if x > 10 { println("big") } else if x > 5 { println("medium") } else { println("small") } ``` Parentheses around the condition are optional. Conditions use the comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`) and logical (`&&`, `||`, `!`) operators, which short-circuit. ## while ```goost let i = 0 while i < 10 { println(i) i += 1 } ``` An infinite loop is just `while true { ... }`. ## for..in Iterate over the elements of an array: ```goost let fruits = ["apple", "banana", "cherry"] for fruit in fruits { println(fruit) } ``` Iterate over a range of integers with the built-in `range`: ```goost for i in range(5) { // 0,1,2,3,4 println(i) } for i in range(2, 7) { // 2,3,4,5,6 println(i) } ``` ## Iterating objects & custom iterators `for..in` also works on objects. If a value has an `iter()` method, it's called once to get an iterator, then the iterator's `next()` is called each round. `next()` returns `{value, done}` (or `nil` when exhausted): ```goost class Range { fn iter() { return Range{n: self.start, end: self.end} } fn next() { if self.n >= self.end { return nil // done } let v = self.n self.n += 1 return {value: v, done: false} } } for x in Range{start: 0, end: 3} { println(x) // 0, 1, 2 } ``` A plain object without `iter()` falls back to iterating its keys in sorted order. ## break and continue `break` exits the nearest enclosing loop; `continue` skips to its next iteration. Both work in `while` and `for..in` loops: ```goost for n in range(100) { if n % 2 == 1 { continue // skip odd numbers } if n > 10 { break // stop once past 10 } println(n) // 0, 2, 4, 6, 8, 10 } ``` ## Notes - There is no `switch`/`case`; use an `if / else if` chain. - There is no C-style `for (init; cond; step)` loop — only `while` and `for..in`. - For recoverable failures, see **[error handling](/docs/error-handling)** (`try` / `catch` / `throw`). Next: **[arrays](/docs/arrays)**. --- # Arrays Arrays are ordered, heterogeneous lists created with `[...]` literals. ```goost let nums: int[] = [1, 2, 3, 4, 5] let mixed = [1, "two", true, 3.14] let empty: string[] = [] ``` ## Indexing Indexing is zero-based for reading and writing, and supports Python-style negative indices: ```goost let first = nums[0] // 1 let last = nums[-1] // 5 nums[0] = 99 ``` `len(nums)` returns the element count. Newlines inside `[...]` are ignored, so literals may span multiple lines. ## append The `append` built-in returns a **new** array — reassign to keep the result: ```goost let xs = [1, 2, 3] xs = append(xs, 4) // [1, 2, 3, 4] ``` ## Array methods Arrays have a rich set of methods, called with dot syntax. Higher-order methods take an anonymous function `fn(element, index)`. | Method | Description | | --- | --- | | `push(...items)` | Append items (mutates); returns new length | | `pop()` | Remove and return the last element | | `first()` / `last()` | First / last element | | `slice(start, end)` | Sub-array `[start, end)` | | `contains(v)` / `indexOf(v)` | Membership / first index (−1 if absent) | | `join(sep)` | Join into a string | | `reverse()` / `sort()` | Reverse / sort in place | | `flatten()` | Flatten one level of nested arrays | | `map(fn)` | Transform each element into a new array | | `filter(fn)` | Keep elements where `fn` is truthy | | `reduce(fn, init?)` | Fold to a single value | | `forEach(fn)` | Run `fn` for each element | | `find(fn)` / `findIndex(fn)` | First matching element / its index | | `every(fn)` / `some(fn)` | All / any element passes `fn` | ```goost let nums = [1, 2, 3, 4, 5, 6] let evenSquares = nums .filter(fn(n) { return n % 2 == 0 }) .map(fn(n) { return n * n }) let total = nums.reduce(fn(acc, n) { return acc + n }, 0) println(evenSquares.join(", ")) // "4, 16, 36" println(total) // 21 ``` For other structures — maps, sets, stacks, queues, heaps, tries — see the **[`collections` module](/docs/stdlib/collections)**. --- # Error handling Langoost handles recoverable errors with `try` / `catch` / `throw`, and runs cleanup with `defer`. ## try / catch Wrap code that might fail in `try`, and handle the thrown value in `catch`. The `catch` clause binds the thrown value to a variable: ```goost try { let data = io.read("config.json") process(data) } catch err { println("failed: " + toString(err)) } ``` ## finally An optional `finally` block runs on every exit path — normal completion, after a `catch`, and even when the `catch` itself re-throws (the value still unwinds *after* `finally` runs): ```goost try { work() } catch err { println("error: " + toString(err)) } finally { cleanup() // always runs } ``` Use `finally` for cleanup tied to a single block; use `defer` (below) for cleanup tied to the whole function. ## throw Raise an error with `throw`. You can throw any value; an object is a convenient way to carry structured detail: ```goost fn parsePort(s: string): int { let n = toInt(s) if n <= 0 { throw {kind: "ParseError", message: "invalid port: " + s} } return n } ``` An uncaught `throw` propagates up the call stack; if it reaches the top it aborts the program with the error. ## defer A `defer` statement schedules a function call to run when the enclosing function returns — whether it returns normally or unwinds from a `throw`. Deferred calls run in LIFO (last-in, first-out) order, and their arguments are evaluated immediately: ```goost fn copyFile(src: string, dst: string) { let input = io.stream.open(src, "r") defer io.stream.close(input) // always runs on return let output = io.stream.open(dst, "w") defer io.stream.close(output) io.stream.write(output, io.stream.readAll(input)) } ``` ## runtime helpers The `runtime` module complements these keywords: - `runtime.error.new(msg)` builds a structured error string, with `runtime.error.message(e)` and `runtime.error.isError(v)` to inspect it. - `runtime.panic.throw(msg)` prints to stderr and exits with code 2 — for unrecoverable conditions rather than expected failures. See the **[`runtime` module](/docs/stdlib/runtime)** for details. --- # Modules & imports ## Namespace import Import an entire module under its name and access members with a dot: ```goost import strings import math strings.upper("hello") // "HELLO" math.sqrt(16.0) // 4.0 ``` ## Named import Import specific exports directly into scope: ```goost import { abs, sqrt, PI } from "math" println(sqrt(PI)) ``` ## Sub-modules Many modules are organized into sub-modules, reached with further dot access after importing the parent: ```goost import time import net import thread time.clock.now() // current time, RFC3339 net.tcp.connect("localhost", 8080) thread.mutex.new() ``` ## Local modules Any `.goost` file is a module. Import it by name (without the extension) to use its top-level functions: ```goost // math_utils.goost defines fn clamp(...) import math_utils println(math_utils.clamp(15, 0, 10)) // 10 ``` ## Resolution order When you `import name`, Langoost resolves it in this order: 1. **Standard-library modules** — always checked first; stdlib names cannot be shadowed by local files. 2. **Filesystem** — the directory of the script being run, then the current working directory. ## Module cache Compiled modules are cached by absolute path and modification time. After the first import, unchanged modules are served from cache as a map lookup — no recompilation. In long-running server mode this keeps imports effectively free. Browse everything available out of the box in the **[standard library](/docs/stdlib)**. --- # HTTP server Langoost has a built-in HTTP server. Call `http.serve(port, handler)` and each incoming request runs your handler in its own **isolated VM** — a private stack and output buffer per request, so concurrent requests never share mutable state. ## A minimal server The handler receives up to four arguments — `method`, `path`, `query`, `body` — and returns a string: ```goost import http import strings import json fn handle(method: string, path: string, query: string, body: string): string { if path == "/health" { return "{\"status\": \"ok\"}" } if path == "/echo" && method == "POST" { return body } if path == "/greet" { let name = "World" if strings.contains(query, "name=") { let parts = strings.splitN(query, "name=", 2) name = parts[1] } let data: json = {message: "Hello"} return json.setString(data, "message", "Hello, " + name + "!") } return "404 Not Found" } http.serve(8080, handle) ``` ## Response format - **Plain string** → HTTP `200` with that body. - **Status prefix** → start the string with a 3-digit code and a space to set the status: `"404 Not Found"`, `"500 Internal Server Error"`, `"201 " + json.stringify(resource)`. - **Content-Type** is detected automatically: responses starting with `{` or `[` are sent as `application/json`; everything else as `text/plain; charset=utf-8`. ## Isolation & shared state Because each request runs in its own VM, there is no shared mutable state by default — which is what makes requests safe to run concurrently. When you do need to share data across requests, use the global key-value store in `runtime.core`: ```goost import runtime runtime.core.share("visits", "0") let n = toInt(runtime.core.fetch("visits")) runtime.core.share("visits", toString(n + 1)) ``` ## HTTP client The same `http` module is also a client for talking to other services: ```goost import http let body = http.get("https://api.example.com/users") let resp = http.request("GET", "https://api.example.com/me", "", [ "Authorization: Bearer my-token", "Accept: application/json" ]) ``` See the **[concurrency](/docs/concurrency)** guide for background work and the `thread` module. --- # Concurrency Concurrency lives in the `thread` module. It is **VM-based**: each spawned function runs in its own isolated VM with a copy of the current globals. That isolation is what keeps concurrent work safe — tasks don't share mutable state unless you opt in through the runtime store. ```goost import thread ``` ## Spawning goroutines ```goost import thread thread.core.spawn(fn() { println("running in the background") }) ``` Other `thread.core` helpers: `sleep(ms)`, `cpus()`, `goroutines()`. ## Async futures Run work and await its result: ```goost import thread import time let future = thread.async.run(fn() { time.timer.sleep(100) return "done" }) println(thread.async.await(future)) // "done" ``` ## Channels Typed message passing between tasks: ```goost import thread let ch = thread.channel.new(10) // buffered channel, capacity 10 thread.core.spawn(fn() { thread.channel.send(ch, "hello from goroutine") }) let msg = thread.channel.recv(ch) println(msg) ``` Channel functions: `new(capacity)`, `send`, `recv`, `recvTimeout(id, ms)`, `close`, `len`, `cap`. ## Locks & primitives The `thread` module also provides the usual primitives: | Sub-module | Purpose | | --- | --- | | `thread.mutex` | Mutual-exclusion lock (`lock`, `unlock`, `tryLock`) | | `thread.rwlock` | Reader/writer lock | | `thread.semaphore` | Bounded concurrency (`acquire`, `release`) | | `thread.atomic` | Atomic integer (`load`, `store`, `add`, `cas`) | | `thread.pool` | Worker pool (`submit`, `wait`) | | `thread.scheduler` | Run a set of functions in sequence | ```goost import thread let mu = thread.mutex.new() fn safeIncrement() { thread.mutex.lock(mu) // ... modify shared state ... thread.mutex.unlock(mu) } ``` ## Sharing state across VMs Because spawned tasks (and HTTP requests) run in isolated VMs, share data through the global store in `runtime.core`: ```goost import runtime import thread runtime.core.share("counter", "0") fn worker() { let val = toInt(runtime.core.fetch("counter")) runtime.core.share("counter", toString(val + 1)) } thread.core.spawn(worker) thread.core.spawn(worker) ``` Guard read-modify-write sequences with a `thread.mutex` when correctness under contention matters. --- # Web libraries These are pure-Langoost libraries shipped in the `lib/` directory, written on top of the `net.*` primitives. You import them by name. They are distinct from the built-in `http.serve` (see [HTTP server](/docs/http-server)) — these give you Langoost-level types and handler functions you can read and extend. Three libraries are covered here: `http_server`, `websocket`, and `sse`. ## http_server An HTTP/1.1 server. Each accepted connection is handled in its own thread. ### Types ```goost type Request = { method: string, path: string, query: string, version: string, headers: string[], // raw "Key: Value" lines body: string, } type Response = { status: int, headers: string[], // raw "Key: Value" lines body: string, } ``` ### Functions | Function | Signature | Description | | --- | --- | --- | | `serve` | `http_server.serve(port: int, handler)` | bind the port and dispatch `handler(req: Request)` per connection; blocks forever | | `header` | `http_server.header(req, name: string) → string` | value of a header (case-insensitive), or `""` if missing | | `statusText` | `http_server.statusText(code: int) → string` | IANA reason phrase for a status code | The handler is called as `handler(req)` and may return either a `string` (sent as `200 OK` with an auto Content-Type) or a `Response` for full control. Content-Length and Content-Type are added automatically if you omit them. ### Example ```goost import http_server import { Response } from "http_server" fn handle(req) { if req.path == "/" { return "Hello, World!" } if req.path == "/json" { return Response{status: 200, headers: [], body: "{\"ok\":true}"} } return Response{status: 404, headers: [], body: "Not Found"} } http_server.serve(8080, handle) ``` ## websocket A minimal RFC 6455 server-side WebSocket implementation. Use `websocket.serve` for end-to-end connections; it runs the handshake and hands your handler the raw connection. ### Types ```goost type WSRequest = { path: string, headers: string[] } ``` ### Functions | Function | Signature | Description | | --- | --- | --- | | `serve` | `websocket.serve(port: int, handler)` | listen and call `handler(conn: int, path: string)` after the handshake completes | | `recv` | `websocket.recv(conn: int) → string` | read one frame's payload; void on close/EOF (auto-replies to pings) | | `send` | `websocket.send(conn: int, msg: string) → bool` | send a text frame | | `sendBinary` | `websocket.sendBinary(conn: int, data: string) → bool` | send a binary frame | | `close` | `websocket.close(conn: int)` | send a close frame and close the connection | | `isUpgrade` | `websocket.isUpgrade(req) → bool` | true if the request is a valid WebSocket upgrade | | `acceptKey` | `websocket.acceptKey(req) → string` | compute the `Sec-WebSocket-Accept` value, or `""` | | `upgradeHeaders` | `websocket.upgradeHeaders(accept: string) → string[]` | response header lines for a 101 handshake | | `handshake` | `websocket.handshake(conn: int, req) → bool` | perform the full server-side upgrade on `conn` | ### Example ```goost import websocket fn handle(conn, path) { println("ws connected: " + path) while true { let msg = websocket.recv(conn) if typeof(msg) == "void" { break } websocket.send(conn, "echo: " + msg) } websocket.close(conn) } websocket.serve(8090, handle) ``` ## sse Server-Sent Events: the connection stays open and the server streams text events to the browser's `EventSource`. ### Types ```goost type SSERequest = { path: string, headers: string[] } ``` ### Functions | Function | Signature | Description | | --- | --- | --- | | `serve` | `sse.serve(port: int, handler)` | accept connections and call `handler(req, emit)`; returning from the handler closes the socket | | `writeHeaders` | `sse.writeHeaders(conn: int) → bool` | write the `text/event-stream` 200 OK header block | | `frame` | `sse.frame(event: string, data: string) → string` | build one event payload; `event=""` uses the default `message` | | `frameWithID` | `sse.frameWithID(event: string, data: string, id: string) → string` | build an event with a `Last-Event-ID` hint | | `retryHint` | `sse.retryHint(ms: int) → string` | build a reconnect-delay hint frame | The handler is called as `handler(req, emit)` where `req` is an `SSERequest` and `emit` is `fn(event: string, data: string)` which writes one event to the client. ### Example ```goost import sse import thread fn handle(req, emit) { let i = 0 while i < 5 { emit("tick", "n=" + toString(i)) thread.core.sleep(1000) i = i + 1 } } sse.serve(8091, handle) ``` --- # Standard library Langoost ships with a large standard library. Import any module by name with `import modulename`, then call functions with dot syntax. Each module below has its own reference page. ## Built-in functions Always in scope, no import required: `print`, `println`, `len`, `toString`, `toInt`, `toFloat`, `range`, `typeof`, `append`, `read`, `exit`. ## Text & data | Module | Purpose | | --- | --- | | [strings](/docs/stdlib/strings) | Search, slice, split, join, case, padding, formatting, builder | | [regex](/docs/stdlib/regex) | Regular expressions: match, find, replace, capture groups | | [types](/docs/stdlib/types) | Runtime type checks and conversions | | [json](/docs/stdlib/json) | Parse, query, edit, and stream JSON | | [yaml](/docs/stdlib/yaml) | Parse and serialize YAML | | [xml](/docs/stdlib/xml) | Validate, query, and convert XML | | [url](/docs/stdlib/url) | Parse and build URLs and query strings | | [encode](/docs/stdlib/encode) | base64, hex, CSV, binary, JSON/XML | | [serialize](/docs/stdlib/serialize) | msgpack and protobuf | | [compress](/docs/stdlib/compress) | gzip, zlib, lz4, brotli | ## Math & collections | Module | Purpose | | --- | --- | | [math](/docs/stdlib/math) | Arithmetic, trig, logs, random, bitwise, interpolation | | [collections](/docs/stdlib/collections) | Stack, queue, set, map, heap, trie, ring, list, tuple | ## Time | Module | Purpose | | --- | --- | | [time](/docs/stdlib/time) | Clock, dates, timers, durations, formatting | | [date](/docs/stdlib/date) | Convenience date API over Unix timestamps | | [timer](/docs/stdlib/timer) | setTimeout, setInterval, clear | ## System & I/O | Module | Purpose | | --- | --- | | [io](/docs/stdlib/io) | Files, streams, and path helpers | | [os](/docs/stdlib/os) | Environment, process, signals, permissions | | [exec](/docs/stdlib/exec) | Run shell commands and programs | | [signal](/docs/stdlib/signal) | POSIX signal handlers | | [terminal](/docs/stdlib/terminal) | Raw mode, key input, ANSI colors and cursor | | [runtime](/docs/stdlib/runtime) | Introspection and a process-global shared store | | [memory](/docs/stdlib/memory) | Byte buffers, interning pools, GC stats | | [logging](/docs/stdlib/logging) | Leveled logging with a debug toggle | ## Networking | Module | Purpose | | --- | --- | | [net](/docs/stdlib/net) | TCP, UDP, TLS, DNS, and an HTTP client | | [http](/docs/stdlib/http) | HTTP client (the built-in server is in [HTTP server](/docs/http-server)) | ## Crypto & data stores | Module | Purpose | | --- | --- | | [crypto](/docs/stdlib/crypto) | Hashing, HMAC, AES, RSA, Ed25519, random, certs, TLS | | [redis](/docs/stdlib/redis) | Redis client — key/value, lists, hashes, pub/sub | | [sql](/docs/stdlib/sql) | SQLite, Postgres, and MySQL | | [mongo](/docs/stdlib/mongo) | MongoDB client — insert, find, update, delete, count | ## Concurrency The `thread` module (goroutines, channels, mutexes, futures, and more) is covered on the **[concurrency](/docs/concurrency)** page. Shared state across tasks lives in `runtime.core` — see the **[runtime](/docs/stdlib/runtime)** module. --- # strings The `strings` module collects everything you need to inspect, transform, and assemble text — searching, slicing, splitting, joining, trimming, case conversion, padding, classification, formatting, and an efficient string builder. Import it with `import strings`. ## Searching | Function | Signature | Description | | --- | --- | --- | | `indexOf` | `strings.indexOf(s: string, sub: string) → int` | First index of `sub`, or -1 if absent | | `lastIndexOf` | `strings.lastIndexOf(s: string, sub: string) → int` | Last index of `sub`, or -1 if absent | | `contains` | `strings.contains(s: string, sub: string) → bool` | Whether `s` contains `sub` | | `count` | `strings.count(s: string, sub: string) → int` | Non-overlapping occurrences of `sub` | | `hasPrefix` | `strings.hasPrefix(s: string, prefix: string) → bool` | Whether `s` starts with `prefix` | | `hasSuffix` | `strings.hasSuffix(s: string, suffix: string) → bool` | Whether `s` ends with `suffix` | ## Slicing | Function | Signature | Description | | --- | --- | --- | | `slice` | `strings.slice(s: string, start: int, end: int) → string` | Byte slice from `start` to `end`; negative indices count from the end | | `sub` | `strings.sub(s: string, start: int, length: int) → string` | Substring of `length` bytes from `start`; negative `start` counts from the end | ## Splitting | Function | Signature | Description | | --- | --- | --- | | `split` | `strings.split(s: string, sep: string) → string[]` | Split on every occurrence of `sep` | | `splitN` | `strings.splitN(s: string, sep: string, n: int) → string[]` | Split into at most `n` parts; -1 means unlimited | | `fields` | `strings.fields(s: string) → string[]` | Split on any whitespace, discarding empty parts | | `lines` | `strings.lines(s: string) → string[]` | Split on newlines, stripping trailing carriage returns | ## Joining | Function | Signature | Description | | --- | --- | --- | | `join` | `strings.join(arr: string[], sep: string) → string` | Join array elements with `sep` | ## Replacement | Function | Signature | Description | | --- | --- | --- | | `replace` | `strings.replace(s: string, old: string, new: string) → string` | Replace all occurrences of `old` | | `replaceN` | `strings.replaceN(s: string, old: string, new: string, n: int) → string` | Replace first `n` occurrences; -1 replaces all | ## Trimming | Function | Signature | Description | | --- | --- | --- | | `trim` | `strings.trim(s: string, cutset?: string) → string` | Trim whitespace, or the given cutset, from both ends | | `trimLeft` | `strings.trimLeft(s: string, cutset: string) → string` | Trim cutset chars from the left | | `trimRight` | `strings.trimRight(s: string, cutset: string) → string` | Trim cutset chars from the right | | `trimPrefix` | `strings.trimPrefix(s: string, prefix: string) → string` | Remove `prefix` if present | | `trimSuffix` | `strings.trimSuffix(s: string, suffix: string) → string` | Remove `suffix` if present | ## Case | Function | Signature | Description | | --- | --- | --- | | `upper` | `strings.upper(s: string) → string` | Convert to uppercase | | `lower` | `strings.lower(s: string) → string` | Convert to lowercase | | `title` | `strings.title(s: string) → string` | Capitalize the first letter of each word | ## Padding and repetition | Function | Signature | Description | | --- | --- | --- | | `padLeft` | `strings.padLeft(s: string, width: int, pad: string) → string` | Right-align in a field of `width` bytes | | `padRight` | `strings.padRight(s: string, width: int, pad: string) → string` | Left-align in a field of `width` bytes | | `repeat` | `strings.repeat(s: string, n: int) → string` | Repeat `s` `n` times | | `reverse` | `strings.reverse(s: string) → string` | Reverse the rune order | ## Characters and codepoints | Function | Signature | Description | | --- | --- | --- | | `char` | `strings.char(code: int) → string` | UTF-8 string for the given codepoint | | `code` | `strings.code(s: string) → int` | Codepoint of the first rune, or -1 if empty | | `byteAt` | `strings.byteAt(s: string, i: int) → int` | Raw byte 0-255 at byte index `i`, or -1 if out of range | | `fromBytes` | `strings.fromBytes(arr: int[]) → string` | Build a string from byte values | | `toBytes` | `strings.toBytes(s: string) → int[]` | Array of byte values 0-255 | | `runeLen` | `strings.runeLen(s: string) → int` | Number of Unicode runes, not bytes | ## Classification | Function | Signature | Description | | --- | --- | --- | | `isAlpha` | `strings.isAlpha(s: string) → bool` | All chars are Unicode letters | | `isDigit` | `strings.isDigit(s: string) → bool` | All chars are ASCII digits 0-9 | | `isAlphaNum` | `strings.isAlphaNum(s: string) → bool` | All chars are letters or digits | | `isSpace` | `strings.isSpace(s: string) → bool` | All chars are whitespace | ## Formatting and conversion | Function | Signature | Description | | --- | --- | --- | | `format` | `strings.format(template: string, args: string[]) → string` | Replace `{0}`, `{1}`, ... with the matching args | | `toInt` | `strings.toInt(s: string) → int` | Parse a decimal integer, 0 on failure | | `toFloat` | `strings.toFloat(s: string) → float` | Parse a float, 0.0 on failure | | `matchGlob` | `strings.matchGlob(pattern: string, s: string) → bool` | Match a glob pattern with `*` and `?` | ## strings.builder Building strings in a loop with `out = out + chunk` is O(n squared). The builder accumulates into one buffer for O(n) growth, then materializes the result once at the end. | Function | Signature | Description | | --- | --- | --- | | `new` | `strings.builder.new() → int` | Allocate a builder, returns its handle | | `write` | `strings.builder.write(id: int, s: any) → bool` | Append a value; false if the handle is unknown | | `writeln` | `strings.builder.writeln(id: int, s: any) → bool` | Append a value followed by a newline | | `toString` | `strings.builder.toString(id: int) → string` | Materialize the accumulated string | | `len` | `strings.builder.len(id: int) → int` | Number of bytes accumulated | | `reset` | `strings.builder.reset(id: int) → void` | Clear the buffer, keep the handle | | `free` | `strings.builder.free(id: int) → bool` | Release the handle; true if it existed | ## Example ```goost import strings let parts = strings.split("a,b,c", ",") let joined = strings.join(parts, " | ") print(strings.upper(joined)) // A | B | C let b = strings.builder.new() strings.builder.write(b, "count=") strings.builder.write(b, "42") print(strings.builder.toString(b)) // count=42 strings.builder.free(b) ``` --- # regex The `regex` module runs regular expressions over strings — testing for matches, extracting matches and capture groups, replacing, splitting, and validating or escaping patterns. Compiled patterns are cached, so reusing the same pattern is cheap. Import it with `import regex`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `match` | `regex.match(pattern: string, s: string) → bool` | Whether `pattern` matches anywhere in `s` | | `find` | `regex.find(pattern: string, s: string) → string` | First match, or empty string if none | | `findAll` | `regex.findAll(pattern: string, s: string) → string[]` | All non-overlapping matches | | `replace` | `regex.replace(pattern: string, s: string, repl: string) → string` | Replace all matches with `repl` | | `split` | `regex.split(pattern: string, s: string) → string[]` | Split `s` on every match of `pattern` | | `groups` | `regex.groups(pattern: string, s: string) → string[]` | First match's capture groups (index 0 is the whole match); empty if no match | | `findAllGroups` | `regex.findAllGroups(pattern: string, s: string) → string[]` | All matches' groups flattened; stride is groupCount + 1 | | `groupCount` | `regex.groupCount(pattern: string) → int` | Number of capture subgroups, excluding group 0 | | `valid` | `regex.valid(pattern: string) → bool` | Whether `pattern` compiles | | `escape` | `regex.escape(s: string) → string` | Escape literal text for use inside a pattern | ## Example ```goost import regex let line = "user=alice id=42" if regex.match("id=\\d+", line) { let g = regex.groups("(\\w+)=(\\w+)", line) print(g[1] + " -> " + g[2]) // user -> alice } let words = regex.findAll("\\w+", "one two three") print(strings.join(words, ",")) // one,two,three ``` --- # types The `types` module provides runtime type inspection and value conversion. Top-level helpers check a value's type, while submodules group parsing, formatting, and predicates for each primitive type. Import it with `import types`. ## Top-level | Function | Signature | Description | | --- | --- | --- | | `isString` | `types.isString(v: any) → bool` | Whether `v` is a string | | `isInt` | `types.isInt(v: any) → bool` | Whether `v` is an int | | `isFloat` | `types.isFloat(v: any) → bool` | Whether `v` is a float | | `isBool` | `types.isBool(v: any) → bool` | Whether `v` is a bool | | `isArray` | `types.isArray(v: any) → bool` | Whether `v` is an array | | `isVoid` | `types.isVoid(v: any) → bool` | Whether `v` is void/null | | `typeOf` | `types.typeOf(v: any) → string` | Name of the value's type | ## types.int | Function | Signature | Description | | --- | --- | --- | | `parse` | `types.int.parse(s: string) → int` | Parse a decimal int, 0 on failure | | `toBin` | `types.int.toBin(n: int) → string` | Binary form with `0b` prefix | | `toHex` | `types.int.toHex(n: int) → string` | Hex form with `0x` prefix | | `toOct` | `types.int.toOct(n: int) → string` | Octal form with `0o` prefix | | `fromBin` | `types.int.fromBin(s: string) → int` | Parse binary (optional `0b`), 0 on failure | | `fromHex` | `types.int.fromHex(s: string) → int` | Parse hex (optional `0x`), 0 on failure | | `fromOct` | `types.int.fromOct(s: string) → int` | Parse octal (optional `0o`), 0 on failure | | `max` | `types.int.max() → int` | Largest representable int | | `min` | `types.int.min() → int` | Smallest representable int | | `isInt` | `types.int.isInt(v: any) → bool` | Whether `v` is an int | ## types.float | Function | Signature | Description | | --- | --- | --- | | `parse` | `types.float.parse(s: string) → float` | Parse a float, 0.0 on failure | | `round` | `types.float.round(f: float, decimals: int) → float` | Round to `decimals` places | | `toFixed` | `types.float.toFixed(f: float, decimals: int) → string` | Format with a fixed number of decimals | | `isNaN` | `types.float.isNaN(v: any) → bool` | Whether `v` is NaN | | `isInf` | `types.float.isInf(v: any) → bool` | Whether `v` is infinite | | `isFloat` | `types.float.isFloat(v: any) → bool` | Whether `v` is a float | ## types.bool | Function | Signature | Description | | --- | --- | --- | | `parse` | `types.bool.parse(s: string) → bool` | Parse true/1/yes/on as true | | `toString` | `types.bool.toString(b: bool) → string` | `"true"` or `"false"` | | `isBool` | `types.bool.isBool(v: any) → bool` | Whether `v` is a bool | ## types.char Each function takes a one-character string. | Function | Signature | Description | | --- | --- | --- | | `code` | `types.char.code(c: string) → int` | Byte code of the character | | `fromCode` | `types.char.fromCode(code: int) → string` | Character for the codepoint | | `isUpper` | `types.char.isUpper(c: string) → bool` | Whether the char is uppercase | | `isLower` | `types.char.isLower(c: string) → bool` | Whether the char is lowercase | | `isDigit` | `types.char.isDigit(c: string) → bool` | Whether the char is a digit | | `isAlpha` | `types.char.isAlpha(c: string) → bool` | Whether the char is a letter | | `isSpace` | `types.char.isSpace(c: string) → bool` | Whether the char is whitespace | | `toLower` | `types.char.toLower(c: string) → string` | Lowercased char | | `toUpper` | `types.char.toUpper(c: string) → string` | Uppercased char | ## types.string | Function | Signature | Description | | --- | --- | --- | | `is` | `types.string.is(v: any) → bool` | Whether `v` is a string | | `empty` | `types.string.empty(v: any) → bool` | Whether `v` is an empty string | | `nonEmpty` | `types.string.nonEmpty(v: any) → bool` | Whether `v` is a non-empty string | | `default` | `types.string.default(v: any, default: string) → any` | `v` unless it is void or empty, then `default` | | `coerce` | `types.string.coerce(v: any) → string` | String form of any value | | `length` | `types.string.length(v: any) → int` | Byte length, 0 if not a string | ## types.null and types.void | Function | Signature | Description | | --- | --- | --- | | `is` | `types.null.is(v: any) → bool` | Whether `v` is null/void | | `value` | `types.null.value` | The null/void value | | `is` | `types.void.is(v: any) → bool` | Whether `v` is void | | `value` | `types.void.value` | The void value | ## Example ```goost import types let n = types.int.parse("255") print(types.int.toHex(n)) // 0xff let v = "hello" print(types.typeOf(v)) // string print(types.string.default("", "fallback")) // fallback ``` --- # json The `json` module parses JSON into navigable values, queries and edits JSON strings by key, merges and reshapes objects and arrays, pretty-prints or minifies, and streams large arrays or NDJSON one value at a time. Import it with `import json`. ## Parsing and serialization | Function | Signature | Description | | --- | --- | --- | | `valid` | `json.valid(s: string) → bool` | Whether `s` is valid JSON | | `parse` | `json.parse(s: string) → any` | Parse into a navigable object/array/scalar tree | | `stringify` | `json.stringify(v: any, indent?: string) → string` | Serialize a value; pass an indent for pretty output. Keys are sorted | | `pretty` | `json.pretty(s: string) → string` | Re-indent existing JSON text with 2 spaces | | `minify` | `json.minify(s: string) → string` | Strip whitespace from JSON text | ## Querying These operate on a JSON string and read top-level keys. | Function | Signature | Description | | --- | --- | --- | | `get` | `json.get(j: string, key: string) → string` | Value at `key` as a string; empty if missing/null | | `getInt` | `json.getInt(j: string, key: string) → int` | Value at `key` as int; 0 if missing/non-numeric | | `getBool` | `json.getBool(j: string, key: string) → bool` | Value at `key` as bool; false if missing | | `has` | `json.has(j: string, key: string) → bool` | Whether `key` exists | | `keys` | `json.keys(obj: any) → string[]` | Sorted field names of a parsed object or JSON string | | `length` | `json.length(j: string) → int` | Object key count or array length | | `arrayGet` | `json.arrayGet(j: string, idx: int) → string` | Element at `idx` of a JSON array | ## Editing These return a new JSON string with the change applied. | Function | Signature | Description | | --- | --- | --- | | `setString` | `json.setString(j: string, key: string, val: string) → string` | Set `key` to a string value | | `setInt` | `json.setInt(j: string, key: string, val: int) → string` | Set `key` to an int value | | `setBool` | `json.setBool(j: string, key: string, val: bool) → string` | Set `key` to a bool value | | `delete` | `json.delete(j: string, key: string) → string` | Remove `key` | | `merge` | `json.merge(j1: string, j2: string) → string` | Merge two objects; `j2` keys overwrite `j1` | | `fromArray` | `json.fromArray(arr: string[]) → string` | Convert an array into a JSON array string | ## json.stream Streams a JSON source one top-level value at a time, calling `fn(value)` for each. Handles both a JSON array (each element delivered separately) and concatenated values or NDJSON. The callback may return `false` to stop early. Returns the count of values delivered. | Function | Signature | Description | | --- | --- | --- | | `parseString` | `json.stream.parseString(s: string, fn) → int` | Stream values from a JSON string | | `parseFile` | `json.stream.parseFile(path: string, fn) → int` | Stream values from a JSON file | ## Example ```goost import json let doc = json.parse("{\"name\": \"alice\", \"age\": 30}") print(doc.name) // alice let updated = json.setInt("{\"age\": 30}", "age", 31) print(updated) json.stream.parseString("[1, 2, 3]", fn(v) { print(v) return true }) ``` --- # yaml The `yaml` module parses YAML into navigable values, converts YAML to and from JSON, and reads top-level keys. Import it with `import yaml`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `valid` | `yaml.valid(s: string) → bool` | Whether `s` is valid YAML | | `parse` | `yaml.parse(s: string) → any` | Parse into a navigable object/array/scalar tree | | `parseString` | `yaml.parseString(s: string) → string` | Parse YAML and re-emit as a JSON string | | `stringify` | `yaml.stringify(j: string) → string` | Convert a JSON string to YAML | | `get` | `yaml.get(s: string, key: string) → string` | Top-level key value as a string | ## Example ```goost import yaml let cfg = "name: alice\nage: 30\n" let doc = yaml.parse(cfg) print(doc.name) // alice let asJson = yaml.parseString(cfg) let backToYaml = yaml.stringify(asJson) print(backToYaml) ``` --- # xml The `xml` module validates and reformats XML, reads element text and attributes, and converts XML into JSON or a navigable object tree. Import it with `import xml`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `valid` | `xml.valid(s: string) → bool` | Whether `s` is well-formed XML | | `pretty` | `xml.pretty(s: string) → string` | Re-indent XML with 2-space indent | | `minify` | `xml.minify(s: string) → string` | Strip whitespace between tags | | `get` | `xml.get(s: string, tag: string) → string` | Inner text of the first matching element | | `attr` | `xml.attr(s: string, tag: string, attr: string) → string` | Value of `attr` on the first matching element | | `toJson` | `xml.toJson(s: string) → string` | Convert XML to a JSON map string | | `parse` | `xml.parse(s: string) → object` | Navigable XML tree (children as fields, attributes prefixed `@`, text under `#text`) | ## Example ```goost import xml let doc = "Goost" print(xml.get(doc, "title")) // Goost print(xml.attr(doc, "book", "id")) // 1 let tree = xml.parse(doc) print(tree.book.title["#text"]) // Goost ``` --- # url Parse URLs into navigable objects, rebuild them from parts, and percent-encode or decode strings and query parameters. Import it with `import url`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `parse` | `url.parse(s: string) → object` | Parse a URL into an object with `scheme`, `host`, `port`, `path`, `rawQuery`, `query`, `fragment`, `user`, `pass` (void on parse failure) | | `build` | `url.build(parts: object) → string` | Build a URL string from an object with the same shape as `parse`; missing fields are omitted, `query` may be a raw string or an object | | `encode` | `url.encode(s: string) → string` | Percent-encode a string for a query value position | | `decode` | `url.decode(s: string) → string` | Reverse `url.encode` (returns the input unchanged if it cannot decode) | | `encodePath` | `url.encodePath(s: string) → string` | Percent-encode a string for a path segment | | `parseQuery` | `url.parseQuery(s: string) → object` | Parse a query string like `"a=1&b=2"` into an object; multi-value params become string arrays | | `buildQuery` | `url.buildQuery(params: object) → string` | Encode an object into a query string with keys sorted for deterministic output | In a parsed `query`/`parseQuery` object, single-value params are string fields and repeated params become string arrays. When building, array fields emit repeated params (`foo=a&foo=b`) and void fields are skipped. ## Example ```goost import url // inspect URL parts let u = url.parse("https://example.com:8443/search?q=hello+world&tag=a&tag=b") print(u.host) // example.com print(u.port) // 8443 print(u.query.q) // hello world // rebuild a URL from parts let link = url.build({ scheme: "https", host: "api.example.com", path: "/v1/items", query: { page: "2", limit: "50" }, }) print(link) // https://api.example.com/v1/items?limit=50&page=2 // encode a single value print(url.encode("a b&c")) // a+b%26c ``` --- # encode Encode and decode data across common formats: base64, hex, CSV, big-endian binary integers and floats, plus JSON and XML helpers. Import it with `import encode`. ## encode.base64 | Function | Signature | Description | | --- | --- | --- | | `encode` | `encode.base64.encode(s: string) → string` | Standard base64-encode a string | | `decode` | `encode.base64.decode(s: string) → string` | Decode a standard base64 string | | `encodeURL` | `encode.base64.encodeURL(s: string) → string` | URL-safe base64-encode a string | | `decodeURL` | `encode.base64.decodeURL(s: string) → string` | Decode a URL-safe base64 string | ## encode.hex | Function | Signature | Description | | --- | --- | --- | | `encode` | `encode.hex.encode(s: string) → string` | Hex-encode a string | | `decode` | `encode.hex.decode(s: string) → string` | Decode a hex string | ## encode.csv | Function | Signature | Description | | --- | --- | --- | | `encodeRows` | `encode.csv.encodeRows(rows: string) → string` | Encode CSV from a JSON string holding an array of string arrays | | `decodeAll` | `encode.csv.decodeAll(data: string) → string` | Parse CSV text into a JSON string of an array of string arrays | | `encodeRow` | `encode.csv.encodeRow(row: string[]) → string` | Encode a single CSV row (no trailing newline) | ## encode.binary All integers and floats use 8-byte (or 4-byte) big-endian encoding, stored as the bytes of a string. | Function | Signature | Description | | --- | --- | --- | | `encodeInt32` | `encode.binary.encodeInt32(n: int) → string` | Encode an int as 4 big-endian bytes | | `encodeInt64` | `encode.binary.encodeInt64(n: int) → string` | Encode an int as 8 big-endian bytes | | `decodeInt32` | `encode.binary.decodeInt32(s: string) → int` | Decode 4 big-endian bytes (0 if too short) | | `decodeInt64` | `encode.binary.decodeInt64(s: string) → int` | Decode 8 big-endian bytes (0 if too short) | | `encodeFloat64` | `encode.binary.encodeFloat64(f: float) → string` | Encode a float as 8-byte IEEE 754 big-endian | | `decodeFloat64` | `encode.binary.decodeFloat64(s: string) → float` | Decode 8-byte IEEE 754 big-endian (0 if too short) | ## encode.json | Function | Signature | Description | | --- | --- | --- | | `parse` | `encode.json.parse(s: string) → any` | Parse JSON into a navigable value tree | | `stringify` | `encode.json.stringify(v: any, indent?: string) → string` | Serialize a value to JSON, optionally indented | | `pretty` | `encode.json.pretty(s: string) → string` | Re-indent a JSON string with two-space indentation | | `valid` | `encode.json.valid(s: string) → bool` | Report whether the string is valid JSON | | `keys` | `encode.json.keys(v: any) → string[]` | Sorted field names of an object or JSON object string | ## encode.xml | Function | Signature | Description | | --- | --- | --- | | `valid` | `encode.xml.valid(s: string) → bool` | Report whether the string is well-formed XML | | `pretty` | `encode.xml.pretty(s: string) → string` | Return the XML string (pass-through) | ## Example ```goost import encode // base64 round-trip let enc = encode.base64.encode("hello") print(encode.base64.decode(enc)) // hello // build CSV from rows let csv = encode.csv.encodeRows("[[\"name\",\"age\"],[\"ada\",\"36\"]]") print(csv) // JSON helpers print(encode.json.valid("{\"a\":1}")) // true let obj = encode.json.parse("{\"a\":1,\"b\":2}") print(encode.json.keys(obj)) // ["a", "b"] ``` --- # serialize Serialize JSON-shaped data into compact binary using msgpack or a simplified protobuf encoder. Input is always a JSON string and binary output is returned as a base64 string. Import it with `import serialize`. ## serialize.msgpack | Function | Signature | Description | | --- | --- | --- | | `encode` | `serialize.msgpack.encode(json: string) → string` | Encode a JSON string to base64-wrapped msgpack | | `decode` | `serialize.msgpack.decode(data: string) → string` | Decode base64 msgpack back into a JSON string | | `size` | `serialize.msgpack.size(json: string) → int` | Byte count of the msgpack encoding (0 on invalid JSON) | ## serialize.protobuf A simplified encoder backed by msgpack internally, since true protobuf requires schemas. | Function | Signature | Description | | --- | --- | --- | | `encode` | `serialize.protobuf.encode(json: string) → string` | Encode a JSON string to a base64 binary string | | `decode` | `serialize.protobuf.decode(data: string) → string` | Decode a base64 binary string back into a JSON string | ## Example ```goost import serialize let payload = "{\"id\":7,\"name\":\"widget\"}" // compact binary round-trip let packed = serialize.msgpack.encode(payload) print(serialize.msgpack.size(payload)) // encoded byte count print(serialize.msgpack.decode(packed)) // {"id":7,"name":"widget"} ``` --- # compress Compress and decompress strings with gzip, zlib, lz4, or brotli. Every `compress` returns a base64 string, and every `decompress` expects that base64 string back. Import it with `import compress`. ## compress.gzip | Function | Signature | Description | | --- | --- | --- | | `compress` | `compress.gzip.compress(data: string) → string` | Gzip-compress and base64-encode | | `decompress` | `compress.gzip.decompress(data: string) → string` | Base64-decode and gzip-decompress | ## compress.zlib | Function | Signature | Description | | --- | --- | --- | | `compress` | `compress.zlib.compress(data: string) → string` | Zlib-compress and base64-encode | | `decompress` | `compress.zlib.decompress(data: string) → string` | Base64-decode and zlib-decompress | ## compress.lz4 Uses the self-describing LZ4 frame format, so decompression needs no size hint. | Function | Signature | Description | | --- | --- | --- | | `compress` | `compress.lz4.compress(data: string) → string` | LZ4-compress and base64-encode | | `decompress` | `compress.lz4.decompress(data: string) → string` | Base64-decode and LZ4-decompress | ## compress.brotli | Function | Signature | Description | | --- | --- | --- | | `compress` | `compress.brotli.compress(data: string) → string` | Brotli-compress and base64-encode | | `decompress` | `compress.brotli.decompress(data: string) → string` | Base64-decode and brotli-decompress | ## Top-level helpers Backward-compatible shortcuts for gzip and zlib. | Function | Signature | Description | | --- | --- | --- | | `gzip_compress` | `compress.gzip_compress(data: string) → string` | Same as `compress.gzip.compress` | | `gunzip` | `compress.gunzip(data: string) → string` | Same as `compress.gzip.decompress` | | `zlib_compress` | `compress.zlib_compress(data: string) → string` | Same as `compress.zlib.compress` | | `unzlib` | `compress.unzlib(data: string) → string` | Same as `compress.zlib.decompress` | ## Example ```goost import compress let original = "the quick brown fox jumps over the lazy dog" // gzip round-trip via base64 let packed = compress.gzip.compress(original) print(compress.gzip.decompress(packed)) // the quick brown fox... // other algorithms share the same shape let br = compress.brotli.compress(original) print(compress.brotli.decompress(br) == original) // true ``` --- # math Numeric helpers grouped into submodules for basic arithmetic, trigonometry, logarithms, randomness, constants, bitwise operations, clamping, and interpolation. Import it with `import math`. ## math.basic | Function | Signature | Description | | --- | --- | --- | | `abs` | `math.basic.abs(x: number) → number` | Absolute value (preserves int or float) | | `sqrt` | `math.basic.sqrt(x: number) → float` | Square root | | `floor` | `math.basic.floor(x: number) → int` | Round down to an int | | `ceil` | `math.basic.ceil(x: number) → int` | Round up to an int | | `round` | `math.basic.round(x: number) → int` | Round to nearest int | | `min` | `math.basic.min(a: number, b: number) → number` | Smaller of two values | | `max` | `math.basic.max(a: number, b: number) → number` | Larger of two values | | `pow` | `math.basic.pow(base: number, exp: number) → float` | `base` raised to `exp` | | `sign` | `math.basic.sign(x: number) → int` | -1, 0, or 1 | | `clamp` | `math.basic.clamp(value: number, min: number, max: number) → float` | Clamp value to the range | ## math.trig | Function | Signature | Description | | --- | --- | --- | | `sin` | `math.trig.sin(x: number) → float` | Sine (radians) | | `cos` | `math.trig.cos(x: number) → float` | Cosine (radians) | | `tan` | `math.trig.tan(x: number) → float` | Tangent (radians) | | `asin` | `math.trig.asin(x: number) → float` | Arcsine | | `acos` | `math.trig.acos(x: number) → float` | Arccosine | | `atan` | `math.trig.atan(x: number) → float` | Arctangent | | `atan2` | `math.trig.atan2(y: number, x: number) → float` | Two-argument arctangent | | `deg` | `math.trig.deg(radians: number) → float` | Radians to degrees | | `rad` | `math.trig.rad(degrees: number) → float` | Degrees to radians | ## math.log | Function | Signature | Description | | --- | --- | --- | | `log` | `math.log.log(x: number) → float` | Natural logarithm | | `log2` | `math.log.log2(x: number) → float` | Base-2 logarithm | | `log10` | `math.log.log10(x: number) → float` | Base-10 logarithm | | `exp` | `math.log.exp(x: number) → float` | e raised to `x` | | `exp2` | `math.log.exp2(x: number) → float` | 2 raised to `x` | ## math.random | Function | Signature | Description | | --- | --- | --- | | `float` | `math.random.float() → float` | Random float in [0, 1) | | `int` | `math.random.int() → int` | Random non-negative int | | `intn` | `math.random.intn(n: int) → int` | Random int in [0, n) (n must be > 0) | | `seed` | `math.random.seed(seed: int) → void` | Seed the generator | ## math.constants | Constant | Type | Value | | --- | --- | --- | | `PI` | float | Pi | | `E` | float | Euler's number | | `PHI` | float | Golden ratio | | `Sqrt2` | float | Square root of 2 | | `Ln2` | float | Natural log of 2 | | `Inf` | float | Positive infinity | | `NaN` | float | Not-a-number | ## math.bitwise | Function | Signature | Description | | --- | --- | --- | | `and` | `math.bitwise.and(a: int, b: int) → int` | Bitwise AND | | `or` | `math.bitwise.or(a: int, b: int) → int` | Bitwise OR | | `xor` | `math.bitwise.xor(a: int, b: int) → int` | Bitwise XOR | | `not` | `math.bitwise.not(a: int) → int` | Bitwise NOT | | `shl` | `math.bitwise.shl(a: int, n: int) → int` | Left shift by `n` bits | | `shr` | `math.bitwise.shr(a: int, n: int) → int` | Right shift by `n` bits | | `popcount` | `math.bitwise.popcount(a: int) → int` | Number of set bits | ## math.clamp | Function | Signature | Description | | --- | --- | --- | | `clamp` | `math.clamp.clamp(value: number, min: number, max: number) → float` | Clamp to the range as a float | | `clampInt` | `math.clamp.clampInt(value: int, min: int, max: int) → int` | Clamp to the range as an int | | `clampFloat` | `math.clamp.clampFloat(value: number, min: number, max: number) → float` | Clamp to the range as a float | ## math.lerp | Function | Signature | Description | | --- | --- | --- | | `lerp` | `math.lerp.lerp(a: number, b: number, t: number) → float` | Linear interpolation between `a` and `b` | | `invLerp` | `math.lerp.invLerp(a: number, b: number, value: number) → float` | Inverse lerp: where `value` falls in [a, b] | | `remap` | `math.lerp.remap(value: number, inMin, inMax, outMin, outMax: number) → float` | Remap from one range to another | | `smoothstep` | `math.lerp.smoothstep(edge0: number, edge1: number, x: number) → float` | Smooth Hermite interpolation in [0, 1] | ## Top-level shortcuts Convenience aliases on `math` itself: `math.PI`, `math.E`, and `math.abs`, `math.sqrt`, `math.floor`, `math.ceil`, `math.pow`, `math.min`, `math.max` (same behavior as the `math.basic` equivalents). ## Example ```goost import math print(math.basic.clamp(15, 0, 10)) // 10 print(math.trig.deg(math.constants.PI)) // 180 // pick a random index math.random.seed(42) let i = math.random.intn(5) // 0..4 // blend two values print(math.lerp.lerp(0, 100, 0.25)) // 25 ``` --- # collections A set of data structures: stack, queue, set, ordered map, min-heap, trie, ring buffer, and singly-linked list, plus helpers for native arrays and tuples. Import it with `import collections`. Most structures are handle-based: `new()` returns an integer id that you pass to every other call, and `free(id)` releases it. The exceptions are `collections.array` and `collections.tuple`, which operate directly on Langoost native arrays (no handle, no `free`). ## collections.stack | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.stack.new() → int` | Create a stack, return its id | | `push` | `collections.stack.push(id: int, value: any) → bool` | Push a value (false if id unknown) | | `pop` | `collections.stack.pop(id: int) → any` | Pop the top value (void if empty) | | `peek` | `collections.stack.peek(id: int) → any` | View the top value (void if empty) | | `size` | `collections.stack.size(id: int) → int` | Number of elements | | `isEmpty` | `collections.stack.isEmpty(id: int) → bool` | Whether the stack is empty | | `free` | `collections.stack.free(id: int) → void` | Release the stack | ## collections.queue | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.queue.new() → int` | Create a queue, return its id | | `enqueue` | `collections.queue.enqueue(id: int, value: any) → bool` | Add to the back (false if id unknown) | | `dequeue` | `collections.queue.dequeue(id: int) → any` | Remove from the front (void if empty) | | `peek` | `collections.queue.peek(id: int) → any` | View the front value (void if empty) | | `size` | `collections.queue.size(id: int) → int` | Number of elements | | `isEmpty` | `collections.queue.isEmpty(id: int) → bool` | Whether the queue is empty | | `free` | `collections.queue.free(id: int) → void` | Release the queue | ## collections.set A set of unique strings. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.set.new() → int` | Create a set, return its id | | `add` | `collections.set.add(id: int, value: string) → bool` | Add a value (false if id unknown) | | `has` | `collections.set.has(id: int, value: string) → bool` | Whether the value is present | | `remove` | `collections.set.remove(id: int, value: string) → void` | Remove a value | | `size` | `collections.set.size(id: int) → int` | Number of elements | | `toArray` | `collections.set.toArray(id: int) → string[]` | Members as an array (unordered) | | `free` | `collections.set.free(id: int) → void` | Release the set | ## collections.map An ordered map with string keys; `keys` preserves insertion order. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.map.new() → int` | Create a map, return its id | | `set` | `collections.map.set(id: int, key: string, value: any) → bool` | Set a key (false if id unknown) | | `get` | `collections.map.get(id: int, key: string) → any` | Get a value (void if missing) | | `has` | `collections.map.has(id: int, key: string) → bool` | Whether the key exists | | `delete` | `collections.map.delete(id: int, key: string) → bool` | Remove a key | | `keys` | `collections.map.keys(id: int) → string[]` | Keys in insertion order | | `size` | `collections.map.size(id: int) → int` | Number of entries | | `free` | `collections.map.free(id: int) → void` | Release the map | ## collections.heap A min-heap of integers. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.heap.new() → int` | Create a heap, return its id | | `push` | `collections.heap.push(id: int, value: int) → bool` | Insert a value (false if id unknown) | | `pop` | `collections.heap.pop(id: int) → int` | Remove and return the minimum (0 if empty) | | `peek` | `collections.heap.peek(id: int) → int` | View the minimum (void if empty) | | `size` | `collections.heap.size(id: int) → int` | Number of elements | | `free` | `collections.heap.free(id: int) → void` | Release the heap | ## collections.trie A prefix tree over strings. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.trie.new() → int` | Create a trie, return its id | | `insert` | `collections.trie.insert(id: int, word: string) → bool` | Insert a word (false if id unknown) | | `search` | `collections.trie.search(id: int, word: string) → bool` | Whether the exact word is present | | `startsWith` | `collections.trie.startsWith(id: int, prefix: string) → bool` | Whether any word has the prefix | | `words` | `collections.trie.words(id: int) → string[]` | All stored words | | `free` | `collections.trie.free(id: int) → void` | Release the trie | ## collections.ring A fixed-capacity ring buffer. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.ring.new(capacity: int) → int` | Create a ring buffer (capacity must be > 0) | | `push` | `collections.ring.push(id: int, value: any) → bool` | Add a value (false if full or id unknown) | | `pop` | `collections.ring.pop(id: int) → any` | Remove the oldest value (void if empty) | | `size` | `collections.ring.size(id: int) → int` | Number of elements | | `isFull` | `collections.ring.isFull(id: int) → bool` | Whether the buffer is at capacity | | `free` | `collections.ring.free(id: int) → void` | Release the ring buffer | ## collections.list A handle-based ordered list of strings. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.list.new() → int` | Create a list, return its id | | `append` | `collections.list.append(id: int, value: any) → void` | Append to the end (stored as a string) | | `prepend` | `collections.list.prepend(id: int, value: any) → void` | Prepend to the front (stored as a string) | | `get` | `collections.list.get(id: int, index: int) → string` | Value at index (empty string if out of range) | | `size` | `collections.list.size(id: int) → int` | Number of elements | | `toArray` | `collections.list.toArray(id: int) → string[]` | All elements as an array | | `free` | `collections.list.free(id: int) → void` | Release the list | ## collections.array Operates on Langoost native arrays (no handle). Mutating helpers return a new array. | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.array.new() → array` | Create an empty array | | `length` | `collections.array.length(arr: array) → int` | Element count | | `push` | `collections.array.push(arr: array, value: any) → array` | New array with `value` appended | | `pop` | `collections.array.pop(arr: array) → any` | Last element (does not mutate) | | `first` | `collections.array.first(arr: array) → any` | First element | | `last` | `collections.array.last(arr: array) → any` | Last element | | `contains` | `collections.array.contains(arr: array, value: any) → bool` | Whether `value` is present | | `indexOf` | `collections.array.indexOf(arr: array, value: any) → int` | Index of `value`, or -1 | | `reverse` | `collections.array.reverse(arr: array) → array` | New reversed array | | `unique` | `collections.array.unique(arr: array) → array` | New array with duplicates removed | | `slice` | `collections.array.slice(arr: array, start: int, end: int) → array` | Sub-array `[start, end)` | | `join` | `collections.array.join(arr: array, sep: string) → string` | Join elements with a separator | ## collections.tuple An immutable fixed-size view over a native array (no handle). | Function | Signature | Description | | --- | --- | --- | | `new` | `collections.tuple.new(vals: array) → array` | Create a tuple from an array (copies it) | | `get` | `collections.tuple.get(tuple: array, index: int) → any` | Element at index (empty string if out of range) | | `size` | `collections.tuple.size(tuple: array) → int` | Element count | | `contains` | `collections.tuple.contains(tuple: array, value: any) → bool` | Whether `value` is present | | `toArray` | `collections.tuple.toArray(tuple: array) → array` | Return the underlying array | ## Example ```goost import collections // handle-based: ordered map let m = collections.map.new() collections.map.set(m, "name", "ada") collections.map.set(m, "role", "engineer") print(collections.map.get(m, "name")) // ada print(collections.map.keys(m)) // ["name", "role"] collections.map.free(m) // native arrays need no handle let nums = [3, 1, 2, 1] print(collections.array.unique(nums)) // [3, 1, 2] print(collections.array.join(nums, "-")) // 3-1-2-1 ``` --- # time The `time` module groups clock readings, date-component extraction, sleeping, durations, and formatting into five submodules, plus a few top-level shortcuts. Timestamps are Unix seconds and all components are computed in UTC. Import it with `import time`. ## time.clock | Function | Signature | Description | | --- | --- | --- | | `now` | `time.clock.now() → string` | current time as an RFC3339 string | | `unix` | `time.clock.unix() → int` | current time in Unix seconds | | `unixMilli` | `time.clock.unixMilli() → int` | current time in Unix milliseconds | | `unixMicro` | `time.clock.unixMicro() → int` | current time in Unix microseconds | | `unixNano` | `time.clock.unixNano() → int` | current time in Unix nanoseconds | ## time.date | Function | Signature | Description | | --- | --- | --- | | `year` | `time.date.year(ts: int) → int` | year of the timestamp | | `month` | `time.date.month(ts: int) → int` | month, 1–12 | | `day` | `time.date.day(ts: int) → int` | day of month | | `hour` | `time.date.hour(ts: int) → int` | hour, 0–23 | | `minute` | `time.date.minute(ts: int) → int` | minute, 0–59 | | `second` | `time.date.second(ts: int) → int` | second, 0–59 | | `weekday` | `time.date.weekday(ts: int) → string` | weekday name, e.g. "Monday" | | `isLeapYear` | `time.date.isLeapYear(year: int) → bool` | true if the year is a leap year | | `fromParts` | `time.date.fromParts(year: int, month: int, day: int, hour: int, min: int, sec: int) → int` | build a Unix timestamp from UTC components | ## time.timer | Function | Signature | Description | | --- | --- | --- | | `sleep` | `time.timer.sleep(ms: int) → void` | block the current execution for `ms` milliseconds | | `since` | `time.timer.since(ts: int) → int` | milliseconds elapsed since `ts` | | `until` | `time.timer.until(ts: int) → int` | milliseconds remaining until `ts` | ## time.duration | Function | Signature | Description | | --- | --- | --- | | `parse` | `time.duration.parse(s: string) → int` | parse a Go duration string (e.g. "1h30m") into milliseconds; 0 on error | | `format` | `time.duration.format(ms: int) → string` | format milliseconds as a duration string | | `seconds` | `time.duration.seconds(n: int) → int` | `n` seconds in milliseconds | | `minutes` | `time.duration.minutes(n: int) → int` | `n` minutes in milliseconds | | `hours` | `time.duration.hours(n: int) → int` | `n` hours in milliseconds | | `days` | `time.duration.days(n: int) → int` | `n` days in milliseconds | ## time.format | Function | Signature | Description | | --- | --- | --- | | `rfc3339` | `time.format.rfc3339(ts: int) → string` | format as RFC3339 | | `rfc1123` | `time.format.rfc1123(ts: int) → string` | format as RFC1123 | | `date` | `time.format.date(ts: int) → string` | format as `2006-01-02` | | `time` | `time.format.time(ts: int) → string` | format as `15:04:05` | | `datetime` | `time.format.datetime(ts: int) → string` | format as `2006-01-02 15:04:05` | | `custom` | `time.format.custom(ts: int, layout: string) → string` | format with a Go layout string | ## Top-level shortcuts | Function | Signature | Description | | --- | --- | --- | | `now` | `time.now() → string` | current time as RFC3339 | | `unix` | `time.unix() → int` | current time in Unix seconds | | `sleep` | `time.sleep(ms: int) → void` | block for `ms` milliseconds | | `parse` | `time.parse(s: string) → int` | parse an RFC3339 string into a Unix timestamp; 0 on error | ## Example ```goost import time // stamp "now", then break it into parts let ts = time.clock.unix() print(time.format.datetime(ts)) // 2026-05-29 14:03:21 print(time.date.weekday(ts)) // Friday // build a timestamp and measure elapsed time let start = time.clock.unixMilli() time.timer.sleep(time.duration.seconds(1)) print(time.timer.since(ts)) // milliseconds since ts ``` --- # date The `date` module is a flat convenience API for working with Unix timestamps (seconds since the epoch). All component extraction is done in UTC. Import it with `import date`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `now` | `date.now() → string` | current time as an RFC3339 string | | `unix` | `date.unix() → int` | current time in Unix seconds | | `parse` | `date.parse(s: string) → int` | parse an RFC3339 string into Unix seconds | | `format` | `date.format(ts: int, layout: string) → string` | format a timestamp; `layout` is a Go layout or a shortcut: `"date"`, `"time"`, `"datetime"`, `"rfc3339"`, `"rfc1123"` | | `year` | `date.year(ts: int) → int` | year of the timestamp | | `month` | `date.month(ts: int) → int` | month, 1–12 | | `day` | `date.day(ts: int) → int` | day of month | | `hour` | `date.hour(ts: int) → int` | hour, 0–23 | | `minute` | `date.minute(ts: int) → int` | minute, 0–59 | | `second` | `date.second(ts: int) → int` | second, 0–59 | | `weekday` | `date.weekday(ts: int) → string` | weekday name, e.g. "Monday" | | `addDays` | `date.addDays(ts: int, n: int) → int` | timestamp `n` days later | | `addHours` | `date.addHours(ts: int, n: int) → int` | timestamp `n` hours later | | `addMinutes` | `date.addMinutes(ts: int, n: int) → int` | timestamp `n` minutes later | | `diff` | `date.diff(ts1: int, ts2: int) → int` | seconds between two timestamps (`ts2 − ts1`) | | `isLeapYear` | `date.isLeapYear(year: int) → bool` | true if the year is a leap year | ## Example ```goost import date let now = date.unix() let tomorrow = date.addDays(now, 1) print(date.format(now, "datetime")) // 2026-05-29 14:03:21 print(date.weekday(tomorrow)) // Saturday print(date.diff(now, tomorrow)) // 86400 seconds ``` --- # timer The `timer` module schedules callback functions to run later or repeatedly. Each scheduling call returns an integer handle that can be passed to `timer.clear` to cancel it. Callbacks are invoked with no arguments and run inside the same VM, so timers should only be used in long-running programs that outlive their callbacks. Import it with `import timer`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `setTimeout` | `timer.setTimeout(ms: int, fn) → int` | run `fn` once after `ms` milliseconds; returns a cancellable handle | | `setInterval` | `timer.setInterval(ms: int, fn) → int` | run `fn` repeatedly every `ms` milliseconds until cleared; returns a handle | | `clear` | `timer.clear(id: int) → bool` | cancel a pending timeout or interval; false if already cleared or fired | Note: a `setInterval` callback that takes longer than `ms` to run causes ticks to coalesce (the next tick fires right after the previous one returns), matching Go's `time.Ticker` rather than browser `setInterval`. ## Example ```goost import timer // fire once after one second timer.setTimeout(1000, fn() { print("tick") }) // repeat every 500ms, then stop after a while let id = timer.setInterval(500, fn() { print("poll") }) timer.clear(id) ``` --- # io The `io` module reads and writes files, manages directories, streams large files through integer handles, manipulates paths, and watches the filesystem for changes. It is organized into the `file`, `stream`, `path`, and `watch` submodules, with flat top-level shortcuts for the most common operations. Import it with `import io`. ## io.file | Function | Signature | Description | | --- | --- | --- | | `read` | `io.file.read(path: string) → string` | read the whole file as a string | | `write` | `io.file.write(path: string, content: string) → bool` | write content, truncating; true on success | | `append` | `io.file.append(path: string, content: string) → bool` | append content, creating if needed | | `exists` | `io.file.exists(path: string) → bool` | true if the path exists | | `delete` | `io.file.delete(path: string) → bool` | remove the file; true on success | | `copy` | `io.file.copy(src: string, dst: string) → bool` | copy `src` to `dst` | | `rename` | `io.file.rename(src: string, dst: string) → bool` | rename/move `src` to `dst` | | `size` | `io.file.size(path: string) → int` | file size in bytes; 0 on error | | `isFile` | `io.file.isFile(path: string) → bool` | true if the path is a regular file | | `isDir` | `io.file.isDir(path: string) → bool` | true if the path is a directory | | `mkdir` | `io.file.mkdir(path: string) → bool` | create the directory and parents | | `readDir` | `io.file.readDir(path: string) → string[]` | list entry names in the directory | ## io.stream Stream functions operate on an integer **handle** returned by `io.stream.open` (or `-1` on failure). Always `close` the handle when done. Open modes are `"r"`, `"w"`, `"a"`, and `"rw"`. | Function | Signature | Description | | --- | --- | --- | | `open` | `io.stream.open(path: string, mode?: string) → int` | open a file and return a handle (`-1` on error); default mode `"r"` | | `read` | `io.stream.read(id: int, n: int) → string` | read up to `n` bytes | | `readLine` | `io.stream.readLine(id: int) → string` | read one line including the trailing newline | | `readAll` | `io.stream.readAll(id: int) → string` | read the rest of the stream | | `write` | `io.stream.write(id: int, data: string) → bool` | write data to the stream | | `seek` | `io.stream.seek(id: int, offset: int) → bool` | seek to `offset` from the start | | `tell` | `io.stream.tell(id: int) → int` | current position; `-1` on error | | `eof` | `io.stream.eof(id: int) → bool` | true if end of file reached | | `close` | `io.stream.close(id: int) → bool` | close the handle | ## io.path | Function | Signature | Description | | --- | --- | --- | | `abs` | `io.path.abs(path: string) → string` | absolute form of the path | | `ext` | `io.path.ext(path: string) → string` | file extension, including the dot | | `base` | `io.path.base(path: string) → string` | last path element | | `dir` | `io.path.dir(path: string) → string` | directory portion of the path | | `join` | `io.path.join(parts: string[]) → string` | join path elements | | `clean` | `io.path.clean(path: string) → string` | normalize the path | | `isAbs` | `io.path.isAbs(path: string) → bool` | true if the path is absolute | | `rel` | `io.path.rel(basePath: string, targetPath: string) → string` | relative path from base to target | ## io.watch Watch the filesystem for changes (built on fsnotify). `start` returns an integer **handle** (`-1` on error) and runs your callback for each event with an object `{path, op}`, where `op` is one of `"create"`, `"write"`, `"remove"`, `"rename"`, or `"chmod"`. A watch covers a single path — walk a directory tree and `add` each subdirectory for recursive watching. | Function | Signature | Description | | --- | --- | --- | | `start` | `io.watch.start(path: string, fn) → int` | watch `path`; `fn({path, op})` fires per event. Returns a handle (`-1` on error) | | `add` | `io.watch.add(handle: int, path: string) → bool` | add another path to an existing watcher | | `stop` | `io.watch.stop(handle: int) → bool` | stop watching and free resources | ```goost import io let h = io.watch.start("./config", fn(ev) { println(ev.op + ": " + ev.path) }) // ... later ... io.watch.stop(h) ``` ## Top-level shortcuts | Function | Signature | Description | | --- | --- | --- | | `read` | `io.read(path: string) → string` | read the whole file | | `write` | `io.write(path: string, content: string) → bool` | write content, truncating | | `append` | `io.append(path: string, content: string) → bool` | append content | | `exists` | `io.exists(path: string) → bool` | true if the path exists | | `delete` | `io.delete(path: string) → bool` | remove the file | | `mkdir` | `io.mkdir(path: string) → bool` | create the directory and parents | | `readDir` | `io.readDir(path: string) → string[]` | list directory entries | | `isFile` | `io.isFile(path: string) → bool` | true if a regular file | | `isDir` | `io.isDir(path: string) → bool` | true if a directory | | `size` | `io.size(path: string) → int` | file size in bytes | | `copy` | `io.copy(src: string, dst: string) → bool` | copy a file | | `rename` | `io.rename(src: string, dst: string) → bool` | rename/move a file | | `abs` | `io.abs(path: string) → string` | absolute path | | `ext` | `io.ext(path: string) → string` | file extension | | `base` | `io.base(path: string) → string` | last path element | | `dir` | `io.dir(path: string) → string` | directory portion | | `join` | `io.join(parts: string[]) → string` | join path elements | ## Example ```goost import io // quick whole-file read/write io.write("notes.txt", "hello\nworld\n") print(io.read("notes.txt")) // stream a large file line by line via a handle let h = io.stream.open("notes.txt", "r") while !io.stream.eof(h) { print(io.stream.readLine(h)) } io.stream.close(h) ``` --- # os The `os` module exposes the host environment, process information, signal trapping, file permissions, low-level system info, command-line arguments, and process exit. It is organized into submodules with flat top-level shortcuts for common needs. Import it with `import os`. ## os.env | Function | Signature | Description | | --- | --- | --- | | `get` | `os.env.get(key: string) → string` | value of an environment variable (empty if unset) | | `set` | `os.env.set(key: string, value: string) → bool` | set an environment variable | | `unset` | `os.env.unset(key: string) → bool` | remove an environment variable | | `has` | `os.env.has(key: string) → bool` | true if the variable is defined | | `all` | `os.env.all() → string` | all variables as a JSON object string | ## os.process | Function | Signature | Description | | --- | --- | --- | | `pid` | `os.process.pid() → int` | current process ID | | `ppid` | `os.process.ppid() → int` | parent process ID | | `uid` | `os.process.uid() → int` | user ID | | `gid` | `os.process.gid() → int` | group ID | | `exit` | `os.process.exit(code?: int) → void` | exit the process with `code` (default 0) | | `args` | `os.process.args() → string[]` | command-line arguments | | `cwd` | `os.process.cwd() → string` | current working directory | | `chdir` | `os.process.chdir(dir: string) → bool` | change the working directory | ## os.signal | Function | Signature | Description | | --- | --- | --- | | `notify` | `os.signal.notify() → void` | start trapping SIGINT/SIGTERM/SIGHUP | | `trap` | `os.signal.trap() → string` | name of the last received signal (empty if none) | | `reset` | `os.signal.reset() → void` | clear the recorded last signal | ## os.permission | Function | Signature | Description | | --- | --- | --- | | `readable` | `os.permission.readable(path: string) → bool` | true if the path can be opened for reading | | `writable` | `os.permission.writable(path: string) → bool` | true if the path can be opened for writing | | `executable` | `os.permission.executable(path: string) → bool` | true if any execute bit is set | | `chmod` | `os.permission.chmod(path: string, mode: int) → bool` | change file mode bits | ## os.syscall | Function | Signature | Description | | --- | --- | --- | | `arch` | `os.syscall.arch() → string` | CPU architecture (e.g. "arm64") | | `os` | `os.syscall.os() → string` | operating system (e.g. "darwin") | | `hostname` | `os.syscall.hostname() → string` | machine hostname | | `pagesize` | `os.syscall.pagesize() → int` | memory page size in bytes | | `numCPU` | `os.syscall.numCPU() → int` | number of logical CPUs | ## os.args | Function | Signature | Description | | --- | --- | --- | | `all` | `os.args.all() → string[]` | all command-line arguments | | `get` | `os.args.get(idx: int) → string` | argument at `idx` (empty if out of range) | | `len` | `os.args.len() → int` | number of arguments | ## os.exit | Function | Signature | Description | | --- | --- | --- | | `code` | `os.exit.code(code?: int) → void` | exit with `code` (default 0) | | `success` | `os.exit.success() → void` | exit with code 0 | | `failure` | `os.exit.failure() → void` | exit with code 1 | ## Top-level shortcuts | Function | Signature | Description | | --- | --- | --- | | `getenv` | `os.getenv(key: string) → string` | read an environment variable | | `setenv` | `os.setenv(key: string, value: string) → bool` | set an environment variable | | `hostname` | `os.hostname() → string` | machine hostname | | `arch` | `os.arch() → string` | CPU architecture | | `platform` | `os.platform() → string` | operating system | | `exitCode` | `os.exitCode(code?: int) → void` | exit with `code` (default 0) | | `pid` | `os.pid() → int` | current process ID | ## Example ```goost import os print(os.platform()) // darwin print(os.getenv("HOME")) // trap shutdown signals in a long-running program os.signal.notify() if os.signal.trap() == "interrupt" { os.exit.success() } ``` --- # exec The `exec` module runs external commands — either through a shell or directly with an argument list — captures their combined stdout/stderr, reads exit codes, and provides quick access to environment variables and the working directory. Import it with `import exec`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `run` | `exec.run(cmd: string) → string` | run `cmd` via `sh -c` and return combined stdout/stderr | | `runArgs` | `exec.runArgs(cmd: string, args: string[]) → string` | run a program directly (no shell) with `args`, returning combined output | | `exitCode` | `exec.exitCode(cmd: string) → int` | run `cmd` via shell and return its exit code (`-1` if it could not run) | | `env` | `exec.env(key: string) → string` | read an environment variable | | `setEnv` | `exec.setEnv(key: string, val: string) → bool` | set an environment variable | | `cwd` | `exec.cwd() → string` | current working directory | | `exists` | `exec.exists(cmd: string) → bool` | true if `cmd` is found on `PATH` | ## Example ```goost import exec if exec.exists("git") { let out = exec.run("git rev-parse --short HEAD") print(out) } // run a program directly with arguments, no shell parsing print(exec.runArgs("echo", ["hello", "world"])) // check whether a command succeeded if exec.exitCode("test -f config.toml") == 0 { print("config present") } ``` --- # signal Register callback functions to run when the process receives a POSIX signal — useful for graceful shutdown and config reloads. Import it with `import signal`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `on` | `signal.on(name: string, fn: function) → bool` | Register `fn()` as a handler for the named signal; multiple handlers on the same signal fire in registration order on each delivery. Returns `false` if the name is unknown | | `off` | `signal.off(name: string) → bool` | Remove all handlers for that signal and stop watching it. Returns `true` if any were removed | Recognized names (case-sensitive): `SIGINT` (also `int`, `interrupt`), `SIGTERM` (`term`), `SIGHUP` (`hup`), `SIGUSR1` (`usr1`), `SIGUSR2` (`usr2`), `SIGQUIT` (`quit`), `SIGPIPE` (`pipe`), `SIGCHLD` (`chld`). ## Example ```goost import signal // graceful shutdown on Ctrl-C or kill signal.on("SIGINT", fn() { print("shutting down...") }) // reload config on SIGHUP signal.on("SIGHUP", fn() { print("reloading config") }) // stop listening for a signal signal.off("SIGHUP") ``` --- # terminal Build terminal UIs: query the terminal, switch stdin to raw mode, read individual keystrokes, and generate ANSI escape sequences for color, cursor, and screen control. Import it with `import terminal`. ## State and input | Function | Signature | Description | | --- | --- | --- | | `isatty` | `terminal.isatty() → bool` | Whether stdout is a terminal | | `size` | `terminal.size() → object` | Terminal dimensions as `{cols, rows}` (defaults to 80x24 if unavailable) | | `rawMode` | `terminal.rawMode() → int` | Switch stdin to raw mode (no echo, no line buffering); returns a state handle, or `-1` on error | | `restoreMode` | `terminal.restoreMode(handle: int) → bool` | Restore the mode saved by `rawMode` | | `readKey` | `terminal.readKey() → string` | Read one keystroke; returns a key name (see below) or a single printable character | | `write` | `terminal.write(s: string) → bool` | Unbuffered write to stdout with no trailing newline — use for mid-frame ANSI sequences | | `flush` | `terminal.flush() → void` | Flush pending stdout output | `readKey` returns names like `up`, `down`, `left`, `right`, `enter`, `tab`, `esc`, `backspace`, `home`, `end`, `pageup`, `pagedown`, `insert`, `delete`, `f1`–`f12`, and `ctrl+a`–`ctrl+z`. ## Cursor and screen These return ANSI escape-sequence strings to pass to `terminal.write`. | Function | Signature | Description | | --- | --- | --- | | `clear` | `terminal.clear() → string` | Clear screen and home the cursor | | `clearLine` | `terminal.clearLine() → string` | Clear the entire current line | | `clearRight` | `terminal.clearRight() → string` | Clear from cursor to end of line | | `hideCursor` | `terminal.hideCursor() → string` | Hide the cursor | | `showCursor` | `terminal.showCursor() → string` | Show the cursor | | `altScreen` | `terminal.altScreen() → string` | Enter the alternate screen buffer | | `mainScreen` | `terminal.mainScreen() → string` | Leave the alternate screen buffer | | `saveCursor` | `terminal.saveCursor() → string` | Save the cursor position | | `loadCursor` | `terminal.loadCursor() → string` | Restore the saved cursor position | | `moveTo` | `terminal.moveTo(row: int, col: int) → string` | Move cursor to a 1-indexed position | ## Styling and color | Function | Signature | Description | | --- | --- | --- | | `reset` | `terminal.reset() → string` | Reset all attributes | | `bold` | `terminal.bold() → string` | Bold | | `dim` | `terminal.dim() → string` | Dim | | `italic` | `terminal.italic() → string` | Italic | | `underline` | `terminal.underline() → string` | Underline | | `reverse` | `terminal.reverse() → string` | Reverse video | | `color` | `terminal.color(name: string) → string` | Foreground color by name (`red`, `green`, …, `brightblue`, `default`) or a `"0"`–`"255"` string for 256-color | | `bg` | `terminal.bg(name: string) → string` | Background color, same names | | `rgb` | `terminal.rgb(r: int, g: int, b: int) → string` | 24-bit truecolor foreground | | `bgRgb` | `terminal.bgRgb(r: int, g: int, b: int) → string` | 24-bit truecolor background | ## Example ```goost import terminal // switch to raw mode and read keys until Esc let st = terminal.rawMode() terminal.write(terminal.clear()) let running = true while running { let key = terminal.readKey() if key == "esc" { running = false } else { // print the key in green at the top-left terminal.write(terminal.moveTo(1, 1) + terminal.clearLine()) terminal.write(terminal.color("green") + "key: " + key + terminal.reset()) terminal.flush() } } terminal.restoreMode(st) ``` --- # runtime Inspect the running program, share state across VMs through a process-global store, reflect on value types, and work with errors, panics, and environment variables. Import it with `import runtime`. ## runtime.core A string key-value store shared across all VMs in the process. | Function | Signature | Description | | --- | --- | --- | | `share` | `runtime.core.share(key: string, value: string) → void` | Store a value under a key | | `fetch` | `runtime.core.fetch(key: string) → string` | Read a key (empty string if absent) | | `remove` | `runtime.core.remove(key: string) → void` | Delete a key | | `keys` | `runtime.core.keys() → string[]` | All stored keys | | `has` | `runtime.core.has(key: string) → bool` | Whether a key exists | ## runtime.reflect | Function | Signature | Description | | --- | --- | --- | | `typeOf` | `runtime.reflect.typeOf(v: any) → string` | The value's type name | | `isInt` | `runtime.reflect.isInt(v: any) → bool` | Whether `v` is an int | | `isFloat` | `runtime.reflect.isFloat(v: any) → bool` | Whether `v` is a float | | `isString` | `runtime.reflect.isString(v: any) → bool` | Whether `v` is a string | | `isBool` | `runtime.reflect.isBool(v: any) → bool` | Whether `v` is a bool | | `isArray` | `runtime.reflect.isArray(v: any) → bool` | Whether `v` is an array | | `isFunction` | `runtime.reflect.isFunction(v: any) → bool` | Whether `v` is a function or builtin | | `isModule` | `runtime.reflect.isModule(v: any) → bool` | Whether `v` is a module | | `isVoid` | `runtime.reflect.isVoid(v: any) → bool` | Whether `v` is void | ## runtime.info | Function | Signature | Description | | --- | --- | --- | | `version` | `runtime.info.version() → string` | Go runtime version | | `numCPU` | `runtime.info.numCPU() → int` | Number of logical CPUs | | `goroutines` | `runtime.info.goroutines() → int` | Current goroutine count | | `goarch` | `runtime.info.goarch() → string` | Target architecture | | `goos` | `runtime.info.goos() → string` | Target OS | ## runtime.error | Function | Signature | Description | | --- | --- | --- | | `new` | `runtime.error.new(msg: any) → string` | Create an error-tagged string | | `message` | `runtime.error.message(errStr: string) → string` | Extract the message from an error string | | `isError` | `runtime.error.isError(v: any) → bool` | Whether the value is an error string | ## runtime.panic | Function | Signature | Description | | --- | --- | --- | | `throw` | `runtime.panic.throw(msg?: any) → void` | Print `[PANIC] ` to stderr and exit with code 2 | ## runtime.env | Function | Signature | Description | | --- | --- | --- | | `get` | `runtime.env.get(key: string) → string` | Read an environment variable (empty string if unset) | | `set` | `runtime.env.set(key: string, val: string) → bool` | Set an environment variable | | `args` | `runtime.env.args() → string[]` | Process command-line arguments | ## Example ```goost import runtime // share state across VMs runtime.core.share("session", "abc123") print(runtime.core.fetch("session")) // abc123 // inspect the runtime print(runtime.info.numCPU()) print(runtime.info.goos()) // type checks and errors let v = 42 if runtime.reflect.isInt(v) { print("int: " + runtime.reflect.typeOf(v)) } let err = runtime.error.new("not found") if runtime.error.isError(err) { print(runtime.error.message(err)) // not found } ``` --- # memory Grow-able byte buffers, string interning pools, and read-only access to allocator and garbage-collector statistics. Import it with `import memory`. Buffers and pools are referenced by integer handles returned from their `new` functions; free them with `free` when done. ## memory.buffer | Function | Signature | Description | | --- | --- | --- | | `new` | `memory.buffer.new() → int` | Allocate a new buffer; returns its handle | | `write` | `memory.buffer.write(id: int, data: string) → bool` | Append data to the buffer | | `read` | `memory.buffer.read(id: int) → string` | Read the full buffer contents | | `reset` | `memory.buffer.reset(id: int) → void` | Empty the buffer, keeping it allocated | | `len` | `memory.buffer.len(id: int) → int` | Current byte length | | `free` | `memory.buffer.free(id: int) → void` | Release the buffer | ## memory.pool A pool deduplicates equal strings (interning). | Function | Signature | Description | | --- | --- | --- | | `new` | `memory.pool.new() → int` | Allocate a new interning pool; returns its handle | | `intern` | `memory.pool.intern(id: int, s: string) → string` | Return the pooled copy of `s`, adding it if new | | `has` | `memory.pool.has(id: int, s: string) → bool` | Whether `s` is already interned | | `size` | `memory.pool.size(id: int) → int` | Number of interned strings | | `free` | `memory.pool.free(id: int) → void` | Release the pool | ## memory.alloc | Function | Signature | Description | | --- | --- | --- | | `stats` | `memory.alloc.stats() → string` | JSON string with `alloc`, `totalAlloc`, `sys`, `numGC`, `goroutines` | | `heapAlloc` | `memory.alloc.heapAlloc() → int` | Bytes of allocated heap objects | | `heapSys` | `memory.alloc.heapSys() → int` | Bytes of heap memory obtained from the OS | | `numGC` | `memory.alloc.numGC() → int` | Completed GC cycles | ## memory.gc | Function | Signature | Description | | --- | --- | --- | | `run` | `memory.gc.run() → void` | Run a garbage collection now | | `setPercent` | `memory.gc.setPercent(pct: int) → int` | Set the GC target percentage; returns the previous value | | `freeOSMemory` | `memory.gc.freeOSMemory() → void` | Force a GC and return unused memory to the OS | ## Example ```goost import memory // build a string with a buffer let buf = memory.buffer.new() memory.buffer.write(buf, "hello ") memory.buffer.write(buf, "world") print(memory.buffer.read(buf)) // hello world print(memory.buffer.len(buf)) // 11 memory.buffer.free(buf) // intern repeated strings let pool = memory.pool.new() let a = memory.pool.intern(pool, "tag") let b = memory.pool.intern(pool, "tag") print(memory.pool.size(pool)) // 1 memory.pool.free(pool) // inspect heap usage print(memory.alloc.heapAlloc()) ``` --- # logging Leveled logging with `[LEVEL]`-prefixed lines. `info` and `debug` go to stdout; `warn`, `error`, and `fatal` go to stderr. Import it with `import logging`. ## Functions | Function | Signature | Description | | --- | --- | --- | | `debug` | `logging.debug(msg?: any) → void` | Log `[DEBUG]` to stdout, but only when debug is enabled | | `info` | `logging.info(msg?: any) → void` | Log `[INFO]` to stdout | | `warn` | `logging.warn(msg?: any) → void` | Log `[WARN]` to stderr | | `error` | `logging.error(msg?: any) → void` | Log `[ERROR]` to stderr | | `fatal` | `logging.fatal(msg?: any) → void` | Log `[FATAL]` to stderr, then exit with code 1 | | `setDebug` | `logging.setDebug(enabled: bool) → void` | Enable or disable debug output | | `isDebug` | `logging.isDebug() → bool` | Whether debug output is enabled | | `withPrefix` | `logging.withPrefix(prefix: string, msg: any) → void` | Log `[] ` to stdout | Debug output is off by default; enable it with `logging.setDebug(true)` or the `--debug` CLI flag. ## Example ```goost import logging logging.info("server starting") logging.setDebug(true) logging.debug("config loaded") // [DEBUG] config loaded logging.withPrefix("db", "connected") // [db] connected logging.warn("retrying connection") if !logging.isDebug() { logging.error("debug should be on") } // logging.fatal("unrecoverable") // prints [FATAL] and exits ``` --- # net Low-level networking: TCP and UDP sockets, TLS, DNS lookups, and an embedded HTTP client. Import it with `import net`. Connections, listeners, and UDP sockets are referenced by integer handles returned from `connect`, `listen`, and `accept`. A handle of `-1` means the operation failed. Close handles with the matching `close` function when done. ## net.tcp | Function | Signature | Description | | --- | --- | --- | | `connect` | `net.tcp.connect(host: string, port: int) → int` | Dial a TCP connection; returns a handle or `-1` | | `connectTLS` | `net.tcp.connectTLS(host: string, port: int) → int` | Dial a TLS connection | | `listen` | `net.tcp.listen(host: string, port: int) → int` | Open a listener; returns a listener handle | | `listenTLS` | `net.tcp.listenTLS(host: string, port: int, certPEM: string, keyPEM: string) → int` | Open a TLS listener with the given cert and key | | `accept` | `net.tcp.accept(listenerId: int) → int` | Accept the next connection; returns a connection handle | | `send` | `net.tcp.send(id: int, data: string) → bool` | Write data to a connection | | `recv` | `net.tcp.recv(id: int, maxBytes: int) → string` | Read up to `maxBytes` (empty string on EOF/error) | | `close` | `net.tcp.close(id: int) → bool` | Close a connection or listener | | `setNoDelay` | `net.tcp.setNoDelay(id: int, noDelay: bool) → bool` | Toggle TCP_NODELAY | | `setKeepAlive` | `net.tcp.setKeepAlive(id: int, keepAlive: bool) → bool` | Toggle TCP keep-alive | `net.socket` is an alias for `net.tcp` with the same API. ## net.udp | Function | Signature | Description | | --- | --- | --- | | `connect` | `net.udp.connect(host: string, port: int) → int` | Open a connected UDP socket | | `listen` | `net.udp.listen(host: string, port: int) → int` | Open a UDP socket bound to an address | | `send` | `net.udp.send(id: int, data: string) → bool` | Send a datagram | | `recv` | `net.udp.recv(id: int, maxBytes: int) → string` | Receive up to `maxBytes` | | `close` | `net.udp.close(id: int) → bool` | Close the socket | ## net.dns | Function | Signature | Description | | --- | --- | --- | | `resolve` | `net.dns.resolve(host: string) → string` | First resolved address (empty on failure) | | `resolveAll` | `net.dns.resolveAll(host: string) → string[]` | All resolved addresses | | `lookupMX` | `net.dns.lookupMX(host: string) → string[]` | MX records as `"host pref"` strings | | `lookupTXT` | `net.dns.lookupTXT(host: string) → string[]` | TXT records | | `lookupNS` | `net.dns.lookupNS(host: string) → string[]` | NS record hostnames | | `reverse` | `net.dns.reverse(ip: string) → string` | Reverse-DNS the IP to a hostname | ## net.http An HTTP client. `get`, `post`, `put`, `patch`, `delete`, and `request` return the response body; `head` and `status` return the status code. See the dedicated [`http`](/docs/stdlib/http) module for the same client surface. | Function | Signature | Description | | --- | --- | --- | | `get` | `net.http.get(url: string) → string` | GET, returns body | | `post` | `net.http.post(url: string, body: string) → string` | POST with JSON body | | `put` | `net.http.put(url: string, body: string) → string` | PUT with JSON body | | `patch` | `net.http.patch(url: string, body: string) → string` | PATCH with JSON body | | `delete` | `net.http.delete(url: string) → string` | DELETE, returns body | | `head` | `net.http.head(url: string) → int` | HEAD, returns status code | | `status` | `net.http.status(url: string) → int` | GET, returns status code only | | `request` | `net.http.request(method: string, url: string, body: string, headers: string[]) → string` | Generic request with custom `"Key: Value"` headers | ## Top-level shortcuts `net` also exposes flat helpers operating on the same handle registry. | Function | Signature | Description | | --- | --- | --- | | `connect` | `net.connect(host: string, port: int) → int` | Dial a TCP connection | | `connectTLS` | `net.connectTLS(host: string, port: int) → int` | Dial a TLS connection | | `listen` | `net.listen(host: string, port: int) → int` | Open a TCP listener | | `accept` | `net.accept(listenerId: int) → int` | Accept a connection | | `send` | `net.send(id: int, data: string) → bool` | Write data | | `sendLine` | `net.sendLine(id: int, line: string) → bool` | Write data followed by CRLF | | `recv` | `net.recv(id: int, maxBytes: int) → string` | Read up to `maxBytes` | | `recvLine` | `net.recvLine(id: int) → string` | Read one CRLF-terminated line (trimmed) | | `recvTimeout` | `net.recvTimeout(id: int, ms: int, maxBytes: int) → string` | Read with a millisecond deadline | | `setTimeout` | `net.setTimeout(id: int, ms: int) → bool` | Set a read/write deadline (`0` clears it) | | `close` | `net.close(id: int) → bool` | Close a connection, listener, or UDP socket | | `localAddr` | `net.localAddr(id: int) → string` | Local address of the handle | | `remoteAddr` | `net.remoteAddr(id: int) → string` | Remote address of a connection | | `connectUDP` | `net.connectUDP(host: string, port: int) → int` | Open a connected UDP socket | | `listenUDP` | `net.listenUDP(host: string, port: int) → int` | Bind a UDP socket | | `sendUDP` | `net.sendUDP(id: int, data: string) → bool` | Send a datagram | | `recvUDP` | `net.recvUDP(id: int, maxBytes: int) → string` | Receive a datagram | | `resolve` | `net.resolve(host: string) → string` | First resolved address | | `resolveAll` | `net.resolveAll(host: string) → string[]` | All resolved addresses | ## Example ```goost import net // simple TCP client let conn = net.tcp.connect("example.com", 80) if conn != -1 { net.sendLine(conn, "GET / HTTP/1.0") net.sendLine(conn, "Host: example.com") net.sendLine(conn, "") let line = net.recvLine(conn) // first response line print(line) net.tcp.close(conn) } // DNS lookup print(net.dns.resolve("example.com")) ``` --- # http A small HTTP client. Body-returning methods give back the response body as a string; `head` and `status` return the status code as an int. Import it with `import http`. > Looking for the built-in HTTP server? `http.serve` is documented separately at [/docs/http-server](/docs/http-server). This page covers the client functions. ## Functions | Function | Signature | Description | | --- | --- | --- | | `get` | `http.get(url: string) → string` | GET request, returns the response body | | `post` | `http.post(url: string, body: string) → string` | POST with a JSON body, returns the body | | `put` | `http.put(url: string, body: string) → string` | PUT with a JSON body, returns the body | | `patch` | `http.patch(url: string, body: string) → string` | PATCH with a JSON body, returns the body | | `delete` | `http.delete(url: string) → string` | DELETE request, returns the body | | `head` | `http.head(url: string) → int` | HEAD request, returns the status code | | `status` | `http.status(url: string) → int` | GET request, returns only the status code | | `request` | `http.request(method: string, url: string, body: string, headers: string[]) → string` | Generic request with custom `"Key: Value"` headers; returns the body | `post`, `put`, and `patch` send `Content-Type: application/json` when the body is non-empty. `request` does the same unless you supply your own `Content-Type` header; pass `""` as the body for methods that carry none. ## Example ```goost import http // fetch a resource let body = http.get("https://api.example.com/items") print(body) // post JSON let created = http.post("https://api.example.com/items", "{\"name\":\"box\"}") // check existence without downloading the body if http.status("https://api.example.com/items/1") == 200 { print("exists") } // custom method and headers let result = http.request( "DELETE", "https://api.example.com/items/1", "", ["Authorization: Bearer token123"], ) ``` --- # crypto The `crypto` module bundles common cryptographic primitives into sub-modules: hashing, HMAC, AES-GCM encryption, RSA and Ed25519 signing, secure random generation, X.509 certificates, and TLS information. Import it with `import crypto`. ## crypto.hash All hash functions take a string and return a lowercase hex digest. | Function | Signature | Description | | --- | --- | --- | | `md5` | `crypto.hash.md5(s: string) → string` | MD5 hex digest (legacy; not secure) | | `sha1` | `crypto.hash.sha1(s: string) → string` | SHA-1 hex digest (legacy; not secure) | | `sha256` | `crypto.hash.sha256(s: string) → string` | SHA-256 hex digest | | `sha512` | `crypto.hash.sha512(s: string) → string` | SHA-512 hex digest | | `blake3` | `crypto.hash.blake3(s: string) → string` | BLAKE3-256 hex digest | ## crypto.hmac | Function | Signature | Description | | --- | --- | --- | | `sha256` | `crypto.hmac.sha256(key: string, data: string) → string` | HMAC-SHA256 hex digest | | `sha512` | `crypto.hmac.sha512(key: string, data: string) → string` | HMAC-SHA512 hex digest | | `verify` | `crypto.hmac.verify(key: string, data: string, expected: string) → bool` | constant-time compare of HMAC-SHA256 against a hex digest | ## crypto.aes AES-256-GCM. The key can be any length; it is hashed to a 32-byte key with SHA-256. Ciphertext is base64-encoded with the nonce prepended. | Function | Signature | Description | | --- | --- | --- | | `encrypt` | `crypto.aes.encrypt(key: string, plaintext: string) → string` | encrypt to base64 ciphertext | | `decrypt` | `crypto.aes.decrypt(key: string, ciphertext: string) → string` | decrypt base64 ciphertext to plaintext | ## crypto.rsa | Function | Signature | Description | | --- | --- | --- | | `generate` | `crypto.rsa.generate(bits?: int) → string` | JSON string `{privateKey, publicKey}` as PEM (default 2048 bits) | | `sign` | `crypto.rsa.sign(privateKeyPEM: string, data: string) → string` | base64 RSA PKCS#1 v1.5 SHA-256 signature | ## crypto.ed25519 | Function | Signature | Description | | --- | --- | --- | | `generate` | `crypto.ed25519.generate() → string` | JSON string `{privateKey, publicKey}` as hex | | `sign` | `crypto.ed25519.sign(privateKeyHex: string, message: string) → string` | hex signature | | `verify` | `crypto.ed25519.verify(publicKeyHex: string, message: string, signatureHex: string) → bool` | verify a hex signature | ## crypto.random | Function | Signature | Description | | --- | --- | --- | | `uuid` | `crypto.random.uuid() → string` | random UUID v4 string | | `bytes` | `crypto.random.bytes(n: int) → string` | n secure random bytes, hex-encoded | ## crypto.cert | Function | Signature | Description | | --- | --- | --- | | `selfSigned` | `crypto.cert.selfSigned(host?: string, bits?: int) → string` | JSON `{cert, key}` PEM self-signed cert (default `localhost`, 2048 bits, 1-year validity) | | `parse` | `crypto.cert.parse(certPEM: string) → string` | JSON with `subject`, `issuer`, `notBefore`, `notAfter`, `dnsNames` | | `verify` | `crypto.cert.verify(certPEM: string) → bool` | validate a PEM cert against system roots | ## crypto.tls | Function | Signature | Description | | --- | --- | --- | | `version` | `crypto.tls.version() → string` | supported TLS version string | | `ciphers` | `crypto.tls.ciphers() → string[]` | names of secure cipher suites | | `insecureCiphers` | `crypto.tls.insecureCiphers() → string[]` | names of insecure cipher suites | | `verify` | `crypto.tls.verify(host: string, port: int) → bool` | true if a TLS handshake to host:port succeeds | ## Top-level shortcuts For convenience the module also exposes flat aliases at the top level: `crypto.md5`, `crypto.sha1`, `crypto.sha224`, `crypto.sha256`, `crypto.sha384`, `crypto.sha512`, `crypto.blake3` (string → hex digest), `crypto.hmacSha256` / `crypto.hmacSha512` `(key, data) → hex`, `crypto.hexEncode` / `crypto.hexDecode`, and `crypto.uuid()`. ## Example ```goost import crypto let digest = crypto.sha256("hello world") println("sha256: " + digest) // HMAC sign and verify let mac = crypto.hmac.sha256("secret-key", "payload") let ok = crypto.hmac.verify("secret-key", "payload", mac) println("hmac valid: " + toString(ok)) // AES round-trip let sealed = crypto.aes.encrypt("my-password", "top secret") let plain = crypto.aes.decrypt("my-password", sealed) println("decrypted: " + plain) // Random ID println("id: " + crypto.uuid()) ``` --- # redis The `redis` module is a Redis client. You connect once to get an integer handle, then pass that handle to every command. Import it with `import redis`. ## Connection `redis.connect` returns an `int` handle on success, or `-1` if the URL is invalid or the connection/ping fails. Pass the handle to every other call. | Function | Signature | Description | | --- | --- | --- | | `connect` | `redis.connect(url: string) → int` | connect via a `redis://` or `rediss://` URL; returns a handle or `-1` | | `close` | `redis.close(handle: int) → bool` | close the connection; true if the handle existed | | `ping` | `redis.ping(handle: int) → bool` | true if the server responds to PING | ## Key/value | Function | Signature | Description | | --- | --- | --- | | `set` | `redis.set(handle: int, key: string, val: any, ttlMs?: int) → bool` | set a key; `ttlMs` ≤ 0 or omitted means no expiry | | `get` | `redis.get(handle: int, key: string) → string` | get a value; void if the key is missing | | `del` | `redis.del(handle: int, key: string) → bool` | delete a key; true if a key was removed | | `exists` | `redis.exists(handle: int, key: string) → bool` | true if the key exists | | `expire` | `redis.expire(handle: int, key: string, ttlMs: int) → bool` | set a TTL in milliseconds | | `ttl` | `redis.ttl(handle: int, key: string) → int` | remaining TTL in ms; `-1` no expiry, `-2` missing | | `incr` | `redis.incr(handle: int, key: string) → int` | increment and return the new value | | `decr` | `redis.decr(handle: int, key: string) → int` | decrement and return the new value | | `keys` | `redis.keys(handle: int, pattern: string) → string[]` | keys matching the glob pattern | ## Lists | Function | Signature | Description | | --- | --- | --- | | `lpush` | `redis.lpush(handle: int, key: string, val: any) → int` | push to the head; returns new length (`-1` on error) | | `rpush` | `redis.rpush(handle: int, key: string, val: any) → int` | push to the tail; returns new length (`-1` on error) | | `lpop` | `redis.lpop(handle: int, key: string) → string` | pop from the head; void if empty/missing | | `rpop` | `redis.rpop(handle: int, key: string) → string` | pop from the tail; void if empty/missing | | `lrange` | `redis.lrange(handle: int, key: string, start: int, stop: int) → string[]` | elements in the index range | | `llen` | `redis.llen(handle: int, key: string) → int` | list length | ## Hashes | Function | Signature | Description | | --- | --- | --- | | `hset` | `redis.hset(handle: int, key: string, field: string, val: any) → bool` | set a field; true if it was a new field | | `hget` | `redis.hget(handle: int, key: string, field: string) → string` | get a field; void if missing | | `hgetall` | `redis.hgetall(handle: int, key: string) → object` | all fields as an object (string values) | | `hdel` | `redis.hdel(handle: int, key: string, field: string) → bool` | delete a field | ## Pub/Sub | Function | Signature | Description | | --- | --- | --- | | `publish` | `redis.publish(handle: int, channel: string, msg: string) → int` | publish a message; returns subscribers reached (`-1` on error) | ## Example ```goost import redis let db = redis.connect("redis://localhost:6379") if db < 0 { println("could not connect") return } // Key/value with a 60s TTL redis.set(db, "session:42", "active", 60000) println("session: " + redis.get(db, "session:42")) // A counter redis.incr(db, "visits") println("visits: " + toString(redis.incr(db, "visits"))) // A list redis.rpush(db, "queue", "job-1") redis.rpush(db, "queue", "job-2") println("next: " + redis.lpop(db, "queue")) // Publish to a channel redis.publish(db, "events", "user-signup") redis.close(db) ``` --- # sql The `sql` module opens SQL databases and runs queries over an integer handle. SQLite, Postgres, and MySQL/MariaDB are supported through bundled pure-Go drivers. Import it with `import sql`. `sql.open` picks the driver from the connection string: - `":memory:"` → in-memory SQLite - `"file.db"` / `"/path/to/file.db"` → on-disk SQLite - `"postgres://user:pass@host:5432/dbname"` (or `postgresql://`) → Postgres - `"mysql://user:pass@host:3306/dbname"` → MySQL/MariaDB It returns an `int` handle, or `-1` on error. Placeholder syntax differs by driver: SQLite and MySQL use `?`, while Postgres uses `$1`, `$2`, … The rest of the API works identically across drivers. ## Functions | Function | Signature | Description | | --- | --- | --- | | `open` | `sql.open(connStr: string) → int` | open a database; returns a handle or `-1` | | `close` | `sql.close(handle: int) → bool` | close the database (rolls back any open tx); true if the handle existed | | `exec` | `sql.exec(handle: int, query: string, params?: any[]) → object` | run a statement; returns `{rowsAffected, lastInsertId}` | | `query` | `sql.query(handle: int, query: string, params?: any[]) → object[]` | run a SELECT; returns an array of row objects keyed by column name | | `queryOne` | `sql.queryOne(handle: int, query: string, params?: any[]) → object` | first row as an object, or void if no rows | | `queryValue` | `sql.queryValue(handle: int, query: string, params?: any[]) → any` | first column of the first row, or void if no rows | | `transaction` | `sql.transaction(handle: int, fn) → bool` | run `fn(handle)` inside BEGIN/COMMIT; rolls back if `fn` errors or returns `false` | Row objects from `query` / `queryOne` have one field per selected column, with values typed as int, float, string, bool, or void (NULL). `transaction` does not support nesting. ## Example ```goost import sql let db = sql.open(":memory:") if db < 0 { println("failed to open database") return } sql.exec(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INT)") // Insert with placeholders (SQLite/MySQL use ?, Postgres uses $1) let res = sql.exec(db, "INSERT INTO users (name, age) VALUES (?, ?)", ["Ada", 36]) println("inserted id: " + toString(res.lastInsertId)) // Read rows back let rows = sql.query(db, "SELECT id, name FROM users WHERE age > ?", [30]) for row in rows { println(toString(row.id) + ": " + row.name) } let count = sql.queryValue(db, "SELECT COUNT(*) FROM users") println("total: " + toString(count)) // Transaction: returning false rolls back sql.transaction(db, fn(h) { sql.exec(h, "UPDATE users SET age = age + 1 WHERE name = ?", ["Ada"]) return true }) sql.close(db) ``` --- # mongo The `mongo` module is a MongoDB client. `connect` returns an integer **handle** (or `-1` on failure) that you pass to every other call. Documents are ordinary Langoost objects; BSON values map to Langoost values and `ObjectId`s are hex-stringified. Import it with `import mongo`. > Object literals accept string-literal keys, which is what makes Mongo > operators read naturally: `{"$set": {status: "active"}}`. ## Connection | Function | Signature | Description | | --- | --- | --- | | `connect` | `mongo.connect(uri: string) → int` | connect using a standard MongoDB URI; returns a handle, `-1` on failure | | `close` | `mongo.close(handle: int) → bool` | disconnect and free the handle | ## Documents | Function | Signature | Description | | --- | --- | --- | | `insertOne` | `mongo.insertOne(handle: int, db: string, coll: string, doc: object) → string` | insert one document; returns the inserted id | | `insertMany` | `mongo.insertMany(handle: int, db: string, coll: string, docs: object[]) → int` | insert many; returns the count inserted | | `find` | `mongo.find(handle: int, db: string, coll: string, filter: object) → object[]` | return all matching documents | | `findOne` | `mongo.findOne(handle: int, db: string, coll: string, filter: object) → object` | return the first match, or `nil` | | `updateOne` | `mongo.updateOne(handle: int, db: string, coll: string, filter: object, update: object) → int` | update one; returns the modified count | | `deleteOne` | `mongo.deleteOne(handle: int, db: string, coll: string, filter: object) → int` | delete one; returns the deleted count | | `count` | `mongo.count(handle: int, db: string, coll: string, filter: object) → int` | count matching documents | ## Example ```goost import mongo let db = mongo.connect("mongodb://localhost:27017") if db == -1 { println("connection failed") exit(1) } mongo.insertOne(db, "shop", "users", {name: "alice", active: true}) let users = mongo.find(db, "shop", "users", {active: true}) for u in users { println(u.name) } mongo.updateOne(db, "shop", "users", {name: "alice"}, {"$set": {active: false}}) println(mongo.count(db, "shop", "users", {})) mongo.close(db) ``` --- # CLI reference The `langoost` binary is the entry point for everything. (During development you can substitute `go run .` for `./langoost`.) ``` ./langoost run Execute a .goost script ./langoost repl Interactive REPL (Ctrl+D to exit) ./langoost server [flags] Start the persistent HTTP execution server ./langoost disasm Print disassembled bytecode ./langoost version Print version ``` ## run Compile and execute a script: ``` $ ./langoost run hello.goost Hello, world! ``` Add `--debug` to enable debug logging (the `logging.debug` level) during the run: ``` $ ./langoost run --debug hello.goost ``` ## repl Start an interactive session that evaluates one line at a time: ``` $ ./langoost repl > let x = 21 > print(x * 2) 42 ``` ## server Run as a long-lived execution server. The process stays warm and serves requests with no cold start: ``` $ ./langoost server --port 8080 ``` | Flag | Default | Description | | --- | --- | --- | | `--port` | `8080` | Port to listen on | | `--timeout` | `30s` | Max execution time per request | | `--dir` | `.` | Directory to search for `.goost` files | ## disasm Print the compiled bytecode for a script — useful for understanding what the compiler produces: ``` $ ./langoost disasm examples/math.goost === examples/math.goost (16 instructions) === 0000 [ 3] CONST #0 () 0001 [ 3] DEF_GLOBAL #1 (add) ... === add (6 instructions) === 0000 [ 4] LOAD_LOCAL slot=0 0001 [ 4] LOAD_LOCAL slot=1 0002 [ 4] ADD 0003 [ 4] RETURN ``` ## Error messages Runtime and compile errors include the file name and line (and column for lex/parse errors): ``` main.goost:12: undefined variable "x" main.goost:7: function "add" expects 2 arguments, got 3 main.goost:3: array index 5 out of bounds (len=3) script.goost:4:12: expected }, got EOF ``` For how the pipeline fits together, see **[architecture](/docs/architecture)**. --- # Architecture Langoost is a bytecode-compiled interpreter written in Go. Each script runs through a small, predictable pipeline. ``` Source text ↓ Lexer (token/lexer.go) Token stream ↓ Parser (parser/parser.go) AST ↓ Compiler (compiler/compiler.go) Bytecode chunk ↓ VM (vm/vm.go) Result ``` ## Bytecode Instructions are fixed-width 4-byte (`uint32`) words. The top byte is the opcode; the lower 3 bytes are the operand (up to ~16 million). A constant width means no alignment work and a simple decode loop. ``` [ opcode: 8 bits | operand: 24 bits ] ``` Inspect the compiled output of any script with `disasm` (see the **[CLI reference](/docs/cli)**). ## Stack model Local variables live directly on the value stack at fixed slot indices (`frame.base + slot`), so `LOAD_LOCAL` is a single array read with no indirection. Temporaries — intermediate expression results — sit above the locals and are cleaned up as each expression completes. ## Module cache Modules are compiled once and cached, keyed by absolute path and file modification time. If a file hasn't changed, importing it is just a map lookup. The cache is guarded by a `sync.RWMutex`, so concurrent requests read it without blocking one another. ## Per-request isolation In server mode, every HTTP request gets its own VM instance with a private stack and output buffer. The only shared state is the (read-mostly) module cache. Scripts handling different requests therefore never share mutable state — which is exactly what makes them safe to run concurrently. When sharing *is* needed, it's explicit, via the `runtime.core` key-value store. This design is the core trade-off behind Langoost: keep one warm process with shared compiled code, but isolate execution per request so there's no cross-request interference and no cold start. ================================================================ EXAMPLES ================================================================ # Hello, world Strings are joined with `+`, and `print` writes a line to stdout. ```goost let name: string = "world" print("Hello, " + name + "!") ``` Run it: ``` $ langoost run hello.goost Hello, world! ``` --- # FizzBuzz Loop over a range with `for..in` and branch with `if / else if / else`. ```goost for n in range(1, 101) { if n % 15 == 0 { println("FizzBuzz") } else if n % 3 == 0 { println("Fizz") } else if n % 5 == 0 { println("Buzz") } else { println(toString(n)) } } ``` `range(1, 101)` produces the integers `1` through `100`. `%` is the modulo operator, and numbers are turned into strings for printing with `toString`. --- # Prime numbers Collect every prime up to 30. Shows a helper function, a `while` loop, and the immutable `append` pattern (each `append` returns a new array). ```goost fn isPrime(n: int): bool { if n < 2 { return false } let i: int = 2 while i * i <= n { if n % i == 0 { return false } i = i + 1 } return true } let primes: int[] = [] let candidate: int = 2 while candidate <= 30 { if isPrime(candidate) { primes = append(primes, candidate) } candidate = candidate + 1 } print("Found " + toString(len(primes)) + " primes:") for p in primes { print(" " + toString(p)) } ``` --- # HTTP server Serve an API with `http.serve`. The handler receives `method`, `path`, `query`, and `body`, and returns a string. A leading status code sets the HTTP status; responses starting with `{` or `[` are sent as JSON automatically. ```goost import http import strings import json fn handle(method: string, path: string, query: string, body: string): string { if path == "/health" { return "{\"status\": \"ok\"}" } if path == "/echo" && method == "POST" { return body } if path == "/greet" { let name = "World" if strings.contains(query, "name=") { let parts = strings.splitN(query, "name=", 2) name = parts[1] } let data: json = {message: "Hello"} return json.setString(data, "message", "Hello, " + name + "!") } return "404 Not Found" } http.serve(8080, handle) ``` Try it: ``` $ langoost run server.goost $ curl localhost:8080/health {"status": "ok"} $ curl "localhost:8080/greet?name=Ada" {"message":"Hello, Ada!"} ``` --- # HTTP client The `http` module is also a client. Each call returns the response body as a string (or, for `head`/`status`, the status code). ```goost import http // GET let post = http.get("https://jsonplaceholder.typicode.com/posts/1") println(post) // POST with a JSON body let newPost = "{\"title\":\"Langoost\",\"body\":\"Fast scripting\",\"userId\":1}" let created = http.post("https://jsonplaceholder.typicode.com/posts", newPost) println(created) // HEAD — check existence without downloading the body let status = http.head("https://jsonplaceholder.typicode.com/posts/1") if status == 200 { println("resource exists") } // Generic request with custom headers (headers is a string[]) let resp = http.request("GET", "https://jsonplaceholder.typicode.com/posts/2", "", [ "Accept: application/json", "Authorization: Bearer my-token" ]) println(resp) ``` Available methods: `get`, `post`, `put`, `patch`, `delete`, `head`, `status`, and the generic `request(method, url, body, headers)`. --- # Concurrency Spawn background work and communicate over a channel. Each spawned function runs in its own isolated VM. ```goost import thread // A buffered channel carrying messages between tasks let ch = thread.channel.new(1) thread.core.spawn(fn() { thread.channel.send(ch, "hello from a goroutine") }) let msg = thread.channel.recv(ch) // blocks until a value arrives println(msg) ``` Or run a task and await its result with a future: ```goost import thread import time let future = thread.async.run(fn() { time.timer.sleep(100) return "done" }) println(thread.async.await(future)) // "done" ``` Because tasks don't share mutable state by default, use `runtime.core.share` / `runtime.core.fetch` (guarded by a `thread.mutex` when needed) to coordinate. --- # JSON data Variables annotated `: json` accept inline object and array literals with unquoted keys. The `json` module then reads and edits them. ```goost import json // Inline literal — identifier keys are quoted automatically let user: json = { name: "alice", age: 30, roles: ["admin", "editor"] } // Read values println(json.get(user, "name")) // "alice" println(json.getInt(user, "age")) // 30 println(json.has(user, "email")) // false // Edit (each call returns a new JSON string) let updated = json.setString(user, "name", "bob") let merged = json.merge(user, "{\"active\": true}") // Pretty-print println(json.pretty(merged)) ``` Useful helpers: `parse`, `valid`, `minify`, `keys`, `length`, `arrayGet`, `delete`, and the typed getters `getInt` / `getBool`.