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.
- tree_sitter_irules-0.1.0/LICENSE +22 -0
- tree_sitter_irules-0.1.0/PKG-INFO +255 -0
- tree_sitter_irules-0.1.0/README.md +237 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules/__init__.py +5 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules/__init__.pyi +1 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules/binding.c +27 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules/py.typed +0 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/PKG-INFO +255 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/SOURCES.txt +16 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/dependency_links.txt +1 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/not-zip-safe +1 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/requires.txt +3 -0
- tree_sitter_irules-0.1.0/bindings/python/tree_sitter_irules.egg-info/top_level.txt +2 -0
- tree_sitter_irules-0.1.0/pyproject.toml +29 -0
- tree_sitter_irules-0.1.0/setup.cfg +4 -0
- tree_sitter_irules-0.1.0/setup.py +60 -0
- tree_sitter_irules-0.1.0/src/parser.c +30208 -0
- tree_sitter_irules-0.1.0/src/scanner.c +46 -0
|
@@ -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
|
+
[](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml)
|
|
22
|
+
[](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
|
+
[](https://github.com/dekobon/tree-sitter-irules/actions/workflows/ci.yml)
|
|
4
|
+
[](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 @@
|
|
|
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
|
+
}
|
|
File without changes
|