rapydscript-ng 0.7.24 → 0.8.1

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.
Files changed (132) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +80 -26
  3. package/bin/build.ts +117 -0
  4. package/bin/package.json +1 -0
  5. package/bin/rapydscript +65 -62
  6. package/build_wheels.py +133 -0
  7. package/editor-plugins/README.md +80 -0
  8. package/editor-plugins/nvim.lua +321 -0
  9. package/local-agent.md +28 -0
  10. package/package.json +4 -5
  11. package/publish.py +27 -17
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +9027 -8139
  15. package/release/signatures.json +27 -27
  16. package/release/stdlib_modules.json +1 -0
  17. package/src/ast.pyj +428 -280
  18. package/src/baselib-builtins.pyj +49 -28
  19. package/src/baselib-containers.pyj +241 -226
  20. package/src/baselib-errors.pyj +8 -1
  21. package/src/baselib-internal.pyj +38 -23
  22. package/src/baselib-itertools.pyj +13 -7
  23. package/src/baselib-str.pyj +59 -84
  24. package/src/compiler.pyj +4 -4
  25. package/src/errors.pyj +3 -4
  26. package/src/lib/aes.pyj +46 -32
  27. package/src/lib/elementmaker.pyj +11 -9
  28. package/src/lib/encodings.pyj +15 -5
  29. package/src/lib/gettext.pyj +9 -4
  30. package/src/lib/math.pyj +106 -45
  31. package/src/lib/operator.pyj +10 -10
  32. package/src/lib/pythonize.pyj +2 -1
  33. package/src/lib/random.pyj +28 -21
  34. package/src/lib/re.pyj +492 -19
  35. package/src/lib/traceback.pyj +171 -29
  36. package/src/lib/uuid.pyj +2 -2
  37. package/src/output/classes.pyj +28 -27
  38. package/src/output/codegen.pyj +87 -83
  39. package/src/output/comments.pyj +8 -8
  40. package/src/output/exceptions.pyj +20 -21
  41. package/src/output/functions.pyj +66 -64
  42. package/src/output/literals.pyj +24 -23
  43. package/src/output/loops.pyj +84 -142
  44. package/src/output/modules.pyj +55 -68
  45. package/src/output/operators.pyj +40 -29
  46. package/src/output/statements.pyj +21 -15
  47. package/src/output/stream.pyj +82 -56
  48. package/src/output/utils.pyj +12 -8
  49. package/src/parse.pyj +971 -620
  50. package/src/string_interpolation.pyj +5 -3
  51. package/src/tokenizer.pyj +188 -148
  52. package/src/utils.pyj +32 -15
  53. package/test/_treeshake_mod.pyj +30 -0
  54. package/test/_treeshake_side_effect.pyj +9 -0
  55. package/test/ast_serialization.pyj +392 -0
  56. package/test/async.pyj +95 -0
  57. package/test/baselib.pyj +1 -2
  58. package/test/embedded_compiler.pyj +57 -0
  59. package/test/fmt.pyj +291 -0
  60. package/test/generic.pyj +16 -3
  61. package/test/imports.pyj +8 -6
  62. package/test/internationalization.pyj +38 -37
  63. package/test/lint.pyj +120 -117
  64. package/test/lsp.pyj +257 -0
  65. package/test/repl.pyj +70 -65
  66. package/test/sourcemaps.pyj +315 -0
  67. package/test/starargs.pyj +46 -39
  68. package/test/str.pyj +8 -0
  69. package/test/traceback.pyj +263 -0
  70. package/test/treeshaking.pyj +181 -0
  71. package/test/web_repl_export.pyj +88 -0
  72. package/tools/ast_serialize.mjs +557 -0
  73. package/tools/cli.mjs +531 -0
  74. package/tools/compile.mjs +205 -0
  75. package/tools/compiler.mjs +159 -0
  76. package/tools/{completer.js → completer.mjs} +6 -6
  77. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  78. package/tools/export.js +109 -56
  79. package/tools/fmt.mjs +975 -0
  80. package/tools/{gettext.js → gettext.mjs} +46 -53
  81. package/tools/ini.mjs +175 -0
  82. package/tools/{lint.js → lint.mjs} +78 -55
  83. package/tools/lsp.mjs +964 -0
  84. package/tools/lsp_protocol.mjs +259 -0
  85. package/tools/lsp_symbols.mjs +418 -0
  86. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  87. package/tools/{repl.js → repl.mjs} +56 -42
  88. package/tools/{self.js → self.mjs} +71 -46
  89. package/tools/sourcemap.mjs +123 -0
  90. package/tools/test.mjs +252 -0
  91. package/tools/treeshake.mjs +400 -0
  92. package/tools/{utils.js → utils.mjs} +26 -23
  93. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  94. package/tools/web_repl_export.mjs +97 -0
  95. package/tree-sitter/README.md +101 -0
  96. package/tree-sitter/grammar.js +992 -0
  97. package/tree-sitter/package.json +43 -0
  98. package/tree-sitter/queries/highlights.scm +232 -0
  99. package/tree-sitter/queries/indents.scm +64 -0
  100. package/tree-sitter/queries/injections.scm +14 -0
  101. package/tree-sitter/queries/locals.scm +108 -0
  102. package/tree-sitter/src/grammar.json +4976 -0
  103. package/tree-sitter/src/node-types.json +2901 -0
  104. package/tree-sitter/src/parser.c +196465 -0
  105. package/tree-sitter/src/scanner.c +294 -0
  106. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  107. package/tree-sitter/src/tree_sitter/array.h +330 -0
  108. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  109. package/tree-sitter/test/corpus/chaining.txt +99 -0
  110. package/tree-sitter/test/corpus/classes.txt +147 -0
  111. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  112. package/tree-sitter/test/corpus/containers.txt +242 -0
  113. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  114. package/tree-sitter/test/corpus/decorators.txt +64 -0
  115. package/tree-sitter/test/corpus/existential.txt +102 -0
  116. package/tree-sitter/test/corpus/functions.txt +293 -0
  117. package/tree-sitter/test/corpus/imports.txt +117 -0
  118. package/tree-sitter/test/corpus/literals.txt +135 -0
  119. package/tree-sitter/test/corpus/operators.txt +296 -0
  120. package/tree-sitter/test/corpus/statements.txt +189 -0
  121. package/tree-sitter/test/corpus/strings.txt +90 -0
  122. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  123. package/tree-sitter/tree-sitter.json +36 -0
  124. package/web-repl/env.js +35 -23
  125. package/web-repl/main.js +8 -8
  126. package/bin/export +0 -75
  127. package/bin/web-repl-export +0 -102
  128. package/tools/cli.js +0 -523
  129. package/tools/compile.js +0 -184
  130. package/tools/compiler.js +0 -84
  131. package/tools/ini.js +0 -65
  132. package/tools/test.js +0 -110
