Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 16 Oct 10:46
· 311 commits to main since this release

Install

curl -fsSL https://raw.githubusercontent.com/orelang/ore/main/install.sh | sh

or brew install orelang/tap/ore; then ore update keeps it up to date.
Each archive carries its LLVM and lld.

For File
Linux x86-64 ore-v0.3.0-linux-amd64.tar.gz
Linux arm64 ore-v0.3.0-linux-arm64.tar.gz
macOS arm64 ore-v0.3.0-darwin-arm64.tar.gz
VS Code extension ore-vscode-v0.3.0.vsix
Zed extension ore-zed-v0.3.0.zip

The editor extensions run ore lsp, so ore must be on PATH. VS Code:
install "Ore" (orelang.ore) from the Marketplace or Open VSX, or
code --install-extension ore-vscode-v0.3.0.vsix. Zed: unzip
ore-zed-v0.3.0.zip and pick its directory in zed: install dev extension
(Zed builds it with Rust, which must be installed with rustup).

What is in 0.3.0

Ore's own garbage collector, precise and concurrent; faster tasks, regular expressions and JSON; benchmarks against C, Go, Bun and Node.

  • Benchmarks in plain C next to Ore, Go, Bun and Node: bench/<name>/main.c
    for every benchmark, C17 and libc only (pthreads for channels and
    tasks, POSIX regex.h for regex), built by bench/run.sh with
    $CC -O2 -march=native. The script's markdown now starts with the date,
    the machine (CPU, cores and threads, memory, OS and kernel), the version
    of each toolchain and the timing method; -o path or RESULTS=path
    writes it to a file as well, and the columns are always ore, c, go, bun,
    node.
  • Each worker thread has its own run queue, as in Go: a lock-free ring that
    tasks it spawns or wakes go to, with a slot for the task the running one
    woke last (so two tasks that wake each other run in turn on one thread),
    stolen from by idle workers, half of it moved to the global queue when
    full; the global queue is looked at every 61 tasks. bench/channels runs
    about 1.5–2x faster on 10 threads; bench/tasks is unchanged.
  • Spawning tasks is about ten times faster (bench/tasks, 100000 tasks:
    1.1 s to 0.09 s on a 10-core Mac). A task gets its stack when it first
    runs, not when it is spawned, and gives it back when it ends to a cache
    of the worker and a shared one, so mapping and unmapping stacks is rare;
    the stacks are listed for the collector without its lock. A WaitGroup
    of "ore:sync" counts atomically and locks only when its count drops to
    zero. An idle worker is signalled only when there is one.
  • JSON.parse<T> reads the text straight into a T, with no tree of
    values in between: keys are compared in place with the field names and
    strings without escapes share the bytes of the text. Only when that
    fails is the text parsed into values, to report the same error as
    before. JSON.stringify writes integers into its buffer without a
    string for each. bench/json runs about 3x faster in a quarter of the
    memory.
  • The collector marks alongside the program: two short stops (scanning
    the roots, then finishing) instead of one for the whole collection, with
    a write barrier the compiler emits before stores of pointers into the
    heap. It scans objects precisely, by a pointer map of their type, and
    never scans arrays of numbers; it gives free pages back to the system;
    and its pace is set by ORE_GOGC and ORE_MEMLIMIT. ORE_GCSTW=1
    marks with the world stopped, as before.
  • Regular expressions are faster (bench/regex about 2.6 times): the
    search skips bytes no match can start with and, after a failed try, the
    rest of a leading loop's run; a greedy loop of one character runs as one
    instruction, and keeps no choices when giving characters back could not
    match; exec and test reuse the expression's matcher; a replacement's
    $ patterns are read once. indexOf compares only where the first byte
    is.
  • ore doc [file.ore | dirs...] prints the API of modules: their exported
    functions, classes with their public members, interfaces, type aliases
    and variables, with their signatures as the hover shows them and their
    JSDoc (@param, @returns, @example and other tags), after the //
    comment the module starts with. On a terminal it is a colored listing;
    --md makes Markdown (with -o dir, a file per module and an
    index.md), --json a JSON dump for tools.
  • ore:test grows toward bun:test: describe groups (their names prefix
    their tests', math > adds), the hooks beforeAll, afterAll,
    beforeEach and afterEach, skip, only and todo (Ore functions
    have no properties, so not test.skip), .not, and the matchers
    toEqual (deep), toBeNull, toBeTruthy, toBeFalsy, toContain,
    toHaveLength, toBeGreaterThan(OrEqual), toBeLessThan(OrEqual),
    toBeCloseTo, toMatch and toPanic (Ore's toThrow: the function
    runs in a task whose panic is caught). Failures show the matcher and the
    expected and received values, as a diff of lines when they take several.
    The tests of a file now run when its top-level code ends, so that only
    can apply; a test declared in another task still runs at once. ore test -t <pattern> runs only the tests whose full names match. The summary
    counts skipped and todo tests.
  • The type parameters of generic methods may have defaults
    (size<U = T>(): int), as those of functions do; ORE5019 is no longer
    reported. A generic method is checked only for the calls it has, so such
    a method of a generic class can use what only some of its instances have.
  • unknown as the type of values: any value converts to it, boxed with
    its type, and console.log prints it as that type. It can be compared
    with ===, tested for null and narrowed with typeof (to string,
    f64, bool or bigint) and instanceof (to a class), which unbox it;
    anything else is an error, Object is of type 'unknown'. (ORE3078).
  • Numeric literal types are types of values: const n: 1 | 2 = 1,
    function rotate(d: 0 | 90 | 180 | 270), type Dice = 1 | 2 | 3 | 4 | 5 | 6.
    Their values are numbers held as an f64; they widen to number, and
    arithmetic and comparisons see numbers. A constant fits when it equals a
    member (ORE2067); === and switch narrow them, and a switch with a
    case for each is exhaustive. ORE2066 now concerns only never inside
    tuples and object types.
  • Generic nested functions (function id<T>(x: T): T in a function body)
    and generic function literals of a const (const id = <T>(x: T): T => x): each list of type arguments makes an instance, a closure that
    captures the variables of the enclosing functions like other nested
    functions. They can only be called; used as a value without a call, such
    a function is an error (ORE6071), and one that calls itself needs a
    return type annotation (ORE6072).
  • Getters and setters, of classes and of object literals, implement
    interface fields: a getter a readonly field, a getter and a setter a
    writable one. Reads and writes through the interface (and through a union
    with interface members) call them. Fields of names that no accessor
    implements are read and written through interfaces as before; the others
    test the table entry first.
  • console.log colors follow Node's rules for the terminal too: with no
    TERM, or one Node does not know, a terminal shows no colors, unless
    COLORTERM, TERM_PROGRAM, tmux or a known CI service says it does.
  • The checker parses builtin.d.ore once per process instead of on every
    check, which the language server runs on every change.
  • The language server tries a GitHub repository whose download failed
    again a minute later, or at once when a document importing it is saved,
    instead of never in the session.
  • Math.min() and Math.max() with no arguments are Infinity and
    -Infinity, as in JavaScript (they were ORE4019), or the extreme of the
    integer type expected, as with only empty arrays spread in.
  • Regular expressions of the v flag take string disjunctions
    ([\q{abc|d}x]), longest strings first, in their set operations too; a
    negated class that may hold strings is an error. With u (or v) and
    i, \w, \W, \b and \B count ſ and the Kelvin sign as word
    characters, as in JavaScript.
  • Maps, sets and channels are scanned precisely by the collector: the
    compiler passes the pointer maps of their keys, values and entries, map
    entries and channel buffers are allocated with them (never scanned when
    they hold no pointers), and the arrays of keys() and values() carry
    the pointer map of their elements.