tree-sitter-ucode 0.6.1 → 0.7.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 +8 -0
- package/grammar.js +94 -56
- package/markup/grammar.js +94 -56
- package/markup/src/grammar.json +326 -398
- package/markup/src/node-types.json +32 -78
- package/markup/src/parser.c +85951 -85390
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/tree-sitter-ucode.node +0 -0
- package/prebuilds/linux-arm64/tree-sitter-ucode.node +0 -0
- package/prebuilds/linux-x64/tree-sitter-ucode.node +0 -0
- package/prebuilds/win32-x64/tree-sitter-ucode.node +0 -0
- package/queries/highlights.scm +0 -1
- package/src/grammar.json +326 -398
- package/src/node-types.json +32 -78
- package/src/parser.c +71125 -70596
- package/src/scanner_impl.h +8 -0
- package/tree-sitter-ucode.wasm +0 -0
- package/tree-sitter-ucode_markup.wasm +0 -0
- package/tree-sitter.json +1 -1
- package/ucdocs/grammar.js +6 -5
- package/ucdocs/src/grammar.json +23 -5
- package/ucdocs/src/node-types.json +8 -0
- package/ucdocs/src/parser.c +1739 -1711
package/README.md
CHANGED
|
@@ -33,9 +33,17 @@ Ucode is an ECMAScript subset with OpenWrt-specific extensions. Key differences:
|
|
|
33
33
|
| Removed features | Destructuring, `for...of`, `do-while`, generators, forward declarations, dynamic `import()` | All supported |
|
|
34
34
|
| Added number literals | `0177` (C octal), `0x1.8` (hex float), `0B`/`0O` prefixes | Standard only |
|
|
35
35
|
| Added escape sequences | `\e` (ESC), `\a` (BEL), octal `\177` | Standard only |
|
|
36
|
+
| String unicode escapes | `\uXXXX` only (no `\u{…}`); no `\u` escapes in identifiers | `\uXXXX` and `\u{…}` |
|
|
36
37
|
| Regex flags | `g`, `i`, `s` only | Full set |
|
|
37
38
|
| Module system | Static `import`/`export` only; no `from` on re-exports | Full ES modules |
|
|
38
39
|
|
|
40
|
+
The grammar tracks ucode's parser closely, with one deliberate exception: **automatic
|
|
41
|
+
semicolon insertion is kept ECMAScript-style (more lenient than the compiler).** ucode
|
|
42
|
+
only lets you drop a statement's `;` before `}`, end-of-file, a template tag close, or an
|
|
43
|
+
alt-syntax end keyword (`endif`/`endfor`/`endwhile`/`endfunction`/`elif`/`else`), whereas
|
|
44
|
+
the grammar also tolerates a bare newline between statements so that in-progress edits are
|
|
45
|
+
not flagged as errors.
|
|
46
|
+
|
|
39
47
|
## Doc comment grammar (ucdocs)
|
|
40
48
|
|
|
41
49
|
`/** */` blocks are parsed by the `ucdocs` grammar and injected into the host parse tree.
|
package/grammar.js
CHANGED
|
@@ -77,7 +77,6 @@ module.exports = grammar({
|
|
|
77
77
|
|
|
78
78
|
inline: $ => [
|
|
79
79
|
$._call_signature,
|
|
80
|
-
$._formal_parameter,
|
|
81
80
|
$._expressions,
|
|
82
81
|
$._semicolon,
|
|
83
82
|
$._identifier,
|
|
@@ -125,7 +124,7 @@ module.exports = grammar({
|
|
|
125
124
|
[$.primary_expression, $._for_header],
|
|
126
125
|
[$.variable_declarator, $._for_header],
|
|
127
126
|
[$.assignment_expression, $.pattern],
|
|
128
|
-
[$.
|
|
127
|
+
[$.primary_expression, $.delete_expression],
|
|
129
128
|
],
|
|
130
129
|
|
|
131
130
|
word: $ => $.identifier,
|
|
@@ -208,18 +207,22 @@ module.exports = grammar({
|
|
|
208
207
|
field('clause', $.export_clause),
|
|
209
208
|
$._semicolon,
|
|
210
209
|
),
|
|
210
|
+
// `export let/const …;` already carries its own terminating semicolon.
|
|
211
211
|
seq(
|
|
212
212
|
'export',
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
213
|
+
field('declaration', $.lexical_declaration),
|
|
214
|
+
),
|
|
215
|
+
// `export function f() {}` requires an explicit trailing `;` in ucode.
|
|
216
|
+
seq(
|
|
217
|
+
'export',
|
|
218
|
+
field('declaration', $.function_declaration),
|
|
219
|
+
';',
|
|
220
|
+
),
|
|
221
|
+
seq(
|
|
222
|
+
'export',
|
|
223
|
+
'default',
|
|
224
|
+
field('value', $.expression),
|
|
225
|
+
';',
|
|
223
226
|
),
|
|
224
227
|
),
|
|
225
228
|
|
|
@@ -322,7 +325,6 @@ module.exports = grammar({
|
|
|
322
325
|
$.continue_statement,
|
|
323
326
|
$.return_statement,
|
|
324
327
|
$.empty_statement,
|
|
325
|
-
$.labeled_statement,
|
|
326
328
|
),
|
|
327
329
|
|
|
328
330
|
expression_statement: $ => seq(
|
|
@@ -330,10 +332,19 @@ module.exports = grammar({
|
|
|
330
332
|
$._semicolon,
|
|
331
333
|
),
|
|
332
334
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
// `let` declarators may omit the initializer; `const` requires one.
|
|
336
|
+
// ucode rejects `const a;` ("Expecting initializer expression").
|
|
337
|
+
lexical_declaration: $ => choice(
|
|
338
|
+
seq(
|
|
339
|
+
field('kind', 'let'),
|
|
340
|
+
commaSep1($.variable_declarator),
|
|
341
|
+
$._semicolon,
|
|
342
|
+
),
|
|
343
|
+
seq(
|
|
344
|
+
field('kind', 'const'),
|
|
345
|
+
commaSep1(alias($._const_declarator, $.variable_declarator)),
|
|
346
|
+
$._semicolon,
|
|
347
|
+
),
|
|
337
348
|
),
|
|
338
349
|
|
|
339
350
|
variable_declarator: $ => seq(
|
|
@@ -341,6 +352,11 @@ module.exports = grammar({
|
|
|
341
352
|
optional($._initializer),
|
|
342
353
|
),
|
|
343
354
|
|
|
355
|
+
_const_declarator: $ => seq(
|
|
356
|
+
field('name', $.identifier),
|
|
357
|
+
$._initializer,
|
|
358
|
+
),
|
|
359
|
+
|
|
344
360
|
statement_block: $ => prec.right(seq(
|
|
345
361
|
'{',
|
|
346
362
|
repeat($.statement),
|
|
@@ -512,17 +528,19 @@ module.exports = grammar({
|
|
|
512
528
|
),
|
|
513
529
|
),
|
|
514
530
|
|
|
515
|
-
// Supports both `for (k in obj)` and `for (k, v in obj)` (ucode two-variable form)
|
|
531
|
+
// Supports both `for (k in obj)` and `for (k, v in obj)` (ucode two-variable form).
|
|
532
|
+
// The loop target must be a plain identifier: ucode rejects member/subscript
|
|
533
|
+
// targets (`for (a.x in o)`) and only `let` (never `const`) may declare it.
|
|
516
534
|
_for_header: $ => seq(
|
|
517
535
|
'(',
|
|
518
536
|
choice(
|
|
519
537
|
seq(
|
|
520
|
-
field('kind',
|
|
538
|
+
field('kind', 'let'),
|
|
521
539
|
field('left', $.identifier),
|
|
522
540
|
optional(seq(',', field('value', $.identifier))),
|
|
523
541
|
),
|
|
524
542
|
seq(
|
|
525
|
-
field('left', $.
|
|
543
|
+
field('left', $.identifier),
|
|
526
544
|
optional(seq(',', field('value', $.identifier))),
|
|
527
545
|
),
|
|
528
546
|
),
|
|
@@ -565,15 +583,14 @@ module.exports = grammar({
|
|
|
565
583
|
optional(field('handler', $.catch_clause)),
|
|
566
584
|
),
|
|
567
585
|
|
|
586
|
+
// ucode has no labeled statements, so `break`/`continue` take no label.
|
|
568
587
|
break_statement: $ => seq(
|
|
569
588
|
'break',
|
|
570
|
-
field('label', optional(alias($.identifier, $.statement_identifier))),
|
|
571
589
|
$._semicolon,
|
|
572
590
|
),
|
|
573
591
|
|
|
574
592
|
continue_statement: $ => seq(
|
|
575
593
|
'continue',
|
|
576
|
-
field('label', optional(alias($.identifier, $.statement_identifier))),
|
|
577
594
|
$._semicolon,
|
|
578
595
|
),
|
|
579
596
|
|
|
@@ -585,12 +602,6 @@ module.exports = grammar({
|
|
|
585
602
|
|
|
586
603
|
empty_statement: _ => ';',
|
|
587
604
|
|
|
588
|
-
labeled_statement: $ => prec.dynamic(-1, seq(
|
|
589
|
-
field('label', alias(choice($.identifier, $._reserved_identifier), $.statement_identifier)),
|
|
590
|
-
':',
|
|
591
|
-
field('body', $.statement),
|
|
592
|
-
)),
|
|
593
|
-
|
|
594
605
|
//
|
|
595
606
|
// Statement components
|
|
596
607
|
//
|
|
@@ -640,6 +651,7 @@ module.exports = grammar({
|
|
|
640
651
|
$.assignment_expression,
|
|
641
652
|
$.augmented_assignment_expression,
|
|
642
653
|
$.unary_expression,
|
|
654
|
+
$.delete_expression,
|
|
643
655
|
$.binary_expression,
|
|
644
656
|
$.ternary_expression,
|
|
645
657
|
$.update_expression,
|
|
@@ -666,25 +678,33 @@ module.exports = grammar({
|
|
|
666
678
|
$.call_expression,
|
|
667
679
|
),
|
|
668
680
|
|
|
681
|
+
// ucode allows a trailing comma but NOT interior elision (`{a:1,,b:2}`).
|
|
669
682
|
object: $ => prec('object', seq(
|
|
670
683
|
'{',
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
684
|
+
optional(seq(
|
|
685
|
+
commaSep1(choice(
|
|
686
|
+
$.pair,
|
|
687
|
+
$.spread_element,
|
|
688
|
+
alias(
|
|
689
|
+
choice($.identifier, $._reserved_identifier),
|
|
690
|
+
$.shorthand_property_identifier,
|
|
691
|
+
),
|
|
692
|
+
)),
|
|
693
|
+
optional(','),
|
|
694
|
+
)),
|
|
679
695
|
'}',
|
|
680
696
|
)),
|
|
681
697
|
|
|
698
|
+
// ucode allows a trailing comma but NOT interior elision (`[1,,2]`).
|
|
682
699
|
array: $ => seq(
|
|
683
700
|
'[',
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
701
|
+
optional(seq(
|
|
702
|
+
commaSep1(choice(
|
|
703
|
+
$.expression,
|
|
704
|
+
$.spread_element,
|
|
705
|
+
)),
|
|
706
|
+
optional(','),
|
|
707
|
+
)),
|
|
688
708
|
']',
|
|
689
709
|
),
|
|
690
710
|
|
|
@@ -795,10 +815,17 @@ module.exports = grammar({
|
|
|
795
815
|
),
|
|
796
816
|
|
|
797
817
|
unary_expression: $ => prec.left('unary_void', seq(
|
|
798
|
-
field('operator', choice('!', '~', '-', '+'
|
|
818
|
+
field('operator', choice('!', '~', '-', '+')),
|
|
799
819
|
field('argument', $.expression),
|
|
800
820
|
)),
|
|
801
821
|
|
|
822
|
+
// ucode's `delete` only accepts a property access (member or subscript)
|
|
823
|
+
// expression: `delete o.k` / `delete o["k"]`. `delete x` is a syntax error.
|
|
824
|
+
delete_expression: $ => prec.left('unary_void', seq(
|
|
825
|
+
field('operator', 'delete'),
|
|
826
|
+
field('argument', choice($.member_expression, $.subscript_expression)),
|
|
827
|
+
)),
|
|
828
|
+
|
|
802
829
|
update_expression: $ => prec.left(choice(
|
|
803
830
|
seq(
|
|
804
831
|
field('argument', $.expression),
|
|
@@ -851,13 +878,16 @@ module.exports = grammar({
|
|
|
851
878
|
),
|
|
852
879
|
|
|
853
880
|
_call_signature: $ => field('parameters', $.formal_parameters),
|
|
854
|
-
_formal_parameter: $ => choice($.identifier, $.rest_element),
|
|
855
881
|
|
|
882
|
+
// A rest element (`...name`) must be the final parameter, and there can be
|
|
883
|
+
// only one. ucode rejects a rest param followed by anything — including a
|
|
884
|
+
// trailing comma. A trailing comma is allowed only after plain parameters.
|
|
856
885
|
formal_parameters: $ => seq(
|
|
857
886
|
'(',
|
|
858
|
-
optional(
|
|
859
|
-
|
|
860
|
-
|
|
887
|
+
optional(choice(
|
|
888
|
+
$.rest_element,
|
|
889
|
+
seq(commaSep1($.identifier), ',', $.rest_element),
|
|
890
|
+
seq(commaSep1($.identifier), optional(',')),
|
|
861
891
|
)),
|
|
862
892
|
')',
|
|
863
893
|
),
|
|
@@ -892,7 +922,8 @@ module.exports = grammar({
|
|
|
892
922
|
unescaped_double_string_fragment: _ => token.immediate(prec(1, /[^"\\\r\n]+/)),
|
|
893
923
|
unescaped_single_string_fragment: _ => token.immediate(prec(1, /[^'\\\r\n]+/)),
|
|
894
924
|
|
|
895
|
-
// Ucode extends JS escapes with \e (ESC), \a (BEL), and octal sequences
|
|
925
|
+
// Ucode extends JS escapes with \e (ESC), \a (BEL), and octal sequences.
|
|
926
|
+
// Unlike JS, ucode supports only the 4-hex `\uXXXX` form \u2014 not `\u{...}`.
|
|
896
927
|
escape_sequence: _ => token.immediate(seq(
|
|
897
928
|
'\\',
|
|
898
929
|
choice(
|
|
@@ -900,7 +931,6 @@ module.exports = grammar({
|
|
|
900
931
|
/[0-7]{1,3}/,
|
|
901
932
|
/x[0-9a-fA-F]{2}/,
|
|
902
933
|
/u[0-9a-fA-F]{4}/,
|
|
903
|
-
/u\{[0-9a-fA-F]+\}/,
|
|
904
934
|
/\r[\n\u2028\u2029]/,
|
|
905
935
|
),
|
|
906
936
|
)),
|
|
@@ -948,27 +978,30 @@ module.exports = grammar({
|
|
|
948
978
|
// - C-style legacy octal: 0177
|
|
949
979
|
// - Hex float: 0x1.8
|
|
950
980
|
// - Uppercase prefixes: 0O, 0B (already in JS grammar)
|
|
981
|
+
//
|
|
982
|
+
// Unlike JS, ucode does NOT support:
|
|
983
|
+
// - numeric underscore separators (1_000)
|
|
984
|
+
// - leading-dot floats (.5) — a digit is required before the dot
|
|
951
985
|
number: _ => {
|
|
952
|
-
const hexDigits = /[\da-fA-F]
|
|
986
|
+
const hexDigits = /[\da-fA-F]+/;
|
|
953
987
|
const hexLiteral = seq(choice('0x', '0X'), hexDigits);
|
|
954
988
|
const hexFloat = seq(choice('0x', '0X'), hexDigits, '.', optional(hexDigits));
|
|
955
989
|
|
|
956
|
-
const decimalDigits = /\d
|
|
990
|
+
const decimalDigits = /\d+/;
|
|
957
991
|
const signedInteger = seq(optional(choice('-', '+')), decimalDigits);
|
|
958
992
|
const exponentPart = seq(choice('e', 'E'), signedInteger);
|
|
959
993
|
|
|
960
|
-
const binaryLiteral = seq(choice('0b', '0B'), /[0-1]
|
|
961
|
-
const octalLiteral = seq(choice('0o', '0O'), /[0-7]
|
|
994
|
+
const binaryLiteral = seq(choice('0b', '0B'), /[0-1]+/);
|
|
995
|
+
const octalLiteral = seq(choice('0o', '0O'), /[0-7]+/);
|
|
962
996
|
const legacyOctalLiteral = seq('0', /[0-7]+/);
|
|
963
997
|
|
|
964
998
|
const decimalIntegerLiteral = choice(
|
|
965
999
|
'0',
|
|
966
|
-
seq(optional('0'), /[1-9]/, optional(
|
|
1000
|
+
seq(optional('0'), /[1-9]/, optional(decimalDigits)),
|
|
967
1001
|
);
|
|
968
1002
|
|
|
969
1003
|
const decimalLiteral = choice(
|
|
970
1004
|
seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)),
|
|
971
|
-
seq('.', decimalDigits, optional(exponentPart)),
|
|
972
1005
|
seq(decimalIntegerLiteral, exponentPart),
|
|
973
1006
|
decimalDigits,
|
|
974
1007
|
);
|
|
@@ -985,9 +1018,12 @@ module.exports = grammar({
|
|
|
985
1018
|
|
|
986
1019
|
_identifier: $ => $.identifier,
|
|
987
1020
|
|
|
1021
|
+
// ucode does NOT support unicode escape sequences in identifiers (unlike JS);
|
|
1022
|
+
// a literal `\u...` is an "Unexpected character". Non-ASCII letters are still
|
|
1023
|
+
// allowed directly via the negated character class.
|
|
988
1024
|
identifier: _ => {
|
|
989
|
-
const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]
|
|
990
|
-
const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]
|
|
1025
|
+
const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]/;
|
|
1026
|
+
const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]/;
|
|
991
1027
|
return token(seq(alpha, repeat(alphanumeric)));
|
|
992
1028
|
},
|
|
993
1029
|
|
|
@@ -996,9 +1032,11 @@ module.exports = grammar({
|
|
|
996
1032
|
false: _ => 'false',
|
|
997
1033
|
null: _ => 'null',
|
|
998
1034
|
|
|
1035
|
+
// Unlike arrays/objects, ucode call arguments allow neither interior
|
|
1036
|
+
// elision (`f(1,,2)`) nor a trailing comma (`f(1,2,)`).
|
|
999
1037
|
arguments: $ => seq(
|
|
1000
1038
|
'(',
|
|
1001
|
-
commaSep(
|
|
1039
|
+
commaSep(choice($.expression, $.spread_element)),
|
|
1002
1040
|
')',
|
|
1003
1041
|
),
|
|
1004
1042
|
|
package/markup/grammar.js
CHANGED
|
@@ -78,7 +78,6 @@ module.exports = grammar({
|
|
|
78
78
|
|
|
79
79
|
inline: $ => [
|
|
80
80
|
$._call_signature,
|
|
81
|
-
$._formal_parameter,
|
|
82
81
|
$._expressions,
|
|
83
82
|
$._semicolon,
|
|
84
83
|
$._identifier,
|
|
@@ -126,7 +125,7 @@ module.exports = grammar({
|
|
|
126
125
|
[$.primary_expression, $._for_header],
|
|
127
126
|
[$.variable_declarator, $._for_header],
|
|
128
127
|
[$.assignment_expression, $.pattern],
|
|
129
|
-
[$.
|
|
128
|
+
[$.primary_expression, $.delete_expression],
|
|
130
129
|
],
|
|
131
130
|
|
|
132
131
|
word: $ => $.identifier,
|
|
@@ -209,18 +208,22 @@ module.exports = grammar({
|
|
|
209
208
|
field('clause', $.export_clause),
|
|
210
209
|
$._semicolon,
|
|
211
210
|
),
|
|
211
|
+
// `export let/const …;` already carries its own terminating semicolon.
|
|
212
212
|
seq(
|
|
213
213
|
'export',
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
214
|
+
field('declaration', $.lexical_declaration),
|
|
215
|
+
),
|
|
216
|
+
// `export function f() {}` requires an explicit trailing `;` in ucode.
|
|
217
|
+
seq(
|
|
218
|
+
'export',
|
|
219
|
+
field('declaration', $.function_declaration),
|
|
220
|
+
';',
|
|
221
|
+
),
|
|
222
|
+
seq(
|
|
223
|
+
'export',
|
|
224
|
+
'default',
|
|
225
|
+
field('value', $.expression),
|
|
226
|
+
';',
|
|
224
227
|
),
|
|
225
228
|
),
|
|
226
229
|
|
|
@@ -323,7 +326,6 @@ module.exports = grammar({
|
|
|
323
326
|
$.continue_statement,
|
|
324
327
|
$.return_statement,
|
|
325
328
|
$.empty_statement,
|
|
326
|
-
$.labeled_statement,
|
|
327
329
|
),
|
|
328
330
|
|
|
329
331
|
expression_statement: $ => seq(
|
|
@@ -331,10 +333,19 @@ module.exports = grammar({
|
|
|
331
333
|
$._semicolon,
|
|
332
334
|
),
|
|
333
335
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
// `let` declarators may omit the initializer; `const` requires one.
|
|
337
|
+
// ucode rejects `const a;` ("Expecting initializer expression").
|
|
338
|
+
lexical_declaration: $ => choice(
|
|
339
|
+
seq(
|
|
340
|
+
field('kind', 'let'),
|
|
341
|
+
commaSep1($.variable_declarator),
|
|
342
|
+
$._semicolon,
|
|
343
|
+
),
|
|
344
|
+
seq(
|
|
345
|
+
field('kind', 'const'),
|
|
346
|
+
commaSep1(alias($._const_declarator, $.variable_declarator)),
|
|
347
|
+
$._semicolon,
|
|
348
|
+
),
|
|
338
349
|
),
|
|
339
350
|
|
|
340
351
|
variable_declarator: $ => seq(
|
|
@@ -342,6 +353,11 @@ module.exports = grammar({
|
|
|
342
353
|
optional($._initializer),
|
|
343
354
|
),
|
|
344
355
|
|
|
356
|
+
_const_declarator: $ => seq(
|
|
357
|
+
field('name', $.identifier),
|
|
358
|
+
$._initializer,
|
|
359
|
+
),
|
|
360
|
+
|
|
345
361
|
statement_block: $ => prec.right(seq(
|
|
346
362
|
'{',
|
|
347
363
|
repeat($.statement),
|
|
@@ -513,17 +529,19 @@ module.exports = grammar({
|
|
|
513
529
|
),
|
|
514
530
|
),
|
|
515
531
|
|
|
516
|
-
// Supports both `for (k in obj)` and `for (k, v in obj)` (ucode two-variable form)
|
|
532
|
+
// Supports both `for (k in obj)` and `for (k, v in obj)` (ucode two-variable form).
|
|
533
|
+
// The loop target must be a plain identifier: ucode rejects member/subscript
|
|
534
|
+
// targets (`for (a.x in o)`) and only `let` (never `const`) may declare it.
|
|
517
535
|
_for_header: $ => seq(
|
|
518
536
|
'(',
|
|
519
537
|
choice(
|
|
520
538
|
seq(
|
|
521
|
-
field('kind',
|
|
539
|
+
field('kind', 'let'),
|
|
522
540
|
field('left', $.identifier),
|
|
523
541
|
optional(seq(',', field('value', $.identifier))),
|
|
524
542
|
),
|
|
525
543
|
seq(
|
|
526
|
-
field('left', $.
|
|
544
|
+
field('left', $.identifier),
|
|
527
545
|
optional(seq(',', field('value', $.identifier))),
|
|
528
546
|
),
|
|
529
547
|
),
|
|
@@ -566,15 +584,14 @@ module.exports = grammar({
|
|
|
566
584
|
optional(field('handler', $.catch_clause)),
|
|
567
585
|
),
|
|
568
586
|
|
|
587
|
+
// ucode has no labeled statements, so `break`/`continue` take no label.
|
|
569
588
|
break_statement: $ => seq(
|
|
570
589
|
'break',
|
|
571
|
-
field('label', optional(alias($.identifier, $.statement_identifier))),
|
|
572
590
|
$._semicolon,
|
|
573
591
|
),
|
|
574
592
|
|
|
575
593
|
continue_statement: $ => seq(
|
|
576
594
|
'continue',
|
|
577
|
-
field('label', optional(alias($.identifier, $.statement_identifier))),
|
|
578
595
|
$._semicolon,
|
|
579
596
|
),
|
|
580
597
|
|
|
@@ -586,12 +603,6 @@ module.exports = grammar({
|
|
|
586
603
|
|
|
587
604
|
empty_statement: _ => ';',
|
|
588
605
|
|
|
589
|
-
labeled_statement: $ => prec.dynamic(-1, seq(
|
|
590
|
-
field('label', alias(choice($.identifier, $._reserved_identifier), $.statement_identifier)),
|
|
591
|
-
':',
|
|
592
|
-
field('body', $.statement),
|
|
593
|
-
)),
|
|
594
|
-
|
|
595
606
|
//
|
|
596
607
|
// Statement components
|
|
597
608
|
//
|
|
@@ -641,6 +652,7 @@ module.exports = grammar({
|
|
|
641
652
|
$.assignment_expression,
|
|
642
653
|
$.augmented_assignment_expression,
|
|
643
654
|
$.unary_expression,
|
|
655
|
+
$.delete_expression,
|
|
644
656
|
$.binary_expression,
|
|
645
657
|
$.ternary_expression,
|
|
646
658
|
$.update_expression,
|
|
@@ -667,25 +679,33 @@ module.exports = grammar({
|
|
|
667
679
|
$.call_expression,
|
|
668
680
|
),
|
|
669
681
|
|
|
682
|
+
// ucode allows a trailing comma but NOT interior elision (`{a:1,,b:2}`).
|
|
670
683
|
object: $ => prec('object', seq(
|
|
671
684
|
'{',
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
685
|
+
optional(seq(
|
|
686
|
+
commaSep1(choice(
|
|
687
|
+
$.pair,
|
|
688
|
+
$.spread_element,
|
|
689
|
+
alias(
|
|
690
|
+
choice($.identifier, $._reserved_identifier),
|
|
691
|
+
$.shorthand_property_identifier,
|
|
692
|
+
),
|
|
693
|
+
)),
|
|
694
|
+
optional(','),
|
|
695
|
+
)),
|
|
680
696
|
'}',
|
|
681
697
|
)),
|
|
682
698
|
|
|
699
|
+
// ucode allows a trailing comma but NOT interior elision (`[1,,2]`).
|
|
683
700
|
array: $ => seq(
|
|
684
701
|
'[',
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
702
|
+
optional(seq(
|
|
703
|
+
commaSep1(choice(
|
|
704
|
+
$.expression,
|
|
705
|
+
$.spread_element,
|
|
706
|
+
)),
|
|
707
|
+
optional(','),
|
|
708
|
+
)),
|
|
689
709
|
']',
|
|
690
710
|
),
|
|
691
711
|
|
|
@@ -796,10 +816,17 @@ module.exports = grammar({
|
|
|
796
816
|
),
|
|
797
817
|
|
|
798
818
|
unary_expression: $ => prec.left('unary_void', seq(
|
|
799
|
-
field('operator', choice('!', '~', '-', '+'
|
|
819
|
+
field('operator', choice('!', '~', '-', '+')),
|
|
800
820
|
field('argument', $.expression),
|
|
801
821
|
)),
|
|
802
822
|
|
|
823
|
+
// ucode's `delete` only accepts a property access (member or subscript)
|
|
824
|
+
// expression: `delete o.k` / `delete o["k"]`. `delete x` is a syntax error.
|
|
825
|
+
delete_expression: $ => prec.left('unary_void', seq(
|
|
826
|
+
field('operator', 'delete'),
|
|
827
|
+
field('argument', choice($.member_expression, $.subscript_expression)),
|
|
828
|
+
)),
|
|
829
|
+
|
|
803
830
|
update_expression: $ => prec.left(choice(
|
|
804
831
|
seq(
|
|
805
832
|
field('argument', $.expression),
|
|
@@ -852,13 +879,16 @@ module.exports = grammar({
|
|
|
852
879
|
),
|
|
853
880
|
|
|
854
881
|
_call_signature: $ => field('parameters', $.formal_parameters),
|
|
855
|
-
_formal_parameter: $ => choice($.identifier, $.rest_element),
|
|
856
882
|
|
|
883
|
+
// A rest element (`...name`) must be the final parameter, and there can be
|
|
884
|
+
// only one. ucode rejects a rest param followed by anything — including a
|
|
885
|
+
// trailing comma. A trailing comma is allowed only after plain parameters.
|
|
857
886
|
formal_parameters: $ => seq(
|
|
858
887
|
'(',
|
|
859
|
-
optional(
|
|
860
|
-
|
|
861
|
-
|
|
888
|
+
optional(choice(
|
|
889
|
+
$.rest_element,
|
|
890
|
+
seq(commaSep1($.identifier), ',', $.rest_element),
|
|
891
|
+
seq(commaSep1($.identifier), optional(',')),
|
|
862
892
|
)),
|
|
863
893
|
')',
|
|
864
894
|
),
|
|
@@ -893,7 +923,8 @@ module.exports = grammar({
|
|
|
893
923
|
unescaped_double_string_fragment: _ => token.immediate(prec(1, /[^"\\\r\n]+/)),
|
|
894
924
|
unescaped_single_string_fragment: _ => token.immediate(prec(1, /[^'\\\r\n]+/)),
|
|
895
925
|
|
|
896
|
-
// Ucode extends JS escapes with \e (ESC), \a (BEL), and octal sequences
|
|
926
|
+
// Ucode extends JS escapes with \e (ESC), \a (BEL), and octal sequences.
|
|
927
|
+
// Unlike JS, ucode supports only the 4-hex `\uXXXX` form \u2014 not `\u{...}`.
|
|
897
928
|
escape_sequence: _ => token.immediate(seq(
|
|
898
929
|
'\\',
|
|
899
930
|
choice(
|
|
@@ -901,7 +932,6 @@ module.exports = grammar({
|
|
|
901
932
|
/[0-7]{1,3}/,
|
|
902
933
|
/x[0-9a-fA-F]{2}/,
|
|
903
934
|
/u[0-9a-fA-F]{4}/,
|
|
904
|
-
/u\{[0-9a-fA-F]+\}/,
|
|
905
935
|
/\r[\n\u2028\u2029]/,
|
|
906
936
|
),
|
|
907
937
|
)),
|
|
@@ -949,27 +979,30 @@ module.exports = grammar({
|
|
|
949
979
|
// - C-style legacy octal: 0177
|
|
950
980
|
// - Hex float: 0x1.8
|
|
951
981
|
// - Uppercase prefixes: 0O, 0B (already in JS grammar)
|
|
982
|
+
//
|
|
983
|
+
// Unlike JS, ucode does NOT support:
|
|
984
|
+
// - numeric underscore separators (1_000)
|
|
985
|
+
// - leading-dot floats (.5) — a digit is required before the dot
|
|
952
986
|
number: _ => {
|
|
953
|
-
const hexDigits = /[\da-fA-F]
|
|
987
|
+
const hexDigits = /[\da-fA-F]+/;
|
|
954
988
|
const hexLiteral = seq(choice('0x', '0X'), hexDigits);
|
|
955
989
|
const hexFloat = seq(choice('0x', '0X'), hexDigits, '.', optional(hexDigits));
|
|
956
990
|
|
|
957
|
-
const decimalDigits = /\d
|
|
991
|
+
const decimalDigits = /\d+/;
|
|
958
992
|
const signedInteger = seq(optional(choice('-', '+')), decimalDigits);
|
|
959
993
|
const exponentPart = seq(choice('e', 'E'), signedInteger);
|
|
960
994
|
|
|
961
|
-
const binaryLiteral = seq(choice('0b', '0B'), /[0-1]
|
|
962
|
-
const octalLiteral = seq(choice('0o', '0O'), /[0-7]
|
|
995
|
+
const binaryLiteral = seq(choice('0b', '0B'), /[0-1]+/);
|
|
996
|
+
const octalLiteral = seq(choice('0o', '0O'), /[0-7]+/);
|
|
963
997
|
const legacyOctalLiteral = seq('0', /[0-7]+/);
|
|
964
998
|
|
|
965
999
|
const decimalIntegerLiteral = choice(
|
|
966
1000
|
'0',
|
|
967
|
-
seq(optional('0'), /[1-9]/, optional(
|
|
1001
|
+
seq(optional('0'), /[1-9]/, optional(decimalDigits)),
|
|
968
1002
|
);
|
|
969
1003
|
|
|
970
1004
|
const decimalLiteral = choice(
|
|
971
1005
|
seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)),
|
|
972
|
-
seq('.', decimalDigits, optional(exponentPart)),
|
|
973
1006
|
seq(decimalIntegerLiteral, exponentPart),
|
|
974
1007
|
decimalDigits,
|
|
975
1008
|
);
|
|
@@ -986,9 +1019,12 @@ module.exports = grammar({
|
|
|
986
1019
|
|
|
987
1020
|
_identifier: $ => $.identifier,
|
|
988
1021
|
|
|
1022
|
+
// ucode does NOT support unicode escape sequences in identifiers (unlike JS);
|
|
1023
|
+
// a literal `\u...` is an "Unexpected character". Non-ASCII letters are still
|
|
1024
|
+
// allowed directly via the negated character class.
|
|
989
1025
|
identifier: _ => {
|
|
990
|
-
const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]
|
|
991
|
-
const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]
|
|
1026
|
+
const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]/;
|
|
1027
|
+
const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]/;
|
|
992
1028
|
return token(seq(alpha, repeat(alphanumeric)));
|
|
993
1029
|
},
|
|
994
1030
|
|
|
@@ -997,9 +1033,11 @@ module.exports = grammar({
|
|
|
997
1033
|
false: _ => 'false',
|
|
998
1034
|
null: _ => 'null',
|
|
999
1035
|
|
|
1036
|
+
// Unlike arrays/objects, ucode call arguments allow neither interior
|
|
1037
|
+
// elision (`f(1,,2)`) nor a trailing comma (`f(1,2,)`).
|
|
1000
1038
|
arguments: $ => seq(
|
|
1001
1039
|
'(',
|
|
1002
|
-
commaSep(
|
|
1040
|
+
commaSep(choice($.expression, $.spread_element)),
|
|
1003
1041
|
')',
|
|
1004
1042
|
),
|
|
1005
1043
|
|