Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

stdlib/ — Orbit standard library

The Orbit-side standard library: the .orb modules shipped with the language. This is the idiomatic surface a user touches with import "io", import "error", … — not the engine primitives the modules sit on (those live in orbit/orbiter/ and are reached as ::orbit::* builtins).

Status: early and growing. A handful of modules are usable today; several listed here are still planned. Each entry below is marked, and work-in-progress modules call out their current limitations. Bytecode and some APIs are not stable yet — expect changes.

Running code against the stdlib

The interpreter finds these modules via the ORBIT_PATH environment variable (:-separated, like PATH). Point it at this directory:

ORBIT_PATH="$PWD/stdlib" ./build/release/bin/Orbit your_script.orb

(build/<preset>/bin/Orbit is where a cmake --preset <preset> build puts the executable; see the root README.)

import "io"

io.print(b"hello, world")        # -> hello, world\n
name := io.input(prompt=b"name? ")
io.print(b"hi,", name)

Module catalog

Status legend: ✅ available · 🚧 work in progress (usable, with caveats) · 📋 planned (not present yet).

Available

Module Import Backing Notes
io import "io" ::orbit::io builtin + pure Orbit Standard streams as File objects (stdin/stdout/stderr), print/perror/input, open, buffered reader/writer, and the core IO traits. The most complete module.
chrono import "chrono" ::orbit::chrono builtin Time and sleeping: monotonic() reads the monotonic clock in milliseconds (origin unspecified, use it for intervals); sleep(ms) parks the current fiber on the event loop, leaving its scheduler thread free, and returns the milliseconds actually elapsed. Very short sleeps are served inline.
error import "error" pure Orbit Ready-made error constructors aligned with the kinds the engine raises (ValueError, TypeError, OSError, IndexError, KeyError, …). Each is a partial application of Error.create(@Kind); call with a reason to build one: panic ValueError("count must be positive"). Also ships helpers that format engine-standard messages, e.g. fmt_typerror(obj, String, Bytes)expected type 'String/Bytes', got '…'.
ffi import "ffi" ::orbit::ffi builtin Native-interop platform metadata: SIZEOF_* for every native type usable in native declarations (SIZEOF_PTR, SIZEOF_U64, …) and the byte order (ENDIAN). Use it to compute Rawptr.alloc layouts instead of hardcoding sizes.
gc import "gc" ::orbit::gc builtin Manual control over the garbage collector, which otherwise runs automatically: collect() forces a full collection cycle and returns the number of objects reclaimed; rearm(obj) re-arms the finalizer of a resurrected object.
regex import "regex" native binding (PCRE2) + pure Orbit Perl-compatible regular expressions over PCRE2 (libpcre2-8 must be installed). Pattern, Match, CompileContext tuning, one-shot helpers.
runtime import "runtime" ::orbit::runtime builtin Environment introspection: os, executable, get_argv(), get_config(), and the engine version (plus parsed version_major/minor/patch).

Work in progress

