tree-sitter-xonsh 0.1.0

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.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Xonsh grammar for TreeSitter
2
+
3
+ A [tree-sitter](https://tree-sitter.github.io/) grammar for [xonsh](https://xon.sh/), the Python-powered shell.
4
+
5
+ ## Overview
6
+
7
+ Xonsh extends Python 3 with shell-like syntax for subprocess execution. This grammar extends `tree-sitter-python` with xonsh-specific constructs.
8
+
9
+ > [!IMPORTANT]
10
+ > - This should be treated as experimental beta-stage software. The output tree layout would change.
11
+ > - Some limitations are forced by the fact that tree-sitter is context-free while some xonsh constructs are resolvable only at runtime.
12
+
13
+ ## Installation
14
+
15
+ ### Building from source
16
+ ```bash
17
+ git clone https://github.com/FoamScience/tree-sitter-xonsh
18
+ cd tree-sitter-xonsh
19
+ npm install
20
+ tree-sitter generate
21
+ tree-sitter parse <your_file>.xsh
22
+ ```
23
+
24
+ ## Known Limitations
25
+
26
+ 1. **Unknown commands parsed as Python** instead of a bare subprocess command.
27
+ - Workaround: Use explicit subprocess syntax: `$[mycommand]` instead of just `mycommand`
28
+ - This is an effect of scanner-based approaches, for context-bound xonsh subprocesses.
29
+
30
+ ## Architecture
31
+
32
+ This grammar extends [tree-sitter-python](https://github.com/tree-sitter/tree-sitter-python). Key components:
33
+
34
+ - **grammar.js**: Defines xonsh-specific rules and overrides Python rules where needed
35
+ - **scanner.c**: External scanner for:
36
+ - Bare subprocess detection (heuristic-based)
37
+ - `@` symbol usage disambiguation (decorator vs `@(...)` vs `@.attr` vs `@modifier`)
38
+ - Subprocess modifier handling (`@json`, `@unthread`, etc.)
39
+ - `&&`/`||` vs `&` disambiguation
40
+ - Brace expansion vs literal detection
41
+ - Python's indent/dedent handling (inherited)
42
+ - String delimiter handling (inherited)
43
+ - **queries/highlights.scm** provides syntax highlighting queries for Neovim.
44
+ - The TreeSitter CLI can read those, but will render the highlighting differently.
45
+
46
+ > Currently the scanner may look-ahead a whole line, which can affect performance.
47
+
48
+ ## License
49
+
50
+ MIT
package/binding.gyp ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "tree_sitter_xonsh_binding",
5
+ "dependencies": [
6
+ "<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
7
+ ],
8
+ "include_dirs": [
9
+ "src",
10
+ ],
11
+ "sources": [
12
+ "bindings/node/binding.cc",
13
+ "src/parser.c",
14
+ ],
15
+ "variables": {
16
+ "has_scanner": "<!(node -p \"fs.existsSync('src/scanner.c')\")"
17
+ },
18
+ "conditions": [
19
+ ["has_scanner=='true'", {
20
+ "sources+": ["src/scanner.c"],
21
+ }],
22
+ ["OS!='win'", {
23
+ "cflags_c": [
24
+ "-std=c11",
25
+ ],
26
+ }, { # OS == "win"
27
+ "cflags_c": [
28
+ "/std:c11",
29
+ "/utf-8",
30
+ ],
31
+ }],
32
+ ],
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,19 @@
1
+ #include <napi.h>
2
+
3
+ typedef struct TSLanguage TSLanguage;
4
+
5
+ extern "C" TSLanguage *tree_sitter_xonsh();
6
+
7
+ // "tree-sitter", "language" hashed with BLAKE2
8
+ const napi_type_tag LANGUAGE_TYPE_TAG = {
9
+ 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
10
+ };
11
+
12
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
13
+ auto language = Napi::External<TSLanguage>::New(env, tree_sitter_xonsh());
14
+ language.TypeTag(&LANGUAGE_TYPE_TAG);
15
+ exports["language"] = language;
16
+ return exports;
17
+ }
18
+
19
+ NODE_API_MODULE(tree_sitter_xonsh_binding, Init)
@@ -0,0 +1,9 @@
1
+ const assert = require("node:assert");
2
+ const { test } = require("node:test");
3
+
4
+ const Parser = require("tree-sitter");
5
+
6
+ test("can load grammar", () => {
7
+ const parser = new Parser();
8
+ assert.doesNotThrow(() => parser.setLanguage(require(".")));
9
+ });
@@ -0,0 +1,27 @@
1
+ type BaseNode = {
2
+ type: string;
3
+ named: boolean;
4
+ };
5
+
6
+ type ChildNode = {
7
+ multiple: boolean;
8
+ required: boolean;
9
+ types: BaseNode[];
10
+ };
11
+
12
+ type NodeInfo =
13
+ | (BaseNode & {
14
+ subtypes: BaseNode[];
15
+ })
16
+ | (BaseNode & {
17
+ fields: { [name: string]: ChildNode };
18
+ children: ChildNode[];
19
+ });
20
+
21
+ type Language = {
22
+ language: unknown;
23
+ nodeTypeInfo: NodeInfo[];
24
+ };
25
+
26
+ declare const language: Language;
27
+ export = language;
@@ -0,0 +1,11 @@
1
+ const root = require("path").join(__dirname, "..", "..");
2
+
3
+ module.exports =
4
+ typeof process.versions.bun === "string"
5
+ // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time
6
+ ? require(`../../prebuilds/${process.platform}-${process.arch}/tree-sitter-xonsh.node`)
7
+ : require("node-gyp-build")(root);
8
+
9
+ try {
10
+ module.exports.nodeTypeInfo = require("../../src/node-types.json");
11
+ } catch (_) {}
package/grammar.js ADDED
@@ -0,0 +1,461 @@
1
+ /**
2
+ * @file Xonsh grammar for tree-sitter
3
+ * @author Mohammed Elwardi Fadeli
4
+ * @license MIT
5
+ *
6
+ * Xonsh extends Python with shell-like syntax for subprocess execution.
7
+ */
8
+
9
+ /// <reference types="tree-sitter-cli/dsl" />
10
+ // @ts-check
11
+
12
+ const Python = require('tree-sitter-python/grammar');
13
+
14
+ module.exports = grammar(Python, {
15
+ name: 'xonsh',
16
+
17
+ // Override externals to add xonsh-specific tokens from scanner
18
+ externals: ($, original) => original.concat([
19
+ $._subprocess_start, // Bare subprocess detection from scanner
20
+ $._logical_and, // && operator (disambiguated from &)
21
+ $._logical_or, // || operator
22
+ $._background_amp, // Single & for background execution
23
+ $._keyword_and, // 'and' keyword in subprocess context
24
+ $._keyword_or, // 'or' keyword in subprocess context
25
+ $._subprocess_macro_start, // Subprocess macro: identifier! (consumed by scanner)
26
+ ]),
27
+
28
+ rules: {
29
+
30
+ // Env. Vars
31
+ env_variable: $ => seq('$', $.identifier),
32
+ env_variable_braced: $ => seq(
33
+ '${',
34
+ field('expression', $.expression),
35
+ '}',
36
+ ),
37
+
38
+ // Env. Var. Assignment: $VAR = value
39
+ env_assignment: $ => seq(
40
+ field('left', $.env_variable),
41
+ '=',
42
+ field('right', $.expression),
43
+ ),
44
+
45
+ // Env. Var. Deletion: del $VAR
46
+ env_deletion: $ => prec(2, seq(
47
+ 'del',
48
+ field('target', $.env_variable),
49
+ )),
50
+
51
+ // Scoped Env. Var.: $VAR=value cmd
52
+ env_scoped_command: $ => prec(5, seq(
53
+ field('env', repeat1($.env_prefix)),
54
+ field('command', $.subprocess_body),
55
+ )),
56
+ env_prefix: $ => seq(
57
+ $.env_variable,
58
+ token.immediate('='),
59
+ field('value', choice(
60
+ $.string,
61
+ $.identifier,
62
+ $.integer,
63
+ )),
64
+ ),
65
+
66
+ // Subprocess Operators
67
+ captured_subprocess: $ => seq(
68
+ '$(',
69
+ field('modifier', optional($.subprocess_modifier)),
70
+ field('body', optional($.subprocess_body)),
71
+ ')',
72
+ ),
73
+
74
+ // Subprocess output modifiers: @json, @yaml, @name, etc.
75
+ // Built-ins transform output into Python objects.
76
+ // Users can also define custom decorator aliases (e.g., @noerr, @path).
77
+ // Higher precedence than custom_function_glob to resolve @identifier ambiguity
78
+ subprocess_modifier: $ => prec(2, seq('@', $.identifier)),
79
+ captured_subprocess_object: $ => seq(
80
+ '!(',
81
+ field('modifier', optional($.subprocess_modifier)),
82
+ field('body', optional($.subprocess_body)),
83
+ ')',
84
+ ),
85
+ uncaptured_subprocess: $ => seq(
86
+ '$[',
87
+ field('modifier', optional($.subprocess_modifier)),
88
+ field('body', optional($.subprocess_body)),
89
+ ']',
90
+ ),
91
+ uncaptured_subprocess_object: $ => seq(
92
+ '![',
93
+ field('modifier', optional($.subprocess_modifier)),
94
+ field('body', optional($.subprocess_body)),
95
+ ']',
96
+ ),
97
+
98
+ // Python Evaluation in Subprocess Context
99
+ python_evaluation: $ => seq(
100
+ '@(',
101
+ field('expression', $.expression),
102
+ ')',
103
+ ),
104
+ tokenized_substitution: $ => seq(
105
+ '@$(',
106
+ field('body', optional($.subprocess_body)),
107
+ ')',
108
+ ),
109
+
110
+ // Special @ Object Access: @.env, @.lastcmd, etc.
111
+ at_object: $ => seq(
112
+ '@',
113
+ '.',
114
+ field('attribute', $.identifier),
115
+ ),
116
+
117
+ // Help Operators: expr? and expr??
118
+ help_expression: $ => seq(
119
+ field('expression', $.expression),
120
+ '?',
121
+ ),
122
+ super_help_expression: $ => seq(
123
+ field('expression', $.expression),
124
+ '??',
125
+ ),
126
+
127
+ // Glob Patterns
128
+ // Regex glob: `pattern` or r`pattern`
129
+ regex_glob: $ => seq(
130
+ choice('`', 'r`'),
131
+ field('pattern', alias(/[^`]+/, $.regex_glob_content)),
132
+ '`',
133
+ ),
134
+
135
+ // Regex path glob - returns Path objects: rp`pattern`
136
+ regex_path_glob: $ => seq(
137
+ 'rp`',
138
+ field('pattern', alias(/[^`]+/, $.regex_path_content)),
139
+ '`',
140
+ ),
141
+ glob_pattern: $ => seq(
142
+ 'g`',
143
+ field('pattern', alias(/[^`]+/, $.glob_pattern_content)),
144
+ '`',
145
+ ),
146
+
147
+ // Formatted glob with variable substitution: f`pattern`
148
+ formatted_glob: $ => seq(
149
+ 'f`',
150
+ field('pattern', alias(/[^`]+/, $.formatted_glob_content)),
151
+ '`',
152
+ ),
153
+
154
+ // Glob path - returns Path objects instead of strings: gp`pattern`
155
+ glob_path: $ => seq(
156
+ 'gp`',
157
+ field('pattern', alias(/[^`]+/, $.glob_path_content)),
158
+ '`',
159
+ ),
160
+
161
+ // Custom function glob: @func`pattern`
162
+ // Calls a Python function with the pattern string
163
+ // e.g., @foo`bi` calls foo("bi") and expects a list of strings
164
+ // High precedence to win against modified_bare_subprocess
165
+ custom_function_glob: $ => prec(10, seq(
166
+ '@',
167
+ field('function', $.identifier),
168
+ token.immediate('`'),
169
+ field('pattern', alias(/[^`]*/, $.custom_glob_content)),
170
+ '`',
171
+ )),
172
+
173
+ // Path Literals
174
+ // Xonsh path prefixes: p (basic), pf (formatted), pr (raw)
175
+ path_string: $ => seq(
176
+ field('prefix', alias(choice('p', 'pf', 'pr', 'P', 'PF', 'PR'), $.path_prefix)),
177
+ field('string', $.string),
178
+ ),
179
+
180
+ // Subprocess body
181
+ subprocess_body: $ => seq(
182
+ $.subprocess_command,
183
+ repeat(choice(
184
+ $.subprocess_pipeline,
185
+ $.subprocess_logical,
186
+ )),
187
+ ),
188
+
189
+ // Subprocess command and args
190
+ subprocess_command: $ => repeat1($.subprocess_argument),
191
+ subprocess_argument: $ => choice(
192
+ $.subprocess_word,
193
+ $.string,
194
+ $.env_variable,
195
+ $.env_variable_braced,
196
+ $.python_evaluation,
197
+ $.captured_subprocess,
198
+ $.uncaptured_subprocess,
199
+ $.tokenized_substitution,
200
+ $.regex_glob,
201
+ $.glob_pattern,
202
+ $.formatted_glob,
203
+ $.glob_path,
204
+ $.regex_path_glob,
205
+ $.custom_function_glob,
206
+ $.brace_expansion,
207
+ $.brace_literal,
208
+ $.subprocess_redirect,
209
+ ),
210
+
211
+ // Brace expansion in subprocess context
212
+ // Range: {1..5}, {a..z} or List: {a,b,c}
213
+ brace_expansion: $ => choice(
214
+ // Range expansion: {start..end} - match entire pattern with token
215
+ alias(
216
+ token(seq('{', /\w+/, '..', /\w+/, '}')),
217
+ $.brace_range
218
+ ),
219
+ // List expansion: {a,b,c} - dots excluded from items
220
+ seq(
221
+ '{',
222
+ alias(/[^{},.]+/, $.brace_item),
223
+ repeat1(seq(',', alias(/[^{},.]+/, $.brace_item))),
224
+ '}',
225
+ ),
226
+ ),
227
+
228
+ // Brace literal: {content} without expansion syntax
229
+ // Used when braces contain content that doesn't match expansion patterns
230
+ // e.g., {123} in "bash -c! echo {123}" is literal, not expansion
231
+ // token() avoids conflicts with brace_expansion
232
+ brace_literal: _ => token(prec(1, seq('{', /[^{},.\s]+/, '}'))),
233
+
234
+ // Subprocess word - any sequence of non-special characters
235
+ // Use token() with high precedence to prevent ! from being leaked as a separate token by other rules
236
+ // Allows backslash escapes (e.g., \; \$ \space)
237
+ // Allows @ in middle of words (e.g., user@host in URLs) but not at word start
238
+ subprocess_word: _ => token(prec(100, /([^\s$@`'"()\[\]{}|<>&;\\](@[^\s$`'"()\[\]{}|<>&;\\]+)?|\\[^\n])+/)),
239
+
240
+ subprocess_pipeline: $ => seq(
241
+ $.pipe_operator,
242
+ $.subprocess_command,
243
+ ),
244
+
245
+ // Xonsh pipe operators:
246
+ // | - pipe stdout
247
+ // e| - pipe stderr (err|)
248
+ // a| - pipe both stdout and stderr (alias: all|)
249
+ // Use token(prec(101, ...)) for letter-prefixed operators
250
+ pipe_operator: _ => choice(
251
+ '|',
252
+ token(prec(101, 'e|')),
253
+ token(prec(101, 'err|')),
254
+ token(prec(101, 'a|')),
255
+ token(prec(101, 'all|')),
256
+ ),
257
+
258
+ // Subprocess Logical Operators: && and ||
259
+ subprocess_logical: $ => seq(
260
+ field('operator', $.logical_operator),
261
+ $.subprocess_command,
262
+ ),
263
+
264
+ // Xonsh supports both symbolic (&&, ||) and keyword (and, or) operators.
265
+ // The scanner should handle disambiguation for all of these.
266
+ logical_operator: $ => choice(
267
+ $._logical_and,
268
+ $._logical_or,
269
+ $._keyword_and,
270
+ $._keyword_or,
271
+ ),
272
+
273
+ // Redirections
274
+ subprocess_redirect: $ => choice(
275
+ // Redirects with targets
276
+ seq(
277
+ field('operator', $.redirect_operator),
278
+ field('target', $.redirect_target),
279
+ ),
280
+ // Stream merging (no target needed)
281
+ field('operator', $.stream_merge_operator),
282
+ ),
283
+
284
+ // Use token to win against subprocess_word
285
+ redirect_operator: _ => choice(
286
+ // Standard redirects
287
+ '>', '>>', '<',
288
+ // Numbered file descriptors - need precedence over word + >
289
+ token(prec(101, '1>')), token(prec(101, '1>>')),
290
+ token(prec(101, '2>')), token(prec(101, '2>>')),
291
+ // Xonsh-specific aliases - need high precedence
292
+ token(prec(101, 'o>')), token(prec(101, 'o>>')), // stdout (alias for 1>)
293
+ token(prec(101, 'e>')), token(prec(101, 'e>>')), // stderr (alias for 2>)
294
+ token(prec(101, 'err>')), token(prec(101, 'err>>')), // stderr
295
+ token(prec(101, 'out>')), token(prec(101, 'out>>')), // stdout
296
+ token(prec(101, 'all>')), token(prec(101, 'all>>')), // both stdout and stderr
297
+ '&>', // both stdout and stderr (bash compat)
298
+ token(prec(101, 'a>')), // append both
299
+ ),
300
+
301
+ // Stream merging operators (don't take a file target)
302
+ // Need high precedence to win against subprocess_word
303
+ stream_merge_operator: _ => choice(
304
+ token(prec(101, '2>&1')), token(prec(101, '1>&2')),
305
+ token(prec(101, 'err>out')), token(prec(101, 'out>err')),
306
+ token(prec(101, 'err>&1')), token(prec(101, 'out>&2')),
307
+ ),
308
+
309
+ redirect_target: $ => choice(
310
+ $.subprocess_word,
311
+ $.string,
312
+ $.env_variable,
313
+ $.env_variable_braced,
314
+ $.python_evaluation,
315
+ ),
316
+
317
+ // Background Execution
318
+ background_command: $ => prec(1, seq(
319
+ choice(
320
+ $.captured_subprocess,
321
+ $.captured_subprocess_object,
322
+ $.uncaptured_subprocess,
323
+ $.uncaptured_subprocess_object,
324
+ ),
325
+ $._background_amp,
326
+ )),
327
+
328
+ // Xontrib Statement: xontrib load name1 name2 ...
329
+ xontrib_statement: $ => seq(
330
+ 'xontrib',
331
+ 'load',
332
+ repeat1($.xontrib_name),
333
+ ),
334
+
335
+ // Xontrib names can start with numbers (e.g., 1password)
336
+ xontrib_name: _ => /[a-zA-Z0-9_][a-zA-Z0-9_]*/,
337
+
338
+ // Macro Call: func!(args)
339
+ macro_call: $ => prec(10, seq(
340
+ field('name', $.identifier),
341
+ token.immediate('!('),
342
+ field('argument', optional($.macro_argument)),
343
+ ')',
344
+ )),
345
+
346
+ macro_argument: _ => /[^)]*/,
347
+
348
+ // =========================================================================
349
+ // Subprocess Macro: cmd! args (passes rest of line as raw string)
350
+ // e.g., echo! "Hello!", bash -c! echo {123}
351
+ // Different from func!(args) which has ! immediately followed by (
352
+ // =========================================================================
353
+
354
+ // Subprocess macro: identifier! followed by space and args
355
+ // e.g., echo! "Hello!", bash -c! echo {123}
356
+ // The scanner emits _subprocess_macro_start after consuming "identifier! "
357
+ subprocess_macro: $ => prec.dynamic(101, seq(
358
+ $._subprocess_macro_start, // Scanner consumed "identifier! "
359
+ field('argument', alias(/[^\n]+/, $.subprocess_macro_argument)),
360
+ )),
361
+
362
+ // Block Macro: with! Context() as var:
363
+ // Captures block content as string for macro processing
364
+ block_macro_statement: $ => prec(20, seq(
365
+ token('with!'),
366
+ field('context', $.expression),
367
+ optional(seq('as', field('alias', prec(20, $.identifier)))),
368
+ ':',
369
+ field('body', $._suite),
370
+ )),
371
+
372
+ // Bare Subprocess (detected by scanner heuristics)
373
+ // e.g., "ls -la", "cd $HOME", "cat file | grep pattern"
374
+ // Also handles modified subprocess: "@unthread ./tool.sh", "@json curl api/data"
375
+ // The _subprocess_start token is emitted by the scanner when it senses
376
+ // shell-like patterns (flags, pipes, redirects, path commands, @modifier + command)
377
+ bare_subprocess: $ => seq(
378
+ $._subprocess_start,
379
+ field('modifier', optional($.subprocess_modifier)),
380
+ field('body', $.subprocess_body),
381
+ optional($._background_amp), // Optional & for background execution
382
+ ),
383
+
384
+ // Xonsh Expression (all supported xonsh-specific constructs)
385
+ xonsh_expression: $ => choice(
386
+ $.env_variable,
387
+ $.env_variable_braced,
388
+ $.captured_subprocess,
389
+ $.captured_subprocess_object,
390
+ $.uncaptured_subprocess,
391
+ $.uncaptured_subprocess_object,
392
+ $.python_evaluation,
393
+ $.tokenized_substitution,
394
+ $.regex_glob,
395
+ $.glob_pattern,
396
+ $.formatted_glob,
397
+ $.glob_path,
398
+ $.regex_path_glob,
399
+ $.custom_function_glob,
400
+ $.path_string,
401
+ $.background_command,
402
+ $.at_object,
403
+ $.macro_call,
404
+ ),
405
+
406
+ // Xonsh Statements (standalone xonsh constructs)
407
+
408
+ // Standalone env_prefix: $VAR="value" (no space around =)
409
+ // Distinct from env_assignment ($VAR = value) and env_scoped_command ($VAR=val cmd)
410
+ env_prefix_statement: $ => prec(3, $.env_prefix),
411
+
412
+ xonsh_statement: $ => choice(
413
+ $.env_assignment,
414
+ $.env_deletion,
415
+ $.env_scoped_command,
416
+ $.env_prefix_statement,
417
+ $.help_expression,
418
+ $.super_help_expression,
419
+ $.xontrib_statement,
420
+ ),
421
+
422
+ // Add xonsh constructs to primary_expression (for use in Python expressions)
423
+ primary_expression: ($, original) => choice(
424
+ original,
425
+ prec(1, $.xonsh_expression),
426
+ ),
427
+
428
+ // Override boolean_operator to also support && and || (xonsh style)
429
+ // This allows ![cmd1] && ![cmd2] to parse correctly at Python level
430
+ boolean_operator: ($, original) => choice(
431
+ original,
432
+ prec.left(11, seq(
433
+ field('left', $.expression),
434
+ field('operator', $._logical_and),
435
+ field('right', $.expression),
436
+ )),
437
+ prec.left(10, seq(
438
+ field('left', $.expression),
439
+ field('operator', $._logical_or),
440
+ field('right', $.expression),
441
+ )),
442
+ ),
443
+
444
+ // Override _simple_statement to include xonsh expressions and statements
445
+ _simple_statement: ($, original) => choice(
446
+ original,
447
+ prec.dynamic(10, $.xonsh_expression),
448
+ prec.dynamic(11, $.xonsh_statement),
449
+ // Bare subprocess has high priority - detected by scanner heuristics
450
+ prec.dynamic(100, $.bare_subprocess),
451
+ // Subprocess macro has highest priority (cmd! args)
452
+ prec.dynamic(101, $.subprocess_macro),
453
+ ),
454
+
455
+ // Override _compound_statement to include block macro
456
+ _compound_statement: ($, original) => choice(
457
+ original,
458
+ prec.dynamic(15, $.block_macro_statement),
459
+ ),
460
+ },
461
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "tree-sitter-xonsh",
3
+ "version": "0.1.0",
4
+ "description": "Xonsh grammar for tree-sitter",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/FoamScience/tree-sitter-xonsh"
8
+ },
9
+ "license": "MIT",
10
+ "main": "bindings/node",
11
+ "types": "bindings/node",
12
+ "keywords": [
13
+ "incremental",
14
+ "parsing",
15
+ "tree-sitter",
16
+ "xonsh",
17
+ "python",
18
+ "shell"
19
+ ],
20
+ "files": [
21
+ "grammar.js",
22
+ "binding.gyp",
23
+ "prebuilds/**",
24
+ "bindings/node/*",
25
+ "queries/*",
26
+ "src/**",
27
+ "*.wasm"
28
+ ],
29
+ "devDependencies": {
30
+ "tree-sitter-cli": "^0.26.3",
31
+ "tree-sitter-python": "^0.23.0"
32
+ },
33
+ "scripts": {
34
+ "generate": "tree-sitter generate",
35
+ "build": "tree-sitter generate",
36
+ "test": "tree-sitter test",
37
+ "parse": "tree-sitter parse",
38
+ "build-wasm": "tree-sitter build --wasm",
39
+ "playground": "tree-sitter build --wasm && tree-sitter playground"
40
+ }
41
+ }