@@ -0,0 +1,992 @@
1
+ /**
2
+ * @file RapydScript grammar for tree-sitter
3
+ * @author Generated for rapydscript-ng
4
+ * @license BSD
5
+ *
6
+ * RapydScript is a Python-like language that compiles to JavaScript. It shares
7
+ * most of Python's surface syntax (indentation based blocks, comprehensions,
8
+ * decorators, keyword arguments, slices, ...) but differs in important ways:
9
+ *
10
+ * - anonymous, *multi-line* functions declared with `def(...)` (and
11
+ * `async def(...)`) instead of Python's single-expression `lambda`.
12
+ * - the existential operator `?` (`a?.b`, `a?[1]`, `a?()`, `a ? b`).
13
+ * - verbatim JavaScript string/for literals (the `v` string modifier).
14
+ * - JavaScript style regular expression literals, including the verbose
15
+ * `/// ... ///` form.
16
+ * - `do: ... .while cond` loops and leading-dot chaining.
17
+ * - the JavaScript keyword set is reserved in addition to Python's.
18
+ *
19
+ * It deliberately does NOT support several newer Python constructs, such as the
20
+ * walrus operator `:=`, `match`/`case`, or `*`-unpacking in assignment targets.
21
+ * Those are therefore intentionally absent from this grammar.
22
+ */
23
+
24
+ /* eslint-disable arrow-parens */
25
+ /* eslint-disable camelcase */
26
+ /* eslint-disable-next-line spaced-comment */
27
+ /// <reference types="tree-sitter-cli/dsl" />
28
+ // @ts-check
29
+
30
+ const PREC = {
31
+ // Lowest precedence first.
32
+ assign: -3,
33
+ lambda: -2,
34
+ conditional: -1,
35
+ or: 10,
36
+ and: 11,
37
+ not: 12,
38
+ compare: 13,
39
+ bitwise_or: 14,
40
+ bitwise_xor: 15,
41
+ bitwise_and: 16,
42
+ shift: 17,
43
+ plus: 18,
44
+ times: 19,
45
+ power: 21,
46
+ unary: 22,
47
+ existential: 23,
48
+ call: 24,
49
+ member: 25,
50
+ };
51
+
52
+ module.exports = grammar({
53
+ name: 'rapydscript',
54
+
55
+ externals: $ => [
56
+ $._newline,
57
+ $._indent,
58
+ $._dedent,
59
+ $.regex,
60
+ ],
61
+
62
+ extras: $ => [
63
+ $.comment,
64
+ // Whitespace, including newlines. At statement boundaries the external
65
+ // scanner claims the newline first (producing _newline/_indent/_dedent);
66
+ // everywhere else (e.g. inside brackets) it is skipped as trivia.
67
+ /[ \t\r\n\f  ​]/,
68
+ // explicit line continuation
69
+ /\\\r?\n/,
70
+ ],
71
+
72
+ supertypes: $ => [
73
+ $._simple_statement,
74
+ $._compound_statement,
75
+ $._expression,
76
+ $._primary_expression,
77
+ ],
78
+
79
+ word: $ => $.identifier,
80
+
81
+ conflicts: $ => [
82
+ // Assignment / for targets overlap with ordinary expressions until the
83
+ // `=`/`in` is seen, so the parser must keep both interpretations alive.
84
+ [$._primary_expression, $.pattern],
85
+ [$.list, $.list_pattern],
86
+ [$.tuple, $.tuple_pattern],
87
+ // `a?()` : safe-call (empty argument list) vs `a ? ()` (default empty tuple)
88
+ [$.argument_list, $.tuple],
89
+ [$.argument_list, $.tuple, $.tuple_pattern],
90
+ [$.argument_list, $.generator_expression],
91
+ // `def(): <block>` at statement tail: is it an expression whose value is an
92
+ // anonymous function, or the dedicated block-tailed compound statement?
93
+ [$._anon_suite, $._block_anonymous_function],
94
+ // `def name(): ...` : a function definition (statement) vs a named function
95
+ // expression (RapydScript allows both).
96
+ [$._suite, $._anon_suite],
97
+ [$._simple_statement, $._inline_simple_statement],
98
+ [$._primary_expression, $.concatenated_string],
99
+ [$._expression_statement, $.expression_list],
100
+ [$._argument, $._collection_element],
101
+ [$.delete_statement, $.expression_list],
102
+ [$._assign_rhs, $.expression_list],
103
+ ],
104
+
105
+ rules: {
106
+ module: $ => repeat($._statement),
107
+
108
+ // ---------------------------------------------------------------------
109
+ // Statements
110
+ // ---------------------------------------------------------------------
111
+
112
+ _statement: $ => choice(
113
+ $._simple_statements,
114
+ $._compound_statement,
115
+ ),
116
+
117
+ // one or more simple statements on a logical line, terminated by a newline
118
+ _simple_statements: $ => seq(
119
+ sep1($._simple_statement, ';'),
120
+ optional(';'),
121
+ $._newline,
122
+ ),
123
+
124
+ _simple_statement: $ => choice(
125
+ $.import_statement,
126
+ $.import_from_statement,
127
+ $.scoped_flag_statement,
128
+ $._expression_statement,
129
+ $.assert_statement,
130
+ $.return_statement,
131
+ $.delete_statement,
132
+ $.raise_statement,
133
+ $.pass_statement,
134
+ $.break_statement,
135
+ $.continue_statement,
136
+ $.global_statement,
137
+ $.nonlocal_statement,
138
+ $.debugger_statement,
139
+ $.yield_statement,
140
+ ),
141
+
142
+ _compound_statement: $ => choice(
143
+ $.if_statement,
144
+ $.for_statement,
145
+ $.for_js_statement,
146
+ $.while_statement,
147
+ $.do_statement,
148
+ $.try_statement,
149
+ $.with_statement,
150
+ $.function_definition,
151
+ $.async_function_definition,
152
+ $.class_definition,
153
+ $.decorated_definition,
154
+ $.block_tailed_statement,
155
+ ),
156
+
157
+ // A statement whose trailing value is a multi-line-block anonymous
158
+ // function, e.g.
159
+ //
160
+ // turnGreen = def(event):
161
+ // div.css('background', 'green')
162
+ // div.mousedown(turnGreen) # <- no blank line needed
163
+ //
164
+ // Because the anonymous function ends with an indented block, the block's
165
+ // closing dedent terminates the statement (there is no trailing newline),
166
+ // so this is modelled as a compound statement rather than a simple one.
167
+ block_tailed_statement: $ => seq(
168
+ optional(choice(
169
+ seq(field('left', $._assign_lhs), '='),
170
+ 'return',
171
+ )),
172
+ field('value', alias($._block_anonymous_function, $.anonymous_function)),
173
+ ),
174
+
175
+ _block_anonymous_function: $ => seq(
176
+ optional('async'),
177
+ 'def',
178
+ field('parameters', $.parameters),
179
+ optional(seq('->', field('return_type', $._expression))),
180
+ ':',
181
+ field('body', $.block),
182
+ ),
183
+
184
+ // ---- simple statements ----------------------------------------------
185
+
186
+ _expression_statement: $ => choice(
187
+ $._expression,
188
+ $.expression_list,
189
+ ),
190
+
191
+ // In RapydScript (as in JavaScript) assignment is an *expression*: it can
192
+ // appear inside conditions, parentheses, subscripts, etc.
193
+ // if m = re.exec(x): ...
194
+ // node = a[i -= 1]
195
+ // while (ch = peek()) and pred(ch): ...
196
+ assignment: $ => prec.right(PREC.assign, seq(
197
+ field('left', $._assign_lhs),
198
+ '=',
199
+ field('right', $._assign_rhs),
200
+ )),
201
+
202
+ augmented_assignment: $ => prec.right(PREC.assign, seq(
203
+ field('left', $._assign_lhs),
204
+ field('operator', choice(
205
+ '+=', '-=', '*=', '/=', '//=', '%=', '**=',
206
+ '>>=', '<<=', '>>>=', '&=', '^=', '|=',
207
+ )),
208
+ field('right', $._assign_rhs),
209
+ )),
210
+
211
+ _assign_lhs: $ => choice(
212
+ $.pattern,
213
+ $.pattern_list,
214
+ ),
215
+
216
+ _assign_rhs: $ => choice(
217
+ $._expression,
218
+ $.expression_list,
219
+ $.yield,
220
+ ),
221
+
222
+ expression_list: $ => prec.right(seq(
223
+ $._expression,
224
+ choice(
225
+ ',',
226
+ seq(repeat1(seq(',', $._expression)), optional(',')),
227
+ ),
228
+ )),
229
+
230
+ // Assignment targets (tuple/list destructuring). RapydScript supports
231
+ // nested destructuring, e.g. `a, (b, c) = ...`, but NOT starred targets.
232
+ pattern_list: $ => prec.right(seq(
233
+ $.pattern,
234
+ choice(
235
+ ',',
236
+ seq(repeat1(seq(',', $.pattern)), optional(',')),
237
+ ),
238
+ )),
239
+
240
+ pattern: $ => choice(
241
+ $.identifier,
242
+ $.this,
243
+ $.attribute,
244
+ $.subscript,
245
+ $.tuple_pattern,
246
+ $.list_pattern,
247
+ ),
248
+
249
+ tuple_pattern: $ => seq('(', optional($._pattern_seq), ')'),
250
+ list_pattern: $ => seq('[', optional($._pattern_seq), ']'),
251
+
252
+ _pattern_seq: $ => seq(
253
+ commaSep1($.pattern),
254
+ optional(','),
255
+ ),
256
+
257
+ return_statement: $ => prec.right(seq(
258
+ 'return',
259
+ optional(choice($._expression, $.expression_list)),
260
+ )),
261
+
262
+ delete_statement: $ => seq(
263
+ 'del',
264
+ choice($._expression, $.expression_list),
265
+ ),
266
+
267
+ raise_statement: $ => prec.right(seq(
268
+ 'raise',
269
+ optional($._expression),
270
+ )),
271
+
272
+ pass_statement: _ => 'pass',
273
+ break_statement: _ => 'break',
274
+ continue_statement: _ => 'continue',
275
+ debugger_statement: _ => 'debugger',
276
+
277
+ assert_statement: $ => prec.right(seq(
278
+ 'assert',
279
+ $._expression,
280
+ optional(seq(',', $._expression)),
281
+ )),
282
+
283
+ global_statement: $ => prec.right(seq('global', commaSep1($.identifier))),
284
+ nonlocal_statement: $ => prec.right(seq('nonlocal', commaSep1($.identifier))),
285
+
286
+ yield_statement: $ => $.yield,
287
+
288
+ yield: $ => prec.right(seq(
289
+ 'yield',
290
+ choice(
291
+ seq('from', field('from', $._expression)),
292
+ optional(choice($._expression, $.expression_list)),
293
+ ),
294
+ )),
295
+
296
+ // ---- imports ---------------------------------------------------------
297
+
298
+ import_statement: $ => prec.right(seq(
299
+ 'import',
300
+ commaSep1(field('name', choice(
301
+ $.dotted_name,
302
+ $.aliased_import,
303
+ ))),
304
+ )),
305
+
306
+ aliased_import: $ => seq(
307
+ field('name', $.dotted_name),
308
+ 'as',
309
+ field('alias', $.identifier),
310
+ ),
311
+
312
+ import_from_statement: $ => seq(
313
+ 'from',
314
+ field('module_name', $.dotted_name),
315
+ 'import',
316
+ choice(
317
+ seq('(', $._import_list, ')'),
318
+ $._import_list,
319
+ ),
320
+ ),
321
+
322
+ _import_list: $ => prec.right(seq(
323
+ commaSep1(field('name', choice(
324
+ $.identifier,
325
+ $.aliased_import_name,
326
+ ))),
327
+ optional(','),
328
+ )),
329
+
330
+ aliased_import_name: $ => seq(
331
+ field('name', $.identifier),
332
+ 'as',
333
+ field('alias', $.identifier),
334
+ ),
335
+
336
+ // `from __python__ import flag, no_flag` -- compiler scoped flags
337
+ scoped_flag_statement: $ => seq(
338
+ 'from',
339
+ '__python__',
340
+ 'import',
341
+ choice(
342
+ seq('(', $._flag_list, ')'),
343
+ $._flag_list,
344
+ ),
345
+ ),
346
+
347
+ _flag_list: $ => prec.right(seq(
348
+ commaSep1(field('flag', $.identifier)),
349
+ optional(','),
350
+ )),
351
+
352
+ dotted_name: $ => prec.left(1, sep1($.identifier, '.')),
353
+
354
+ // ---- compound statements --------------------------------------------
355
+
356
+ if_statement: $ => seq(
357
+ 'if',
358
+ field('condition', $._expression),
359
+ ':',
360
+ field('consequence', $._suite),
361
+ repeat(field('alternative', $.elif_clause)),
362
+ optional(field('alternative', $.else_clause)),
363
+ ),
364
+
365
+ elif_clause: $ => seq(
366
+ 'elif',
367
+ field('condition', $._expression),
368
+ ':',
369
+ field('consequence', $._suite),
370
+ ),
371
+
372
+ else_clause: $ => seq(
373
+ 'else',
374
+ ':',
375
+ field('body', $._suite),
376
+ ),
377
+
378
+ for_statement: $ => seq(
379
+ 'for',
380
+ field('left', $._for_target),
381
+ 'in',
382
+ field('right', choice($._expression, $.expression_list)),
383
+ ':',
384
+ field('body', $._suite),
385
+ optional(field('alternative', $.else_clause)),
386
+ ),
387
+
388
+ _for_target: $ => choice(
389
+ $.pattern,
390
+ $.pattern_list,
391
+ ),
392
+
393
+ // native JavaScript for loop: for v'i = 0; i < n; i++':
394
+ for_js_statement: $ => seq(
395
+ 'for',
396
+ field('condition', $.verbatim),
397
+ ':',
398
+ field('body', $._suite),
399
+ ),
400
+
401
+ while_statement: $ => seq(
402
+ 'while',
403
+ field('condition', $._expression),
404
+ ':',
405
+ field('body', $._suite),
406
+ optional(field('alternative', $.else_clause)),
407
+ ),
408
+
409
+ // do: ... .while cond
410
+ do_statement: $ => seq(
411
+ 'do',
412
+ ':',
413
+ field('body', $._suite),
414
+ '.',
415
+ 'while',
416
+ field('condition', $._expression),
417
+ ),
418
+
419
+ try_statement: $ => seq(
420
+ 'try',
421
+ ':',
422
+ field('body', $._suite),
423
+ choice(
424
+ seq(
425
+ repeat1($.except_clause),
426
+ optional($.else_clause),
427
+ optional($.finally_clause),
428
+ ),
429
+ $.finally_clause,
430
+ ),
431
+ ),
432
+
433
+ except_clause: $ => seq(
434
+ 'except',
435
+ optional(commaSep1($._expression)),
436
+ optional(seq('as', field('alias', $.identifier))),
437
+ ':',
438
+ field('body', $._suite),
439
+ ),
440
+
441
+ finally_clause: $ => seq(
442
+ 'finally',
443
+ ':',
444
+ field('body', $._suite),
445
+ ),
446
+
447
+ with_statement: $ => seq(
448
+ 'with',
449
+ commaSep1($.with_clause),
450
+ ':',
451
+ field('body', $._suite),
452
+ ),
453
+
454
+ with_clause: $ => seq(
455
+ field('value', $._expression),
456
+ optional(seq('as', field('alias', $._expression))),
457
+ ),
458
+
459
+ // ---- function & class definitions -----------------------------------
460
+
461
+ function_definition: $ => seq(
462
+ 'def',
463
+ field('name', $.identifier),
464
+ field('parameters', $.parameters),
465
+ optional(seq('->', field('return_type', $._expression))),
466
+ ':',
467
+ field('body', $._suite),
468
+ ),
469
+
470
+ async_function_definition: $ => seq(
471
+ 'async',
472
+ 'def',
473
+ field('name', $.identifier),
474
+ field('parameters', $.parameters),
475
+ optional(seq('->', field('return_type', $._expression))),
476
+ ':',
477
+ field('body', $._suite),
478
+ ),
479
+
480
+ parameters: $ => seq(
481
+ '(',
482
+ optional($._parameters),
483
+ ')',
484
+ ),
485
+
486
+ _parameters: $ => seq(
487
+ commaSep1($._parameter),
488
+ optional(','),
489
+ ),
490
+
491
+ _parameter: $ => choice(
492
+ $.identifier,
493
+ $.typed_parameter,
494
+ $.default_parameter,
495
+ $.typed_default_parameter,
496
+ $.list_splat_parameter,
497
+ $.dictionary_splat_parameter,
498
+ ),
499
+
500
+ typed_parameter: $ => seq(
501
+ field('name', $.identifier),
502
+ ':',
503
+ field('type', $._expression),
504
+ ),
505
+
506
+ default_parameter: $ => seq(
507
+ field('name', $.identifier),
508
+ '=',
509
+ field('value', $._expression),
510
+ ),
511
+
512
+ typed_default_parameter: $ => seq(
513
+ field('name', $.identifier),
514
+ ':',
515
+ field('type', $._expression),
516
+ '=',
517
+ field('value', $._expression),
518
+ ),
519
+
520
+ list_splat_parameter: $ => seq(
521
+ '*',
522
+ field('name', $.identifier),
523
+ optional(seq(':', field('type', $._expression))),
524
+ ),
525
+ dictionary_splat_parameter: $ => seq(
526
+ '**',
527
+ field('name', $.identifier),
528
+ optional(seq(':', field('type', $._expression))),
529
+ ),
530
+
531
+ class_definition: $ => seq(
532
+ 'class',
533
+ field('name', $.identifier),
534
+ optional(field('superclasses', $.argument_list)),
535
+ ':',
536
+ field('body', $._suite),
537
+ ),
538
+
539
+ decorated_definition: $ => seq(
540
+ repeat1($.decorator),
541
+ field('definition', choice(
542
+ $.function_definition,
543
+ $.async_function_definition,
544
+ $.class_definition,
545
+ )),
546
+ ),
547
+
548
+ decorator: $ => seq(
549
+ '@',
550
+ $._expression,
551
+ $._newline,
552
+ ),
553
+
554
+ // ---- suites / blocks -------------------------------------------------
555
+
556
+ _suite: $ => choice(
557
+ alias($._simple_statements, $.block),
558
+ $.block,
559
+ ),
560
+
561
+ block: $ => seq(
562
+ $._indent,
563
+ repeat($._statement),
564
+ $._dedent,
565
+ ),
566
+
567
+ // ---------------------------------------------------------------------
568
+ // Expressions
569
+ // ---------------------------------------------------------------------
570
+
571
+ _expression: $ => choice(
572
+ $._primary_expression,
573
+ $.not_operator,
574
+ $.boolean_operator,
575
+ $.comparison_operator,
576
+ $.binary_operator,
577
+ $.unary_operator,
578
+ $.conditional_expression,
579
+ $.await,
580
+ $.assignment,
581
+ $.augmented_assignment,
582
+ ),
583
+
584
+ _primary_expression: $ => choice(
585
+ $.identifier,
586
+ $.this,
587
+ $.true,
588
+ $.false,
589
+ $.none,
590
+ $.number,
591
+ $.string,
592
+ $.concatenated_string,
593
+ $.regex,
594
+ $.verbatim,
595
+ $.list,
596
+ $.set,
597
+ $.dictionary,
598
+ $.tuple,
599
+ $.parenthesized_expression,
600
+ $.list_comprehension,
601
+ $.set_comprehension,
602
+ $.dictionary_comprehension,
603
+ $.generator_expression,
604
+ $.attribute,
605
+ $.subscript,
606
+ $.slice_call,
607
+ $.call,
608
+ $.new_expression,
609
+ $.existential,
610
+ $.anonymous_function,
611
+ ),
612
+
613
+ // ---- operators -------------------------------------------------------
614
+
615
+ not_operator: $ => prec(PREC.not, seq(
616
+ 'not',
617
+ field('argument', $._expression),
618
+ )),
619
+
620
+ boolean_operator: $ => choice(
621
+ prec.left(PREC.and, seq(
622
+ field('left', $._expression),
623
+ field('operator', 'and'),
624
+ field('right', $._expression),
625
+ )),
626
+ prec.left(PREC.or, seq(
627
+ field('left', $._expression),
628
+ field('operator', 'or'),
629
+ field('right', $._expression),
630
+ )),
631
+ ),
632
+
633
+ binary_operator: $ => {
634
+ const table = [
635
+ [prec.left, '+', PREC.plus],
636
+ [prec.left, '-', PREC.plus],
637
+ [prec.left, '*', PREC.times],
638
+ [prec.left, '/', PREC.times],
639
+ [prec.left, '//', PREC.times],
640
+ [prec.left, '%', PREC.times],
641
+ [prec.left, '@', PREC.times],
642
+ [prec.right, '**', PREC.power],
643
+ [prec.left, '|', PREC.bitwise_or],
644
+ [prec.left, '^', PREC.bitwise_xor],
645
+ [prec.left, '&', PREC.bitwise_and],
646
+ [prec.left, '<<', PREC.shift],
647
+ [prec.left, '>>', PREC.shift],
648
+ [prec.left, '>>>', PREC.shift],
649
+ ];
650
+
651
+ return choice(...table.map(([fn, operator, precedence]) => fn(precedence, seq(
652
+ field('left', $._expression),
653
+ field('operator', operator),
654
+ field('right', $._expression),
655
+ ))));
656
+ },
657
+
658
+ unary_operator: $ => prec(PREC.unary, seq(
659
+ field('operator', choice('+', '-', '~', 'typeof', 'void')),
660
+ field('argument', $._expression),
661
+ )),
662
+
663
+ comparison_operator: $ => prec.left(PREC.compare, seq(
664
+ $._expression,
665
+ repeat1(seq(
666
+ field('operator', choice(
667
+ '<', '<=', '==', '===', '!=', '!==', '>=', '>',
668
+ 'in',
669
+ seq('not', 'in'),
670
+ 'is',
671
+ seq('is', 'not'),
672
+ 'instanceof',
673
+ )),
674
+ $._expression,
675
+ )),
676
+ )),
677
+
678
+ // ternary conditional: consequence if condition else alternative
679
+ conditional_expression: $ => prec.right(PREC.conditional, seq(
680
+ $._expression,
681
+ 'if',
682
+ $._expression,
683
+ 'else',
684
+ $._expression,
685
+ )),
686
+
687
+ // ---- postfix / primary chains ---------------------------------------
688
+
689
+ attribute: $ => prec(PREC.member, seq(
690
+ field('object', $._primary_expression),
691
+ '.',
692
+ field('attribute', $.identifier),
693
+ )),
694
+
695
+ subscript: $ => prec(PREC.member, seq(
696
+ field('object', $._primary_expression),
697
+ '[',
698
+ field('subscript', choice($._expression, $.slice, $.expression_list)),
699
+ ']',
700
+ )),
701
+
702
+ slice: $ => seq(
703
+ optional($._expression),
704
+ ':',
705
+ optional($._expression),
706
+ optional(seq(':', optional($._expression))),
707
+ ),
708
+
709
+ call: $ => prec(PREC.call, seq(
710
+ field('function', $._primary_expression),
711
+ field('arguments', $.argument_list),
712
+ )),
713
+
714
+ // A slice used as a call argument (delslice etc.) is just a subscript;
715
+ // this alias keeps the primary-expression choice list explicit.
716
+ slice_call: $ => prec(PREC.call, seq(
717
+ field('function', $._primary_expression),
718
+ '[',
719
+ $.slice,
720
+ ']',
721
+ )),
722
+
723
+ argument_list: $ => seq(
724
+ '(',
725
+ optional(choice(
726
+ // a bare generator expression as the sole argument: f(x for x in y)
727
+ seq(field('body', $._expression), $._comprehension_clauses),
728
+ seq(commaSep1($._argument), optional(',')),
729
+ )),
730
+ ')',
731
+ ),
732
+
733
+ _argument: $ => choice(
734
+ $._expression,
735
+ $.keyword_argument,
736
+ $.list_splat_argument,
737
+ $.dictionary_splat_argument,
738
+ ),
739
+
740
+ // In a call, `name=value` is always a keyword argument (never an
741
+ // assignment expression); the higher precedence enforces that.
742
+ keyword_argument: $ => prec(1, seq(
743
+ field('name', $.identifier),
744
+ '=',
745
+ field('value', $._expression),
746
+ )),
747
+
748
+ list_splat_argument: $ => seq('*', $._expression),
749
+ dictionary_splat_argument: $ => seq('**', $._expression),
750
+
751
+ new_expression: $ => prec.right(PREC.call, seq(
752
+ 'new',
753
+ field('constructor', $._primary_expression),
754
+ )),
755
+
756
+ // The existential operator `?`. It has four forms:
757
+ // a? -- existence check (null-like test)
758
+ // a?.b -- safe attribute access
759
+ // a?[i] -- safe subscript
760
+ // a?() -- safe call
761
+ // a ? b -- fallback / default operator (use b if a is null-like)
762
+ existential: $ => prec.right(PREC.existential, seq(
763
+ field('object', $._primary_expression),
764
+ '?',
765
+ optional(choice(
766
+ seq('.', field('attribute', $.identifier)),
767
+ seq('[', field('subscript', choice($._expression, $.slice)), ']'),
768
+ field('arguments', $.argument_list),
769
+ prec(-1, field('default', $._expression)),
770
+ )),
771
+ )),
772
+
773
+ await: $ => prec(PREC.unary, seq('await', $._expression)),
774
+
775
+ // ---- anonymous (lambda) functions -----------------------------------
776
+ // The signature RapydScript feature: multi-line anonymous functions.
777
+
778
+ // Anonymous function. RapydScript also allows an optional name on a
779
+ // function *expression* (e.g. `x = def named(a): ...`).
780
+ anonymous_function: $ => prec(PREC.lambda, seq(
781
+ optional('async'),
782
+ 'def',
783
+ optional(field('name', $.identifier)),
784
+ field('parameters', $.parameters),
785
+ optional(seq('->', field('return_type', $._expression))),
786
+ ':',
787
+ field('body', $._anon_suite),
788
+ )),
789
+
790
+ // The body of an anonymous function. Unlike a statement-level suite, the
791
+ // inline form is NOT terminated by a newline: an inline `def(): a; b`
792
+ // appearing inside a container ends at the enclosing `,`/`}`/`)` (or the
793
+ // outer statement's newline). Semicolons chain statements into the body.
794
+ _anon_suite: $ => choice(
795
+ $.block,
796
+ alias($._inline_body, $.block),
797
+ ),
798
+
799
+ _inline_body: $ => prec.right(seq(
800
+ sep1($._inline_simple_statement, ';'),
801
+ optional(';'),
802
+ )),
803
+
804
+ // Statements permitted in an inline anonymous-function body. This is a
805
+ // curated subset: constructs like `import`, `with`, `global` never appear
806
+ // inline in practice, and allowing them creates unresolvable ambiguities
807
+ // with enclosing-container commas. They remain available in the multi-line
808
+ // (block) form.
809
+ _inline_simple_statement: $ => choice(
810
+ $._expression_statement,
811
+ $.return_statement,
812
+ $.delete_statement,
813
+ $.raise_statement,
814
+ $.pass_statement,
815
+ $.break_statement,
816
+ $.continue_statement,
817
+ $.assert_statement,
818
+ $.yield_statement,
819
+ ),
820
+
821
+ // ---- containers ------------------------------------------------------
822
+
823
+ list: $ => seq(
824
+ '[',
825
+ optional(seq(commaSep1($._collection_element), optional(','))),
826
+ ']',
827
+ ),
828
+
829
+ _collection_element: $ => choice(
830
+ $._expression,
831
+ $.list_splat_argument,
832
+ ),
833
+
834
+ set: $ => seq(
835
+ '{',
836
+ commaSep1($._collection_element),
837
+ optional(','),
838
+ '}',
839
+ ),
840
+
841
+ // A tuple is `()`, `(a,)`, or `(a, b, ...)`. A single element with no
842
+ // trailing comma, `(a)`, is a parenthesized_expression (grouping) instead.
843
+ tuple: $ => seq(
844
+ '(',
845
+ optional(choice(
846
+ seq($._collection_element, ','),
847
+ seq(
848
+ $._collection_element,
849
+ repeat1(seq(',', $._collection_element)),
850
+ optional(','),
851
+ ),
852
+ )),
853
+ ')',
854
+ ),
855
+
856
+ parenthesized_expression: $ => prec(1, seq(
857
+ '(',
858
+ choice($._expression, $.yield),
859
+ ')',
860
+ )),
861
+
862
+ dictionary: $ => seq(
863
+ '{',
864
+ optional(seq(
865
+ commaSep1(choice($.pair, $.dictionary_splat_argument)),
866
+ optional(','),
867
+ )),
868
+ '}',
869
+ ),
870
+
871
+ pair: $ => seq(
872
+ field('key', $._expression),
873
+ ':',
874
+ field('value', $._expression),
875
+ ),
876
+
877
+ // ---- comprehensions --------------------------------------------------
878
+
879
+ list_comprehension: $ => seq(
880
+ '[',
881
+ field('body', $._expression),
882
+ $._comprehension_clauses,
883
+ ']',
884
+ ),
885
+
886
+ set_comprehension: $ => seq(
887
+ '{',
888
+ field('body', $._expression),
889
+ $._comprehension_clauses,
890
+ '}',
891
+ ),
892
+
893
+ dictionary_comprehension: $ => seq(
894
+ '{',
895
+ field('body', $.pair),
896
+ $._comprehension_clauses,
897
+ '}',
898
+ ),
899
+
900
+ generator_expression: $ => seq(
901
+ '(',
902
+ field('body', $._expression),
903
+ $._comprehension_clauses,
904
+ ')',
905
+ ),
906
+
907
+ _comprehension_clauses: $ => seq(
908
+ $.for_in_clause,
909
+ repeat(choice($.for_in_clause, $.if_clause)),
910
+ ),
911
+
912
+ for_in_clause: $ => seq(
913
+ 'for',
914
+ field('left', $._for_target),
915
+ 'in',
916
+ field('right', choice($._expression, $.expression_list)),
917
+ ),
918
+
919
+ if_clause: $ => seq(
920
+ 'if',
921
+ $._expression,
922
+ ),
923
+
924
+ // ---- atoms / literals ------------------------------------------------
925
+
926
+ concatenated_string: $ => prec.left(seq(
927
+ $.string,
928
+ repeat1($.string),
929
+ )),
930
+
931
+ this: _ => 'this',
932
+ true: _ => 'True',
933
+ false: _ => 'False',
934
+ none: _ => 'None',
935
+
936
+ // string with optional r/u/f/b modifiers (the `v` modifier denotes a
937
+ // verbatim JavaScript literal and is handled separately). Interpolation
938
+ // and escapes are not sub-tokenised; the whole literal is a single node.
939
+ string: _ => token(seq(
940
+ optional(/[rRuUfFbB]+/),
941
+ choice(
942
+ seq('"""', repeat(choice(/[^"\\]/, /\\(.|\n)/, /"[^"]/, /""[^"]/)), '"""'),
943
+ seq("'''", repeat(choice(/[^'\\]/, /\\(.|\n)/, /'[^']/, /''[^']/)), "'''"),
944
+ seq('"', repeat(choice(/[^"\\\n]/, /\\(.|\n)/)), '"'),
945
+ seq("'", repeat(choice(/[^'\\\n]/, /\\(.|\n)/)), "'"),
946
+ ),
947
+ )),
948
+
949
+ // verbatim JavaScript literal: v'...' v"..." (optionally combined with
950
+ // other string modifiers, e.g. rv'...'). At least one `v` is required.
951
+ verbatim: _ => token(seq(
952
+ /[rRuUfFbBvV]*[vV][rRuUfFbBvV]*/,
953
+ choice(
954
+ seq('"""', repeat(choice(/[^"\\]/, /\\(.|\n)/, /"[^"]/, /""[^"]/)), '"""'),
955
+ seq("'''", repeat(choice(/[^'\\]/, /\\(.|\n)/, /'[^']/, /''[^']/)), "'''"),
956
+ seq('"', repeat(choice(/[^"\\\n]/, /\\(.|\n)/)), '"'),
957
+ seq("'", repeat(choice(/[^'\\\n]/, /\\(.|\n)/)), "'"),
958
+ ),
959
+ )),
960
+
961
+ number: _ => {
962
+ const hex = /0[xX][0-9a-fA-F]+/;
963
+ const binary = /0[bB][01]+/;
964
+ const octal = /0[oO][0-7]+/;
965
+ const decimal = /(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/;
966
+ return token(choice(hex, binary, octal, decimal));
967
+ },
968
+
969
+ identifier: _ => /[A-Za-z_$ª-￿][A-Za-z0-9_$ª-￿]*/,
970
+
971
+ comment: _ => token(seq('#', /.*/)),
972
+ },
973
+ });
974
+
975
+ /**
976
+ * Creates a rule to match one or more of the rule separated by a comma
977
+ * @param {RuleOrLiteral} rule
978
+ * @return {SeqRule}
979
+ */
980
+ function commaSep1(rule) {
981
+ return sep1(rule, ',');
982
+ }
983
+
984
+ /**
985
+ * Creates a rule to match one or more occurrences of `rule` separated by `sep`
986
+ * @param {RuleOrLiteral} rule
987
+ * @param {RuleOrLiteral} separator
988
+ * @return {SeqRule}
989
+ */
990
+ function sep1(rule, separator) {
991
+ return seq(rule, repeat(seq(separator, rule)));
992
+ }