Module Import Status Caveats
readline import "readline" 🚧 Line editing + history via the system readline-compatible library (GNU readline, or libedit on macOS/BSD), selected at load time by a when runtime.os block. POSIX/macOS only — no Windows branch. Depends on the FFI layer (native func … from "lib…"), which is itself POSIX-complete / Windows-partial. Surface is intentionally thin: read(prompt) + add(line) + auto-history; no completion or custom key-binding API, and duplicate history entries are not filtered.
repl import "repl" 🚧 Interactive read-eval-print loop (repl.default_session.run(); the interpreter's interactive mode runs this). Built on readline (inherits its platform limits) and on the engine's eval + Context + trap/await. Multi-line input works: a bracket-balancing scanner (string-aware) switches to the ps2 continuation prompt until braces/brackets/parens close; :exit ends the session; errors are trapped and printed so the loop survives. No signal handling yet (Ctrl+C behavior is whatever the readline library does).

Planned

Not present yet — listed so contributors know the intended shape. The names and scope mirror the modules already in the works (same surface, implemented the Orbit way):

Group Modules
Text & data json (encode/decode) · ini (INI parser) · base64 (Base16/32/64 encodings) · enum (algorithms over enumerables)
OS & processes ospath (pathname manipulation) · subprocess (spawn / manage processes)
Numeric random (pseudo-random numbers)
Concurrency syncutil (synchronization primitives)
Cryptography hashlib (secure hashes / digests) · ssl (TLS over sockets)
Compression & archives zlib · bz2 · lzma · zipfile
Networking http (client/server) · url (URL handling)
Tooling argparse (CLI option parsing) · unittest (unit-testing framework)

Layout

Flat by default; package only when a module outgrows one file.

  • One top-level entry = one .orb file or one directory-as-package.
  • A package's entry file has the same name as its directory (io/io.orb, regex/regex.orb) — import "io" resolves to it. The entry file re-exports the package's public surface.
  • Submodules are reached by path: import Readable from "io/traits", import Pattern from "regex/pattern". They are implementation detail — prefer importing the package entry (import "io") from user code.
  • A module stays small by default; split into a package once it grows clear internal sub-areas (as io did).

The three layers

A module combines up to three building blocks; pick the lightest that does the job.

Layer Where it lives Use when …
C++ builtin (::orbit::*) orbit/orbiter/… (engine) the operation needs deep VM/GC/type machinery (e.g. raw IO syscalls, runtime introspection)
Native binding (native func / native from "lib" { … }) inside the .orb module the operation maps cleanly to a libc/OS symbol (e.g. readline, add_history); the block form groups declarations sharing the same library
Pure Orbit inside the .orb module composition over the above, or no foreign call at all (e.g. error constructors, iterator helpers)
# readline.orb — native binding hidden behind a pure-Orbit class
when runtime.os == "darwin" {
    native func readline(prompt: ptr): ptr from "libedit"
} else {
    native func readline(prompt: ptr): ptr from "libreadline"
}

pub class Readline {
    pub func read(prompt="") {
        line_ptr := readline(prompt)        # raw native call
        
        if line_ptr.is_null() { return nil }
        defer line_ptr.free()

        line := line_ptr.read_string()      # wrapped into an Orbit String

        return line
    }
}

Rule of thumb: don't add a ::orbit::* builtin unless the operation can't be expressed with native func + pure Orbit at acceptable cost. Growing the stdlib is far cheaper than growing the engine surface.

Conventions

Naming

  • snake_case for functions, parameters, module-level variables.
  • PascalCase for types/classes/traits and error kinds.
  • SCREAMING_SNAKE_CASE for constants (O_READ, SEEK_SET, BUFFER_SIZE).
  • Private helpers are simply not marked pub.

Visibility

  • pub marks the public API; everything else is private to the module.
  • A package's entry file decides the package surface via pub (re-)exports; submodules are private by construction.

Composition with traits

The io subsystem is organised around small traits (Closable, Readable, LineReadable, Writeable in io/traits.orb). Concrete types declare what they honor (class File impl Closable + Readable + Writeable), and generic code (e.g. BufferedReader) targets the trait, not the concrete type. New IO-shaped resources (sockets, pipes, in-memory buffers) should implement the relevant traits to interoperate.

Error handling

  • Default: panic. Idiomatic and wired through the VM already; most functions should raise on failure rather than return a status.
  • Result-style when failure is normal flow — parsing user input, probing for existence, retry-in-a-loop attempts. Don't use it for rare/exceptional conditions; that's what panicking is for.
  • Pick one style per public feature and stick to it.
  • Raise specific kinds from error.orb (with a clear kind atom), not bare strings — so user code and engine code can be caught symmetrically.

Cross-OS constants (flags, modes, …)

When a module exposes integer flags that differ between POSIX and Windows (open flags, …):

  • Define stable Orbit-side values in the .orb module (clean bitmask powers of two), independent of any OS header.
  • The backing ::orbit::* builtin translates them to the platform's real bit pattern at the call boundary.

This decouples the Orbit ABI from libc/Win32 header drift. Constants that are historically identical across platforms (SEEK_SET=0, SEEK_CUR=1, SEEK_END=2) may be reused as-is, documented as a stability guarantee.

Documentation

Document every pub symbol with the doc-comment format in ../docs/documentation-guide.md:

  • /*! … */ at the top of the file documents the module.
  • /** … */ before a func/class/trait/native declaration documents it (works through pub/prot/@[decorator] prefixes too).
  • Same @brief / @param / @return / @panic / @see / @example tags as the C++ runtime methods, written in English. @brief is the first line and a single sentence; @panic lists each error kind that can be raised; @example covers the happy path plus an edge case for non-trivial functions.

The existing modules (io/io.orb, error.orb, regex/pattern.orb, …) are the working style reference.

Adding a new module — checklist

  1. Pick the layer mix. Pure Orbit + native func if possible; justify any new ::orbit::* builtin in the module header.
  2. Name it lowercase, short, ideally one word (io, json, regex). No _ in the module name itself.
  3. Start as a single .orb file. Promote to a package only when it grows clear internal sub-areas.
  4. Public surface only via pub; everything else private.
  5. Docstring every pub per the documentation guide (module /*! */ + per-declaration /** */).
  6. Consistent error style across the module (raise or Result per feature).
  7. No leaked native types — wrap raw pointers / native ints behind ergonomic Orbit values (see how readline hides ptr).
  8. Update this README's catalog with the right status marker.

Roadmap

Ordered by enablement (what unblocks what):

DONE  io / error / runtime / ffi / regex / gc / chrono   (usable today)
WIP   readline / repl                               (POSIX/macOS; depend on FFI + eval/Context)
─────────────────────────────────────────────────────────────────
Pure-Orbit first (need only the language + import pipeline):
      enum · ospath · json · base64 · url
Native-backed (need FFI / engine support):
      random · hashlib · subprocess · syncutil
      compression: zlib · bz2 · lzma · zipfile
      ssl · http
Tooling:
      argparse · unittest

The pure-Orbit modules need nothing beyond the existing language types and the import pipeline, so they can be drafted in parallel. The native-backed ones land as the FFI surface and any required engine support fill in. WIP modules graduate to ✅ once their platform coverage and the engine features they lean on (FFI on Windows, eval/Context for the REPL) are solid.