zuzu-js 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.
Files changed (167) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +113 -0
  3. package/bin/zuzu +17 -0
  4. package/bin/zuzu-build-browser-bundle +57 -0
  5. package/bin/zuzu-generate-browser-stdlib +584 -0
  6. package/bin/zuzu-js +23 -0
  7. package/bin/zuzu-js-compile +152 -0
  8. package/bin/zuzu-js-electron +19 -0
  9. package/dist/zuzu-browser-worker.js +45574 -0
  10. package/dist/zuzu-browser.js +45362 -0
  11. package/lib/browser-bundle-entry.js +160 -0
  12. package/lib/browser-gui-renderer.js +387 -0
  13. package/lib/browser-runtime.js +167 -0
  14. package/lib/browser-worker-entry.js +413 -0
  15. package/lib/browser-ztests/runner.html +103 -0
  16. package/lib/browser-ztests/runner.js +369 -0
  17. package/lib/cli.js +350 -0
  18. package/lib/collections.js +367 -0
  19. package/lib/compiler.js +303 -0
  20. package/lib/electron/launcher.js +70 -0
  21. package/lib/electron/main.js +956 -0
  22. package/lib/electron/preload.js +80 -0
  23. package/lib/electron/renderer.html +122 -0
  24. package/lib/electron/renderer.js +24 -0
  25. package/lib/execution-metadata.js +18 -0
  26. package/lib/gui/dom-renderer.js +778 -0
  27. package/lib/host/browser-host.js +278 -0
  28. package/lib/host/capabilities.js +47 -0
  29. package/lib/host/electron-host.js +15 -0
  30. package/lib/host/node-host.js +74 -0
  31. package/lib/paths.js +150 -0
  32. package/lib/runtime-entrypoints.js +60 -0
  33. package/lib/runtime-helpers.js +886 -0
  34. package/lib/runtime.js +3529 -0
  35. package/lib/tap.js +37 -0
  36. package/lib/transpiler-new/ast.js +23 -0
  37. package/lib/transpiler-new/codegen.js +2455 -0
  38. package/lib/transpiler-new/errors.js +28 -0
  39. package/lib/transpiler-new/index.js +26 -0
  40. package/lib/transpiler-new/lexer.js +834 -0
  41. package/lib/transpiler-new/parser.js +2332 -0
  42. package/lib/transpiler-new/validate-bindings.js +326 -0
  43. package/lib/transpiler-utils.js +95 -0
  44. package/lib/transpiler.js +33 -0
  45. package/lib/zuzu.js +53 -0
  46. package/modules/javascript.js +193 -0
  47. package/modules/std/archive.js +603 -0
  48. package/modules/std/clib.js +338 -0
  49. package/modules/std/data/csv.js +1331 -0
  50. package/modules/std/data/json.js +531 -0
  51. package/modules/std/data/xml.js +441 -0
  52. package/modules/std/data/yaml.js +256 -0
  53. package/modules/std/db-worker.js +250 -0
  54. package/modules/std/db.js +664 -0
  55. package/modules/std/digest/_hash.js +443 -0
  56. package/modules/std/digest/md5.js +26 -0
  57. package/modules/std/digest/sha.js +72 -0
  58. package/modules/std/eval.js +10 -0
  59. package/modules/std/gui/objects.js +1519 -0
  60. package/modules/std/internals.js +571 -0
  61. package/modules/std/io/socks-worker.js +318 -0
  62. package/modules/std/io/socks.js +186 -0
  63. package/modules/std/io.js +475 -0
  64. package/modules/std/marshal/cbor.js +463 -0
  65. package/modules/std/marshal/graph.js +1624 -0
  66. package/modules/std/marshal.js +87 -0
  67. package/modules/std/math/bignum.js +91 -0
  68. package/modules/std/math.js +79 -0
  69. package/modules/std/net/dns.js +306 -0
  70. package/modules/std/net/http.js +820 -0
  71. package/modules/std/net/smtp.js +943 -0
  72. package/modules/std/net/url.js +109 -0
  73. package/modules/std/proc.js +602 -0
  74. package/modules/std/secure.js +3724 -0
  75. package/modules/std/string/base64.js +138 -0
  76. package/modules/std/string.js +299 -0
  77. package/modules/std/task.js +914 -0
  78. package/modules/std/time.js +579 -0
  79. package/modules/std/tui.js +188 -0
  80. package/modules/std/worker-thread.js +246 -0
  81. package/modules/std/worker.js +790 -0
  82. package/package.json +67 -0
  83. package/stdlib/modules/javascript.zzm +99 -0
  84. package/stdlib/modules/perl.zzm +105 -0
  85. package/stdlib/modules/std/archive.zzm +132 -0
  86. package/stdlib/modules/std/cache/lru.zzm +174 -0
  87. package/stdlib/modules/std/clib.zzm +112 -0
  88. package/stdlib/modules/std/colour.zzm +220 -0
  89. package/stdlib/modules/std/config.zzm +818 -0
  90. package/stdlib/modules/std/data/cbor.zzm +497 -0
  91. package/stdlib/modules/std/data/csv.zzm +285 -0
  92. package/stdlib/modules/std/data/ini.zzm +472 -0
  93. package/stdlib/modules/std/data/json/schema/core.zzm +573 -0
  94. package/stdlib/modules/std/data/json/schema/format.zzm +581 -0
  95. package/stdlib/modules/std/data/json/schema/model.zzm +255 -0
  96. package/stdlib/modules/std/data/json/schema/output.zzm +272 -0
  97. package/stdlib/modules/std/data/json/schema/relative_pointer.zzm +299 -0
  98. package/stdlib/modules/std/data/json/schema/validation.zzm +1503 -0
  99. package/stdlib/modules/std/data/json/schema.zzm +306 -0
  100. package/stdlib/modules/std/data/json.zzm +102 -0
  101. package/stdlib/modules/std/data/kdl/json.zzm +460 -0
  102. package/stdlib/modules/std/data/kdl/xml.zzm +387 -0
  103. package/stdlib/modules/std/data/kdl.zzm +1631 -0
  104. package/stdlib/modules/std/data/toml.zzm +756 -0
  105. package/stdlib/modules/std/data/toon.zzm +1017 -0
  106. package/stdlib/modules/std/data/xml/escape.zzm +156 -0
  107. package/stdlib/modules/std/data/xml.zzm +276 -0
  108. package/stdlib/modules/std/data/yaml.zzm +94 -0
  109. package/stdlib/modules/std/db.zzm +173 -0
  110. package/stdlib/modules/std/defer.zzm +75 -0
  111. package/stdlib/modules/std/digest/crc32.zzm +196 -0
  112. package/stdlib/modules/std/digest/md5.zzm +54 -0
  113. package/stdlib/modules/std/digest/sha.zzm +83 -0
  114. package/stdlib/modules/std/dump.zzm +317 -0
  115. package/stdlib/modules/std/eval.zzm +63 -0
  116. package/stdlib/modules/std/getopt.zzm +432 -0
  117. package/stdlib/modules/std/gui/dialogue.zzm +592 -0
  118. package/stdlib/modules/std/gui/objects.zzm +123 -0
  119. package/stdlib/modules/std/gui.zzm +1914 -0
  120. package/stdlib/modules/std/internals.zzm +139 -0
  121. package/stdlib/modules/std/io/socks.zzm +139 -0
  122. package/stdlib/modules/std/io.zzm +157 -0
  123. package/stdlib/modules/std/lingua/en.zzm +347 -0
  124. package/stdlib/modules/std/log.zzm +169 -0
  125. package/stdlib/modules/std/mail.zzm +2726 -0
  126. package/stdlib/modules/std/marshal.zzm +138 -0
  127. package/stdlib/modules/std/math/bignum.zzm +98 -0
  128. package/stdlib/modules/std/math/range.zzm +116 -0
  129. package/stdlib/modules/std/math/roman.zzm +156 -0
  130. package/stdlib/modules/std/math.zzm +141 -0
  131. package/stdlib/modules/std/net/dns.zzm +93 -0
  132. package/stdlib/modules/std/net/http.zzm +278 -0
  133. package/stdlib/modules/std/net/smtp.zzm +257 -0
  134. package/stdlib/modules/std/net/url.zzm +69 -0
  135. package/stdlib/modules/std/path/jsonpointer.zzm +526 -0
  136. package/stdlib/modules/std/path/kdl.zzm +1003 -0
  137. package/stdlib/modules/std/path/simple.zzm +520 -0
  138. package/stdlib/modules/std/path/z/context.zzm +147 -0
  139. package/stdlib/modules/std/path/z/evaluate.zzm +549 -0
  140. package/stdlib/modules/std/path/z/functions.zzm +874 -0
  141. package/stdlib/modules/std/path/z/lexer.zzm +490 -0
  142. package/stdlib/modules/std/path/z/node.zzm +1455 -0
  143. package/stdlib/modules/std/path/z/operators.zzm +445 -0
  144. package/stdlib/modules/std/path/z/parser.zzm +359 -0
  145. package/stdlib/modules/std/path/z.zzm +403 -0
  146. package/stdlib/modules/std/path/zz/functions.zzm +828 -0
  147. package/stdlib/modules/std/path/zz/operators.zzm +1036 -0
  148. package/stdlib/modules/std/path/zz.zzm +100 -0
  149. package/stdlib/modules/std/proc.zzm +155 -0
  150. package/stdlib/modules/std/result.zzm +149 -0
  151. package/stdlib/modules/std/secure.zzm +606 -0
  152. package/stdlib/modules/std/string/base64.zzm +66 -0
  153. package/stdlib/modules/std/string/quoted_printable.zzm +485 -0
  154. package/stdlib/modules/std/string.zzm +179 -0
  155. package/stdlib/modules/std/task.zzm +221 -0
  156. package/stdlib/modules/std/template/z.zzm +531 -0
  157. package/stdlib/modules/std/template/zz.zzm +62 -0
  158. package/stdlib/modules/std/time.zzm +188 -0
  159. package/stdlib/modules/std/tui.zzm +89 -0
  160. package/stdlib/modules/std/uuid.zzm +223 -0
  161. package/stdlib/modules/std/web/session.zzm +388 -0
  162. package/stdlib/modules/std/web/static.zzm +329 -0
  163. package/stdlib/modules/std/web.zzm +1942 -0
  164. package/stdlib/modules/std/worker.zzm +202 -0
  165. package/stdlib/modules/std/zuzuzoo.zzm +3960 -0
  166. package/stdlib/modules/test/more.zzm +528 -0
  167. package/stdlib/modules/test/parser.zzm +209 -0
