tree-sitter-pyrope

June 14, 2026 · View on GitHub

A tree-sitter grammar and parser for Pyrope, plus the tooling built on top of it.

What's in here

PathWhat it is
grammar.js, src/The tree-sitter grammar and the generated parser. This is the package.
queries/Editor queries — highlights.scm, indents.scm, folds.scm.
editors/Editor integration (currently a Neovim / nvim-treesitter plugin).
prpfmt/A Pyrope code formatter (C) that walks the tree-sitter tree. See prpfmt/README.md.
prpparse/Design for a future recursive-descent parser for LiveHD (faster than tree-sitter, not for editors). See prpparse/plan.md.
scripts/extract.rb (build the test corpus from docs) and test.sh (parse it).
full_pyrope/Auto-generated test corpus (git-ignored — rebuild with make corpus).

Build & test

The repo uses a single Makefile as the entry point. It needs tree-sitter-cli, installed locally via npm:

npm install            # one-time: installs node_modules/tree-sitter-cli
CommandDoes
make / make generateRegenerate src/parser.c from grammar.js.
make test / make test-grammarParse all full_pyrope/*.prp — the canonical regression (must stay green).
make test-allAlso run prpfmt + prpparse (prpfmt is WIP, so this may be red).
make corpusRebuild full_pyrope/ from the Pyrope docs in ../docs.
make prpfmt / make test-prpfmtBuild the formatter / verify it over the corpus.
make cleanClean build artifacts.

Parsing a single file (debugging the grammar)

# Print the parse tree (-t adds timing):
./node_modules/tree-sitter-cli/tree-sitter parse -t ./full_pyrope/file1.prp

# Just check it parses (no tree), the way scripts/test.sh does:
./node_modules/tree-sitter-cli/tree-sitter parse -q ./full_pyrope/file1.prp

Syntax highlighting (Neovim)

Pyrope syntax highlighting in Neovim is provided through the nvim-treesitter plugin.

These instructions target the main branch of nvim-treesitter (the current rewrite, used by recent LazyVim). The older master-branch API (get_parser_configs(), manual query copying, :TSEnable) no longer applies.

lazy.nvim / LazyVim

Copy editors/nvim/pyrope.lua into your plugins directory (e.g. ~/.config/nvim/lua/plugins/pyrope.lua). It registers the parser from this repo, maps *.prp files to the pyrope filetype, and adds pyrope to ensure_installed:

local function register_pyrope()
  require("nvim-treesitter.parsers").pyrope = {
    install_info = {
      url = "https://github.com/masc-ucsc/tree-sitter-pyrope",
      branch = "main",
      queries = "queries",
    },
  }
end

return {
  {
    "nvim-treesitter/nvim-treesitter",
    init = function()
      vim.filetype.add({ extension = { prp = "pyrope" } })
      vim.api.nvim_create_autocmd("User", {
        pattern = "TSUpdate", -- re-register before every install/update
        callback = register_pyrope,
      })
      -- Pyrope uses C-style comments; needed for `gc`/`gcc` commenting.
      vim.api.nvim_create_autocmd("FileType", {
        pattern = "pyrope",
        callback = function()
          vim.bo.commentstring = "// %s"
        end,
      })
    end,
    opts = function(_, opts)
      opts.ensure_installed = opts.ensure_installed or {}
      vim.list_extend(opts.ensure_installed, { "pyrope" })
    end,
  },
}

Restart Neovim (or :Lazy reload nvim-treesitter), then run :TSInstall pyrope.

The parser is registered on the User TSUpdate event, not once at startup. nvim-treesitter reloads its parser table from disk before every install/update (wiping runtime registrations) and re-fires this event so out-of-tree parsers can re-register. Registering only at startup yields skipping unsupported language: pyrope.

Plain nvim-treesitter (no LazyVim)

Add the following to your init.lua, then run :TSInstall pyrope:

vim.api.nvim_create_autocmd("User", {
  pattern = "TSUpdate", -- re-register before every install/update
  callback = function()
    require("nvim-treesitter.parsers").pyrope = {
      install_info = {
        url = "https://github.com/masc-ucsc/tree-sitter-pyrope",
        branch = "main",
        queries = "queries",
      },
    }
  end,
})

vim.filetype.add({ extension = { prp = "pyrope" } })

vim.api.nvim_create_autocmd("FileType", {
  pattern = "pyrope",
  callback = function()
    vim.treesitter.start()
    vim.bo.commentstring = "// %s" -- Pyrope uses C-style comments
  end,
})

The queries = "queries" field installs this repo's queries/*.scm (highlights, folds, indents) automatically — no manual copying needed.

Verify

Open a .prp file and check:

  • :set ft? shows filetype=pyrope
  • :checkhealth nvim-treesitter lists pyrope as installed
  • :Inspect on a token shows the matched @capture and highlight group

Local development

To highlight from a local checkout instead of the public repo (so grammar and query edits show up locally), replace url/branch with a path:

install_info = {
  path = vim.fn.expand("~/projs/tree-sitter-pyrope"),
  queries = "queries",
},

Query edits are picked up live (symlinked). After changing grammar.js, run make generate and then :TSInstall! pyrope to rebuild the parser.

prpfmt

prpfmt is a Pyrope code formatter (think clang-format): it parses with the tree-sitter grammar and re-emits standardized, vertically-aligned Pyrope. Build it with make prpfmt; details and options are in prpfmt/README.md.

prpparse

prpparse is a fast, recursive-descent Pyrope parser intended for LiveHD (not editors), using this grammar as a differential oracle. The parser is implemented (Phases 0–3: lexer, tree schema, parser; accept-parity on all 428 corpus files) and the next step is a streaming statement interface so the LiveHD front-end can replace tree-sitter — see prpparse/plan.md.