- Scheme 84.4%
- C 13.1%
- Makefile 2.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| gambit | ||
| t | ||
| api.ss | ||
| build.ss | ||
| gerbil.pkg | ||
| LICENSE | ||
| README.md | ||
gerbil-treesitter
Tree-sitter bindings for Gerbil Scheme, built on a portable Gambit FFI layer that should works on any POSIX Linux distribution.
The package re-exports two layers from :clan/treesitter/api:
| Layer | Identifier prefix | Role |
|---|---|---|
| Raw FFI | ts-ffi-* |
ffi bindings — one symbol per tree-sitter C function |
| High-level | ts-* |
Idiomatic Scheme wrappers (resource handling, points, etc.) |
The high-level layer is written in Gambit Scheme and lives under gambit/src/*.scm.
It is included verbatim by the Gerbil wrapper, so the same code drives both runtimes — no logic duplication.
Usage
(import :clan/treesitter/api)
;; Load a language from a compiled grammar
(def lang
(ts-language-load "path/to/libtree-sitter-json.so" "tree_sitter_json"))
;; Create a parser and parse a string
(def parser (ts-parser-new lang))
(def tree (ts-parser-parse-string parser "{\"a\":1}"))
;; Walk the tree
(def root (ts-tree-root-node tree))
(ts-node-type root) ; => "document"
(def obj (ts-node-child root 0))
(def pair (ts-node-named-child obj 0))
(def key (ts-node-child-by-field-name pair "key"))
;; Clean up
(ts-tree-delete tree)
(ts-parser-delete parser)
(ts-language-close lang)
For the raw C-level layer, use the ts-ffi-* names declared in api.ss.
They map one-to-one to tree-sitter's C API, and are useful when the high-level
wrapper doesn't expose something you need (e.g. ts-ffi-node-id,
ts-ffi-node-from-parts, ts-ffi-tree-edit).
Requirements
- Gerbil Scheme ≥ 0.18 (bundled Gambit is fine)
- tree-sitter ≥ 0.26.8: headers and shared library
gcc,make,pkg-config(optional)- Optional for tests: a pre-built JSON grammar
.so(a copy is bundled undergambit/tests-lib/)
Platform setup
Debian / Ubuntu
sudo apt update
sudo apt install -y build-essential pkg-config libtree-sitter-dev
Arch Linux
sudo pacman -S --needed base-devel pkgconf tree-sitter
Fedora / RHEL
sudo dnf install -y gcc make pkgconf-pkg-config tree-sitter-devel
NixOS
You can find the required paths with:
nix eval --raw nixpkgs#tree-sitter.outPath
Insert the store hash to your installation:
TREE_SITTER_CFLAGS="-I$(nix eval --raw nixpkgs#tree-sitter.outPath)/include" \
TREE_SITTER_LIBS="-L$(nix eval --raw nixpkgs#tree-sitter.outPath)/lib -ltree-sitter -ldl" \
./build.ss
If Gerbil itself is installed via Nix, the gerbil wrapper usually sets
GERBIL_PATH; otherwise the default ~/.gerbil is used.
Build
chmod +x build.ss
./build.ss
Expected output (Debian/Ubuntu, pkg-config working):
copied: /home/<you>/.../gambit/src -> /home/<you>/.gerbil/lib/clan/treesitter/gambit/src
cc-options: -I/usr/include -I/home/<you>/.../gambit/src
ld-options: -L/usr/lib -ltree-sitter -ldl /home/<you>/.../gambit/src/tree-sitter.o
gxc api.ss
ok
The script also prints the resolved cc-options and ld-options, so if something fails you can immediately see which flags were used.
Development
Gerbil smoke test
Run from the package root, so gambit/tests-lib/libtree-sitter-json.so resolves:
./build.ss
gxi t/test-gerbil.ss
Gambit tests
The Gambit side has its own tests, independent of Gerbil:
make -C gambit build-tests
./gambit/tests/bin/test-node
./gambit/tests/bin/test-api
./gambit/tests/bin/test-json
./gambit/tests/bin/test
These use the same C wrappers (gambit/src/tree-sitter.c) but the pure-Gambit FFI layer (gambit/src/ffi.scm).
Layout
.
├── build.ss # Gerbil build script (gxc wrapper)
├── api.ss # Gerbil FFI module: begin-ffi + includes
├── gerbil.pkg # package metadata
├── t/
│ └── test-gerbil.ss # Gerbil smoke test (parse + navigate JSON)
└── gambit/ # Gambit reference implementation
├── src/
│ ├── tree-sitter.c # C wrappers around libtree-sitter
│ ├── ffi.h # C declarations shared by both c-declare blocks
│ ├── ffi.scm # pure-Gambit c-lambda bindings
│ ├── language.scm # ts-language-*
│ ├── parser.scm # ts-parser-*
│ ├── tree.scm # ts-tree-*
│ └── node.scm # ts-node-*
├── tests-lib/
│ └── libtree-sitter-json.so # pre-built JSON grammar (for tests)
└── tests/ # Gambit executable tests
├── (...)
└── bin/ # compiled Gambit test binaries
How it works
-
gambit/src/tree-sitter.cis compiled togambit/src/tree-sitter.o. The.ois linked into the final Gerbil module so thelml_ts_*wrappers are always available. -
build.sscopiesgambit/src/to~/.gerbil/lib/clan/treesitter/gambit/src/, so thatbegin-ffi's(include "./gambit/src/...")resolves both at build time and after installation. A real copy is used (not a symlink) so the installed module is self-contained. -
api.ssdeclares the FFI (begin-ffi), includes the Gambit Scheme layers, and exposes everything as:clan/treesitter/api. -
tree-sitter flags are picked up in this order:
TREE_SITTER_CFLAGS/TREE_SITTER_LIBSenvironment variables (highest priority)pkg-config --cflags/--libs tree-sitter- POSIX default: empty C flags,
-ltree-sitter -ldl
This means on Debian/Ubuntu/Arch (where
pkg-configknows about the package), plain./build.ssjust works. On NixOS you pass both explicitly.
Notes on the high-level Scheme layer
The files under gambit/src/*.scm are written in portable Gambit Scheme and do
not use Gerbil-specific macros. The Gerbil api.ss merely includes them
inside its own module, so:
- the same source builds against both Gambit and Gerbil,
- bug fixes and new wrappers only need to be written once,
- the Gambit test binaries under
gambit/tests/bin/and the Gerbil smoke test undert/test-gerbil.ssexercise the same underlying code.
License
MIT.