@@ -0,0 +1,2332 @@
1
+ 'use strict';
2
+
3
+ const { withLoc } = require( './ast' );
4
+ const { tokenize } = require( './lexer' );
5
+ const {
6
+ TranspilerSyntaxError,
7
+ UnsupportedSyntaxError,
8
+ } = require( './errors' );
9
+
10
+ function parse( tokens, options = {} ) {
11
+ let index = 0;
12
+ let asyncContextDepth = 1;
13
+
14
+ function startLocFromToken( token ) {
15
+ return {
16
+ start: token.start,
17
+ line: token.line,
18
+ column: token.column,
19
+ };
20
+ }
21
+
22
+ function startLocFromNode( node ) {
23
+ return {
24
+ start: node.loc.start.offset,
25
+ line: node.loc.start.line,
26
+ column: node.loc.start.column,
27
+ };
28
+ }
29
+
30
+ function endLocFromToken( token ) {
31
+ return {
32
+ end: token.end,
33
+ endLine: token.endLine,
34
+ endColumn: token.endColumn,
35
+ };
36
+ }
37
+
38
+ function endLocFromNode( node ) {
39
+ return {
40
+ end: node.loc.end.offset,
41
+ endLine: node.loc.end.line,
42
+ endColumn: node.loc.end.column,
43
+ };
44
+ }
45
+
46
+ function current() {
47
+ return tokens[index];
48
+ }
49
+
50
+ function peekToken( n = 1 ) {
51
+ return tokens[index + n] || tokens[tokens.length - 1];
52
+ }
53
+
54
+ function previous() {
55
+ return tokens[index - 1];
56
+ }
57
+
58
+ function canTerminateStatement() {
59
+ const token = current();
60
+ const prev = previous();
61
+ if ( !prev ) {
62
+ return false;
63
+ }
64
+ if ( token.type === 'punctuation' && token.value === '}' ) {
65
+ return true;
66
+ }
67
+ if ( token.type === 'eof' ) {
68
+ return true;
69
+ }
70
+ return token.line > prev.endLine;
71
+ }
72
+
73
+ function consumeStatementTerminator(message) {
74
+ if ( match( 'punctuation', ';' ) ) {
75
+ return;
76
+ }
77
+ if ( canTerminateStatement() ) {
78
+ return;
79
+ }
80
+ throw new TranspilerSyntaxError( message || 'Expected statement terminator', current() );
81
+ }
82
+
83
+ function atEnd() {
84
+ return current().type === 'eof';
85
+ }
86
+
87
+ function match(type, value = null) {
88
+ const token = current();
89
+ if ( token.type !== type ) {
90
+ return false;
91
+ }
92
+ if ( value != null && token.value !== value ) {
93
+ return false;
94
+ }
95
+ index++;
96
+ return true;
97
+ }
98
+
99
+ function expect(type, value = null, message = null) {
100
+ const token = current();
101
+ if ( match( type, value ) ) {
102
+ return previous();
103
+ }
104
+ throw new TranspilerSyntaxError(
105
+ message || `Expected ${value != null ? `${type} ${value}` : type}`,
106
+ token
107
+ );
108
+ }
109
+
110
+ function expectIdentifier(message = 'Expected identifier') {
111
+ const token = current();
112
+ if ( token.type === 'identifier' || token.type === 'keyword' ) {
113
+ if ( token.value === '^^' ) {
114
+ throw new TranspilerSyntaxError( "'^^' is reserved for the chain placeholder", token );
115
+ }
116
+ index++;
117
+ return token;
118
+ }
119
+ throw new TranspilerSyntaxError( message, token );
120
+ }
121
+
122
+ function parseProgram() {
123
+ const body = [];
124
+ const start = current();
125
+ while ( !atEnd() ) {
126
+ body.push( parseStatement() );
127
+ }
128
+ return withLoc( {
129
+ type: 'Program',
130
+ body,
131
+ }, start, previous() || start );
132
+ }
133
+
134
+ function matchPairListClose() {
135
+ if (
136
+ current().type === 'punctuation'
137
+ && current().value === '}'
138
+ && peekToken().type === 'punctuation'
139
+ && peekToken().value === '}'
140
+ ) {
141
+ index += 2;
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+
147
+ function parseExpressionRoot() {
148
+ const expr = parseExpression();
149
+ if ( !atEnd() ) {
150
+ throw new TranspilerSyntaxError( 'Unexpected token after expression', current() );
151
+ }
152
+ return expr;
153
+ }
154
+
155
+ function parseStatement() {
156
+ const token = current();
157
+ if ( token.type === 'punctuation' && token.value === ';' ) {
158
+ index++;
159
+ return withLoc( { type: 'EmptyStatement' }, token, token );
160
+ }
161
+ if ( token.type === 'keyword' ) {
162
+ switch ( token.value ) {
163
+ case 'from':
164
+ return parseImportDeclaration();
165
+ case 'let':
166
+ case 'const':
167
+ return parseVariableDeclaration();
168
+ case 'function':
169
+ return parseFunctionDeclaration();
170
+ case 'async':
171
+ if ( peekToken().type === 'keyword' && peekToken().value === 'function' ) {
172
+ return parseFunctionDeclaration();
173
+ }
174
+ if ( peekToken().type === 'keyword' && peekToken().value === 'fn' ) {
175
+ const start = expect( 'keyword', 'async' );
176
+ return parseFunctionDeclarationLikeFn( true, start );
177
+ }
178
+ break;
179
+ case 'return':
180
+ return parseReturnStatement();
181
+ case 'if':
182
+ return parseIfStatement();
183
+ case 'for':
184
+ return parseForStatement();
185
+ case 'while':
186
+ return parseWhileStatement();
187
+ case 'switch':
188
+ return parseSwitchStatement();
189
+ case 'next':
190
+ case 'continue':
191
+ return parseLoopControlStatement( 'ContinueStatement' );
192
+ case 'last':
193
+ return parseLoopControlStatement( 'BreakStatement' );
194
+ case 'throw':
195
+ return parseThrowStatement();
196
+ case 'die':
197
+ return parseDieStatement();
198
+ case 'warn':
199
+ return parseWarnStatement();
200
+ case 'assert':
201
+ return parseAssertStatement();
202
+ case 'debug':
203
+ return parseDebugStatement();
204
+ case 'say':
205
+ return parseSayStatement();
206
+ case 'try':
207
+ return parseTryStatement();
208
+ case 'class':
209
+ return parseClassDeclaration();
210
+ case 'trait':
211
+ return parseTraitDeclaration();
212
+ case 'export':
213
+ return parseExportDeclaration();
214
+ case 'catch':
215
+ case 'unless':
216
+ throw new UnsupportedSyntaxError(
217
+ `Keyword ${token.value} is not supported by zuzu-js transpilation yet`,
218
+ token
219
+ );
220
+ case 'fn':
221
+ return parseFunctionDeclarationLikeFn();
222
+ default:
223
+ break;
224
+ }
225
+ }
226
+ if ( token.type === 'punctuation' && token.value === '{' ) {
227
+ return parseBlockStatement();
228
+ }
229
+ return parseExpressionStatement();
230
+ }
231
+
232
+ function parseExportDeclaration() {
233
+ const start = expect( 'keyword', 'export' );
234
+ const token = current();
235
+ const allowed = new Set( [ 'let', 'const', 'function', 'async', 'fn', 'class', 'trait' ] );
236
+ if ( token.type !== 'keyword' || !allowed.has( token.value ) ) {
237
+ throw new TranspilerSyntaxError( 'Expected declaration after export', token );
238
+ }
239
+ const declaration = parseStatement();
240
+ declaration.exported = true;
241
+ declaration.loc.start = {
242
+ offset: start.start,
243
+ line: start.line,
244
+ column: start.column,
245
+ };
246
+ return declaration;
247
+ }
248
+
249
+ function parseImportDeclaration() {
250
+ const start = expect( 'keyword', 'from' );
251
+ const moduleParts = [];
252
+ while (
253
+ !( current().type === 'keyword'
254
+ && ( current().value === 'import' || current().value === 'try' ) )
255
+ ) {
256
+ if (
257
+ current().type === 'identifier'
258
+ || current().type === 'keyword'
259
+ || ( current().type === 'operator' && current().value === '/' )
260
+ || ( current().type === 'operator' && current().value === '.' )
261
+ || ( current().type === 'punctuation' && current().value === '.' )
262
+ ) {
263
+ moduleParts.push( current().value );
264
+ index++;
265
+ continue;
266
+ }
267
+ if ( current().type === 'string' ) {
268
+ moduleParts.push( current().value );
269
+ index++;
270
+ break;
271
+ }
272
+ throw new TranspilerSyntaxError( 'Invalid module path in import', current() );
273
+ }
274
+ const tryMode = match( 'keyword', 'try' );
275
+ expect( 'keyword', 'import' );
276
+ let importAll = false;
277
+ const specifiers = [];
278
+ if ( match( 'operator', '*' ) ) {
279
+ if ( tryMode ) {
280
+ throw new TranspilerSyntaxError(
281
+ "Wildcard import '*' cannot be combined with try import",
282
+ previous(),
283
+ );
284
+ }
285
+ importAll = true;
286
+ }
287
+ else {
288
+ do {
289
+ const imported = expectIdentifier();
290
+ let local = imported;
291
+ if ( match( 'keyword', 'as' ) ) {
292
+ local = expectIdentifier( 'Expected alias name after as' );
293
+ }
294
+ specifiers.push( {
295
+ type: 'ImportSpecifier',
296
+ imported: imported.value,
297
+ local: local.value,
298
+ } );
299
+ }
300
+ while ( match( 'punctuation', ',' ) );
301
+ }
302
+ let condition = null;
303
+ if ( current().type === 'keyword' && [ 'if', 'unless' ].includes( current().value ) ) {
304
+ const keyword = current();
305
+ if ( importAll ) {
306
+ throw new TranspilerSyntaxError(
307
+ "Wildcard import '*' cannot be combined with postfix if/unless",
308
+ keyword,
309
+ );
310
+ }
311
+ index++;
312
+ condition = {
313
+ type: 'PostfixCondition',
314
+ keyword: keyword.value,
315
+ test: parseExpression(),
316
+ };
317
+ }
318
+ consumeStatementTerminator( 'Expected ; after import declaration' );
319
+ return withLoc( {
320
+ type: 'ImportDeclaration',
321
+ source: moduleParts.join( '' ),
322
+ tryMode,
323
+ importAll,
324
+ specifiers,
325
+ condition,
326
+ }, start, previous() );
327
+ }
328
+
329
+ function parseVariableDeclaration() {
330
+ const start = expect( 'keyword' );
331
+ if ( current().type === 'punctuation' && current().value === '{' ) {
332
+ return parseVariableUnpackDeclaration( start );
333
+ }
334
+ const declaration = parseVariableDeclarationParts( start );
335
+ consumeStatementTerminator( 'Expected ; after variable declaration' );
336
+ return withLoc( {
337
+ type: 'VariableDeclaration',
338
+ ...declaration,
339
+ }, start, previous() );
340
+ }
341
+
342
+ function parseVariableDeclarationParts( start ) {
343
+ let declaredType = null;
344
+ let id = expectIdentifier();
345
+ if (
346
+ current().type === 'identifier'
347
+ && ![ ':=', '=', ';', ',', ')' ].includes( current().value )
348
+ ) {
349
+ declaredType = id.value;
350
+ id = expectIdentifier( 'Expected variable name after type' );
351
+ }
352
+ let isWeakStorage = parseOptionalWeakModifier( 'declaration' );
353
+ let init = null;
354
+ if ( match( 'operator', ':=' ) || match( 'operator', '=' ) ) {
355
+ init = parseExpression();
356
+ isWeakStorage = parseOptionalWeakModifier( 'declaration' ) || isWeakStorage;
357
+ }
358
+ return {
359
+ kind: start.value,
360
+ declaredType,
361
+ id: withLoc( {
362
+ type: 'Identifier',
363
+ name: id.value,
364
+ }, id, id ),
365
+ init,
366
+ isWeakStorage,
367
+ };
368
+ }
369
+
370
+ function parseVariableUnpackDeclaration( start ) {
371
+ const pattern = parseDeclarationBindingPattern();
372
+ expect( 'operator', ':=', "Expected ':=' after declaration binding pattern" );
373
+ const init = parseExpression();
374
+ consumeStatementTerminator( 'Expected ; after variable unpack declaration' );
375
+ return withLoc( {
376
+ type: 'VariableUnpackDeclaration',
377
+ kind: start.value,
378
+ pattern,
379
+ init,
380
+ }, start, previous() );
381
+ }
382
+
383
+ function parseDeclarationBindingPattern() {
384
+ const start = expect( 'punctuation', '{', "Expected '{' in declaration binding pattern" );
385
+ const entries = [];
386
+ if ( !match( 'punctuation', '}' ) ) {
387
+ while ( true ) {
388
+ if ( match( 'punctuation', ',' ) ) {
389
+ continue;
390
+ }
391
+ if ( match( 'punctuation', '}' ) ) {
392
+ break;
393
+ }
394
+ entries.push( parseDeclarationBindingEntry() );
395
+ if ( match( 'punctuation', '}' ) ) {
396
+ break;
397
+ }
398
+ match( 'punctuation', ',' );
399
+ }
400
+ }
401
+ return withLoc( {
402
+ type: 'KeyedBindingPattern',
403
+ entries,
404
+ }, start, previous() );
405
+ }
406
+
407
+ function parseDeclarationBindingEntry() {
408
+ const start = current();
409
+ const binding = parseDeclarationBindingKeyAndName();
410
+ let defaultValue = null;
411
+ if ( match( 'operator', ':=' ) ) {
412
+ defaultValue = parseExpression();
413
+ }
414
+ const isWeakStorage = parseOptionalWeakModifier( 'declaration' );
415
+ return withLoc( {
416
+ type: 'DeclarationBindingEntry',
417
+ key: binding.key,
418
+ declaredType: binding.declaredType,
419
+ name: binding.name,
420
+ defaultValue,
421
+ isWeakStorage,
422
+ }, start, previous() || start );
423
+ }
424
+
425
+ function parseDeclarationBindingKeyAndName() {
426
+ if ( current().type === 'identifier' ) {
427
+ const first = current();
428
+ index++;
429
+ if ( match( 'operator', ':' ) ) {
430
+ const typed = parseTypedBindingName();
431
+ return {
432
+ key: bindingIdentifierKey( first ),
433
+ declaredType: typed.declaredType,
434
+ name: typed.name,
435
+ };
436
+ }
437
+ if ( current().type === 'identifier' || current().type === 'keyword' ) {
438
+ const name = expectIdentifier( 'Expected unpacked binding name after type' );
439
+ return {
440
+ key: bindingIdentifierKey( name ),
441
+ declaredType: first.value,
442
+ name: name.value,
443
+ };
444
+ }
445
+ return {
446
+ key: bindingIdentifierKey( first ),
447
+ declaredType: null,
448
+ name: first.value,
449
+ };
450
+ }
451
+ if ( current().type === 'keyword' || current().type === 'string' ) {
452
+ const key = parseDeclarationBindingKey();
453
+ expect( 'operator', ':', "Expected ':' after declaration binding key" );
454
+ const typed = parseTypedBindingName();
455
+ return {
456
+ key,
457
+ declaredType: typed.declaredType,
458
+ name: typed.name,
459
+ };
460
+ }
461
+ if ( current().type === 'template' || ( current().type === 'punctuation' && current().value === '(' ) ) {
462
+ const key = parseDeclarationBindingKey();
463
+ expect( 'operator', ':', "Expected ':' after declaration binding key" );
464
+ const typed = parseTypedBindingName();
465
+ return {
466
+ key,
467
+ declaredType: typed.declaredType,
468
+ name: typed.name,
469
+ };
470
+ }
471
+ throw new TranspilerSyntaxError( 'Expected unpacked binding in declaration', current() );
472
+ }
473
+
474
+ function parseTypedBindingName() {
475
+ let declaredType = null;
476
+ let name = expectIdentifier( 'Expected unpacked binding name' );
477
+ if ( current().type === 'identifier' ) {
478
+ declaredType = name.value;
479
+ name = expectIdentifier( 'Expected unpacked binding name after type' );
480
+ }
481
+ return {
482
+ declaredType,
483
+ name: name.value,
484
+ };
485
+ }
486
+
487
+ function bindingIdentifierKey( token ) {
488
+ return withLoc( {
489
+ type: 'BraceIdentifier',
490
+ name: token.value,
491
+ }, token, token );
492
+ }
493
+
494
+ function parseDeclarationBindingKey() {
495
+ const token = current();
496
+ if ( token.type === 'identifier' || token.type === 'keyword' ) {
497
+ index++;
498
+ return withLoc( {
499
+ type: 'BraceIdentifier',
500
+ name: token.value,
501
+ }, token, token );
502
+ }
503
+ if ( token.type === 'string' ) {
504
+ index++;
505
+ return withLoc( {
506
+ type: 'StringLiteral',
507
+ value: token.value,
508
+ }, token, token );
509
+ }
510
+ if ( token.type === 'template' ) {
511
+ return parsePrimary();
512
+ }
513
+ if ( match( 'punctuation', '(' ) ) {
514
+ const expr = parseExpression();
515
+ expect( 'punctuation', ')', 'Expected ) after declaration binding key' );
516
+ return expr;
517
+ }
518
+ throw new TranspilerSyntaxError( 'Expected declaration binding key', token );
519
+ }
520
+
521
+ function parseFunctionDeclaration() {
522
+ let isAsync = false;
523
+ let start = current();
524
+ if ( match( 'keyword', 'async' ) ) {
525
+ isAsync = true;
526
+ start = previous();
527
+ }
528
+ expect( 'keyword', 'function' );
529
+ const id = expectIdentifier();
530
+ if ( match( 'punctuation', ';' ) ) {
531
+ return withLoc( {
532
+ type: 'FunctionDeclaration',
533
+ id: {
534
+ type: 'Identifier',
535
+ name: id.value,
536
+ },
537
+ params: [],
538
+ returnType: null,
539
+ isAsync,
540
+ isPredeclared: true,
541
+ body: withLoc( {
542
+ type: 'BlockStatement',
543
+ body: [],
544
+ }, id, id ),
545
+ }, start, previous() );
546
+ }
547
+ const params = parseParameterList();
548
+ const returnType = parseOptionalReturnType();
549
+ const body = parseBlockInAsyncContext( isAsync );
550
+ return withLoc( {
551
+ type: 'FunctionDeclaration',
552
+ id: {
553
+ type: 'Identifier',
554
+ name: id.value,
555
+ },
556
+ params,
557
+ returnType,
558
+ isAsync,
559
+ isPredeclared: false,
560
+ body,
561
+ }, start, previous() );
562
+ }
563
+
564
+ function parseFunctionDeclarationLikeFn( isAsync = false, startOverride = null ) {
565
+ const start = startOverride || current();
566
+ expect( 'keyword', 'fn' );
567
+ const id = expectIdentifier();
568
+ const params = parseParameterList();
569
+ const returnType = parseOptionalReturnType();
570
+ const body = parseBlockInAsyncContext( isAsync );
571
+ return withLoc( {
572
+ type: 'FunctionDeclaration',
573
+ id: {
574
+ type: 'Identifier',
575
+ name: id.value,
576
+ },
577
+ params,
578
+ returnType,
579
+ isAsync,
580
+ body,
581
+ }, start, previous() );
582
+ }
583
+
584
+ function parseClassDeclaration() {
585
+ const start = expect( 'keyword', 'class' );
586
+ const id = expectIdentifier( 'Expected class name' );
587
+ let base = null;
588
+ if ( match( 'keyword', 'extends' ) ) {
589
+ const baseId = expectIdentifier( 'Expected base class name after extends' );
590
+ base = withLoc( {
591
+ type: 'Identifier',
592
+ name: baseId.value,
593
+ }, baseId, baseId );
594
+ }
595
+ const traits = parseTraitCompositionList();
596
+ if ( match( 'punctuation', ';' ) ) {
597
+ return withLoc( {
598
+ type: 'ClassDeclaration',
599
+ id: withLoc( {
600
+ type: 'Identifier',
601
+ name: id.value,
602
+ }, id, id ),
603
+ base,
604
+ traits,
605
+ body: [],
606
+ shorthand: true,
607
+ }, start, previous() );
608
+ }
609
+ expect( 'punctuation', '{', 'Expected { to start class body' );
610
+ const body = [];
611
+ while ( !( current().type === 'punctuation' && current().value === '}' ) ) {
612
+ if ( atEnd() ) {
613
+ throw new TranspilerSyntaxError( 'Unterminated class declaration', current() );
614
+ }
615
+ if ( current().type === 'punctuation' && current().value === ';' ) {
616
+ index++;
617
+ continue;
618
+ }
619
+ body.push( parseClassMember() );
620
+ }
621
+ expect( 'punctuation', '}' );
622
+ return withLoc( {
623
+ type: 'ClassDeclaration',
624
+ id: withLoc( {
625
+ type: 'Identifier',
626
+ name: id.value,
627
+ }, id, id ),
628
+ base,
629
+ traits,
630
+ body,
631
+ shorthand: false,
632
+ }, start, previous() );
633
+ }
634
+
635
+ function parseTraitDeclaration() {
636
+ const start = expect( 'keyword', 'trait' );
637
+ const id = expectIdentifier( 'Expected trait name' );
638
+ expect( 'punctuation', '{', 'Expected { to start trait body' );
639
+ const body = [];
640
+ while ( !( current().type === 'punctuation' && current().value === '}' ) ) {
641
+ if ( atEnd() ) {
642
+ throw new TranspilerSyntaxError( 'Unterminated trait declaration', current() );
643
+ }
644
+ if ( current().type === 'punctuation' && current().value === ';' ) {
645
+ index++;
646
+ continue;
647
+ }
648
+ body.push( parseMethodDeclaration( { allowStatic: false } ) );
649
+ }
650
+ expect( 'punctuation', '}' );
651
+ return withLoc( {
652
+ type: 'TraitDeclaration',
653
+ id: withLoc( {
654
+ type: 'Identifier',
655
+ name: id.value,
656
+ }, id, id ),
657
+ body,
658
+ }, start, previous() );
659
+ }
660
+
661
+ function parseTraitCompositionList() {
662
+ const traits = [];
663
+ if ( current().type === 'keyword' && [ 'with', 'but' ].includes( current().value ) ) {
664
+ index++;
665
+ do {
666
+ const traitId = expectIdentifier( 'Expected trait name' );
667
+ traits.push( withLoc( {
668
+ type: 'Identifier',
669
+ name: traitId.value,
670
+ }, traitId, traitId ) );
671
+ }
672
+ while ( match( 'punctuation', ',' ) );
673
+ }
674
+ return traits;
675
+ }
676
+
677
+ function parseOptionalWeakModifier( context ) {
678
+ if ( current().type !== 'keyword' || current().value !== 'but' ) {
679
+ return false;
680
+ }
681
+ index++;
682
+ const modifier = current();
683
+ if (
684
+ !( modifier.type === 'identifier' || modifier.type === 'keyword' )
685
+ || modifier.value !== 'weak'
686
+ ) {
687
+ throw new TranspilerSyntaxError(
688
+ `Unknown but modifier '${modifier.value || ''}' in ${context}; expected 'but weak'`,
689
+ modifier,
690
+ );
691
+ }
692
+ index++;
693
+ return true;
694
+ }
695
+
696
+ function parseClassMember() {
697
+ if ( current().type === 'keyword' && [ 'static', 'async' ].includes( current().value ) ) {
698
+ return parseMethodDeclaration( { allowStatic: true } );
699
+ }
700
+ if ( current().type === 'keyword' && current().value === 'method' ) {
701
+ return parseMethodDeclaration( { allowStatic: true } );
702
+ }
703
+ if ( current().type === 'keyword' && [ 'let', 'const' ].includes( current().value ) ) {
704
+ return parseFieldDeclaration();
705
+ }
706
+ if ( current().type === 'keyword' && current().value === 'class' ) {
707
+ return parseClassDeclaration();
708
+ }
709
+ if ( current().type === 'keyword' && current().value === 'trait' ) {
710
+ return parseTraitDeclaration();
711
+ }
712
+ throw new UnsupportedSyntaxError( 'Unsupported class member', current() );
713
+ }
714
+
715
+ function parseMethodDeclaration( { allowStatic = true } = {} ) {
716
+ const start = current();
717
+ let isStatic = false;
718
+ let isAsync = false;
719
+ while ( current().type === 'keyword' && [ 'static', 'async' ].includes( current().value ) ) {
720
+ if ( current().value === 'static' ) {
721
+ if ( !allowStatic || isStatic ) {
722
+ break;
723
+ }
724
+ isStatic = true;
725
+ index++;
726
+ continue;
727
+ }
728
+ if ( isAsync ) {
729
+ break;
730
+ }
731
+ isAsync = true;
732
+ index++;
733
+ }
734
+ expect( 'keyword', 'method', 'Expected method declaration' );
735
+ const id = expectIdentifier( 'Expected method name' );
736
+ if ( match( 'punctuation', ';' ) ) {
737
+ return withLoc( {
738
+ type: 'MethodDeclaration',
739
+ id: withLoc( {
740
+ type: 'Identifier',
741
+ name: id.value,
742
+ }, id, id ),
743
+ params: [],
744
+ returnType: null,
745
+ body: withLoc( {
746
+ type: 'BlockStatement',
747
+ body: [],
748
+ }, id, id ),
749
+ static: isStatic,
750
+ isAsync,
751
+ isPredeclared: true,
752
+ }, start, previous() );
753
+ }
754
+ const params = parseParameterList();
755
+ const returnType = parseOptionalReturnType();
756
+ const body = parseBlockInAsyncContext( isAsync );
757
+ return withLoc( {
758
+ type: 'MethodDeclaration',
759
+ id: withLoc( {
760
+ type: 'Identifier',
761
+ name: id.value,
762
+ }, id, id ),
763
+ params,
764
+ returnType,
765
+ body,
766
+ static: isStatic,
767
+ isAsync,
768
+ isPredeclared: false,
769
+ }, start, previous() );
770
+ }
771
+
772
+ function parseFieldDeclaration() {
773
+ const start = expect( 'keyword' );
774
+ let typeName = null;
775
+ let id = expectIdentifier( 'Expected field name' );
776
+ if (
777
+ current().type === 'identifier'
778
+ && current().value !== 'with'
779
+ && !( current().type === 'operator' && current().value === ':=' )
780
+ && !( current().type === 'punctuation' && [ ';', '}' ].includes( current().value ) )
781
+ ) {
782
+ typeName = id.value;
783
+ id = expectIdentifier( 'Expected field name after type' );
784
+ }
785
+ const accessors = [];
786
+ if ( match( 'keyword', 'with' ) ) {
787
+ do {
788
+ accessors.push( expectIdentifier( 'Expected accessor name' ).value );
789
+ }
790
+ while ( match( 'punctuation', ',' ) );
791
+ }
792
+ let isWeakStorage = parseOptionalWeakModifier( 'field declaration' );
793
+ let defaultValue = null;
794
+ if ( match( 'operator', ':=' ) ) {
795
+ defaultValue = parseExpression();
796
+ isWeakStorage = parseOptionalWeakModifier( 'field declaration' ) || isWeakStorage;
797
+ }
798
+ consumeStatementTerminator( 'Expected ; after field declaration' );
799
+ return withLoc( {
800
+ type: 'FieldDeclaration',
801
+ kind: start.value,
802
+ typeName,
803
+ id: withLoc( {
804
+ type: 'Identifier',
805
+ name: id.value,
806
+ }, id, id ),
807
+ accessors,
808
+ defaultValue,
809
+ isWeakStorage,
810
+ }, start, previous() );
811
+ }
812
+
813
+ function parseParameterList() {
814
+ expect( 'punctuation', '(' );
815
+ const params = [];
816
+ if ( !match( 'punctuation', ')' ) ) {
817
+ while ( true ) {
818
+ if ( match( 'punctuation', ',' ) ) {
819
+ continue;
820
+ }
821
+ params.push( parseParameter() );
822
+ if ( match( 'punctuation', ')' ) ) {
823
+ break;
824
+ }
825
+ if ( match( 'punctuation', ',' ) && match( 'punctuation', ')' ) ) {
826
+ break;
827
+ }
828
+ }
829
+ }
830
+ return params;
831
+ }
832
+
833
+ function parseParameter() {
834
+ const start = current();
835
+ let rest = false;
836
+ if ( match( 'operator', '...' ) ) {
837
+ if ( current().type === 'identifier' && peekToken().type === 'identifier' ) {
838
+ const containerType = expectIdentifier( 'Expected parameter type after ...' );
839
+ const id = expectIdentifier( 'Expected parameter name' );
840
+ return withLoc( {
841
+ type: 'SpecialParameter',
842
+ special: 'rest_only',
843
+ leadName: null,
844
+ containerType: containerType.value,
845
+ name: id.value,
846
+ }, start, previous() || start );
847
+ }
848
+ rest = true;
849
+ }
850
+ let typeName = null;
851
+ let id = expectIdentifier( 'Expected parameter name' );
852
+ if (
853
+ !rest
854
+ && current().type === 'operator'
855
+ && current().value === '...'
856
+ ) {
857
+ index++;
858
+ const first = expectIdentifier( 'Expected parameter type or name after ...' );
859
+ let containerType;
860
+ let target;
861
+ if (
862
+ current().type === 'identifier'
863
+ && ![ '?', ':=', ',', ')' ].includes( current().value )
864
+ ) {
865
+ containerType = first.value;
866
+ target = expectIdentifier( 'Expected parameter name after special variadic type' );
867
+ }
868
+ else {
869
+ containerType = 'Array';
870
+ target = first;
871
+ }
872
+ return withLoc( {
873
+ type: 'SpecialParameter',
874
+ special: 'lead_rest',
875
+ leadName: id.value,
876
+ containerType,
877
+ name: target.value,
878
+ }, start, previous() || start );
879
+ }
880
+ if (
881
+ !rest
882
+ && current().type === 'identifier'
883
+ && ![ '?', ':=', ',', ')' ].includes( current().value )
884
+ && peekToken().type !== 'identifier'
885
+ ) {
886
+ typeName = id.value;
887
+ id = expectIdentifier( 'Expected parameter name after type' );
888
+ }
889
+ let optional = false;
890
+ let defaultValue = null;
891
+ if ( match( 'operator', '?' ) ) {
892
+ optional = true;
893
+ }
894
+ if ( match( 'operator', ':=' ) ) {
895
+ defaultValue = parseExpression();
896
+ }
897
+ return withLoc( {
898
+ type: 'Parameter',
899
+ name: id.value,
900
+ typeName,
901
+ optional,
902
+ defaultValue,
903
+ rest,
904
+ }, start, previous() || start );
905
+ }
906
+
907
+ function parseOptionalReturnType() {
908
+ if (
909
+ current().type === 'operator'
910
+ && ( current().value === '->' || current().value === '→' )
911
+ ) {
912
+ index++;
913
+ return expectIdentifier( 'Expected return type after ->' ).value;
914
+ }
915
+ return null;
916
+ }
917
+
918
+ function parseReturnStatement() {
919
+ const start = expect( 'keyword', 'return' );
920
+ let argument = null;
921
+ if (
922
+ !( current().type === 'punctuation' && current().value === ';' )
923
+ && !( current().type === 'keyword' && [ 'if', 'unless' ].includes( current().value ) )
924
+ ) {
925
+ argument = parseExpression();
926
+ }
927
+ const stmt = withLoc( {
928
+ type: 'ReturnStatement',
929
+ argument,
930
+ }, start, previous() || start );
931
+ return finishStatement( stmt, 'Expected ; after return statement' );
932
+ }
933
+
934
+ function parseIfStatement() {
935
+ const start = expect( 'keyword', 'if' );
936
+ expect( 'punctuation', '(' );
937
+ let declaration = null;
938
+ let test = null;
939
+ if ( current().type === 'keyword' && [ 'let', 'const' ].includes( current().value ) ) {
940
+ declaration = parseInlineVariableDeclaration();
941
+ test = declaration.id;
942
+ }
943
+ else {
944
+ test = parseExpression();
945
+ }
946
+ expect( 'punctuation', ')', 'Expected ) after if condition' );
947
+ const consequent = parseBlockStatement();
948
+ let alternate = null;
949
+ if ( match( 'keyword', 'else' ) ) {
950
+ if ( current().type === 'keyword' && current().value === 'if' ) {
951
+ alternate = parseIfStatement();
952
+ }
953
+ else {
954
+ alternate = parseBlockStatement();
955
+ }
956
+ }
957
+ return withLoc( {
958
+ type: 'IfStatement',
959
+ declaration,
960
+ test,
961
+ consequent,
962
+ alternate,
963
+ }, start, previous() );
964
+ }
965
+
966
+ function parseForStatement() {
967
+ const start = expect( 'keyword', 'for' );
968
+ expect( 'punctuation', '(' );
969
+ let decl = null;
970
+ let id = null;
971
+ let iterable;
972
+ if ( current().type === 'keyword' && [ 'let', 'const' ].includes( current().value ) ) {
973
+ decl = expect( 'keyword' );
974
+ id = expectIdentifier( 'Expected loop variable' );
975
+ expect( 'keyword', 'in', 'Expected in inside for loop' );
976
+ iterable = parseExpression();
977
+ }
978
+ else if ( current().type === 'identifier' && peekToken().type === 'keyword' && peekToken().value === 'in' ) {
979
+ id = expectIdentifier( 'Expected loop variable' );
980
+ expect( 'keyword', 'in', 'Expected in inside for loop' );
981
+ iterable = parseExpression();
982
+ }
983
+ else {
984
+ decl = { value: 'const' };
985
+ id = {
986
+ value: '^^',
987
+ start: current().start,
988
+ end: current().end,
989
+ line: current().line,
990
+ column: current().column,
991
+ };
992
+ iterable = parseExpression();
993
+ }
994
+ expect( 'punctuation', ')', 'Expected ) after for loop header' );
995
+ const body = parseBlockStatement();
996
+ let elseBlock = null;
997
+ if ( match( 'keyword', 'else' ) ) {
998
+ elseBlock = parseBlockStatement();
999
+ }
1000
+ return withLoc( {
1001
+ type: 'ForInStatement',
1002
+ kind: decl ? decl.value : null,
1003
+ left: {
1004
+ type: 'Identifier',
1005
+ name: id.value,
1006
+ },
1007
+ iterable,
1008
+ body,
1009
+ elseBlock,
1010
+ }, start, previous() );
1011
+ }
1012
+
1013
+ function parseWhileStatement() {
1014
+ const start = expect( 'keyword', 'while' );
1015
+ expect( 'punctuation', '(' );
1016
+ let declaration = null;
1017
+ let test = null;
1018
+ if ( current().type === 'keyword' && [ 'let', 'const' ].includes( current().value ) ) {
1019
+ declaration = parseInlineVariableDeclaration();
1020
+ test = declaration.id;
1021
+ }
1022
+ else {
1023
+ test = parseExpression();
1024
+ }
1025
+ expect( 'punctuation', ')', 'Expected ) after while condition' );
1026
+ const body = parseBlockStatement();
1027
+ return withLoc( {
1028
+ type: 'WhileStatement',
1029
+ declaration,
1030
+ test,
1031
+ body,
1032
+ }, start, previous() );
1033
+ }
1034
+
1035
+ function parseSwitchStatement() {
1036
+ const start = expect( 'keyword', 'switch' );
1037
+ expect( 'punctuation', '(' );
1038
+ const discriminant = parseExpression();
1039
+ let comparator = '==';
1040
+ if ( match( 'operator', ':' ) ) {
1041
+ const token = current();
1042
+ if ( ![ 'identifier', 'keyword', 'operator', 'string' ].includes( token.type ) ) {
1043
+ throw new TranspilerSyntaxError( 'Expected switch comparator after :', token );
1044
+ }
1045
+ index++;
1046
+ comparator = token.value;
1047
+ }
1048
+ expect( 'punctuation', ')', 'Expected ) after switch header' );
1049
+ expect( 'punctuation', '{', 'Expected { to start switch body' );
1050
+ const cases = [];
1051
+ let defaultCase = null;
1052
+ while ( !( current().type === 'punctuation' && current().value === '}' ) ) {
1053
+ if ( atEnd() ) {
1054
+ throw new TranspilerSyntaxError( 'Unterminated switch statement', current() );
1055
+ }
1056
+ if ( current().type === 'keyword' && current().value === 'case' ) {
1057
+ cases.push( parseSwitchCase() );
1058
+ continue;
1059
+ }
1060
+ if ( current().type === 'keyword' && current().value === 'default' ) {
1061
+ if ( defaultCase ) {
1062
+ throw new TranspilerSyntaxError( 'Duplicate default clause in switch statement', current() );
1063
+ }
1064
+ defaultCase = parseSwitchDefaultCase();
1065
+ continue;
1066
+ }
1067
+ throw new TranspilerSyntaxError( 'Expected case or default inside switch statement', current() );
1068
+ }
1069
+ expect( 'punctuation', '}' );
1070
+ return withLoc( {
1071
+ type: 'SwitchStatement',
1072
+ discriminant,
1073
+ comparator,
1074
+ cases,
1075
+ defaultCase,
1076
+ }, start, previous() );
1077
+ }
1078
+
1079
+ function parseSwitchCase() {
1080
+ const start = expect( 'keyword', 'case' );
1081
+ const values = [ parseExpression() ];
1082
+ while ( match( 'punctuation', ',' ) ) {
1083
+ values.push( parseExpression() );
1084
+ }
1085
+ expect( 'operator', ':', 'Expected : after switch case' );
1086
+ const consequent = parseSwitchConsequent( start );
1087
+ return withLoc( {
1088
+ type: 'SwitchCase',
1089
+ values,
1090
+ consequent,
1091
+ }, start, endLocFromNode( consequent ) );
1092
+ }
1093
+
1094
+ function parseSwitchDefaultCase() {
1095
+ const start = expect( 'keyword', 'default' );
1096
+ expect( 'operator', ':', 'Expected : after default' );
1097
+ const consequent = parseSwitchConsequent( start );
1098
+ return withLoc( {
1099
+ type: 'SwitchCase',
1100
+ values: null,
1101
+ consequent,
1102
+ }, start, endLocFromNode( consequent ) );
1103
+ }
1104
+
1105
+ function parseSwitchConsequent( startToken ) {
1106
+ const body = [];
1107
+ while ( true ) {
1108
+ if ( atEnd() ) {
1109
+ throw new TranspilerSyntaxError( 'Unterminated switch case body', current() );
1110
+ }
1111
+ if ( current().type === 'punctuation' && current().value === '}' ) {
1112
+ break;
1113
+ }
1114
+ if ( current().type === 'keyword' && [ 'case', 'default' ].includes( current().value ) ) {
1115
+ break;
1116
+ }
1117
+ body.push( parseStatement() );
1118
+ }
1119
+ return withLoc( {
1120
+ type: 'BlockStatement',
1121
+ body,
1122
+ }, startToken, previous() || startToken );
1123
+ }
1124
+
1125
+ function parseInlineVariableDeclaration() {
1126
+ const start = expect( 'keyword' );
1127
+ const declaration = parseVariableDeclarationParts( start );
1128
+ return withLoc( {
1129
+ type: 'VariableDeclaration',
1130
+ ...declaration,
1131
+ }, start, previous() );
1132
+ }
1133
+
1134
+ function parseLoopControlStatement( type ) {
1135
+ const start = expect( 'keyword' );
1136
+ const stmt = withLoc( { type }, start, previous() || start );
1137
+ return finishStatement( stmt, `Expected ; after ${start.value}` );
1138
+ }
1139
+
1140
+ function parseThrowStatement() {
1141
+ const start = expect( 'keyword', 'throw' );
1142
+ const argument = parseExpression();
1143
+ const stmt = withLoc( {
1144
+ type: 'ThrowStatement',
1145
+ argument,
1146
+ }, start, previous() );
1147
+ return finishStatement( stmt, 'Expected ; after throw statement' );
1148
+ }
1149
+
1150
+ function parseDieStatement() {
1151
+ const start = expect( 'keyword', 'die' );
1152
+ const argument = parseExpression();
1153
+ const stmt = withLoc( {
1154
+ type: 'DieStatement',
1155
+ argument,
1156
+ }, start, previous() );
1157
+ return finishStatement( stmt, 'Expected ; after die statement' );
1158
+ }
1159
+
1160
+ function parseWarnStatement() {
1161
+ const start = expect( 'keyword', 'warn' );
1162
+ const argument = parseExpression();
1163
+ const stmt = withLoc( {
1164
+ type: 'WarnStatement',
1165
+ argument,
1166
+ }, start, previous() );
1167
+ return finishStatement( stmt, 'Expected ; after warn statement' );
1168
+ }
1169
+
1170
+ function parseAssertStatement() {
1171
+ const start = expect( 'keyword', 'assert' );
1172
+ const argument = parseExpression();
1173
+ const stmt = withLoc( {
1174
+ type: 'AssertStatement',
1175
+ argument,
1176
+ }, start, previous() );
1177
+ return finishStatement( stmt, 'Expected ; after assert statement' );
1178
+ }
1179
+
1180
+ function parseDebugStatement() {
1181
+ const start = expect( 'keyword', 'debug' );
1182
+ const argumentsList = [];
1183
+ if ( !( current().type === 'punctuation' && current().value === ';' ) ) {
1184
+ argumentsList.push( parseExpression() );
1185
+ while ( match( 'punctuation', ',' ) ) {
1186
+ argumentsList.push( parseExpression() );
1187
+ }
1188
+ }
1189
+ const stmt = withLoc( {
1190
+ type: 'DebugStatement',
1191
+ arguments: argumentsList,
1192
+ }, start, previous() || start );
1193
+ return finishStatement( stmt, 'Expected ; after debug statement' );
1194
+ }
1195
+
1196
+ function parseSayStatement() {
1197
+ const start = expect( 'keyword', 'say' );
1198
+ const args = [];
1199
+ if ( !( current().type === 'punctuation' && current().value === ';' ) ) {
1200
+ args.push( parseExpression() );
1201
+ while ( match( 'punctuation', ',' ) ) {
1202
+ args.push( parseExpression() );
1203
+ }
1204
+ }
1205
+ const stmt = withLoc( {
1206
+ type: 'ExpressionStatement',
1207
+ expression: withLoc( {
1208
+ type: 'CallExpression',
1209
+ callee: withLoc( {
1210
+ type: 'Identifier',
1211
+ name: 'say',
1212
+ }, start, start ),
1213
+ arguments: args,
1214
+ }, start, previous() || start ),
1215
+ }, start, previous() || start );
1216
+ return finishStatement( stmt, 'Expected ; after say statement' );
1217
+ }
1218
+
1219
+ function parseTryStatement() {
1220
+ const parsed = parseTryLike();
1221
+ return withLoc( {
1222
+ type: 'TryStatement',
1223
+ block: parsed.block,
1224
+ handlers: parsed.handlers,
1225
+ }, parsed.start, previous() );
1226
+ }
1227
+
1228
+ function parseTryLike() {
1229
+ const start = expect( 'keyword', 'try' );
1230
+ const block = parseBlockStatement();
1231
+ const handlers = [];
1232
+ while ( current().type === 'keyword' && current().value === 'catch' ) {
1233
+ const catchTok = expect( 'keyword', 'catch' );
1234
+ let typeName = 'Exception';
1235
+ let paramName = 'e';
1236
+ if ( match( 'punctuation', '(' ) ) {
1237
+ if ( current().type === 'identifier' ) {
1238
+ const first = current();
1239
+ index++;
1240
+ if ( current().type === 'identifier' ) {
1241
+ typeName = first.value;
1242
+ paramName = current().value;
1243
+ index++;
1244
+ }
1245
+ else {
1246
+ paramName = first.value;
1247
+ }
1248
+ }
1249
+ expect( 'punctuation', ')', 'Expected ) after catch clause' );
1250
+ }
1251
+ const handlerBody = parseBlockStatement();
1252
+ handlers.push( withLoc( {
1253
+ type: 'CatchClause',
1254
+ typeName,
1255
+ paramName,
1256
+ body: handlerBody,
1257
+ }, catchTok, previous() ) );
1258
+ }
1259
+ return {
1260
+ start,
1261
+ block,
1262
+ handlers,
1263
+ };
1264
+ }
1265
+
1266
+ function parseBlockStatement() {
1267
+ const start = expect( 'punctuation', '{' );
1268
+ const body = [];
1269
+ while ( !( current().type === 'punctuation' && current().value === '}' ) ) {
1270
+ if ( atEnd() ) {
1271
+ throw new TranspilerSyntaxError( 'Unterminated block statement', current() );
1272
+ }
1273
+ body.push( parseStatement() );
1274
+ }
1275
+ expect( 'punctuation', '}' );
1276
+ return withLoc( {
1277
+ type: 'BlockStatement',
1278
+ body,
1279
+ }, start, previous() );
1280
+ }
1281
+
1282
+ function parseBlockInAsyncContext( isAsync ) {
1283
+ const previousDepth = asyncContextDepth;
1284
+ asyncContextDepth = isAsync ? previousDepth + 1 : 0;
1285
+ try {
1286
+ return parseBlockStatement();
1287
+ }
1288
+ finally {
1289
+ asyncContextDepth = previousDepth;
1290
+ }
1291
+ }
1292
+
1293
+ function parseExpressionStatement() {
1294
+ const start = current();
1295
+ const expression = parseExpression();
1296
+ const stmt = withLoc( {
1297
+ type: 'ExpressionStatement',
1298
+ expression,
1299
+ }, start, previous() );
1300
+ return finishStatement( stmt, 'Expected ; after expression' );
1301
+ }
1302
+
1303
+ function finishStatement(stmt, semicolonMessage) {
1304
+ if ( current().type === 'keyword' && [ 'if', 'unless' ].includes( current().value ) ) {
1305
+ const keyword = current();
1306
+ index++;
1307
+ let test = parseExpression();
1308
+ if ( keyword.value === 'unless' ) {
1309
+ test = withLoc( {
1310
+ type: 'UnaryExpression',
1311
+ operator: 'not',
1312
+ argument: test,
1313
+ prefix: true,
1314
+ }, keyword, endLocFromNode( test ) );
1315
+ }
1316
+ expect( 'punctuation', ';', semicolonMessage );
1317
+ const consequent = withLoc( {
1318
+ type: 'BlockStatement',
1319
+ body: [ stmt ],
1320
+ }, startLocFromNode( stmt ), endLocFromNode( stmt ) );
1321
+ return withLoc( {
1322
+ type: 'IfStatement',
1323
+ declaration: null,
1324
+ test,
1325
+ consequent,
1326
+ alternate: null,
1327
+ }, startLocFromNode( stmt ), endLocFromNode( consequent ) );
1328
+ }
1329
+ if ( current().type === 'keyword' && current().value === 'for' ) {
1330
+ const keyword = current();
1331
+ index++;
1332
+ const iterable = parseExpression();
1333
+ expect( 'punctuation', ';', semicolonMessage );
1334
+ const body = withLoc( {
1335
+ type: 'BlockStatement',
1336
+ body: [ stmt ],
1337
+ }, startLocFromNode( stmt ), endLocFromNode( stmt ) );
1338
+ return withLoc( {
1339
+ type: 'ForInStatement',
1340
+ kind: 'const',
1341
+ left: {
1342
+ type: 'Identifier',
1343
+ name: '^^',
1344
+ },
1345
+ iterable,
1346
+ body,
1347
+ elseBlock: null,
1348
+ }, startLocFromNode( stmt ), endLocFromNode( iterable ) || keyword );
1349
+ }
1350
+ consumeStatementTerminator( semicolonMessage );
1351
+ return stmt;
1352
+ }
1353
+
1354
+ function parseExpression() {
1355
+ return parseAssignment();
1356
+ }
1357
+
1358
+ function parseConditional() {
1359
+ return parseConditionalTail( parseLogicalOr() );
1360
+ }
1361
+
1362
+ function parseConditionalTail(expr) {
1363
+ if ( match( 'operator', '?:' ) ) {
1364
+ const alternate = parseConditional();
1365
+ return withLoc( {
1366
+ type: 'ShortTernaryExpression',
1367
+ test: expr,
1368
+ alternate,
1369
+ }, startLocFromNode( expr ), endLocFromNode( alternate ) );
1370
+ }
1371
+ if ( match( 'operator', '?' ) ) {
1372
+ const consequent = parseExpression();
1373
+ expect( 'operator', ':', 'Expected : in ternary expression' );
1374
+ const alternate = parseConditional();
1375
+ return withLoc( {
1376
+ type: 'ConditionalExpression',
1377
+ test: expr,
1378
+ consequent,
1379
+ alternate,
1380
+ }, startLocFromNode( expr ), endLocFromNode( alternate ) );
1381
+ }
1382
+ return expr;
1383
+ }
1384
+
1385
+ function parseAssignment() {
1386
+ const left = parseChain();
1387
+ if (
1388
+ current().type === 'operator'
1389
+ && [ ':=', '+=', '-=', '*=', '/=', '%=', '**=', '_=', '?:=', '×=', '÷=' ].includes( current().value )
1390
+ ) {
1391
+ const op = current();
1392
+ index++;
1393
+ const right = parseAssignment();
1394
+ const isWeakWrite = parseOptionalWeakModifier( 'assignment' );
1395
+ if ( isWeakWrite && op.value !== ':=' ) {
1396
+ throw new TranspilerSyntaxError(
1397
+ "but weak is only valid on ':=' assignments",
1398
+ op,
1399
+ );
1400
+ }
1401
+ if (
1402
+ isWeakWrite
1403
+ && left
1404
+ && left.type === 'BinaryExpression'
1405
+ && left.operator === '@?'
1406
+ ) {
1407
+ throw new TranspilerSyntaxError(
1408
+ 'but weak is not valid on @? path assignments',
1409
+ op,
1410
+ );
1411
+ }
1412
+ return withLoc( {
1413
+ type: 'AssignmentExpression',
1414
+ operator: op.value,
1415
+ left,
1416
+ right,
1417
+ isWeakWrite,
1418
+ }, startLocFromNode( left ), endLocFromNode( right ) );
1419
+ }
1420
+ if ( current().type === 'operator' && current().value === '~=' ) {
1421
+ const op = current();
1422
+ index++;
1423
+ const pattern = parseChain();
1424
+ if (
1425
+ current().type !== 'operator'
1426
+ || !( current().value === '->' || current().value === '→' )
1427
+ ) {
1428
+ throw new TranspilerSyntaxError( 'Expected -> after regex replacement pattern', current() );
1429
+ }
1430
+ index++;
1431
+ const replacement = parseAssignment();
1432
+ return withLoc( {
1433
+ type: 'RegexReplaceExpression',
1434
+ operator: op.value,
1435
+ left,
1436
+ pattern,
1437
+ replacement,
1438
+ }, startLocFromNode( left ), endLocFromNode( replacement ) );
1439
+ }
1440
+ return left;
1441
+ }
1442
+
1443
+ function chainDirection(token) {
1444
+ if ( token.type !== 'operator' ) {
1445
+ return null;
1446
+ }
1447
+ if ( token.value === '▷' || token.value === '|>' ) {
1448
+ return 'right';
1449
+ }
1450
+ if ( token.value === '◁' || token.value === '<|' ) {
1451
+ return 'left';
1452
+ }
1453
+ return null;
1454
+ }
1455
+
1456
+ function parseChain() {
1457
+ let expr = parseConditional();
1458
+ const firstDirection = chainDirection( current() );
1459
+ if ( !firstDirection ) {
1460
+ return expr;
1461
+ }
1462
+
1463
+ let op = current();
1464
+ index++;
1465
+ const right = firstDirection === 'left'
1466
+ ? parseLeftwardChainRhs()
1467
+ : parseConditional();
1468
+ expr = withLoc( {
1469
+ type: 'BinaryExpression',
1470
+ operator: op.value,
1471
+ left: expr,
1472
+ right,
1473
+ }, startLocFromNode( expr ), endLocFromNode( right ) );
1474
+
1475
+ while ( firstDirection === 'right' ) {
1476
+ const nextDirection = chainDirection( current() );
1477
+ if ( !nextDirection ) {
1478
+ break;
1479
+ }
1480
+ if ( nextDirection !== firstDirection ) {
1481
+ throw new TranspilerSyntaxError( 'Mixed chain directions require parentheses', current() );
1482
+ }
1483
+ op = current();
1484
+ index++;
1485
+ const nextRight = parseConditional();
1486
+ expr = withLoc( {
1487
+ type: 'BinaryExpression',
1488
+ operator: op.value,
1489
+ left: expr,
1490
+ right: nextRight,
1491
+ }, startLocFromNode( expr ), endLocFromNode( nextRight ) );
1492
+ }
1493
+
1494
+ if ( firstDirection === 'left' && chainDirection( current() ) ) {
1495
+ throw new TranspilerSyntaxError( 'Mixed chain directions require parentheses', current() );
1496
+ }
1497
+
1498
+ return expr;
1499
+ }
1500
+
1501
+ function parseLeftwardChainRhs() {
1502
+ const left = parseConditional();
1503
+ const direction = chainDirection( current() );
1504
+ if ( !direction ) {
1505
+ return left;
1506
+ }
1507
+ if ( direction !== 'left' ) {
1508
+ throw new TranspilerSyntaxError( 'Mixed chain directions require parentheses', current() );
1509
+ }
1510
+ const op = current();
1511
+ index++;
1512
+ const right = parseLeftwardChainRhs();
1513
+ return withLoc( {
1514
+ type: 'BinaryExpression',
1515
+ operator: op.value,
1516
+ left,
1517
+ right,
1518
+ }, startLocFromNode( left ), endLocFromNode( right ) );
1519
+ }
1520
+
1521
+ function parseLogicalOr() {
1522
+ return parseLeftAssociative(
1523
+ parseLogicalAnd,
1524
+ (token) => token.type === 'keyword' && [ 'or', 'xor', 'nand' ].includes( token.value )
1525
+ || ( token.type === 'operator' && [ '⋁', '⊻', '⊼' ].includes( token.value ) )
1526
+ );
1527
+ }
1528
+
1529
+ function parseLogicalAnd() {
1530
+ return parseLeftAssociative(
1531
+ parseEquality,
1532
+ (token) => token.type === 'keyword' && token.value === 'and'
1533
+ || ( token.type === 'operator' && token.value === '⋀' )
1534
+ );
1535
+ }
1536
+
1537
+ function parseEquality() {
1538
+ return parseLeftAssociative(
1539
+ parseBitwise,
1540
+ (token) => token.type === 'operator' && [ '==', '!=', '=' ].includes( token.value )
1541
+ || ( token.type === 'operator' && [ '≡', '≢', '≠' ].includes( token.value ) )
1542
+ || ( token.type === 'keyword' && [ 'eq', 'ne', 'default' ].includes( token.value ) )
1543
+ );
1544
+ }
1545
+
1546
+ function parseBitwise() {
1547
+ return parseLeftAssociative(
1548
+ parseComparison,
1549
+ (token) => token.type === 'operator' && [ '&', '|', '^' ].includes( token.value )
1550
+ );
1551
+ }
1552
+
1553
+ function parseComparison() {
1554
+ return parseLeftAssociative(
1555
+ parseRange,
1556
+ (token) => (
1557
+ token.type === 'operator'
1558
+ && [ '<', '<=', '>', '>=', '≤', '≥', '~', '<=>', '@', '@@', '@?', '\\', '∈', '∉', '⋃', '⋂', '∖', '⊂', '⊃', '⊂⊃', '≶', '≷' ].includes( token.value )
1559
+ ) || (
1560
+ token.type === 'keyword'
1561
+ && [ 'gt', 'ge', 'lt', 'le', 'cmp', 'eqi', 'nei', 'gti', 'gei', 'lti', 'lei', 'cmpi', 'in', 'not_in', 'union', 'intersection', 'difference', 'subsetof', 'supersetof', 'equivalentof', 'does', 'can' ].includes( token.value )
1562
+ ) || (
1563
+ token.type === 'identifier' && token.value === 'instanceof'
1564
+ )
1565
+ );
1566
+ }
1567
+
1568
+ function parseRange() {
1569
+ return parseLeftAssociative(
1570
+ parseTerm,
1571
+ (token) => token.type === 'operator' && token.value === '...'
1572
+ );
1573
+ }
1574
+
1575
+ function parseTerm() {
1576
+ return parseLeftAssociative(
1577
+ parseFactor,
1578
+ (token) => token.type === 'operator' && [ '+', '-', '_' ].includes( token.value )
1579
+ );
1580
+ }
1581
+
1582
+ function parseFactor() {
1583
+ return parseLeftAssociative(
1584
+ parseUnary,
1585
+ (token) => (
1586
+ token.type === 'operator' && [ '*', '/', '%', '**' ].includes( token.value )
1587
+ || ( token.type === 'operator' && [ '×', '÷' ].includes( token.value ) )
1588
+ ) || (
1589
+ token.type === 'keyword' && token.value === 'mod'
1590
+ )
1591
+ );
1592
+ }
1593
+
1594
+ function parseLeftAssociative(nextFn, predicate) {
1595
+ let expr = nextFn();
1596
+ while ( predicate( current() ) ) {
1597
+ const op = current();
1598
+ index++;
1599
+ const right = nextFn();
1600
+ expr = withLoc( {
1601
+ type: 'BinaryExpression',
1602
+ operator: op.value,
1603
+ left: expr,
1604
+ right,
1605
+ }, startLocFromNode( expr ), endLocFromNode( right ) );
1606
+ }
1607
+ return expr;
1608
+ }
1609
+
1610
+ function parseUnary() {
1611
+ if (
1612
+ ( current().type === 'operator' && [ '-', '+', '++', '--', '!', '~', '\\', '√', '¬' ].includes( current().value ) )
1613
+ || ( current().type === 'keyword' && [ 'not', 'typeof', 'abs', 'sqrt', 'floor', 'ceil', 'round', 'int', 'length', 'uc', 'lc' ].includes( current().value ) )
1614
+ ) {
1615
+ const op = current();
1616
+ index++;
1617
+ const argument = op.value === '\\' ? parseReferenceTarget() : parseUnary();
1618
+ let expr = withLoc( {
1619
+ type: op.value === '\\' ? 'RefExpression' : 'UnaryExpression',
1620
+ operator: op.value,
1621
+ argument,
1622
+ prefix: true,
1623
+ }, startLocFromToken( op ), endLocFromNode( argument ) );
1624
+ if ( op.value === '\\' ) {
1625
+ expr = parsePostfixSuffixes( expr );
1626
+ }
1627
+ return expr;
1628
+ }
1629
+ return parsePostfix();
1630
+ }
1631
+
1632
+ function parseReferenceTarget() {
1633
+ let expr = parsePrimary();
1634
+ while ( true ) {
1635
+ const nextExpr = parseMemberAccess( expr );
1636
+ if ( nextExpr === expr ) {
1637
+ break;
1638
+ }
1639
+ expr = nextExpr;
1640
+ }
1641
+ return expr;
1642
+ }
1643
+
1644
+ function parsePostfix() {
1645
+ return parsePostfixSuffixes( parsePrimary() );
1646
+ }
1647
+
1648
+ function parsePostfixSuffixes(expr) {
1649
+ while ( true ) {
1650
+ const nextExpr = parseMemberAccess( expr );
1651
+ if ( nextExpr !== expr ) {
1652
+ expr = nextExpr;
1653
+ continue;
1654
+ }
1655
+ if ( current().type === 'punctuation' && current().value === '(' ) {
1656
+ const args = parseCallArgumentsOnly();
1657
+ expr = withLoc( {
1658
+ type: 'CallExpression',
1659
+ callee: expr,
1660
+ arguments: args,
1661
+ }, startLocFromNode( expr ), endLocFromToken( previous() ) );
1662
+ continue;
1663
+ }
1664
+ if ( current().type === 'operator' && [ '++', '--' ].includes( current().value ) ) {
1665
+ const op = current();
1666
+ index++;
1667
+ expr = withLoc( {
1668
+ type: 'UpdateExpression',
1669
+ operator: op.value,
1670
+ argument: expr,
1671
+ prefix: false,
1672
+ }, startLocFromNode( expr ), endLocFromToken( op ) );
1673
+ continue;
1674
+ }
1675
+ break;
1676
+ }
1677
+ return expr;
1678
+ }
1679
+
1680
+ function parseMemberAccess(expr) {
1681
+ if ( match( 'punctuation', '.' ) ) {
1682
+ if ( match( 'punctuation', '(' ) ) {
1683
+ const property = parseExpression();
1684
+ expect( 'punctuation', ')', 'Expected ) after dynamic member name' );
1685
+ return withLoc( {
1686
+ type: 'MemberExpression',
1687
+ object: expr,
1688
+ property,
1689
+ computed: true,
1690
+ }, startLocFromNode( expr ), endLocFromToken( previous() ) );
1691
+ }
1692
+ const property = expectIdentifier( 'Expected property name after .' );
1693
+ return withLoc( {
1694
+ type: 'MemberExpression',
1695
+ object: expr,
1696
+ property: {
1697
+ type: 'Identifier',
1698
+ name: property.value,
1699
+ },
1700
+ computed: false,
1701
+ }, startLocFromNode( expr ), endLocFromToken( property ) );
1702
+ }
1703
+ if ( match( 'punctuation', '[' ) ) {
1704
+ const closeBracket = current().type === 'punctuation' && current().value === ']';
1705
+ const startsWithColon = current().type === 'operator' && current().value === ':';
1706
+ let first = null;
1707
+ if ( !closeBracket && !startsWithColon ) {
1708
+ first = parseExpression();
1709
+ }
1710
+ if ( match( 'operator', ':' ) ) {
1711
+ let second = null;
1712
+ if ( !( current().type === 'punctuation' && current().value === ']' ) ) {
1713
+ second = parseExpression();
1714
+ }
1715
+ expect( 'punctuation', ']' );
1716
+ return withLoc( {
1717
+ type: 'SliceExpression',
1718
+ object: expr,
1719
+ start: first,
1720
+ length: second,
1721
+ }, startLocFromNode( expr ), endLocFromToken( previous() ) );
1722
+ }
1723
+ if ( first == null ) {
1724
+ throw new TranspilerSyntaxError( 'Expected index or slice expression', current() );
1725
+ }
1726
+ expect( 'punctuation', ']' );
1727
+ return withLoc( {
1728
+ type: 'MemberExpression',
1729
+ object: expr,
1730
+ property: first,
1731
+ computed: true,
1732
+ }, startLocFromNode( expr ), endLocFromToken( previous() ) );
1733
+ }
1734
+ if ( match( 'punctuation', '{' ) ) {
1735
+ let property;
1736
+ if (
1737
+ ( current().type === 'identifier' || current().type === 'keyword' )
1738
+ && peekToken().type === 'punctuation'
1739
+ && peekToken().value === '}'
1740
+ ) {
1741
+ const key = current();
1742
+ index++;
1743
+ property = withLoc(
1744
+ key.type === 'identifier'
1745
+ ? {
1746
+ type: 'BraceIdentifier',
1747
+ name: key.value,
1748
+ }
1749
+ : {
1750
+ type: 'StringLiteral',
1751
+ value: key.value,
1752
+ },
1753
+ key,
1754
+ key
1755
+ );
1756
+ }
1757
+ else {
1758
+ property = parseExpression();
1759
+ }
1760
+ expect( 'punctuation', '}' );
1761
+ return withLoc( {
1762
+ type: 'MemberExpression',
1763
+ object: expr,
1764
+ property,
1765
+ computed: true,
1766
+ }, startLocFromNode( expr ), endLocFromToken( previous() ) );
1767
+ }
1768
+ return expr;
1769
+ }
1770
+
1771
+ function parseCallArgumentsOnly() {
1772
+ expect( 'punctuation', '(' );
1773
+ const args = [];
1774
+ let closed = false;
1775
+ if ( !match( 'punctuation', ')' ) ) {
1776
+ while ( true ) {
1777
+ if ( match( 'punctuation', ',' ) ) {
1778
+ if ( current().type === 'punctuation' && current().value === ')' ) {
1779
+ break;
1780
+ }
1781
+ continue;
1782
+ }
1783
+ if ( current().type === 'punctuation' && current().value === ')' ) {
1784
+ break;
1785
+ }
1786
+ args.push( parseCallArgument() );
1787
+ if ( match( 'punctuation', ')' ) ) {
1788
+ closed = true;
1789
+ break;
1790
+ }
1791
+ match( 'punctuation', ',' );
1792
+ }
1793
+ if ( !closed && current().type === 'punctuation' && current().value === ')' ) {
1794
+ expect( 'punctuation', ')', 'Expected ) after call arguments' );
1795
+ }
1796
+ }
1797
+ return args;
1798
+ }
1799
+
1800
+ function parseCallArgument() {
1801
+ const start = current();
1802
+ if ( match( 'operator', '...' ) ) {
1803
+ const argument = parseExpression();
1804
+ return withLoc( {
1805
+ type: 'SpreadArgument',
1806
+ argument,
1807
+ }, start, endLocFromNode( argument ) );
1808
+ }
1809
+ const expr = parseExpression();
1810
+ if ( current().type === 'keyword' && current().value === 'but' ) {
1811
+ throw new TranspilerSyntaxError(
1812
+ 'but weak is not valid as a function argument marker',
1813
+ current(),
1814
+ );
1815
+ }
1816
+ if ( current().type === 'operator' && current().value === ':' ) {
1817
+ const normalized = normalizeNamedArgumentKey( expr );
1818
+ expect( 'operator', ':', 'Expected : in named argument' );
1819
+ const value = parseExpression();
1820
+ return withLoc( {
1821
+ type: 'NamedArgument',
1822
+ key: normalized.key,
1823
+ keyExpr: normalized.keyExpr,
1824
+ value,
1825
+ }, startLocFromNode( expr ), previous() );
1826
+ }
1827
+ return expr;
1828
+ }
1829
+
1830
+ function normalizeNamedArgumentKey(expr) {
1831
+ if ( expr.type === 'Identifier' ) {
1832
+ return {
1833
+ key: expr.name,
1834
+ keyExpr: withLoc( {
1835
+ type: 'StringLiteral',
1836
+ value: expr.name,
1837
+ }, startLocFromNode( expr ), endLocFromNode( expr ) ),
1838
+ };
1839
+ }
1840
+ if ( expr.type === 'StringLiteral' ) {
1841
+ return {
1842
+ key: expr.value,
1843
+ keyExpr: expr,
1844
+ };
1845
+ }
1846
+ if ( expr.type === 'GroupedExpression' ) {
1847
+ return normalizeNamedArgumentKey( expr.expression );
1848
+ }
1849
+ if ( expr.type === 'MemberExpression' ) {
1850
+ if ( expr.computed && expr.property.type === 'StringLiteral' ) {
1851
+ return {
1852
+ key: expr.property.value,
1853
+ keyExpr: withLoc( {
1854
+ type: 'StringLiteral',
1855
+ value: expr.property.value,
1856
+ }, startLocFromNode( expr.property ), endLocFromNode( expr.property ) ),
1857
+ };
1858
+ }
1859
+ if ( !expr.computed && expr.property.type === 'Identifier' ) {
1860
+ return {
1861
+ key: expr.property.name,
1862
+ keyExpr: withLoc( {
1863
+ type: 'StringLiteral',
1864
+ value: expr.property.name,
1865
+ }, startLocFromNode( expr.property ), endLocFromNode( expr.property ) ),
1866
+ };
1867
+ }
1868
+ }
1869
+ return {
1870
+ key: null,
1871
+ keyExpr: expr,
1872
+ };
1873
+ }
1874
+
1875
+ function parsePrimary() {
1876
+ const token = current();
1877
+ if ( match( 'number' ) ) {
1878
+ return withLoc( {
1879
+ type: 'NumericLiteral',
1880
+ value: Number( token.value ),
1881
+ }, token, token );
1882
+ }
1883
+ if ( match( 'string' ) ) {
1884
+ return withLoc( {
1885
+ type: 'StringLiteral',
1886
+ value: token.value,
1887
+ }, token, token );
1888
+ }
1889
+ if ( match( 'binary_string' ) ) {
1890
+ return withLoc( {
1891
+ type: 'BinaryStringLiteral',
1892
+ value: token.value,
1893
+ }, token, token );
1894
+ }
1895
+ if ( match( 'template' ) ) {
1896
+ const parts = parseInterpolationParts( token.value, token );
1897
+ return withLoc( {
1898
+ type: 'TemplateLiteral',
1899
+ parts,
1900
+ }, token, token );
1901
+ }
1902
+ if ( match( 'regexp' ) ) {
1903
+ return withLoc( {
1904
+ type: 'RegExpLiteral',
1905
+ pattern: token.value.pattern,
1906
+ parts: parseInterpolationParts( token.value.parts || [], token ),
1907
+ flags: token.value.flags,
1908
+ }, token, token );
1909
+ }
1910
+ if ( token.type === 'keyword' && [ 'true', 'false', 'null' ].includes( token.value ) ) {
1911
+ index++;
1912
+ return withLoc( {
1913
+ type: 'Literal',
1914
+ value: token.value === 'null' ? null : token.value === 'true',
1915
+ }, token, token );
1916
+ }
1917
+ if ( token.type === 'keyword' && token.value === 'super' ) {
1918
+ index++;
1919
+ return withLoc( {
1920
+ type: 'Super',
1921
+ }, token, token );
1922
+ }
1923
+ if ( match( 'identifier' ) ) {
1924
+ return withLoc( {
1925
+ type: 'Identifier',
1926
+ name: token.value,
1927
+ }, token, token );
1928
+ }
1929
+ if ( token.type === 'keyword' && token.value === 'say' ) {
1930
+ index++;
1931
+ return withLoc( {
1932
+ type: 'Identifier',
1933
+ name: token.value,
1934
+ }, token, token );
1935
+ }
1936
+ if ( token.type === 'keyword' && token.value === 'function' ) {
1937
+ index++;
1938
+ const params = parseParameterList();
1939
+ const body = parseBlockInAsyncContext( false );
1940
+ return withLoc( {
1941
+ type: 'FunctionExpression',
1942
+ params,
1943
+ isAsync: false,
1944
+ body,
1945
+ }, token, previous() );
1946
+ }
1947
+ if ( token.type === 'keyword' && token.value === 'async' ) {
1948
+ index++;
1949
+ if ( current().type === 'keyword' && current().value === 'function' ) {
1950
+ const functionToken = current();
1951
+ index++;
1952
+ const params = parseParameterList();
1953
+ const body = parseBlockInAsyncContext( true );
1954
+ return withLoc( {
1955
+ type: 'FunctionExpression',
1956
+ params,
1957
+ isAsync: true,
1958
+ body,
1959
+ }, token, previous() || functionToken );
1960
+ }
1961
+ if ( current().type === 'keyword' && current().value === 'fn' ) {
1962
+ return parseFnExpression( token, true );
1963
+ }
1964
+ throw new TranspilerSyntaxError( 'Expected function or fn after async', current() );
1965
+ }
1966
+ if ( token.type === 'keyword' && token.value === 'fn' ) {
1967
+ return parseFnExpression( token, false );
1968
+ }
1969
+ if ( token.type === 'operator' && ( token.value === '->' || token.value === '→' ) ) {
1970
+ return parseArrowLambdaExpression( token );
1971
+ }
1972
+ if ( token.type === 'keyword' && token.value === 'await' ) {
1973
+ if ( asyncContextDepth <= 0 ) {
1974
+ throw new TranspilerSyntaxError( 'await may only be used inside async code', token );
1975
+ }
1976
+ index++;
1977
+ const block = parseBlockStatement();
1978
+ return withLoc( {
1979
+ type: 'AwaitExpression',
1980
+ block,
1981
+ }, token, previous() );
1982
+ }
1983
+ if ( token.type === 'keyword' && token.value === 'spawn' ) {
1984
+ if ( asyncContextDepth <= 0 ) {
1985
+ throw new TranspilerSyntaxError( 'spawn may only be used inside async code', token );
1986
+ }
1987
+ index++;
1988
+ const block = parseBlockStatement();
1989
+ return withLoc( {
1990
+ type: 'SpawnExpression',
1991
+ block,
1992
+ }, token, previous() );
1993
+ }
1994
+ if ( token.type === 'keyword' && token.value === 'try' ) {
1995
+ const parsed = parseTryLike();
1996
+ return withLoc( {
1997
+ type: 'TryExpression',
1998
+ block: parsed.block,
1999
+ handlers: parsed.handlers,
2000
+ }, parsed.start, previous() );
2001
+ }
2002
+ if ( token.type === 'keyword' && token.value === 'do' ) {
2003
+ index++;
2004
+ const block = parseBlockStatement();
2005
+ return withLoc( {
2006
+ type: 'DoExpression',
2007
+ block,
2008
+ }, token, previous() );
2009
+ }
2010
+ if ( token.type === 'keyword' && token.value === 'new' ) {
2011
+ index++;
2012
+ let callee = parsePrimary();
2013
+ while ( true ) {
2014
+ const nextExpr = parseMemberAccess( callee );
2015
+ if ( nextExpr === callee ) {
2016
+ break;
2017
+ }
2018
+ callee = nextExpr;
2019
+ }
2020
+ const traits = parseTraitCompositionList();
2021
+ if ( current().type === 'punctuation' && current().value === '(' ) {
2022
+ const args = parseCallArgumentsOnly();
2023
+ callee = withLoc( {
2024
+ type: 'CallExpression',
2025
+ callee,
2026
+ arguments: args,
2027
+ }, startLocFromNode( callee ), endLocFromToken( previous() ) );
2028
+ }
2029
+ return withLoc( {
2030
+ type: 'NewExpression',
2031
+ callee,
2032
+ traits,
2033
+ }, token, previous() );
2034
+ }
2035
+ if (
2036
+ token.type === 'keyword'
2037
+ && [ 'let', 'const' ].includes( token.value )
2038
+ && peekToken().type === 'punctuation'
2039
+ && peekToken().value === '{'
2040
+ ) {
2041
+ throw new TranspilerSyntaxError(
2042
+ 'Expected name after let or const in expression',
2043
+ peekToken()
2044
+ );
2045
+ }
2046
+ if ( token.type === 'keyword' && [ 'let', 'const' ].includes( token.value ) ) {
2047
+ return parseLetExpression();
2048
+ }
2049
+ if ( token.type === 'keyword' ) {
2050
+ index++;
2051
+ return withLoc( {
2052
+ type: 'Identifier',
2053
+ name: token.value,
2054
+ }, token, token );
2055
+ }
2056
+ if ( match( 'operator', '⌊' ) ) {
2057
+ return parseCircumfixUnary( token, 'floor', '⌋', 'Expected closing floor operator' );
2058
+ }
2059
+ if ( match( 'operator', '⌈' ) ) {
2060
+ return parseCircumfixUnary( token, 'ceil', '⌉', 'Expected closing ceil operator' );
2061
+ }
2062
+ if ( match( 'punctuation', '(' ) ) {
2063
+ const expr = parseExpression();
2064
+ expect( 'punctuation', ')', 'Expected ) after grouped expression' );
2065
+ return withLoc( {
2066
+ type: 'GroupedExpression',
2067
+ expression: expr,
2068
+ }, token, previous() );
2069
+ }
2070
+ if ( match( 'punctuation', '[' ) ) {
2071
+ const elements = [];
2072
+ if ( !match( 'punctuation', ']' ) ) {
2073
+ while ( true ) {
2074
+ if ( match( 'punctuation', ',' ) ) {
2075
+ continue;
2076
+ }
2077
+ if ( match( 'punctuation', ']' ) ) {
2078
+ break;
2079
+ }
2080
+ elements.push( parseExpression() );
2081
+ if ( match( 'punctuation', ']' ) ) {
2082
+ break;
2083
+ }
2084
+ match( 'punctuation', ',' );
2085
+ }
2086
+ }
2087
+ return withLoc( {
2088
+ type: 'ArrayExpression',
2089
+ elements,
2090
+ }, token, previous() );
2091
+ }
2092
+ if (
2093
+ match( 'operator', '{{' )
2094
+ ) {
2095
+ const opener = previous();
2096
+ const entries = [];
2097
+ if ( !matchPairListClose() ) {
2098
+ while ( true ) {
2099
+ if ( match( 'punctuation', ',' ) ) {
2100
+ continue;
2101
+ }
2102
+ if ( matchPairListClose() ) {
2103
+ break;
2104
+ }
2105
+ const start = current();
2106
+ const keyExpr = parseExpression();
2107
+ if ( current().type === 'operator' && current().value === ':' ) {
2108
+ const normalized = normalizeNamedArgumentKey( keyExpr );
2109
+ expect( 'operator', ':', 'Expected : in pairlist literal' );
2110
+ const value = parseExpression();
2111
+ entries.push( withLoc( {
2112
+ type: 'PairListEntry',
2113
+ key: normalized.key,
2114
+ keyExpr: normalized.keyExpr,
2115
+ value,
2116
+ }, startLocFromNode( keyExpr ), endLocFromNode( value ) ) );
2117
+ }
2118
+ else {
2119
+ entries.push( keyExpr );
2120
+ }
2121
+ if ( matchPairListClose() ) {
2122
+ break;
2123
+ }
2124
+ match( 'punctuation', ',' );
2125
+ }
2126
+ }
2127
+ return withLoc( {
2128
+ type: 'PairListLiteral',
2129
+ entries,
2130
+ }, opener, previous() );
2131
+ }
2132
+ if (
2133
+ match( 'operator', '<<' )
2134
+ || match( 'operator', '<<<' )
2135
+ || match( 'operator', '«' )
2136
+ ) {
2137
+ const opener = previous();
2138
+ const closer = opener.value === '<<<'
2139
+ ? '>>>'
2140
+ : opener.value === '«'
2141
+ ? '»'
2142
+ : '>>';
2143
+ const elements = parseDelimitedCollection( closer );
2144
+ return withLoc( {
2145
+ type: opener.value === '<<<' ? 'BagLiteral' : 'SetLiteral',
2146
+ elements,
2147
+ }, opener, previous() );
2148
+ }
2149
+ if ( match( 'punctuation', '{' ) ) {
2150
+ const properties = [];
2151
+ if ( !match( 'punctuation', '}' ) ) {
2152
+ while ( true ) {
2153
+ if ( match( 'punctuation', ',' ) ) {
2154
+ continue;
2155
+ }
2156
+ if ( match( 'punctuation', '}' ) ) {
2157
+ break;
2158
+ }
2159
+ let key;
2160
+ if ( current().type === 'identifier' || current().type === 'keyword' ) {
2161
+ key = current().value;
2162
+ index++;
2163
+ }
2164
+ else if ( current().type === 'string' ) {
2165
+ key = current().value;
2166
+ index++;
2167
+ }
2168
+ else {
2169
+ throw new TranspilerSyntaxError( 'Expected object literal key', current() );
2170
+ }
2171
+ expect( 'operator', ':', 'Expected : in object literal' );
2172
+ const value = parseExpression();
2173
+ properties.push( { key, value } );
2174
+ if ( match( 'punctuation', '}' ) ) {
2175
+ break;
2176
+ }
2177
+ match( 'punctuation', ',' );
2178
+ }
2179
+ }
2180
+ return withLoc( {
2181
+ type: 'ObjectExpression',
2182
+ properties,
2183
+ }, token, previous() );
2184
+ }
2185
+ throw new TranspilerSyntaxError( 'Unexpected token in expression', token );
2186
+ }
2187
+
2188
+ function parseFnExpression( token, isAsync ) {
2189
+ index++;
2190
+ let params;
2191
+ if ( current().type === 'punctuation' && current().value === '(' ) {
2192
+ params = parseParameterList();
2193
+ }
2194
+ else {
2195
+ params = [ parseParameter() ];
2196
+ }
2197
+ if ( current().type === 'operator' && ( current().value === '->' || current().value === '→' ) ) {
2198
+ index++;
2199
+ }
2200
+ else {
2201
+ throw new TranspilerSyntaxError( 'Expected -> after fn parameters', current() );
2202
+ }
2203
+ const previousDepth = asyncContextDepth;
2204
+ asyncContextDepth = isAsync ? previousDepth + 1 : 0;
2205
+ let expr;
2206
+ try {
2207
+ expr = parseExpression();
2208
+ }
2209
+ finally {
2210
+ asyncContextDepth = previousDepth;
2211
+ }
2212
+ const returnStmt = withLoc( {
2213
+ type: 'ReturnStatement',
2214
+ argument: expr,
2215
+ }, token, token );
2216
+ const body = withLoc( {
2217
+ type: 'BlockStatement',
2218
+ body: [ returnStmt ],
2219
+ }, token, token );
2220
+ return withLoc( {
2221
+ type: 'FunctionExpression',
2222
+ params,
2223
+ isAsync,
2224
+ body,
2225
+ }, token, previous() || token );
2226
+ }
2227
+
2228
+ function parseArrowLambdaExpression( token ) {
2229
+ index++;
2230
+ const expr = parseExpression();
2231
+ const returnStmt = withLoc( {
2232
+ type: 'ReturnStatement',
2233
+ argument: expr,
2234
+ }, token, token );
2235
+ const body = withLoc( {
2236
+ type: 'BlockStatement',
2237
+ body: [ returnStmt ],
2238
+ }, token, token );
2239
+ return withLoc( {
2240
+ type: 'FunctionExpression',
2241
+ params: [ {
2242
+ type: 'Parameter',
2243
+ name: '^^',
2244
+ typeName: null,
2245
+ optional: true,
2246
+ defaultValue: null,
2247
+ rest: false,
2248
+ } ],
2249
+ isAsync: false,
2250
+ body,
2251
+ }, token, previous() || token );
2252
+ }
2253
+
2254
+ function parseDelimitedCollection( closer ) {
2255
+ const elements = [];
2256
+ if ( match( 'operator', closer ) ) {
2257
+ return elements;
2258
+ }
2259
+ while ( true ) {
2260
+ if ( match( 'punctuation', ',' ) ) {
2261
+ continue;
2262
+ }
2263
+ if ( match( 'operator', closer ) ) {
2264
+ break;
2265
+ }
2266
+ elements.push( parseExpression() );
2267
+ if ( match( 'operator', closer ) ) {
2268
+ break;
2269
+ }
2270
+ match( 'punctuation', ',' );
2271
+ }
2272
+ return elements;
2273
+ }
2274
+
2275
+ function parseLetExpression() {
2276
+ const start = expect( 'keyword' );
2277
+ if ( current().type === 'punctuation' && current().value === '{' ) {
2278
+ throw new TranspilerSyntaxError(
2279
+ 'Expected name after let or const in expression',
2280
+ current()
2281
+ );
2282
+ }
2283
+ return withLoc( {
2284
+ type: 'LetExpression',
2285
+ ...parseVariableDeclarationParts( start ),
2286
+ }, start, previous() );
2287
+ }
2288
+
2289
+ function parseCircumfixUnary( start, operator, close, message ) {
2290
+ const argument = parseExpression();
2291
+ expect( 'operator', close, message );
2292
+ return withLoc( {
2293
+ type: 'UnaryExpression',
2294
+ operator,
2295
+ argument,
2296
+ prefix: true,
2297
+ }, start, previous() );
2298
+ }
2299
+
2300
+ function parseTemplateExpression( source, token ) {
2301
+ try {
2302
+ return parse( tokenize( source ), { expression: true } );
2303
+ }
2304
+ catch ( err ) {
2305
+ if ( err instanceof TranspilerSyntaxError || err instanceof UnsupportedSyntaxError ) {
2306
+ throw new TranspilerSyntaxError( 'Invalid template interpolation', token );
2307
+ }
2308
+ throw err;
2309
+ }
2310
+ }
2311
+
2312
+ function parseInterpolationParts( parts, token ) {
2313
+ return parts.map( (part) => {
2314
+ if ( part.type === 'text' ) {
2315
+ return withLoc( {
2316
+ type: 'StringLiteral',
2317
+ value: part.value,
2318
+ }, token, token );
2319
+ }
2320
+ return parseTemplateExpression( part.value, token );
2321
+ } );
2322
+ }
2323
+
2324
+ if ( options.expression ) {
2325
+ return parseExpressionRoot();
2326
+ }
2327
+ return parseProgram();
2328
+ }
2329
+
2330
+ module.exports = {
2331
+ parse,
2332
+ };