Gerbil tree-sitter bindings
  • Scheme 84.4%
  • C 13.1%
  • Makefile 2.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-27 12:50:40 +02:00
gambit Update Makefile for gambit 2026-09-27 12:50:40 +02:00
t Rename 2026-09-27 01:55:15 +02:00
api.ss Git init, adding files 2026-09-27 01:35:34 +02:00
build.ss Update build.ss and README.md 2026-09-27 12:44:49 +02:00
gerbil.pkg Git init, adding files 2026-09-27 01:35:34 +02:00
LICENSE Git init, adding files 2026-09-27 01:35:34 +02:00
README.md Update build.ss and README.md 2026-09-27 12:44:49 +02:00

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 under gambit/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

  1. gambit/src/tree-sitter.c is compiled to gambit/src/tree-sitter.o. The .o is linked into the final Gerbil module so the lml_ts_* wrappers are always available.

  2. build.ss copies gambit/src/ to ~/.gerbil/lib/clan/treesitter/gambit/src/, so that begin-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.

  3. api.ss declares the FFI (begin-ffi), includes the Gambit Scheme layers, and exposes everything as :clan/treesitter/api.

  4. tree-sitter flags are picked up in this order:

    1. TREE_SITTER_CFLAGS / TREE_SITTER_LIBS environment variables (highest priority)
    2. pkg-config --cflags/--libs tree-sitter
    3. POSIX default: empty C flags, -ltree-sitter -ldl

    This means on Debian/Ubuntu/Arch (where pkg-config knows about the package), plain ./build.ss just 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 under t/test-gerbil.ss exercise the same underlying code.

License

MIT.