Introduction

Amber is a small, fast, self-contained array language. The interpreter is portable C99 that builds clean on gcc and clang; the vocabulary on top of it is q's. It is one folder, it installs nothing system-wide, and deleting the folder uninstalls it completely.

v2.9.1 C99 · portable 1,450 assertions AGPLv3

What it is

Amber's interpreter core is built on ngn/k, ngn's compact AGPLv3 implementation of the K array language. Amber keeps that engine's speed and small footprint and layers on:

  • the q working vocabulary. Dictionaries, tables and keyed tables with ([]…) literal syntax, qSQL select/exec/update/delete, and the full join family: left, inner, union, plus, equi, as-of and window;
  • column attributes implemented in C: `s sorted, `u unique, `p parted, `g grouped. These turn ? (find) from a linear scan into a binary search;
  • native temporal types, q's: date, month, minute, second, time, timespan and timestamp, as literals and as typed lists, with type-aware arithmetic and a nanosecond epoch anchored at 2000-01-01;
  • a tick / HFT toolkit: a native C as-of-join kernel, a zero-allocation arena, market-data generators, OHLCV bars, VWAP, Lee-Ready trade signing and effective spread;
  • a modern REPL with a native, UTF-8-aware line editor, multi-line input, a live status bar with scroll-back, bracketed paste, Rust-style diagnostics, a workspace inspector, an AST viewer, a bytecode disassembler and a phase profiler.

What it leaves out

Not a q distribution

Amber is an independent language. The interpreter is named amber, never k or q; it reads no QHOME, no config and no dotfiles, and your kona or ngn/k installs are untouched.

q's wire, nothing more

There is no AI code and no TLS in the engine. Since 2.9.0 it speaks q's own wire format (\p, hopen, .z.pw), listening on this machine unless told otherwise. A JSON line protocol for dashboards lives outside, in amberd.

How the pieces fit

the boot path
   ./a ──► build.sh ──► cc -std=c99 src/*.c ext/*.c ──► ./amber ──► repl.k
                                        │                             │
                                        │                             └─ amber.k · fin.k · std.k
                                        │                                qsql.k · temporal.k
                                        │                                sys.k · hdb.k · ipc.k
                                        │                                lib/ext.k  (optional)
       ┌────────────────────────────────┴─────────────────────────────────┐
       │                          the interpreter                          │
       │  a.c b.c f.c h.c i.c m.c v.c …   ngn/k evaluator core, heap, verbs │
       │  p.c ast.c vm.c                  parser · `\ast` tree · `\disasm`  │
       │  3.c simd.c parallel.c peachpool.c   vector kernels · threads      │
       │  arena.c ser.c csv.c ar.c        HFT arena · `ser · CSV · Arrow    │
       │  e.c diagnostic.c                the Rust-style error reporter     │
       │  inspect.c trace.c               `\v` · `\trace`                   │
       │  ln.c lnk.c                      the line editor and its `rdl` verb│
       │  ext.c                           the extension seam (src/ext.h)    │
       └───────────────────────────────────────────────────────────────────┘

Three properties of that layout are worth stating explicitly, because they are what it is for:

  • No optional feature is switched off. Everything in src/ is compiled, always. There is no build matrix and no feature flag to get wrong.
  • src/ln.c has no interpreter dependency. It includes <termios.h> and src/ext.h and nothing else of Amber's. The editor can be lifted into another project as-is.
  • Extensions never patch src/. An out-of-tree package drops .c files into ext/ and re-runs ./build.sh; they are compiled with the same flags and plug themselves in from a constructor.

A two-minute taste

amber>
/ tables are first-class and render without `show` at the interactive prompt
([]sym:`a`b`c; px:100 200 300)
/  ╭─────┬─────╮       tables render as a rounded box by default
/  │ sym │  px │       (\grid clean|rounded|sharp|heavy switches the style)
/  ├─────┼─────┤
/  │ a   │ 100 │
/  │ b   │ 200 │
/  │ c   │ 300 │
/  ╰─────┴─────╯
/  [3 rows x 2 cols]

meta ([]sym:`a`b; px:1.5 2.5)      / column types + attributes (c | t a)

/ the join every tick shop needs — as-of (native C kernel since 1.9)
trade:([]sym:`a`b`a; time:3 4 9; px:100 200 300)
quote:([]sym:`a`a`b`a; time:1 5 2 8; bid:10 11 20 12)
aj[`sym`time; trade; quote]        / last quote at or before each trade

/ qSQL — type select/exec/update/delete straight, no sel"…" wrapper
select last px by sym from trade   / grouped aggregate
select from trade where px>150     / filter rows

/ q's bytes: -8! gives them for any value, -9! reads them back
b:-8!+`a`b!(1 2 3;4 5 6)
(-9!b)~+`a`b!(1 2 3;4 5 6)         / 1b, exact

/ multi-core: peach runs f over items on a pool of threads (no GIL)
peach[{avg x?1.0}; 8#1000000]
Where bare qSQL works

At the interactive prompt, in a .q script and in any file loaded with \l, select … by … from … where … works as it is, and a bare table auto-renders as a grid at the prompt. In the .k script ./amber file.k runs, use the sel"…" string form, and show to print a table.

Where to go next