tree-sitter-cfml
Tree-sitter grammars for ColdFusion Markup Language (CFML).
There are three grammars: two for CFML in .cfc/.cfm and .cfs files, and one for SQL inside <cfquery> (embedded dialect).
| Grammar | Scope | File types | Description |
|---|---|---|---|
cfml |
source.cfml |
.cfc, .cfm |
ColdFusion components and template files - CFScript, tag-based components, and HTML with embedded CF tags |
cfscript |
source.cfscript |
.cfs |
Pure CFScript files |
cfquery |
source.cfquery |
(embedded) | SQL inside <cfquery> bodies (including QueryExecute-style usage), with #hash# interpolation and CF tags in the body |
Playground
Browser demo: cfmleditor.github.io/tree-sitter-cfml
Installation
Node.js
const = require;
const Parser = require;
const parser = ;
parser.;
const tree = parser.;
console.log;
Rust
[]
= "0.25"
= "0.26.34"
The tree-sitter crate should be 0.25+ so the ABI matches the generated parsers (see LANGUAGE_VERSION in cf*/src/parser.c).
use LANGUAGE_CFML;
let mut parser = new;
parser.set_language
.expect;
// LANGUAGE_CFSCRIPT, LANGUAGE_CFQUERY load the same way.
Python
# cfml for .cfc and .cfm files
=
=
# cfscript for .cfs pure script files
=
# cfquery SQL dialect (embedded)
=
Go
import (
tree_sitter_cfml "github.com/cfmleditor/tree-sitter-cfml/bindings/go"
sitter "github.com/tree-sitter/go-tree-sitter"
)
// cfml for .cfc and .cfm files
parser := sitter.NewParser()
parser.SetLanguage(sitter.NewLanguage(tree_sitter_cfml.LanguageCfml()))
// cfscript for .cfs pure script files
parser.SetLanguage(sitter.NewLanguage(tree_sitter_cfml.LanguageCfscript()))
// cfquery SQL dialect (embedded)
parser.SetLanguage(sitter.NewLanguage(tree_sitter_cfml.LanguageCfquery()))
Java
Needs JDK 23+ — the binding is built on the Foreign Function & Memory API, through jtreesitter.
io.github.cfmleditor
tree-sitter-cfml
0.26.31
;
;
;
;
;
// cfml for .cfc and .cfm files
try
// cfscript for .cfs pure script files
var cfscript ;
// cfquery SQL dialect (embedded)
var cfquery ;
Unlike the other bindings, this one does not compile the C for you. It loads
four shared libraries at runtime — libtree-sitter plus one per grammar — so
they have to be somewhere the loader looks (LD_LIBRARY_PATH,
java.library.path, or a system library directory). Either install them:
&&
or, from a checkout, build all four (including the tree-sitter runtime, pinned
by package-lock.json) into build/native/:
&&
Run the binding's own tests with mvn test — it points java.library.path at
both locations.
The artifact is not on Maven Central yet; see the note on publish-maven in
.github/workflows/release.yml.
Development
Each dialect has grammar.js, generated C under src/, corpus tests under test/corpus/, and queries under queries/. Shared scanner code is under common/. The multi-grammar CLI and playground config is tree-sitter.json. Upstream docs: Creating parsers, CLI.
Setup
Use Node >=18 and <24 (package.json engines). Optional: .nvmrc with nvm / fnm (nvm use).
That installs dependencies, builds the Node native addon (node-gyp-build), and runs postinstall, which downloads the tree-sitter CLI binary into node_modules/tree-sitter-cli/ when needed. Repo npm scripts do not require a global tree-sitter on PATH.
Windows
Put GCC from MinGW-w64 on your PATH (gcc / g++). This repo uses GCC for npm test (tree-sitter compile), the Node native binding, Python extension builds, and Go CGO — not MSVC.
Standalone toolchain (recommended): install WinLibs with winget, then add the extracted mingw64\bin directory (contains gcc.exe) to your user PATH, open a new terminal, and verify:
gcc --version
Example package (UCRT, POSIX threads):
winget install BrechtSanders.WinLibs.POSIX.UCRT
The installer path varies by machine; locate mingw64\bin under the WinLibs folder (or under %LOCALAPPDATA%\Microsoft\WinGet\Packages\ after install) and add that bin to PATH.
Alternatively: MSYS2 with pacman -S mingw-w64-x86_64-gcc, then prepend msys64\mingw64\bin to PATH (or develop from an MSYS2 MinGW64 shell).
If node-gyp still picks Visual Studio instead of MinGW, set CC / CXX to your MinGW gcc / g++ for npm install / npm rebuild, or keep MinGW’s bin before MSVC entries on PATH.
macOS
Install the Xcode command-line tools:
Homebrew
Linux
Install a C/C++ toolchain (for example build-essential on Debian/Ubuntu, gcc / clang plus development headers on other distributions).
CI
CI (.github/workflows/ci.yml): npm install, npm test, npm run lint on Ubuntu, macOS, and Windows. It does not run npm run build; generated cf*/src/ files are committed.
Tree-sitter CLI
Scripts use scripts/tree-sitter-cli.cjs (node node_modules/tree-sitter-cli/cli.js). A global tree-sitter-cli install is optional. If the binary is missing after install:
From a dialect directory (after npm install at repo root):
Dependency versions
Pinned in package.json / tree-sitter.json; approximate roles:
| Role | Package | Version |
|---|---|---|
| Native binding (peer / dev) | tree-sitter |
0.25.0 |
| Parser CLI | tree-sitter-cli |
0.26.8 |
| Native addon | node-addon-api |
^8.3.0 |
| Native addon | node-gyp-build |
^4.8.4 |
| Prebuild | prebuildify |
^6.0.1 |
| Runtime | Node.js | >=18 <24 |
Java binding (pom.xml) |
jtreesitter |
0.26.1 |
| Java binding | JDK | >=23 |
CFML engines
Corpus and behavior are checked mainly against Lucee. Overlapping Adobe ColdFusion syntax should still parse in a reasonable way. Avoid Adobe-only or Lucee-only assumptions in examples or grammar design where portable CFML is enough.
Building
After changing common/define-grammar.js or a grammar.js:
On Unix, make generate at the repo root works if tree-sitter is on your PATH; otherwise use npm run build.
tree-sitter generate may warn about “unnecessary conflicts” (expressions vs _property_name, cfscript declaration / primary_expression, cfquery hash rules, etc.). Those come from common/define-grammar.js. If npm run build and npm test succeed, the warnings can be ignored.
Testing and helpers
See Setup for npm test, npm run lint, and npm run build.
&&
One grammar only: run test via the CLI from that dialect’s directory (above), or npm test for all three.
Real-world corpus: npm run corpus:fetch shallow-clones ~25 public CFML projects into a gitignored corpus/, npm run scan corpus reports every ERROR/MISSING node, and npm run corpus:report clusters those into distinct failure sites. See CORPUS.md for the current results and the known gaps they turned up.
Parse a file: from the dialect folder (e.g. cfml for .cfc), use the parse subcommand with the same node ../node_modules/.../cli.js pattern.
Playground / WebAssembly (WASM)
npm start— playground at repo root (tree-sitter.json). Runnpm run prestartfirst if WASM is stale.npm run prestart—tree-sitter build --wasmnpm run playground— playground in each ofcfml/,cfscript/,cfquery/npm run docswasm— writesdocs/tree-sitter-{cfml,cfscript,cfquery}.wasmfordocs/(e.g. GitHub Pages)
Releasing
The release script (scripts/release.js) will:
- Validate version format and ensure it's greater than current
- Ensure the working tree is clean and local branch is not behind remote
- Verify tag
v<version>doesn't already exist - Verify
CHANGELOG.mdhas a## [<version>]or## [Unreleased]entry with notes - Update the version in
package.json,Cargo.toml,pyproject.toml,tree-sitter.jsonandpom.xml - Run
npm run build(regenerate parsers) - Run
npm run lint(ESLint) - Run
npm test(all three grammars) - Run
npm run docswasm(rebuild playground WASM) - Run
npm run install(rebuild native addon) - Commit all changes and create a
v<version>tag (prompted) - Push commit and tag (prompted)
Once the tag is pushed, the GitHub Release workflow (.github/workflows/release.yml) will automatically publish to npm, PyPI, crates.io, and create a GitHub Release with the changelog notes. Maven Central is wired up but disabled — see publish-maven in that workflow.
Grammar structure
Shared rules: common/define-grammar.js. External scanner: common/scanner.h (implicit end tags, CF tag names, hash expressions, raw text).
common/
define-grammar.js
scanner.h
tag.h
cfml/ # .cfc, .cfm
grammar.js
src/ # generated
queries/
cfscript/ # .cfs
grammar.js
src/
queries/
cfquery/ # embedded SQL
grammar.js
src/
queries/
Queries
| Grammar | Highlights | Indents | Injections | Tags |
|---|---|---|---|---|
cfml |
yes | yes | yes | yes |
cfscript |
yes | no | no | yes |
cfquery |
yes | no | no | yes |
Contributing
See CONTRIBUTING.md.
Security
See SECURITY.md.
Agent and AI assistant guidance
See AGENTS.md.
