A tree-sitter grammar for Chialisp (cl-26) and CLVM — the on-chain languages of the Chia blockchain.
Chialisp is a Lisp. Its surface is fully described by the s-expression reader in
the clvm_tools_rs compiler frontend (src/compiler/sexp.rs): a list, an atom,
a number, a hex byte-string, a quoted string, a comment, and the cons dot. Every
form — mod, defun[-inline], defconstant/defconst, let/let*,
assign[-inline]/assign-lambda, lambda, include, embed-file/
compile-file, namespace/import/export, and every operator/opcode — is an
ordinary list whose head atom carries its meaning. The compiler applies a
keyword table after reading plain s-expressions; it does not have a distinct
grammar per keyword, and neither does this grammar.
So the node-type inventory is small and stable:
| Node | Meaning |
|---|---|
source_file |
the whole file: a sequence of s-expressions |
list |
( … ) — proper, or dotted/improper ( … . x ) |
symbol |
an atom / identifier (defun, *standard-cl-26*, @, +) |
number |
a decimal integer (5, -5) |
hex |
a hex byte-string (0xdeadbeef) |
string |
a "…" or '…' quoted string (with \ escapes) |
comment |
a ; line comment (in extras) |
dot |
the cons dot in a dotted pair ( a . b ) |
There are no per-keyword rules. A downstream consumer keys on list + the
text of the head symbol — exactly the shape a Scheme-family extractor already
understands.
Authored clean-room from the authoritative reader — the clvm_tools_rs
compiler frontend src/compiler/{sexp.rs, frontend.rs, dialect.rs, preprocessor/mod.rs}. Quexington's tree-sitter-chialisp and Chia Network's LSP
were read for understanding only; no code was copied.
Notable correctness points modeled directly from sexp.rs:
- Comment —
token(seq(';', /[^\n]*/)). The token holds no terminator, so it ends cleanly at LF, at CRLF (the\rrides along; the LF is whitespace), or at EOF. This is the fix for Quexington's;.*\r\n, which required a CRLF and so failed on every LF-terminated file and at end-of-file. - Cons dot vs. namespaced symbol — a lone whitespace-bounded
.is thedottoken; a.inside a bareword (std.prelude,foo.clib,e.g.) is part of thesymbol. This mirrorssexp.rs, where a.arriving with aBarewordinner state is appended to the word, and only a.arriving with anEmptyinner state opens aTermList. - Strings — both
"and'terminate, and\escapes the following character verbatim (QuotedText/QuotedEscaped). - Hex and number — hex byte-strings (
0x…) and decimals (-?[0-9]+) are distinct from symbols; a bare-is the subtraction operator (asymbol), not a number.
npm install
npx tree-sitter generate
npx tree-sitter test # corpus tests
npx tree-sitter parse file.clspFile types: .clsp, .clib, .clinc.
commentis inextras, but note that tree-sitter's C APIts_node_named_childstill counts named extras — acommentbetween a form's head and its name would shiftnamed_childindices. Consumers should skip nodes of typecommentwhen locating the head/name. (In the pinned cl-26 corpus this never occurs, but the guard is one line and worth having.)dotis a named node and appears innamed_childfor dotted pairs; it never appears at a definition's head position.
MIT.