tree-sitter grammar for F# (still WIP) Based on the 4.1 F# language specification (Mostly, Appendix A) and the F# compiler parser
If you just want to use the F# parsers rather than work on the grammar, install the package for your ecosystem — each has its own usage guide:
| Ecosystem | Package | Guide |
|---|---|---|
| Node.js | npm: tree-sitter-fsharp | bindings/node/README.md |
| Python | PyPI: tree-sitter-fsharp | bindings/python/README.md |
| Rust | crates.io: tree-sitter-fsharp | bindings/rust/README.md |
| Go | github.com/ionide/tree-sitter-fsharp/bindings/go | bindings/go/README.md |
| Swift | SwiftPM: TreeSitterFsharp | bindings/swift/README.md |
| C | built from source (make / CMake) | bindings/c/README.md |
| Neovim | via nvim-treesitter | below |
The rest of this README covers building and developing the grammar itself.
First, run npm install to install the tree-sitter CLI as a local devDependency (it lands at node_modules/.bin/tree-sitter, not on your global $PATH).
Run the CLI with npx tree-sitter <command> — this resolves the local binary automatically. To regenerate both parsers use npm run generate.
To parse a file: npx tree-sitter parse <file> (see the Development section for more).
This project defines two parsers:
.fs/.fsx (source files)..fsi (signature files). This is not a standalone grammar: it starts with grammar(require("../fsharp/grammar"), { ... }) and overrides/adds a few rules on top of the base. Any change to fsharp/grammar.js also changes the derived signature grammar, so both parsers must be regenerated together (npm run generate).Both parsers share the external scanner at ./common/scanner.h. The per-parser src/scanner.c files are thin shims that #include the header — real scanner logic (newlines, comments, indent/dedent bookkeeping) lives in the header. Each grammar starts with the file node.
The generated src/grammar.json and src/parser.c files are checked into git and validated by CI. Do not hand-edit them; merge conflicts are resolved by re-running npm run generate. See AGENTS.md for the full development workflow.
tree-sitter-fsharp is supported through the usual installation methods of nvim-treesitter.
Installing the lastest grammar from this repo involves the following three steps:
:TSInstall fsharp inside Neovim.$XDG_CONFIG_HOME/nvim/queries/fsharp/ - see the Adding queries section of the nvim-treesitter README.The config you need is this (you can use a local path for url if you prefer):
local parser_config = require('nvim-treesitter.parsers').get_parser_configs()
parser_config.fsharp = {
install_info = {
url = 'https://github.com/ionide/tree-sitter-fsharp',
branch = 'main',
files = { 'src/scanner.c', 'src/parser.c' },
location = "fsharp"
},
requires_generate_from_grammar = false,
filetype = 'fsharp',
}
The package.json defines some helpful targets for developing the grammar:
tree-sitter.json.npm run generate rebuilds both parsers.npx tree-sitter test runs all tests for both parsers.npx tree-sitter parse $file runs the fsharp parser on $file and outputs the parse tree.npx tree-sitter parse -d $file runs the fsharp parser on $file and prints debug information..fsi files, use npx tree-sitter parse -p fsharp_signature $file because tree-sitter parse does not reliably select the signature parser automatically.Clone the project and start playing around with it.
If you find a code example which fails to parse, please reduce it to a minimal example and added to the corpus (fsharp/test/corpus/*.txt) as a test case.
For an introduction to developing tree-sitter parsers the official documentation is a good reference point.
PRs fleshing out the grammar or fixing bugs are welcome!
C
41.2%
JavaScript
38.2%
Tree-sitter Query
9.1%
F#
9.0%
CMake
2.4%
tree-sitter grammar for F# (still WIP) Based on the 4.1 F# language specification (Mostly, Appendix A) and the F# compiler parser
If you just want to use the F# parsers rather than work on the grammar, install the package for your ecosystem — each has its own usage guide:
| Ecosystem | Package | Guide |
|---|---|---|
| Node.js | npm: tree-sitter-fsharp | bindings/node/README.md |
| Python | PyPI: tree-sitter-fsharp | bindings/python/README.md |
| Rust | crates.io: tree-sitter-fsharp | bindings/rust/README.md |
| Go | github.com/ionide/tree-sitter-fsharp/bindings/go | bindings/go/README.md |
| Swift | SwiftPM: TreeSitterFsharp | bindings/swift/README.md |
| C | built from source (make / CMake) | bindings/c/README.md |
| Neovim | via nvim-treesitter | below |
The rest of this README covers building and developing the grammar itself.
First, run npm install to install the tree-sitter CLI as a local devDependency (it lands at node_modules/.bin/tree-sitter, not on your global $PATH).
Run the CLI with npx tree-sitter <command> — this resolves the local binary automatically. To regenerate both parsers use npm run generate.
To parse a file: npx tree-sitter parse <file> (see the Development section for more).
This project defines two parsers:
.fs/.fsx (source files)..fsi (signature files). This is not a standalone grammar: it starts with grammar(require("../fsharp/grammar"), { ... }) and overrides/adds a few rules on top of the base. Any change to fsharp/grammar.js also changes the derived signature grammar, so both parsers must be regenerated together (npm run generate).Both parsers share the external scanner at ./common/scanner.h. The per-parser src/scanner.c files are thin shims that #include the header — real scanner logic (newlines, comments, indent/dedent bookkeeping) lives in the header. Each grammar starts with the file node.
The generated src/grammar.json and src/parser.c files are checked into git and validated by CI. Do not hand-edit them; merge conflicts are resolved by re-running npm run generate. See AGENTS.md for the full development workflow.
tree-sitter-fsharp is supported through the usual installation methods of nvim-treesitter.
Installing the lastest grammar from this repo involves the following three steps:
:TSInstall fsharp inside Neovim.$XDG_CONFIG_HOME/nvim/queries/fsharp/ - see the Adding queries section of the nvim-treesitter README.The config you need is this (you can use a local path for url if you prefer):
local parser_config = require('nvim-treesitter.parsers').get_parser_configs()
parser_config.fsharp = {
install_info = {
url = 'https://github.com/ionide/tree-sitter-fsharp',
branch = 'main',
files = { 'src/scanner.c', 'src/parser.c' },
location = "fsharp"
},
requires_generate_from_grammar = false,
filetype = 'fsharp',
}
The package.json defines some helpful targets for developing the grammar:
tree-sitter.json.npm run generate rebuilds both parsers.npx tree-sitter test runs all tests for both parsers.npx tree-sitter parse $file runs the fsharp parser on $file and outputs the parse tree.npx tree-sitter parse -d $file runs the fsharp parser on $file and prints debug information..fsi files, use npx tree-sitter parse -p fsharp_signature $file because tree-sitter parse does not reliably select the signature parser automatically.Clone the project and start playing around with it.
If you find a code example which fails to parse, please reduce it to a minimal example and added to the corpus (fsharp/test/corpus/*.txt) as a test case.
For an introduction to developing tree-sitter parsers the official documentation is a good reference point.
PRs fleshing out the grammar or fixing bugs are welcome!
C
41.2%
JavaScript
38.2%
Tree-sitter Query
9.1%
F#
9.0%
CMake
2.4%