tree-sitter-ucode 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/grammar.js ADDED
@@ -0,0 +1,903 @@
1
+ /**
2
+ * @file Ucode grammar for tree-sitter
3
+ * @license MIT
4
+ *
5
+ * Based on tree-sitter-javascript (MIT, Max Brunsfeld, Amaan Qureshi).
6
+ * Ucode is an ECMAScript-like language for OpenWrt system scripting.
7
+ * See linter/diff-to-js.md for a full list of syntax differences from JS.
8
+ */
9
+
10
+ /// <reference types="tree-sitter-cli/dsl" />
11
+ // @ts-check
12
+
13
+ module.exports = grammar({
14
+ name: 'ucode',
15
+
16
+ externals: $ => [
17
+ $._automatic_semicolon,
18
+ $._template_chars,
19
+ $._ternary_qmark,
20
+ ],
21
+
22
+ extras: $ => [
23
+ $.comment,
24
+ /[\s\p{Zs}\uFEFF\u2028\u2029\u2060\u200B]/,
25
+ ],
26
+
27
+ reserved: {
28
+ global: $ => [
29
+ 'break',
30
+ 'case',
31
+ 'catch',
32
+ 'const',
33
+ 'continue',
34
+ 'default',
35
+ 'delete',
36
+ 'else',
37
+ 'elif',
38
+ 'endif',
39
+ 'endfor',
40
+ 'endwhile',
41
+ 'endfunction',
42
+ 'export',
43
+ 'false',
44
+ 'for',
45
+ 'function',
46
+ 'if',
47
+ 'import',
48
+ 'in',
49
+ 'null',
50
+ 'return',
51
+ 'switch',
52
+ 'this',
53
+ 'true',
54
+ 'try',
55
+ 'while',
56
+ ],
57
+ properties: $ => [],
58
+ },
59
+
60
+ supertypes: $ => [
61
+ $.statement,
62
+ $.declaration,
63
+ $.expression,
64
+ $.primary_expression,
65
+ ],
66
+
67
+ inline: $ => [
68
+ $._call_signature,
69
+ $._formal_parameter,
70
+ $._expressions,
71
+ $._semicolon,
72
+ $._identifier,
73
+ $._reserved_identifier,
74
+ $._lhs_expression,
75
+ ],
76
+
77
+ precedences: $ => [
78
+ [
79
+ 'member',
80
+ 'call',
81
+ $.update_expression,
82
+ 'unary_void',
83
+ 'binary_exp',
84
+ 'binary_times',
85
+ 'binary_plus',
86
+ 'binary_shift',
87
+ 'binary_compare',
88
+ 'binary_relation',
89
+ 'binary_equality',
90
+ 'bitwise_and',
91
+ 'bitwise_xor',
92
+ 'bitwise_or',
93
+ 'logical_and',
94
+ 'logical_or',
95
+ 'ternary',
96
+ $.sequence_expression,
97
+ $.arrow_function,
98
+ ],
99
+ ['assign', $.primary_expression],
100
+ ['member', 'call', $.expression],
101
+ ['declaration', 'literal'],
102
+ [$.primary_expression, $.statement_block, 'object'],
103
+ [$.import_statement, $.import],
104
+ [$.export_statement, $.primary_expression],
105
+ [$.lexical_declaration, $.primary_expression],
106
+ ],
107
+
108
+ conflicts: $ => [
109
+ [$.primary_expression, $._property_name],
110
+ [$.primary_expression, $.arrow_function],
111
+ [$.primary_expression, $.arrow_function, $._property_name],
112
+ [$.primary_expression, $.formal_parameters],
113
+ [$.primary_expression, $._for_header],
114
+ [$.variable_declarator, $._for_header],
115
+ [$.assignment_expression, $.pattern],
116
+ [$.labeled_statement, $._property_name],
117
+ [$.computed_property_name, $.array],
118
+ [$.binary_expression, $._initializer],
119
+ ],
120
+
121
+ word: $ => $.identifier,
122
+
123
+ rules: {
124
+ program: $ => seq(
125
+ optional($.hash_bang_line),
126
+ repeat($.statement),
127
+ ),
128
+
129
+ hash_bang_line: _ => /#!.*/,
130
+
131
+ //
132
+ // Export declarations
133
+ // Ucode export is simpler than JS: no `from`, no namespace re-exports.
134
+ // `export default` always takes an expression (requires trailing semicolon).
135
+ // `export function name() {}` does NOT require a semicolon.
136
+ //
137
+
138
+ export_statement: $ => choice(
139
+ seq(
140
+ 'export',
141
+ $.export_clause,
142
+ $._semicolon,
143
+ ),
144
+ seq(
145
+ 'export',
146
+ choice(
147
+ field('declaration', $.declaration),
148
+ seq(
149
+ 'default',
150
+ seq(
151
+ field('value', $.expression),
152
+ $._semicolon,
153
+ ),
154
+ ),
155
+ ),
156
+ ),
157
+ ),
158
+
159
+ export_clause: $ => seq(
160
+ '{',
161
+ commaSep($.export_specifier),
162
+ optional(','),
163
+ '}',
164
+ ),
165
+
166
+ export_specifier: $ => seq(
167
+ field('name', $._module_export_name),
168
+ optional(seq(
169
+ 'as',
170
+ field('alias', $._module_export_name),
171
+ )),
172
+ ),
173
+
174
+ _module_export_name: $ => choice(
175
+ $.identifier,
176
+ $.string,
177
+ 'default',
178
+ ),
179
+
180
+ declaration: $ => choice(
181
+ $.function_declaration,
182
+ $.function_forward_declaration,
183
+ $.lexical_declaration,
184
+ ),
185
+
186
+ //
187
+ // Import declarations
188
+ // Ucode keeps full ES module static import syntax.
189
+ // Also adds dynamic `import(path)` as a unary expression (see import_expression).
190
+ //
191
+
192
+ import: _ => token('import'),
193
+
194
+ import_statement: $ => seq(
195
+ 'import',
196
+ choice(
197
+ seq($.import_clause, 'from', field('source', $.string)),
198
+ field('source', $.string),
199
+ ),
200
+ $._semicolon,
201
+ ),
202
+
203
+ import_clause: $ => choice(
204
+ $.namespace_import,
205
+ $.named_imports,
206
+ seq(
207
+ $.identifier,
208
+ optional(seq(
209
+ ',',
210
+ choice(
211
+ $.namespace_import,
212
+ $.named_imports,
213
+ ),
214
+ )),
215
+ ),
216
+ ),
217
+
218
+ namespace_import: $ => seq('*', 'as', $.identifier),
219
+
220
+ named_imports: $ => seq(
221
+ '{',
222
+ commaSep($.import_specifier),
223
+ optional(','),
224
+ '}',
225
+ ),
226
+
227
+ import_specifier: $ => choice(
228
+ field('name', $.identifier),
229
+ seq(
230
+ field('name', $._module_export_name),
231
+ 'as',
232
+ field('alias', $.identifier),
233
+ ),
234
+ ),
235
+
236
+ //
237
+ // Statements
238
+ //
239
+
240
+ statement: $ => choice(
241
+ $.export_statement,
242
+ $.import_statement,
243
+ $.expression_statement,
244
+ $.declaration,
245
+ $.statement_block,
246
+
247
+ $.if_statement,
248
+ $.if_alt_statement,
249
+ $.switch_statement,
250
+ $.for_statement,
251
+ $.for_alt_statement,
252
+ $.for_in_statement,
253
+ $.for_in_alt_statement,
254
+ $.while_statement,
255
+ $.while_alt_statement,
256
+ $.try_statement,
257
+
258
+ $.break_statement,
259
+ $.continue_statement,
260
+ $.return_statement,
261
+ $.empty_statement,
262
+ $.labeled_statement,
263
+ ),
264
+
265
+ expression_statement: $ => seq(
266
+ $._expressions,
267
+ $._semicolon,
268
+ ),
269
+
270
+ lexical_declaration: $ => seq(
271
+ field('kind', choice('let', 'const')),
272
+ commaSep1($.variable_declarator),
273
+ $._semicolon,
274
+ ),
275
+
276
+ variable_declarator: $ => seq(
277
+ field('name', $.identifier),
278
+ optional($._initializer),
279
+ ),
280
+
281
+ statement_block: $ => prec.right(seq(
282
+ '{',
283
+ repeat($.statement),
284
+ '}',
285
+ optional($._automatic_semicolon),
286
+ )),
287
+
288
+ else_clause: $ => seq('else', $.statement),
289
+
290
+ // Standard brace-based if
291
+ if_statement: $ => prec.right(seq(
292
+ 'if',
293
+ field('condition', $.parenthesized_expression),
294
+ field('consequence', $.statement),
295
+ optional(field('alternative', $.else_clause)),
296
+ )),
297
+
298
+ // Alternative colon/endif syntax
299
+ if_alt_statement: $ => seq(
300
+ 'if',
301
+ field('condition', $.parenthesized_expression),
302
+ ':',
303
+ field('body', repeat($.statement)),
304
+ repeat(field('elif_clause', $.elif_clause)),
305
+ optional(field('else_body', $.else_alt_clause)),
306
+ 'endif',
307
+ ),
308
+
309
+ elif_clause: $ => seq(
310
+ 'elif',
311
+ field('condition', $.parenthesized_expression),
312
+ ':',
313
+ field('body', repeat($.statement)),
314
+ ),
315
+
316
+ else_alt_clause: $ => seq(
317
+ 'else',
318
+ field('body', repeat($.statement)),
319
+ ),
320
+
321
+ switch_statement: $ => seq(
322
+ 'switch',
323
+ field('value', $.parenthesized_expression),
324
+ field('body', $.switch_body),
325
+ ),
326
+
327
+ for_statement: $ => seq(
328
+ 'for',
329
+ '(',
330
+ choice(
331
+ field('initializer', $.lexical_declaration),
332
+ seq(field('initializer', $._expressions), ';'),
333
+ field('initializer', $.empty_statement),
334
+ ),
335
+ field('condition', choice(
336
+ seq($._expressions, ';'),
337
+ $.empty_statement,
338
+ )),
339
+ field('increment', optional($._expressions)),
340
+ ')',
341
+ field('body', $.statement),
342
+ ),
343
+
344
+ for_alt_statement: $ => seq(
345
+ 'for',
346
+ '(',
347
+ choice(
348
+ field('initializer', $.lexical_declaration),
349
+ seq(field('initializer', $._expressions), ';'),
350
+ field('initializer', $.empty_statement),
351
+ ),
352
+ field('condition', choice(
353
+ seq($._expressions, ';'),
354
+ $.empty_statement,
355
+ )),
356
+ field('increment', optional($._expressions)),
357
+ ')',
358
+ ':',
359
+ field('body', repeat($.statement)),
360
+ 'endfor',
361
+ ),
362
+
363
+ for_in_statement: $ => seq(
364
+ 'for',
365
+ $._for_header,
366
+ field('body', $.statement),
367
+ ),
368
+
369
+ for_in_alt_statement: $ => seq(
370
+ 'for',
371
+ $._for_header,
372
+ ':',
373
+ field('body', repeat($.statement)),
374
+ 'endfor',
375
+ ),
376
+
377
+ // Supports both `for (k in obj)` and `for (k, v in obj)` (ucode two-variable form)
378
+ _for_header: $ => seq(
379
+ '(',
380
+ choice(
381
+ seq(
382
+ field('kind', choice('let', 'const')),
383
+ field('left', $.identifier),
384
+ optional(seq(',', field('value', $.identifier))),
385
+ ),
386
+ seq(
387
+ field('left', $._lhs_expression),
388
+ optional(seq(',', field('value', $.identifier))),
389
+ ),
390
+ ),
391
+ 'in',
392
+ field('right', $._expressions),
393
+ ')',
394
+ ),
395
+
396
+ while_statement: $ => seq(
397
+ 'while',
398
+ field('condition', $.parenthesized_expression),
399
+ field('body', $.statement),
400
+ ),
401
+
402
+ while_alt_statement: $ => seq(
403
+ 'while',
404
+ field('condition', $.parenthesized_expression),
405
+ ':',
406
+ field('body', repeat($.statement)),
407
+ 'endwhile',
408
+ ),
409
+
410
+ try_statement: $ => seq(
411
+ 'try',
412
+ field('body', $.statement_block),
413
+ optional(field('handler', $.catch_clause)),
414
+ ),
415
+
416
+ break_statement: $ => seq(
417
+ 'break',
418
+ field('label', optional(alias($.identifier, $.statement_identifier))),
419
+ $._semicolon,
420
+ ),
421
+
422
+ continue_statement: $ => seq(
423
+ 'continue',
424
+ field('label', optional(alias($.identifier, $.statement_identifier))),
425
+ $._semicolon,
426
+ ),
427
+
428
+ return_statement: $ => seq(
429
+ 'return',
430
+ optional($._expressions),
431
+ $._semicolon,
432
+ ),
433
+
434
+ empty_statement: _ => ';',
435
+
436
+ labeled_statement: $ => prec.dynamic(-1, seq(
437
+ field('label', alias(choice($.identifier, $._reserved_identifier), $.statement_identifier)),
438
+ ':',
439
+ field('body', $.statement),
440
+ )),
441
+
442
+ //
443
+ // Statement components
444
+ //
445
+
446
+ switch_body: $ => seq(
447
+ '{',
448
+ repeat(choice($.switch_case, $.switch_default)),
449
+ '}',
450
+ ),
451
+
452
+ switch_case: $ => seq(
453
+ 'case',
454
+ field('value', $._expressions),
455
+ ':',
456
+ field('body', repeat($.statement)),
457
+ ),
458
+
459
+ switch_default: $ => seq(
460
+ 'default',
461
+ ':',
462
+ field('body', repeat($.statement)),
463
+ ),
464
+
465
+ catch_clause: $ => seq(
466
+ 'catch',
467
+ optional(seq('(', field('parameter', $.identifier), ')')),
468
+ field('body', $.statement_block),
469
+ ),
470
+
471
+ parenthesized_expression: $ => seq(
472
+ '(',
473
+ $._expressions,
474
+ ')',
475
+ ),
476
+
477
+ //
478
+ // Expressions
479
+ //
480
+
481
+ _expressions: $ => choice(
482
+ $.expression,
483
+ $.sequence_expression,
484
+ ),
485
+
486
+ expression: $ => choice(
487
+ $.primary_expression,
488
+ $.assignment_expression,
489
+ $.augmented_assignment_expression,
490
+ $.unary_expression,
491
+ $.binary_expression,
492
+ $.ternary_expression,
493
+ $.update_expression,
494
+ $.import_expression,
495
+ ),
496
+
497
+ primary_expression: $ => choice(
498
+ $.subscript_expression,
499
+ $.member_expression,
500
+ $.parenthesized_expression,
501
+ $._identifier,
502
+ alias($._reserved_identifier, $.identifier),
503
+ $.this,
504
+ $.number,
505
+ $.string,
506
+ $.template_string,
507
+ $.regex,
508
+ $.true,
509
+ $.false,
510
+ $.null,
511
+ $.object,
512
+ $.array,
513
+ $.function_expression,
514
+ $.arrow_function,
515
+ $.call_expression,
516
+ ),
517
+
518
+ // Dynamic import expression: `let m = import("./mod.uc")`
519
+ import_expression: $ => prec('call', seq(
520
+ $.import,
521
+ '(',
522
+ field('source', $.expression),
523
+ ')',
524
+ )),
525
+
526
+ object: $ => prec('object', seq(
527
+ '{',
528
+ commaSep(optional(choice(
529
+ $.pair,
530
+ $.spread_element,
531
+ alias(
532
+ choice($.identifier, $._reserved_identifier),
533
+ $.shorthand_property_identifier,
534
+ ),
535
+ ))),
536
+ '}',
537
+ )),
538
+
539
+ array: $ => seq(
540
+ '[',
541
+ commaSep(optional(choice(
542
+ $.expression,
543
+ $.spread_element,
544
+ ))),
545
+ ']',
546
+ ),
547
+
548
+ optional_chain: _ => '?.',
549
+
550
+ call_expression: $ => choice(
551
+ prec('call', seq(
552
+ field('function', $.expression),
553
+ field('arguments', $.arguments),
554
+ )),
555
+ prec('member', seq(
556
+ field('function', $.primary_expression),
557
+ field('optional_chain', $.optional_chain),
558
+ field('arguments', $.arguments),
559
+ )),
560
+ ),
561
+
562
+ member_expression: $ => prec('member', seq(
563
+ field('object', choice($.expression, $.primary_expression)),
564
+ choice('.', field('optional_chain', $.optional_chain)),
565
+ field('property', reserved('properties', alias($.identifier, $.property_identifier))),
566
+ )),
567
+
568
+ subscript_expression: $ => prec.right('member', seq(
569
+ field('object', choice($.expression, $.primary_expression)),
570
+ optional(field('optional_chain', $.optional_chain)),
571
+ '[', field('index', $._expressions), ']',
572
+ )),
573
+
574
+ _lhs_expression: $ => choice(
575
+ $.member_expression,
576
+ $.subscript_expression,
577
+ $._identifier,
578
+ alias($._reserved_identifier, $.identifier),
579
+ ),
580
+
581
+ assignment_expression: $ => prec.right('assign', seq(
582
+ field('left', choice($.parenthesized_expression, $._lhs_expression)),
583
+ '=',
584
+ field('right', $.expression),
585
+ )),
586
+
587
+ _augmented_assignment_lhs: $ => choice(
588
+ $.member_expression,
589
+ $.subscript_expression,
590
+ alias($._reserved_identifier, $.identifier),
591
+ $.identifier,
592
+ $.parenthesized_expression,
593
+ ),
594
+
595
+ augmented_assignment_expression: $ => prec.right('assign', seq(
596
+ field('left', $._augmented_assignment_lhs),
597
+ field('operator', choice(
598
+ '+=', '-=', '*=', '/=', '%=', '**=',
599
+ '^=', '&=', '|=', '>>=', '<<=',
600
+ '&&=', '||=', '??=',
601
+ )),
602
+ field('right', $.expression),
603
+ )),
604
+
605
+ _initializer: $ => seq(
606
+ '=',
607
+ field('value', $.expression),
608
+ ),
609
+
610
+ spread_element: $ => seq('...', $.expression),
611
+
612
+ ternary_expression: $ => prec.right('ternary', seq(
613
+ field('condition', $.expression),
614
+ alias($._ternary_qmark, '?'),
615
+ field('consequence', $.expression),
616
+ ':',
617
+ field('alternative', $.expression),
618
+ )),
619
+
620
+ binary_expression: $ => choice(
621
+ ...[
622
+ ['&&', 'logical_and'],
623
+ ['||', 'logical_or'],
624
+ ['>>', 'binary_shift'],
625
+ ['<<', 'binary_shift'],
626
+ ['&', 'bitwise_and'],
627
+ ['^', 'bitwise_xor'],
628
+ ['|', 'bitwise_or'],
629
+ ['+', 'binary_plus'],
630
+ ['-', 'binary_plus'],
631
+ ['*', 'binary_times'],
632
+ ['/', 'binary_times'],
633
+ ['%', 'binary_times'],
634
+ ['**', 'binary_exp', 'right'],
635
+ ['<', 'binary_relation'],
636
+ ['<=', 'binary_relation'],
637
+ ['==', 'binary_equality'],
638
+ ['===', 'binary_equality'],
639
+ ['!=', 'binary_equality'],
640
+ ['!==', 'binary_equality'],
641
+ ['>=', 'binary_relation'],
642
+ ['>', 'binary_relation'],
643
+ ['??', 'logical_or'], // same level as ||, freely mixable
644
+ ['in', 'binary_relation'],
645
+ ].map(([operator, precedence, associativity]) =>
646
+ (associativity === 'right' ? prec.right : prec.left)(precedence, seq(
647
+ field('left', $.expression),
648
+ field('operator', operator),
649
+ field('right', $.expression),
650
+ )),
651
+ ),
652
+ ),
653
+
654
+ unary_expression: $ => prec.left('unary_void', seq(
655
+ field('operator', choice('!', '~', '-', '+', 'delete')),
656
+ field('argument', $.expression),
657
+ )),
658
+
659
+ update_expression: $ => prec.left(choice(
660
+ seq(
661
+ field('argument', $.expression),
662
+ field('operator', choice('++', '--')),
663
+ ),
664
+ seq(
665
+ field('operator', choice('++', '--')),
666
+ field('argument', $.expression),
667
+ ),
668
+ )),
669
+
670
+ sequence_expression: $ => prec.right(commaSep1($.expression)),
671
+
672
+ //
673
+ // Functions
674
+ //
675
+
676
+ function_expression: $ => prec('literal', seq(
677
+ 'function',
678
+ field('name', optional($.identifier)),
679
+ $._call_signature,
680
+ field('body', $.statement_block),
681
+ )),
682
+
683
+ // Named function declaration with brace body
684
+ function_declaration: $ => prec.right('declaration', seq(
685
+ 'function',
686
+ field('name', $.identifier),
687
+ $._call_signature,
688
+ field('body', choice(
689
+ $.statement_block,
690
+ seq(':', repeat($.statement), 'endfunction'),
691
+ )),
692
+ optional($._automatic_semicolon),
693
+ )),
694
+
695
+ // Forward declaration: `function name;`
696
+ function_forward_declaration: $ => seq(
697
+ 'function',
698
+ field('name', $.identifier),
699
+ ';',
700
+ ),
701
+
702
+ arrow_function: $ => seq(
703
+ choice(
704
+ field('parameter', choice(
705
+ alias($._reserved_identifier, $.identifier),
706
+ $.identifier,
707
+ )),
708
+ $._call_signature,
709
+ ),
710
+ '=>',
711
+ field('body', choice(
712
+ $.expression,
713
+ $.statement_block,
714
+ )),
715
+ ),
716
+
717
+ _call_signature: $ => field('parameters', $.formal_parameters),
718
+ _formal_parameter: $ => choice($.identifier, $.rest_element),
719
+
720
+ formal_parameters: $ => seq(
721
+ '(',
722
+ optional(seq(
723
+ commaSep1($._formal_parameter),
724
+ optional(','),
725
+ )),
726
+ ')',
727
+ ),
728
+
729
+ rest_element: $ => seq('...', $.identifier),
730
+
731
+ pattern: $ => prec.dynamic(-1, $._lhs_expression),
732
+
733
+ //
734
+ // Primitives
735
+ //
736
+
737
+ string: $ => choice(
738
+ seq(
739
+ '"',
740
+ repeat(choice(
741
+ alias($.unescaped_double_string_fragment, $.string_fragment),
742
+ $.escape_sequence,
743
+ )),
744
+ '"',
745
+ ),
746
+ seq(
747
+ '\'',
748
+ repeat(choice(
749
+ alias($.unescaped_single_string_fragment, $.string_fragment),
750
+ $.escape_sequence,
751
+ )),
752
+ '\'',
753
+ ),
754
+ ),
755
+
756
+ unescaped_double_string_fragment: _ => token.immediate(prec(1, /[^"\\\r\n]+/)),
757
+ unescaped_single_string_fragment: _ => token.immediate(prec(1, /[^'\\\r\n]+/)),
758
+
759
+ // Ucode extends JS escapes with \e (ESC), \a (BEL), and octal sequences
760
+ escape_sequence: _ => token.immediate(seq(
761
+ '\\',
762
+ choice(
763
+ /[^xu0-7]/,
764
+ /[0-7]{1,3}/,
765
+ /x[0-9a-fA-F]{2}/,
766
+ /u[0-9a-fA-F]{4}/,
767
+ /u\{[0-9a-fA-F]+\}/,
768
+ /[\r?][\n\u2028\u2029]/,
769
+ ),
770
+ )),
771
+
772
+ comment: _ => token(choice(
773
+ seq("//", /[^\r\n\u2028\u2029]*/),
774
+ seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/'),
775
+ )),
776
+
777
+ template_string: $ => seq(
778
+ '`',
779
+ repeat(choice(
780
+ alias($._template_chars, $.string_fragment),
781
+ $.escape_sequence,
782
+ $.template_substitution,
783
+ )),
784
+ '`',
785
+ ),
786
+
787
+ template_substitution: $ => seq(
788
+ '${',
789
+ $._expressions,
790
+ '}',
791
+ ),
792
+
793
+ regex: $ => seq(
794
+ '/',
795
+ field('pattern', $.regex_pattern),
796
+ token.immediate(prec(1, '/')),
797
+ optional(field('flags', $.regex_flags)),
798
+ ),
799
+
800
+ regex_pattern: _ => token.immediate(prec(-1,
801
+ repeat1(choice(
802
+ seq('[', repeat(choice(seq('\\', /./), /[^\]\n\\]/)), ']'),
803
+ seq('\\', /./),
804
+ /[^/\\\[\n]/,
805
+ )),
806
+ )),
807
+
808
+ // Ucode supports only g, i, s flags (not m, u, y, d)
809
+ regex_flags: _ => token.immediate(/[gis]+/),
810
+
811
+ // Ucode number literals extend JS with:
812
+ // - C-style legacy octal: 0177
813
+ // - Hex float: 0x1.8
814
+ // - Uppercase prefixes: 0O, 0B (already in JS grammar)
815
+ number: _ => {
816
+ const hexDigits = /[\da-fA-F](_?[\da-fA-F])*/;
817
+ const hexLiteral = seq(choice('0x', '0X'), hexDigits);
818
+ const hexFloat = seq(choice('0x', '0X'), hexDigits, '.', optional(hexDigits));
819
+
820
+ const decimalDigits = /\d(_?\d)*/;
821
+ const signedInteger = seq(optional(choice('-', '+')), decimalDigits);
822
+ const exponentPart = seq(choice('e', 'E'), signedInteger);
823
+
824
+ const binaryLiteral = seq(choice('0b', '0B'), /[0-1](_?[0-1])*/);
825
+ const octalLiteral = seq(choice('0o', '0O'), /[0-7](_?[0-7])*/);
826
+ const legacyOctalLiteral = seq('0', /[0-7]+/);
827
+
828
+ const decimalIntegerLiteral = choice(
829
+ '0',
830
+ seq(optional('0'), /[1-9]/, optional(seq(optional('_'), decimalDigits))),
831
+ );
832
+
833
+ const decimalLiteral = choice(
834
+ seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)),
835
+ seq('.', decimalDigits, optional(exponentPart)),
836
+ seq(decimalIntegerLiteral, exponentPart),
837
+ decimalDigits,
838
+ );
839
+
840
+ return token(choice(
841
+ hexFloat,
842
+ hexLiteral,
843
+ decimalLiteral,
844
+ binaryLiteral,
845
+ octalLiteral,
846
+ legacyOctalLiteral,
847
+ ));
848
+ },
849
+
850
+ _identifier: $ => $.identifier,
851
+
852
+ identifier: _ => {
853
+ const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/;
854
+ const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/;
855
+ return token(seq(alpha, repeat(alphanumeric)));
856
+ },
857
+
858
+ this: _ => 'this',
859
+ true: _ => 'true',
860
+ false: _ => 'false',
861
+ null: _ => 'null',
862
+
863
+ arguments: $ => seq(
864
+ '(',
865
+ commaSep(optional(choice($.expression, $.spread_element))),
866
+ ')',
867
+ ),
868
+
869
+ _property_name: $ => reserved('properties', choice(
870
+ alias(
871
+ choice($.identifier, $._reserved_identifier),
872
+ $.property_identifier,
873
+ ),
874
+ $.string,
875
+ $.number,
876
+ $.computed_property_name,
877
+ )),
878
+
879
+ computed_property_name: $ => seq('[', $.expression, ']'),
880
+
881
+ pair: $ => seq(
882
+ field('key', $._property_name),
883
+ ':',
884
+ field('value', $.expression),
885
+ ),
886
+
887
+ _reserved_identifier: _ => choice(
888
+ 'get',
889
+ 'set',
890
+ 'let',
891
+ ),
892
+
893
+ _semicolon: $ => choice($._automatic_semicolon, ';'),
894
+ },
895
+ });
896
+
897
+ function commaSep1(rule) {
898
+ return seq(rule, repeat(seq(',', rule)));
899
+ }
900
+
901
+ function commaSep(rule) {
902
+ return optional(commaSep1(rule));
903
+ }