Skip to content

Axiom 0.3.6

Choose a tag to compare

@github-actions github-actions released this 28 Aug 22:32
· 416 commits to trunk since this release

0.3.6 — 2026-08-28

The three compiler defects the language-server work found and 0.3.5
recorded under Found, not fixed are fixed, the server stops sending
semantic tokens so the grammar is the one source of colour, and the
grammar gains rainbow brackets. No gate was added: every change here
is covered by the thirty-seven gates that build the compiler under
test through gate_build_axc, and check-tree-sitter.sh learns one
more query file.

Removed

  • Semantic tokens. 0.3.5's textDocument/semanticTokens/full and
    /range are withdrawn, by decision rather than defect: the
    tree-sitter grammar is the one source of colour, and two sources that
    can disagree about the same token are worse than one. The SECTION HL
    of self_host/lsp.ax, its drive.py block and its sweep entry are
    gone; the capability object shrinks by one key and the eight goldens
    are re-blessed for that alone. docs/lsp.md says which editors take
    colour from the grammar (Helix, Neovim, Emacs 29, Zed) and which
    therefore have none from this repository (VS Code, which has no
    tree-sitter and for which no TextMate grammar is shipped).

Added

  • Rainbow brackets in the grammar. tree-sitter-axiom/queries/ rainbows.scm names every bracket-opening rule of the grammar as a
    @rainbow.scope — 59 of them, checked against src/node-types.json
    so a hidden or aliased rule cannot be named — and the six bracket
    tokens as @rainbow.bracket; Helix colours each pair by its nesting
    depth with [editor] rainbow-brackets = true, and
    rainbow-delimiters.nvim reads the same captures. Measured on
    stdlib/Vec.ax: 868 brackets in 288 scopes. check-tree-sitter.sh
    loads it against every .ax file beside highlights.scm.

Fixed

  • A macro-generated struct's fields carried the parser's float flag
    where their type belonged.
    expBuildStructFieldsIn substituted
    word b of each field — the float flag — as if it were the type and
    stored the result back in b, leaving ty 0. Measured on the tree
    before the change: a template spelling (f : Float) made check,
    build and symbols die of SIGSEGV in expansion (a dereferenced
    1); a Float arriving through a parameter reached the checker as
    a silent wildcard, symbols printed #fields=n:{unknown},f:{unknown}
    beside the handwritten twin's n:Int,f:Float, and (* p.f p.f) was
    refused with a false AX3004. The builder reads the type from ty,
    recomputes the flag from what the substitution produced (as
    expCtorFlags already does for constructors), and parks the type
    where the parser does. tests/selfhost/396-macro-struct-field-types.ax
    exits 139 on the old compiler and 73 on this one; the symbols zoo's
    RecTwin row now carries Rec's own #fields=.

  • The formatter reserved thirty-two words the language does not.
    axiom fmt refused (fn (g data) data) while check accepted it;
    docs/reference.md has always said a keyword is special only in the
    position its rule claims. fpIsReservedWord reproduced the retired
    Rust compiler's lexer, and the parity bank had pinned that answer as
    070-keyword-in-expr since the day it was materialized. Measured
    over thirty-six shapes: every non-head keyword-spelled identifier was
    check OK (or a checker error) and fmt refused; in head position
    the parser refuses only consume and begin (AX2004) — and the
    printer PRINTED those two, output check cannot read. Two words
    replace the list (fpIsRefusedHead, the two parseInner refuses);
    070 and 131 flip to rewrites, 190197 pin the identifiers
    that print and the refusals the parser shares. 48 parity cases, 24
    rewrites and 24 refusals; the tree-sitter corpus gains the shape (38
    cases). The ten identifiers renamed in f0a2fa3 could be renamed
    back.

  • Every name the emitter invents begins with a byte no identifier can
    spell.
    (fn (f t0) (+ t0 1)) passed check and died in opt with
    multiple definition of local value named 't0' (AX4003): temporaries
    were %tN and a parameter %name, one LLVM namespace, two schemes
    that could spell the same string — and t<digits> was only the first;
    measured one prefix at a time, label_3, f1, c0, d0, p1 and a
    lambda parameter named _env each failed the same way. The fix is a
    construction, not a list: every local value and block label the
    emitter makes up now begins with .%.t0, %.d0, .L3, %.env
    — which LLVM accepts and no Axiom identifier can contain
    (isIdentStart/isIdentChar leave byte 46 out), so the two
    namespaces are disjoint by what they are made of; 28 label sites and
    17 typed-temporary sites go through tmpStr/labelStr. Global
    symbols are untouched byte for byte (check-symbol-names,
    check-freestanding, check-c-abi). tests/selfhost/955-source- names-vs-emitter-names.ax names a parameter after every old prefix:
    exit 4 in opt on 0.3.5, 42 here. The committed seed keeps its %tN
    and the ladder built from it reaches its fixpoint on the new
    spelling (check-bootstrap.sh).

  • bootstrap-from-seed.sh --install copied the compiler onto the
    binary it was replacing.
    cp onto an existing executable rewrites
    the same inode, and macOS keeps a signature cached per inode for a
    binary that was ever run: the rewritten .axiom-bin/axiom passed
    codesign -v and was SIGKILLed — exit 137, no output — on every
    exec, so every check in the tree died silently for three quarters
    of an hour while three gates shared it. The install is a copy beside
    the target and a rename over it now, which is atomic and a new inode.