tree-sitter-cfml 0.26.2

cfml grammar for tree-sitter
Documentation

tree-sitter-cfml

npm crates.io "Buy Me A Coffee"

Tree-sitter grammars for ColdFusion Markup Language (CFML).

Three grammars are provided to cover the different ways CFML is written, plus an embedded SQL dialect for cfquery:

Grammar Scope File types Description
cfml source.cfml .cfc ColdFusion components — CFScript inside a component {} block or tag-based component files
cfhtml source.cfhtml .cfm CFML template files — HTML with embedded CF tags and hash expressions
cfscript source.cfscript .cfs Pure CFScript files
cfquery source.cfquery (embedded) SQL dialect used inside <cfquery> bodies (and compatible with QueryExecute-style usage) with CF-style #hash# interpolation and CF tags in the body

Playground

Try the grammars in the browser: cfmleditor.github.io/tree-sitter-cfml

Installation

Node.js

npm install @cfmleditor/tree-sitter-cfml
const { cfml, cfhtml, cfscript, cfquery } = require('@cfmleditor/tree-sitter-cfml');
const Parser = require('tree-sitter');

const parser = new Parser();
parser.setLanguage(cfhtml.language);

const tree = parser.parse('<cfif condition>#value#</cfif>');
console.log(tree.rootNode.toString());

Rust

Add to your Cargo.toml:

[dependencies]
tree-sitter = "0.25"
tree-sitter-cfml = "0.26"
use tree_sitter_cfml::{LANGUAGE_CFML, LANGUAGE_CFHTML, LANGUAGE_CFSCRIPT, LANGUAGE_CFQUERY};

// cfml for .cfc component files
let mut parser = tree_sitter::Parser::new();
parser.set_language(&LANGUAGE_CFML.into())
    .expect("Error loading CFML grammar");

// cfhtml for .cfm template files
parser.set_language(&LANGUAGE_CFHTML.into())
    .expect("Error loading CFHTML grammar");

// cfscript for .cfs pure script files
parser.set_language(&LANGUAGE_CFSCRIPT.into())
    .expect("Error loading CFSCRIPT grammar");

Python

pip install tree-sitter-cfml
import tree_sitter_cfml as ts_cfml
from tree_sitter import Language, Parser

# cfhtml for .cfm template files
parser = Parser(Language(ts_cfml.language_cfhtml()))
tree = parser.parse(b'<cfif x GT 0>#x#</cfif>')

# cfml for .cfc component files
parser = Parser(Language(ts_cfml.language_cfml()))

# cfscript for .cfs pure script files
parser = Parser(Language(ts_cfml.language_cfscript()))

# cfquery SQL dialect (embedded)
parser = Parser(Language(ts_cfml.language_cfquery()))

Go

import (
    tree_sitter_cfml "github.com/cfmleditor/tree-sitter-cfml/bindings/go"
    sitter "github.com/tree-sitter/go-tree-sitter"
)

// cfml for .cfc component files
parser := sitter.NewParser()
parser.SetLanguage(sitter.NewLanguage(tree_sitter_cfml.LanguageCfml()))

// cfhtml for .cfm template files
parser.SetLanguage(sitter.NewLanguage(tree_sitter_cfml.LanguageCfhtml()))

// 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()))

Development

Requirements

Building

Each grammar is built independently from its subdirectory:

cd cfml && make
cd cfhtml && make
cd cfscript && make

Or build all three plus the Node.js bindings:

npm run build

Note on build warnings When running npm run build, you may see tree-sitter warnings about “unnecessary conflicts” such as binary_expression, call_expression, switch_case, or assignment_expression versus _property_name, and hash-related conflicts for the cfquery dialect. These conflicts are intentionally declared in common/define-grammar.js to resolve real ambiguities in CFML/CFHTML/cfquery syntax; attempts to remove them cause tree-sitter generate to fail with unresolved conflicts. It is safe to ignore these warnings as long as the build and tests succeed.

Generating the parser

After editing common/define-grammar.js:

cd cfml && tree-sitter generate
cd cfhtml && tree-sitter generate
cd cfscript && tree-sitter generate

Testing

# Test a single grammar
cd cfml && tree-sitter test

# Test all grammars
npm test

Parsing a file

cd cfml && tree-sitter parse path/to/file.cfc

Playground

npm run playground

Grammar structure

All four grammars share a common base defined in common/define-grammar.js, with an external scanner in common/scanner.h that handles context-sensitive tokenisation (implicit end tags, CF tag names, hash expressions, raw text, etc.).

common/
  define-grammar.js   # shared grammar rules for all three dialects
  scanner.h           # external scanner (C)
  tag.h               # HTML/CF tag type definitions

cfml/                 # .cfc grammar
cfhtml/               # .cfm grammar
cfscript/             # .cfs grammar
cfquery/              # cfquery SQL dialect (embedded)
  grammar.js          # entry point (calls defineGrammar with dialect name)
  src/                # generated parser (do not edit)
  queries/
    highlights.scm    # syntax highlighting
    indents.scm       # indentation
    injections.scm    # language injections
    tags.scm          # symbol navigation

Queries

Each grammar ships with query files for editor integration:

  • highlights.scm — syntax highlighting captures
  • indents.scm — indentation rules
  • injections.scm — embedded language injections (e.g. SQL inside <cfquery>)
  • tags.scm — function/method definitions and call references for go-to-definition and symbol search

License

MIT