14 releases (2 stable)

1.0.2 Jan 24, 2023
0.6.2 Dec 25, 2022
0.6.0 Jul 26, 2022
0.4.1 Feb 3, 2022
0.3.0 Oct 6, 2021

#226 in Parser implementations

Download history 2/week @ 2022-10-25 3/week @ 2022-11-01 1/week @ 2022-11-15 5/week @ 2022-11-22 21/week @ 2022-12-06 22/week @ 2022-12-13 22/week @ 2022-12-20 56/week @ 2022-12-27 77/week @ 2023-01-03 9/week @ 2023-01-10 41/week @ 2023-01-17 38/week @ 2023-01-24

175 downloads per month
Used in tlauc

MIT license

37MB
1M SLoC

C 1M SLoC JavaScript 1.5K SLoC // 0.1% comments C++ 1K SLoC // 0.3% comments Scheme 271 SLoC // 0.1% comments Rust 44 SLoC // 0.1% comments

tree-sitter-tlaplus

Build & Test npm crates.io

Overview

This is a tree-sitter grammar for the formal specification language TLA⁺ (and PlusCal). Tree-sitter is an incremental error-tolerant parser generator primarily aimed at language tooling such as highlighting, code folding, symbol finding, and other tasks making use of its fully-featured syntax tree query API. This grammar is intended to function gracefully while parsing a source file mid-edit, when the syntax isn't fully correct. It is also fast enough to re-parse the file on every keystroke. You can take the parser for a spin at https://tlaplus-community.github.io/tree-sitter-tlaplus/

The most important files in this repo are grammar.js and src/scanner.cc. The former is the source of truth for parser code generation and the latter contains logic for parsing the context-sensitive parts of TLA⁺ like nested proofs and conjunction/disjunction lists.

A blog post detailing the development process of this parser can be found here.

Aims & Capabilities

The aim of this project is to facilitate creation of modern user-assistive language tooling for TLA⁺. To that end, the project provides two main capabilities:

  1. Provide an approximately-correct parse tree for TLA⁺ specifications in standardized form, for easy integration with general projects designed to consume the tree-sitter grammars of many languages.
  2. Provide a tree query API for efficiently querying the TLA⁺ parse tree, in addition to an API for arbitrary programmatic exploration of same, with bindings in multiple languages for easy integration with projects specifically targeting TLA⁺.

The correctness criterion of this parser is as follows: if the TLA⁺ specification being parsed constitutes valid TLA⁺ (both syntactically and semantically), the parse tree will be correct. If the spec is not valid TLA⁺, the parse tree will be approximately correct - perhaps permissively allowing illegal syntax, or interpreting erroneous syntax in strange ways. This permissive behavior makes it excellent for user-assistive language tooling, but probably a poor choice as the backbone for an interpreter or model-checker. Application possibilities include:

If you really want to use this project to write an interpreter, nobody's stopping you from trying. You could first use SANY to check spec validity, then use this parser to extract & interact with the actual parse tree. For a REPL, you might want to wait until the multiple entry points feature is added to tree-sitter so you can parse standalone TLA⁺ expressions without an encapsulating module.

Use & Notable Integrations

There are a number of avenues available for consuming & using the parser in a project of your own; see the tlaplus-tool-dev-examples repo.

Notable projects currently using or integrating this grammar include:

  • nvim-treesitter for TLA⁺ syntax highlighting & code folding in Neovim
  • tla-web for a web-based TLA⁺ interpreter and trace explorer
  • GitHub for syntax highlighting of TLA⁺ files and snippets
  • tlauc for translating between ASCII and Unicode TLA⁺ symbols
  • tla-mode for TLA⁺ syntax highlighting in Emacs

As applicable, query files for integrations live in the integrations directory.

Build & Test

  1. Install Node.js and npm

  2. Install a C compiler

  3. Clone the repo with the --recurse-submodules parameter

  4. Open a terminal in the repo root and run npm install to download packages & build the project

  5. Run npm test to run Tree-sitter unit tests

  6. Before running corpus tests (which parse all specifications in the tlaplus/examples repo), you might have to run npx tree-sitter init-config. Then, run either:

    • ./test/run-corpus.sh (Shell script on Unix operating systems)
    • .\test\run-corpus.ps1 (Powershell script on Windows)

    No output means successful tests.

The Playground

The playground enables you to easily try out the parser in your browser. You can use the playground online (serving the latest release) or set it up locally as follows:

  1. Install Emscripten 2.0.17 or earlier (why?);
  2. Run npx tree-sitter build-wasm;
  3. Run npx tree-sitter playground;

The playground consists of a pane containing an editable TLA⁺ spec, and another pane containing the parse tree for that spec. The parse tree is updated in real time as you edit the TLA⁺ spec. You can click parse tree nodes to highlight the corresponding snippet of TLA⁺, and move the cursor around the spec to show the corresponding parse tree node. You can click the "log" checkbox and open your browser's development console to see the parser's debug output as it attempts to parse the TLA⁺ spec. You can also click the "query" checkbox to open a third pane for testing tree queries; for example, enter the following to match all operator names in a capture named @operator (indicated by the names becoming highlighted):

(operator_definition name: (identifier) @operator)

Contributions

Pull requests are welcome. If you modify grammar.js, make sure you run npx tree-sitter generate before committing & pushing. Generated files are (unfortunately) currently present in the repo but will hopefully be removed in the future. Their correspondence is enforced during CI.

Dependencies