tree-sitter-irules 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Elijah Zupancic
4
+ Copyright (c) 2022 Lewis Russell (tree-sitter-tcl, on which this work is based)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: tree-sitter-irules
3
+ Version: 0.1.0
4
+ Summary: F5 iRules grammar for tree-sitter
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/dekobon/tree-sitter-irules
7
+ Keywords: incremental,parsing,tree-sitter,irules,tcl,f5
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Topic :: Software Development :: Compilers
10
+ Classifier: Topic :: Text Processing :: Linguistic
11
+ Classifier: Typing :: Typed
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Provides-Extra: core
16
+ Requires-Dist: tree-sitter<1.0,>=0.25; extra == "core"
17
+ Dynamic: license-file
18
+
19
+ # tree-sitter-irules
20
+
21
+ [![CI](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml/badge.svg)](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml)
22
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
23
+
24
+ A [tree-sitter](https://tree-sitter.github.io/tree-sitter/) parser for F5
25
+ [iRules](https://clouddocs.f5.com/api/irules/) — the TCL-derived scripting
26
+ language used to program traffic management on F5 BIG-IP.
27
+
28
+ Repository: <https://github.com/dekobon/tree-sitter-irules>.
29
+
30
+ iRules are syntactically a dialect of TCL with three additions:
31
+
32
+ - **Event handlers**: `when CLIENT_ACCEPTED { ... }`, with optional
33
+ `priority N` and `timing on|off` modifiers.
34
+ - **Namespace-qualified built-in commands**: `HTTP::host`, `IP::client_addr`,
35
+ `LB::server`, `SSL::cert`, `TCP::respond`, etc.
36
+ - **Extra expression operators** on top of TCL's `eq`/`ne`/`in`/`ni`:
37
+ `starts_with`, `ends_with`, `contains`, `equals`, `matches`,
38
+ `matches_regex`, `matches_glob`.
39
+
40
+ This grammar is built as an extension of
41
+ [`tree-sitter-tcl`](https://github.com/tree-sitter-grammars/tree-sitter-tcl)
42
+ by Lewis Russell, redistributed under the MIT license. See `LICENSE`.
43
+
44
+ ## Recognised iRules surface
45
+
46
+ Names below come from F5's authoritative sources:
47
+ [`Commands.html`](https://clouddocs.f5.com/api/irules/Commands.html),
48
+ [`iRulesReference.html`](https://clouddocs.f5.com/api/irules/iRulesReference.html),
49
+ [`Operators.html`](https://clouddocs.f5.com/api/irules/Operators.html), and
50
+ [`when.html`](https://clouddocs.f5.com/api/irules/when.html). The list
51
+ reflects what `queries/irules/highlights.scm` will tag as
52
+ `@function.builtin`; commands outside the list still parse, they just fall
53
+ through to the generic `@function` capture.
54
+
55
+ **Namespaced commands** (`<NS>::<command>`):
56
+
57
+ | Group | Namespaces |
58
+ |-------|-----------|
59
+ | Transport / IP | `IP`, `TCP`, `UDP`, `SCTP`, `LINK`, `VLAN`, `ROUTE`, `DATAGRAM`, `DHCP`, `FLOW`, `FLOWTABLE`, `IPFIX`, `LSN`, `NSH`, `PCP` |
60
+ | TLS | `SSL`, `CLIENTSSL`, `SERVERSSL`, `TLS`, `X509`, `IKE` |
61
+ | HTTP | `HTTP`, `HTTP2`, `HTTP3`, `WS`, `WEBSOCKET`, `CACHE`, `COMPRESS`, `REWRITE`, `STREAM`, `URI`, `JSON`, `XML`, `HTML`, `SSE` |
62
+ | Other application | `DNS`, `SIP`, `SDP`, `RTSP`, `FTP`, `MQTT`, `FIX`, `DIAMETER`, `RADIUS`, `ICAP`, `GTP`, `TDS`, `NTLM`, `MR`, `GENERICMESSAGE`, `CONNECTOR`, `DNSMSG`, `IMAP`, `POP3`, `SIPALG`, `SMTPS`, `SOCKS` |
63
+ | Load balancing / virtual | `LB`, `POOL`, `NODE`, `MEMBER`, `VIRTUAL`, `SNAT`, `SNATPOOL`, `PERSIST`, `PROFILE`, `PROXY`, `ONECONNECT`, `RATELIMIT`, `SCRUBBER`, `GTM`, `TMM`, `TMSH`, `BWC`, `ISESSION`, `IVS`, `L7CHECK` |
64
+ | Access / security | `ACL`, `ACCESS`, `ACCESS2`, `AAA`, `AUTH`, `WEBSSO`, `VDI`, `WAM`, `TAP`, `ASM`, `BOTDEFENSE`, `ANTIFRAUD`, `DOSL7`, `CATEGORY`, `CLASSIFICATION`, `CLASSIFY`, `CLASS`, `ADM`, `APM`, `ECA`, `POLICY`, `PSC` |
65
+ | Adaptation / data | `ADAPT`, `PEM`, `AVR`, `STATS`, `TABLE`, `SESSION`, `EVENT`, `LOG`, `LOGGING`, `MEMORY`, `RESOLV`, `RESOLVER`, `REST`, `XLAT`, `NAME`, `HSL`, `ILX`, `QOE`, `MATRIX`, `NS`, `ISTATS`, `MESSAGE`, `SIDEBAND` |
66
+ | Crypto / encoding | `CRYPTO`, `AES`, `DES`, `RC4`, `HMAC`, `MD5`, `SHA1`, `SHA256`, `SHA384`, `SHA512`, `B64`, `HEX`, `BIGNUM`, `ASN1` |
67
+
68
+ **Global (non-namespaced) iRules commands** also tagged as
69
+ `@function.builtin`: `accumulate`, `active_members`, `active_nodes`,
70
+ `after`, `b64decode`, `b64encode`, `call`, `clientside`, `clone`,
71
+ `collect`, `connect`, `crc32`, `decode_uri`, `discard`, `domain`, `drop`,
72
+ `event`, `findclass`, `findstr`, `forward`, `getfield`, `htonl`, `htons`,
73
+ `listen`, `matchclass`, `member`, `members`, `node`, `nodes`, `ntohl`,
74
+ `ntohs`, `peer`, `persist`, `pool`, `recv`, `reject`, `release`, `send`,
75
+ `serverside`, `session`, `sharedvar`, `snat`, `snatpool`, `substr`,
76
+ `table`, `virtual`.
77
+
78
+ **Event names** are intentionally open: anything matching
79
+ `/[A-Z][A-Z0-9_]*/` parses as an `event_name`. F5 documents 200+ events
80
+ across protocol families and adds new ones each BIG-IP release; encoding
81
+ a closed set in the parser would force a regen on every release. Validate
82
+ event spellings in a linter, not the parser.
83
+
84
+ ## TCL baseline
85
+
86
+ iRules is a TCL 8.4 dialect (per F5
87
+ [K6091](https://my.f5.com/manage/s/article/K6091)). TCL 8.5 features
88
+ (`dict`, `lassign`, `**` operator, expanded `lsearch` switches) and
89
+ TCL 8.6 features (`try`/`on error`/`finally`, `lmap`, `throw`) are
90
+ available on BIG-IP 12.x and later when explicitly enabled. The grammar
91
+ follows the `tree-sitter-tcl` baseline, so 8.5/8.6 syntax parses
92
+ fine — but keep in mind that older BIG-IP runtimes will reject those
93
+ constructs.
94
+
95
+ A subset of TCL commands is **disabled at runtime** in iRules for safety
96
+ (`exec`, `file`, `open`, `socket`, and others; see F5's
97
+ [`DisabledTclCommands.html`](https://clouddocs.f5.com/api/irules/DisabledTclCommands.html)).
98
+ The parser does **not** enforce the disabled list — disabled commands
99
+ parse as ordinary TCL commands. Linting/validation is out of scope here.
100
+
101
+ ## Known limitations
102
+
103
+ - **Bare-word operands in `expr` contexts must be quoted or braced**, as
104
+ in stock TCL. `if {$x eq foo}` produces an `(ERROR ...)` node because
105
+ TCL's own `expr` rejects unquoted barewords (`invalid bareword "foo"`);
106
+ write `if {$x eq "foo"}` or `if {$x eq {foo}}`. `$var`, numbers,
107
+ booleans, `[cmd]`, and `"strings"` are all accepted operands.
108
+ - **Plain `matches` operator** (no `_glob`/`_regex` suffix) is accepted by
109
+ the grammar but is **not** documented on F5's Operators page. It is
110
+ retained for upstream `tree-sitter-tcl` compatibility; prefer
111
+ `matches_glob` or `matches_regex`.
112
+ - **Test-runner hang on certain comment-only headers**: if a
113
+ `test/highlight/*.irules` file's leading comment block contains *both*
114
+ a dot-and-slash heavy URL AND a literal quoted regex token (the
115
+ combination that occurs naturally when documenting the namespace match
116
+ predicate), `npx tree-sitter test` hangs on that file. Each pattern in
117
+ isolation is harmless. Workaround: keep header comments to a single
118
+ line in highlight test files.
119
+
120
+ ## Status
121
+
122
+ Early. The grammar parses iRules as TCL plus iRules-specific event handlers
123
+ and tags iRules namespace commands and globals in `queries/irules/highlights.scm`.
124
+
125
+ ## Building
126
+
127
+ ```sh
128
+ git clone https://github.com/dekobon/tree-sitter-irules.git
129
+ cd tree-sitter-irules
130
+ npm install
131
+ npx tree-sitter generate
132
+ npx tree-sitter test
133
+ ```
134
+
135
+ The committed `src/parser.c` is generated by the exact `tree-sitter-cli`
136
+ version recorded in `package-lock.json` and CI pins to that same version,
137
+ so contributors must regenerate `parser.c` with the locally-installed CLI
138
+ (`npx tree-sitter generate`) — never with a globally-installed one — when
139
+ bumping `tree-sitter-cli`. See `AGENTS.md` for the full versioning,
140
+ commit, and changelog conventions.
141
+
142
+ ## Using it
143
+
144
+ Pick the binding for your toolchain. All bindings expose
145
+ `tree_sitter_irules` (or the language-idiomatic equivalent) and resolve to
146
+ the same `src/parser.c` + `src/scanner.c`.
147
+
148
+ No release has been tagged yet, so all snippets below resolve from the
149
+ default branch. Once a release `vX.Y.Z` is cut, swap each snippet to a
150
+ pinned form: Cargo `tag = "vX.Y.Z"` (instead of `branch = "main"`), Go
151
+ `@vX.Y.Z` (instead of `@latest`), npm `github:dekobon/tree-sitter-irules#vX.Y.Z`,
152
+ pip `git+https://github.com/dekobon/tree-sitter-irules@vX.Y.Z`, or the
153
+ published package version once available on the relevant registry.
154
+
155
+ ```toml
156
+ # Cargo.toml
157
+ [dependencies]
158
+ tree-sitter-irules = { git = "https://github.com/dekobon/tree-sitter-irules", branch = "main" }
159
+ ```
160
+
161
+ ```sh
162
+ # Go: resolves to a pseudo-version of the latest commit on main.
163
+ go get github.com/dekobon/tree-sitter-irules@latest
164
+ ```
165
+
166
+ ```jsonc
167
+ // package.json
168
+ "dependencies": { "tree-sitter-irules": "github:dekobon/tree-sitter-irules" }
169
+ ```
170
+
171
+ ```toml
172
+ # pyproject.toml
173
+ [project]
174
+ dependencies = ["tree-sitter-irules @ git+https://github.com/dekobon/tree-sitter-irules"]
175
+ ```
176
+
177
+ Bindings ship the parser only. Editor / runtime integrations also need
178
+ to load `queries/irules/highlights.scm` (and optionally `folds.scm` /
179
+ `indents.scm`) for highlighting and structural navigation; consult your
180
+ editor's tree-sitter integration docs for how to register the queries.
181
+ The Rust binding additionally re-exports the highlights query as
182
+ `HIGHLIGHTS_QUERY`.
183
+
184
+ ### Filetype detection
185
+
186
+ The repo ships `ftdetect/irules.lua` and `ftplugin/irules.lua` for
187
+ Neovim. The ftdetect file maps `*.irule` and `*.irules` to filetype
188
+ `irules` unconditionally; the ftplugin then calls
189
+ `vim.treesitter.start()` for that filetype. Highlighting only renders
190
+ once the parser binary and `queries/irules/highlights.scm` are
191
+ registered with `nvim-treesitter` (or equivalent) — without that, the
192
+ `start()` call is a no-op and the buffer falls back to non-treesitter
193
+ highlighting.
194
+
195
+ iRules are also commonly stored with a plain `.tcl` extension — we
196
+ deliberately do **not** claim `.tcl` globally, since most `.tcl` files
197
+ on disk are ordinary TCL. To opt specific `.tcl` files into the iRules
198
+ parser, pick one of:
199
+
200
+ - **Modeline** at the top of the file:
201
+ `# vim: set filetype=irules :`
202
+ - **Per-project autocmd** in `.nvim.lua` (sourced after `:cd` into the
203
+ project via Neovim's `'exrc'`). Note `vim.fn.getcwd()` is evaluated
204
+ when the autocmd is *defined*, so this snippet belongs in a
205
+ per-project config, not a global `init.lua`:
206
+
207
+ ```lua
208
+ vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
209
+ pattern = vim.fn.getcwd() .. "/irules/*.tcl",
210
+ callback = function() vim.bo.filetype = "irules" end,
211
+ })
212
+ ```
213
+
214
+ - **Content-based detection** for files that always start with a `when`
215
+ block:
216
+
217
+ ```lua
218
+ vim.filetype.add({
219
+ pattern = {
220
+ [".*%.tcl$"] = function(_, bufnr)
221
+ local first = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or ""
222
+ if first:match("^%s*when%s+[A-Z][A-Z0-9_]*") then return "irules" end
223
+ end,
224
+ },
225
+ })
226
+ ```
227
+
228
+ Other editors follow the same pattern: keep `.tcl` mapped to TCL by
229
+ default and override per-directory or via a header comment.
230
+
231
+ ## Layout
232
+
233
+ - `grammar.js` — grammar definition (TCL base + iRules `when` event handler).
234
+ - `queries/irules/` — highlight, fold, and indent queries with iRules-aware tags.
235
+ - `test/corpus/` — corpus tests (TCL tests inherited; iRules-specific tests in
236
+ `test/corpus/irules.txt`).
237
+ - `bindings/` — language bindings (C, Go, Node, Python, Rust, Swift).
238
+
239
+ ## Contributing
240
+
241
+ - Issues and feature requests:
242
+ <https://github.com/dekobon/tree-sitter-irules/issues>
243
+ - Pull requests welcome. Read `AGENTS.md` first — it documents the
244
+ Conventional Commits / SemVer / Keep-a-Changelog conventions, the
245
+ validation gates (`npx tree-sitter generate`, `npx tree-sitter test`,
246
+ `npm run lint`), and the rule that committed `src/parser.c` must be
247
+ generated by the `tree-sitter-cli` version recorded in
248
+ `package-lock.json`.
249
+
250
+ ## License
251
+
252
+ MIT. See [`LICENSE`](LICENSE). This grammar is a fork of
253
+ [`tree-sitter-tcl`](https://github.com/tree-sitter-grammars/tree-sitter-tcl)
254
+ by Lewis Russell; original copyright is retained alongside the
255
+ `tree-sitter-irules` project copyright.
@@ -0,0 +1,237 @@
1
+ # tree-sitter-irules
2
+
3
+ [![CI](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml/badge.svg)](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+
6
+ A [tree-sitter](https://tree-sitter.github.io/tree-sitter/) parser for F5
7
+ [iRules](https://clouddocs.f5.com/api/irules/) — the TCL-derived scripting
8
+ language used to program traffic management on F5 BIG-IP.
9
+
10
+ Repository: <https://github.com/dekobon/tree-sitter-irules>.
11
+
12
+ iRules are syntactically a dialect of TCL with three additions:
13
+
14
+ - **Event handlers**: `when CLIENT_ACCEPTED { ... }`, with optional
15
+ `priority N` and `timing on|off` modifiers.
16
+ - **Namespace-qualified built-in commands**: `HTTP::host`, `IP::client_addr`,
17
+ `LB::server`, `SSL::cert`, `TCP::respond`, etc.
18
+ - **Extra expression operators** on top of TCL's `eq`/`ne`/`in`/`ni`:
19
+ `starts_with`, `ends_with`, `contains`, `equals`, `matches`,
20
+ `matches_regex`, `matches_glob`.
21
+
22
+ This grammar is built as an extension of
23
+ [`tree-sitter-tcl`](https://github.com/tree-sitter-grammars/tree-sitter-tcl)
24
+ by Lewis Russell, redistributed under the MIT license. See `LICENSE`.
25
+
26
+ ## Recognised iRules surface
27
+
28
+ Names below come from F5's authoritative sources:
29
+ [`Commands.html`](https://clouddocs.f5.com/api/irules/Commands.html),
30
+ [`iRulesReference.html`](https://clouddocs.f5.com/api/irules/iRulesReference.html),
31
+ [`Operators.html`](https://clouddocs.f5.com/api/irules/Operators.html), and
32
+ [`when.html`](https://clouddocs.f5.com/api/irules/when.html). The list
33
+ reflects what `queries/irules/highlights.scm` will tag as
34
+ `@function.builtin`; commands outside the list still parse, they just fall
35
+ through to the generic `@function` capture.
36
+
37
+ **Namespaced commands** (`<NS>::<command>`):
38
+
39
+ | Group | Namespaces |
40
+ |-------|-----------|
41
+ | Transport / IP | `IP`, `TCP`, `UDP`, `SCTP`, `LINK`, `VLAN`, `ROUTE`, `DATAGRAM`, `DHCP`, `FLOW`, `FLOWTABLE`, `IPFIX`, `LSN`, `NSH`, `PCP` |
42
+ | TLS | `SSL`, `CLIENTSSL`, `SERVERSSL`, `TLS`, `X509`, `IKE` |
43
+ | HTTP | `HTTP`, `HTTP2`, `HTTP3`, `WS`, `WEBSOCKET`, `CACHE`, `COMPRESS`, `REWRITE`, `STREAM`, `URI`, `JSON`, `XML`, `HTML`, `SSE` |
44
+ | Other application | `DNS`, `SIP`, `SDP`, `RTSP`, `FTP`, `MQTT`, `FIX`, `DIAMETER`, `RADIUS`, `ICAP`, `GTP`, `TDS`, `NTLM`, `MR`, `GENERICMESSAGE`, `CONNECTOR`, `DNSMSG`, `IMAP`, `POP3`, `SIPALG`, `SMTPS`, `SOCKS` |
45
+ | Load balancing / virtual | `LB`, `POOL`, `NODE`, `MEMBER`, `VIRTUAL`, `SNAT`, `SNATPOOL`, `PERSIST`, `PROFILE`, `PROXY`, `ONECONNECT`, `RATELIMIT`, `SCRUBBER`, `GTM`, `TMM`, `TMSH`, `BWC`, `ISESSION`, `IVS`, `L7CHECK` |
46
+ | Access / security | `ACL`, `ACCESS`, `ACCESS2`, `AAA`, `AUTH`, `WEBSSO`, `VDI`, `WAM`, `TAP`, `ASM`, `BOTDEFENSE`, `ANTIFRAUD`, `DOSL7`, `CATEGORY`, `CLASSIFICATION`, `CLASSIFY`, `CLASS`, `ADM`, `APM`, `ECA`, `POLICY`, `PSC` |
47
+ | Adaptation / data | `ADAPT`, `PEM`, `AVR`, `STATS`, `TABLE`, `SESSION`, `EVENT`, `LOG`, `LOGGING`, `MEMORY`, `RESOLV`, `RESOLVER`, `REST`, `XLAT`, `NAME`, `HSL`, `ILX`, `QOE`, `MATRIX`, `NS`, `ISTATS`, `MESSAGE`, `SIDEBAND` |
48
+ | Crypto / encoding | `CRYPTO`, `AES`, `DES`, `RC4`, `HMAC`, `MD5`, `SHA1`, `SHA256`, `SHA384`, `SHA512`, `B64`, `HEX`, `BIGNUM`, `ASN1` |
49
+
50
+ **Global (non-namespaced) iRules commands** also tagged as
51
+ `@function.builtin`: `accumulate`, `active_members`, `active_nodes`,
52
+ `after`, `b64decode`, `b64encode`, `call`, `clientside`, `clone`,
53
+ `collect`, `connect`, `crc32`, `decode_uri`, `discard`, `domain`, `drop`,
54
+ `event`, `findclass`, `findstr`, `forward`, `getfield`, `htonl`, `htons`,
55
+ `listen`, `matchclass`, `member`, `members`, `node`, `nodes`, `ntohl`,
56
+ `ntohs`, `peer`, `persist`, `pool`, `recv`, `reject`, `release`, `send`,
57
+ `serverside`, `session`, `sharedvar`, `snat`, `snatpool`, `substr`,
58
+ `table`, `virtual`.
59
+
60
+ **Event names** are intentionally open: anything matching
61
+ `/[A-Z][A-Z0-9_]*/` parses as an `event_name`. F5 documents 200+ events
62
+ across protocol families and adds new ones each BIG-IP release; encoding
63
+ a closed set in the parser would force a regen on every release. Validate
64
+ event spellings in a linter, not the parser.
65
+
66
+ ## TCL baseline
67
+
68
+ iRules is a TCL 8.4 dialect (per F5
69
+ [K6091](https://my.f5.com/manage/s/article/K6091)). TCL 8.5 features
70
+ (`dict`, `lassign`, `**` operator, expanded `lsearch` switches) and
71
+ TCL 8.6 features (`try`/`on error`/`finally`, `lmap`, `throw`) are
72
+ available on BIG-IP 12.x and later when explicitly enabled. The grammar
73
+ follows the `tree-sitter-tcl` baseline, so 8.5/8.6 syntax parses
74
+ fine — but keep in mind that older BIG-IP runtimes will reject those
75
+ constructs.
76
+
77
+ A subset of TCL commands is **disabled at runtime** in iRules for safety
78
+ (`exec`, `file`, `open`, `socket`, and others; see F5's
79
+ [`DisabledTclCommands.html`](https://clouddocs.f5.com/api/irules/DisabledTclCommands.html)).
80
+ The parser does **not** enforce the disabled list — disabled commands
81
+ parse as ordinary TCL commands. Linting/validation is out of scope here.
82
+
83
+ ## Known limitations
84
+
85
+ - **Bare-word operands in `expr` contexts must be quoted or braced**, as
86
+ in stock TCL. `if {$x eq foo}` produces an `(ERROR ...)` node because
87
+ TCL's own `expr` rejects unquoted barewords (`invalid bareword "foo"`);
88
+ write `if {$x eq "foo"}` or `if {$x eq {foo}}`. `$var`, numbers,
89
+ booleans, `[cmd]`, and `"strings"` are all accepted operands.
90
+ - **Plain `matches` operator** (no `_glob`/`_regex` suffix) is accepted by
91
+ the grammar but is **not** documented on F5's Operators page. It is
92
+ retained for upstream `tree-sitter-tcl` compatibility; prefer
93
+ `matches_glob` or `matches_regex`.
94
+ - **Test-runner hang on certain comment-only headers**: if a
95
+ `test/highlight/*.irules` file's leading comment block contains *both*
96
+ a dot-and-slash heavy URL AND a literal quoted regex token (the
97
+ combination that occurs naturally when documenting the namespace match
98
+ predicate), `npx tree-sitter test` hangs on that file. Each pattern in
99
+ isolation is harmless. Workaround: keep header comments to a single
100
+ line in highlight test files.
101
+
102
+ ## Status
103
+
104
+ Early. The grammar parses iRules as TCL plus iRules-specific event handlers
105
+ and tags iRules namespace commands and globals in `queries/irules/highlights.scm`.
106
+
107
+ ## Building
108
+
109
+ ```sh
110
+ git clone https://github.com/dekobon/tree-sitter-irules.git
111
+ cd tree-sitter-irules
112
+ npm install
113
+ npx tree-sitter generate
114
+ npx tree-sitter test
115
+ ```
116
+
117
+ The committed `src/parser.c` is generated by the exact `tree-sitter-cli`
118
+ version recorded in `package-lock.json` and CI pins to that same version,
119
+ so contributors must regenerate `parser.c` with the locally-installed CLI
120
+ (`npx tree-sitter generate`) — never with a globally-installed one — when
121
+ bumping `tree-sitter-cli`. See `AGENTS.md` for the full versioning,
122
+ commit, and changelog conventions.
123
+
124
+ ## Using it
125
+
126
+ Pick the binding for your toolchain. All bindings expose
127
+ `tree_sitter_irules` (or the language-idiomatic equivalent) and resolve to
128
+ the same `src/parser.c` + `src/scanner.c`.
129
+
130
+ No release has been tagged yet, so all snippets below resolve from the
131
+ default branch. Once a release `vX.Y.Z` is cut, swap each snippet to a
132
+ pinned form: Cargo `tag = "vX.Y.Z"` (instead of `branch = "main"`), Go
133
+ `@vX.Y.Z` (instead of `@latest`), npm `github:dekobon/tree-sitter-irules#vX.Y.Z`,
134
+ pip `git+https://github.com/dekobon/tree-sitter-irules@vX.Y.Z`, or the
135
+ published package version once available on the relevant registry.
136
+
137
+ ```toml
138
+ # Cargo.toml
139
+ [dependencies]
140
+ tree-sitter-irules = { git = "https://github.com/dekobon/tree-sitter-irules", branch = "main" }
141
+ ```
142
+
143
+ ```sh
144
+ # Go: resolves to a pseudo-version of the latest commit on main.
145
+ go get github.com/dekobon/tree-sitter-irules@latest
146
+ ```
147
+
148
+ ```jsonc
149
+ // package.json
150
+ "dependencies": { "tree-sitter-irules": "github:dekobon/tree-sitter-irules" }
151
+ ```
152
+
153
+ ```toml
154
+ # pyproject.toml
155
+ [project]
156
+ dependencies = ["tree-sitter-irules @ git+https://github.com/dekobon/tree-sitter-irules"]
157
+ ```
158
+
159
+ Bindings ship the parser only. Editor / runtime integrations also need
160
+ to load `queries/irules/highlights.scm` (and optionally `folds.scm` /
161
+ `indents.scm`) for highlighting and structural navigation; consult your
162
+ editor's tree-sitter integration docs for how to register the queries.
163
+ The Rust binding additionally re-exports the highlights query as
164
+ `HIGHLIGHTS_QUERY`.
165
+
166
+ ### Filetype detection
167
+
168
+ The repo ships `ftdetect/irules.lua` and `ftplugin/irules.lua` for
169
+ Neovim. The ftdetect file maps `*.irule` and `*.irules` to filetype
170
+ `irules` unconditionally; the ftplugin then calls
171
+ `vim.treesitter.start()` for that filetype. Highlighting only renders
172
+ once the parser binary and `queries/irules/highlights.scm` are
173
+ registered with `nvim-treesitter` (or equivalent) — without that, the
174
+ `start()` call is a no-op and the buffer falls back to non-treesitter
175
+ highlighting.
176
+
177
+ iRules are also commonly stored with a plain `.tcl` extension — we
178
+ deliberately do **not** claim `.tcl` globally, since most `.tcl` files
179
+ on disk are ordinary TCL. To opt specific `.tcl` files into the iRules
180
+ parser, pick one of:
181
+
182
+ - **Modeline** at the top of the file:
183
+ `# vim: set filetype=irules :`
184
+ - **Per-project autocmd** in `.nvim.lua` (sourced after `:cd` into the
185
+ project via Neovim's `'exrc'`). Note `vim.fn.getcwd()` is evaluated
186
+ when the autocmd is *defined*, so this snippet belongs in a
187
+ per-project config, not a global `init.lua`:
188
+
189
+ ```lua
190
+ vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
191
+ pattern = vim.fn.getcwd() .. "/irules/*.tcl",
192
+ callback = function() vim.bo.filetype = "irules" end,
193
+ })
194
+ ```
195
+
196
+ - **Content-based detection** for files that always start with a `when`
197
+ block:
198
+
199
+ ```lua
200
+ vim.filetype.add({
201
+ pattern = {
202
+ [".*%.tcl$"] = function(_, bufnr)
203
+ local first = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or ""
204
+ if first:match("^%s*when%s+[A-Z][A-Z0-9_]*") then return "irules" end
205
+ end,
206
+ },
207
+ })
208
+ ```
209
+
210
+ Other editors follow the same pattern: keep `.tcl` mapped to TCL by
211
+ default and override per-directory or via a header comment.
212
+
213
+ ## Layout
214
+
215
+ - `grammar.js` — grammar definition (TCL base + iRules `when` event handler).
216
+ - `queries/irules/` — highlight, fold, and indent queries with iRules-aware tags.
217
+ - `test/corpus/` — corpus tests (TCL tests inherited; iRules-specific tests in
218
+ `test/corpus/irules.txt`).
219
+ - `bindings/` — language bindings (C, Go, Node, Python, Rust, Swift).
220
+
221
+ ## Contributing
222
+
223
+ - Issues and feature requests:
224
+ <https://github.com/dekobon/tree-sitter-irules/issues>
225
+ - Pull requests welcome. Read `AGENTS.md` first — it documents the
226
+ Conventional Commits / SemVer / Keep-a-Changelog conventions, the
227
+ validation gates (`npx tree-sitter generate`, `npx tree-sitter test`,
228
+ `npm run lint`), and the rule that committed `src/parser.c` must be
229
+ generated by the `tree-sitter-cli` version recorded in
230
+ `package-lock.json`.
231
+
232
+ ## License
233
+
234
+ MIT. See [`LICENSE`](LICENSE). This grammar is a fork of
235
+ [`tree-sitter-tcl`](https://github.com/tree-sitter-grammars/tree-sitter-tcl)
236
+ by Lewis Russell; original copyright is retained alongside the
237
+ `tree-sitter-irules` project copyright.
@@ -0,0 +1,5 @@
1
+ "F5 iRules grammar for tree-sitter"
2
+
3
+ from ._binding import language
4
+
5
+ __all__ = ["language"]
@@ -0,0 +1 @@
1
+ def language() -> int: ...
@@ -0,0 +1,27 @@
1
+ #include <Python.h>
2
+
3
+ typedef struct TSLanguage TSLanguage;
4
+
5
+ TSLanguage *tree_sitter_irules(void);
6
+
7
+ static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
8
+ return PyCapsule_New(tree_sitter_irules(), "tree_sitter.Language", NULL);
9
+ }
10
+
11
+ static PyMethodDef methods[] = {
12
+ {"language", _binding_language, METH_NOARGS,
13
+ "Get the tree-sitter language for this grammar."},
14
+ {NULL, NULL, 0, NULL}
15
+ };
16
+
17
+ static struct PyModuleDef module = {
18
+ .m_base = PyModuleDef_HEAD_INIT,
19
+ .m_name = "_binding",
20
+ .m_doc = NULL,
21
+ .m_size = -1,
22
+ .m_methods = methods
23
+ };
24
+
25
+ PyMODINIT_FUNC PyInit__binding(void) {
26
+ return PyModule_Create(&module);
27
+ }