rapydscript-ng 0.7.24 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +80 -26
  3. package/bin/build.ts +117 -0
  4. package/bin/package.json +1 -0
  5. package/bin/rapydscript +65 -62
  6. package/build_wheels.py +133 -0
  7. package/editor-plugins/README.md +80 -0
  8. package/editor-plugins/nvim.lua +321 -0
  9. package/local-agent.md +28 -0
  10. package/package.json +4 -5
  11. package/publish.py +27 -17
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +9027 -8139
  15. package/release/signatures.json +27 -27
  16. package/release/stdlib_modules.json +1 -0
  17. package/src/ast.pyj +428 -280
  18. package/src/baselib-builtins.pyj +49 -28
  19. package/src/baselib-containers.pyj +241 -226
  20. package/src/baselib-errors.pyj +8 -1
  21. package/src/baselib-internal.pyj +38 -23
  22. package/src/baselib-itertools.pyj +13 -7
  23. package/src/baselib-str.pyj +59 -84
  24. package/src/compiler.pyj +4 -4
  25. package/src/errors.pyj +3 -4
  26. package/src/lib/aes.pyj +46 -32
  27. package/src/lib/elementmaker.pyj +11 -9
  28. package/src/lib/encodings.pyj +15 -5
  29. package/src/lib/gettext.pyj +9 -4
  30. package/src/lib/math.pyj +106 -45
  31. package/src/lib/operator.pyj +10 -10
  32. package/src/lib/pythonize.pyj +2 -1
  33. package/src/lib/random.pyj +28 -21
  34. package/src/lib/re.pyj +492 -19
  35. package/src/lib/traceback.pyj +171 -29
  36. package/src/lib/uuid.pyj +2 -2
  37. package/src/output/classes.pyj +28 -27
  38. package/src/output/codegen.pyj +87 -83
  39. package/src/output/comments.pyj +8 -8
  40. package/src/output/exceptions.pyj +20 -21
  41. package/src/output/functions.pyj +66 -64
  42. package/src/output/literals.pyj +24 -23
  43. package/src/output/loops.pyj +84 -142
  44. package/src/output/modules.pyj +55 -68
  45. package/src/output/operators.pyj +40 -29
  46. package/src/output/statements.pyj +21 -15
  47. package/src/output/stream.pyj +82 -56
  48. package/src/output/utils.pyj +12 -8
  49. package/src/parse.pyj +971 -620
  50. package/src/string_interpolation.pyj +5 -3
  51. package/src/tokenizer.pyj +188 -148
  52. package/src/utils.pyj +32 -15
  53. package/test/_treeshake_mod.pyj +30 -0
  54. package/test/_treeshake_side_effect.pyj +9 -0
  55. package/test/ast_serialization.pyj +392 -0
  56. package/test/async.pyj +95 -0
  57. package/test/baselib.pyj +1 -2
  58. package/test/embedded_compiler.pyj +57 -0
  59. package/test/fmt.pyj +291 -0
  60. package/test/generic.pyj +16 -3
  61. package/test/imports.pyj +8 -6
  62. package/test/internationalization.pyj +38 -37
  63. package/test/lint.pyj +120 -117
  64. package/test/lsp.pyj +257 -0
  65. package/test/repl.pyj +70 -65
  66. package/test/sourcemaps.pyj +315 -0
  67. package/test/starargs.pyj +46 -39
  68. package/test/str.pyj +8 -0
  69. package/test/traceback.pyj +263 -0
  70. package/test/treeshaking.pyj +181 -0
  71. package/test/web_repl_export.pyj +88 -0
  72. package/tools/ast_serialize.mjs +557 -0
  73. package/tools/cli.mjs +531 -0
  74. package/tools/compile.mjs +205 -0
  75. package/tools/compiler.mjs +159 -0
  76. package/tools/{completer.js → completer.mjs} +6 -6
  77. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  78. package/tools/export.js +109 -56
  79. package/tools/fmt.mjs +975 -0
  80. package/tools/{gettext.js → gettext.mjs} +46 -53
  81. package/tools/ini.mjs +175 -0
  82. package/tools/{lint.js → lint.mjs} +78 -55
  83. package/tools/lsp.mjs +964 -0
  84. package/tools/lsp_protocol.mjs +259 -0
  85. package/tools/lsp_symbols.mjs +418 -0
  86. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  87. package/tools/{repl.js → repl.mjs} +56 -42
  88. package/tools/{self.js → self.mjs} +71 -46
  89. package/tools/sourcemap.mjs +123 -0
  90. package/tools/test.mjs +252 -0
  91. package/tools/treeshake.mjs +400 -0
  92. package/tools/{utils.js → utils.mjs} +26 -23
  93. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  94. package/tools/web_repl_export.mjs +97 -0
  95. package/tree-sitter/README.md +101 -0
  96. package/tree-sitter/grammar.js +992 -0
  97. package/tree-sitter/package.json +43 -0
  98. package/tree-sitter/queries/highlights.scm +232 -0
  99. package/tree-sitter/queries/indents.scm +64 -0
  100. package/tree-sitter/queries/injections.scm +14 -0
  101. package/tree-sitter/queries/locals.scm +108 -0
  102. package/tree-sitter/src/grammar.json +4976 -0
  103. package/tree-sitter/src/node-types.json +2901 -0
  104. package/tree-sitter/src/parser.c +196465 -0
  105. package/tree-sitter/src/scanner.c +294 -0
  106. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  107. package/tree-sitter/src/tree_sitter/array.h +330 -0
  108. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  109. package/tree-sitter/test/corpus/chaining.txt +99 -0
  110. package/tree-sitter/test/corpus/classes.txt +147 -0
  111. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  112. package/tree-sitter/test/corpus/containers.txt +242 -0
  113. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  114. package/tree-sitter/test/corpus/decorators.txt +64 -0
  115. package/tree-sitter/test/corpus/existential.txt +102 -0
  116. package/tree-sitter/test/corpus/functions.txt +293 -0
  117. package/tree-sitter/test/corpus/imports.txt +117 -0
  118. package/tree-sitter/test/corpus/literals.txt +135 -0
  119. package/tree-sitter/test/corpus/operators.txt +296 -0
  120. package/tree-sitter/test/corpus/statements.txt +189 -0
  121. package/tree-sitter/test/corpus/strings.txt +90 -0
  122. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  123. package/tree-sitter/tree-sitter.json +36 -0
  124. package/web-repl/env.js +35 -23
  125. package/web-repl/main.js +8 -8
  126. package/bin/export +0 -75
  127. package/bin/web-repl-export +0 -102
  128. package/tools/cli.js +0 -523
  129. package/tools/compile.js +0 -184
  130. package/tools/compiler.js +0 -84
  131. package/tools/ini.js +0 -65
  132. package/tools/test.js +0 -110
package/src/parse.pyj CHANGED
@@ -1,13 +1,11 @@
1
1
  # vim:fileencoding=utf-8
2
2
  # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
- # globals: readfile
3
+ # globals: readfile, writefile, stat_file, sha1sum, ast_from_json, ast_to_json, make_lazy_ast_module, decode_cache, encode_cache
4
4
  from __python__ import hash_literals
5
5
 
6
- from utils import make_predicate, array_to_hash, defaults, has_prop, cache_file_name
7
- from errors import SyntaxError, ImportError
8
6
  from ast import (
9
- AST_Array, AST_Assign, AST_Binary, AST_BlockStatement, AST_Break,
10
- AST_Call, AST_Catch, AST_Class, AST_ClassCall, AST_Conditional,
7
+ AST_ArgsDef, AST_Array, AST_Assign, AST_Binary, AST_BlockStatement, AST_Break,
8
+ AST_Call, AST_CallArg, AST_CallArgs, AST_Catch, AST_Class, AST_ClassCall, AST_Conditional,
11
9
  AST_Constant, AST_Continue, AST_DWLoop, AST_Debugger, AST_Decorator,
12
10
  AST_Definitions, AST_DictComprehension, AST_Directive, AST_Do, AST_Dot,
13
11
  AST_Else, AST_EmptyStatement, AST_Except, AST_ExpressiveObject, AST_False, AST_Finally,
@@ -21,13 +19,16 @@ AST_SymbolCatch, AST_SymbolDefun, AST_SymbolFunarg,
21
19
  AST_SymbolLambda, AST_SymbolNonlocal, AST_SymbolRef, AST_SymbolVar, AST_This,
22
20
  AST_Throw, AST_Toplevel, AST_True, AST_Try, AST_UnaryPrefix,
23
21
  AST_Undefined, AST_Var, AST_VarDef, AST_Verbatim, AST_While, AST_With, AST_WithClause,
24
- AST_Yield, AST_Assert, AST_Existential, is_node_type
22
+ AST_Yield, AST_Await, AST_Assert, AST_Existential, is_node_type, TreeWalker
25
23
  )
24
+ from errors import SyntaxError, ImportError
26
25
  from tokenizer import tokenizer, is_token, RESERVED_WORDS
26
+ from utils import make_predicate, array_to_hash, defaults, has_prop, cache_file_name
27
27
 
28
28
 
29
29
  COMPILER_VERSION = '__COMPILER_VERSION__'
30
- PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True}
30
+ PYTHON_FLAGS = {'dict_literals': True, 'overload_getitem': True, 'bound_methods': True, 'hash_literals': True}
31
+ CACHE_VERSION = 1
31
32
 
32
33
 
33
34
  def get_compiler_version():
@@ -35,7 +36,7 @@ def get_compiler_version():
35
36
 
36
37
 
37
38
  def static_predicate(names):
38
- return {k:True for k in names.split(' ')}
39
+ return {k: True for k in names.split(' ')}
39
40
 
40
41
  NATIVE_CLASSES = {
41
42
  'Image': {},
@@ -59,14 +60,14 @@ NATIVE_CLASSES = {
59
60
  )
60
61
  },
61
62
  'String': {
62
- 'static': static_predicate("fromCharCode")
63
+ 'static': static_predicate('fromCharCode')
63
64
  },
64
65
  'Array': {
65
- 'static': static_predicate("isArray from of")
66
+ 'static': static_predicate('isArray from of')
66
67
  },
67
68
  'Function': {},
68
69
  'Date': {
69
- 'static': static_predicate("UTC now parse")
70
+ 'static': static_predicate('UTC now parse')
70
71
  },
71
72
  'ArrayBuffer': {
72
73
  'static': static_predicate('isView transfer')
@@ -116,34 +117,35 @@ UNARY_PREFIX = make_predicate('typeof void delete ~ - + ! @')
116
117
 
117
118
  ASSIGNMENT = make_predicate('= += -= /= //= *= %= >>= <<= >>>= |= ^= &=')
118
119
 
119
- PRECEDENCE = (def(a, ret):
120
+ PRECEDENCE = (def (a, ret):
120
121
  for i in range(a.length):
121
122
  b = a[i]
122
123
  for j in range(b.length):
123
- ret[b[j]] = i+1
124
+ ret[b[j]] = i + 1
124
125
  return ret
125
126
  )([
126
127
  # lowest precedence
127
- [ "||" ],
128
- [ "&&" ],
129
- [ "|" ],
130
- [ "^" ],
131
- [ "&" ],
132
- [ "==", "===", "!=", "!==" ],
133
- [ "<", ">", "<=", ">=", "in", "nin", "instanceof" ],
134
- [ ">>", "<<", ">>>" ],
135
- [ "+", "-" ],
136
- [ "*", "/", "//", "%" ],
137
- [ "**" ]
128
+ ['||'],
129
+ ['&&'],
130
+ ['|'],
131
+ ['^'],
132
+ ['&'],
133
+ ['==', '===', '!=', '!=='],
134
+ ['<', '>', '<=', '>=', 'in', 'nin', 'instanceof'],
135
+ ['>>', '<<', '>>>'],
136
+ ['+', '-'],
137
+ ['*', '/', '//', '%'],
138
+ ['**']
138
139
  # highest precedence
139
140
  ], {})
140
141
 
141
- STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ])
142
+ STATEMENTS_WITH_LABELS = array_to_hash(['for', 'do', 'while', 'switch'])
142
143
 
143
- ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name", "js" ])
144
+ ATOMIC_START_TOKEN = array_to_hash(['atom', 'num', 'string', 'regexp', 'name', 'js'])
144
145
 
145
146
  compile_time_decorators = ['staticmethod', 'external', 'property']
146
147
 
148
+
147
149
  def has_simple_decorator(decorators, name):
148
150
  remove = v'[]'
149
151
  for v'var i = 0; i < decorators.length; i++':
@@ -157,6 +159,7 @@ def has_simple_decorator(decorators, name):
157
159
  return True
158
160
  return False
159
161
 
162
+
160
163
  def has_setter_decorator(decorators, name):
161
164
  remove = v'[]'
162
165
  for v'var i = 0; i < decorators.length; i++':
@@ -170,9 +173,9 @@ def has_setter_decorator(decorators, name):
170
173
  return True
171
174
  return False
172
175
 
176
+
173
177
  # -----[ Parser ]-----
174
178
  def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_ids, imported_modules, importing_modules, options):
175
-
176
179
  def next():
177
180
  S.prev = S.token
178
181
  if S.peeked.length:
@@ -206,26 +209,61 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
206
209
  def unexpected(token):
207
210
  if token is undefined:
208
211
  token = S.token
209
- token_error(token, "Unexpected token: " + token.type + " «" + token.value + "»")
212
+ token_error(token, 'Unexpected token: ' + token.type + ' «' + token.value + '»')
213
+
214
+ # --- Error recovery (only active when options.recover_errors is True) --------
215
+ # These helpers let the LSP obtain a usable AST from a file that contains
216
+ # syntax errors. They are never invoked on the normal compile path, so they
217
+ # cannot introduce a performance or behavior regression there.
218
+
219
+ def is_recoverable_error(e):
220
+ return v'e instanceof SyntaxError'
221
+
222
+ def record_recovery(e):
223
+ S.recovered_errors.push({
224
+ 'message': e.message, 'line': e.line, 'col': e.col, 'pos': e.pos,
225
+ 'endpos': e.endpos, 'is_eof': e.is_eof,
226
+ })
227
+
228
+ async def skip_to_recovery_point():
229
+ # Resynchronize after a syntax error: always consume at least one token
230
+ # (guaranteeing forward progress) then stop at the start of the next
231
+ # logical line, a block dedent, or eof. next() may itself raise a
232
+ # tokenizer level SyntaxError (e.g. an unterminated string on the skipped
233
+ # line); such errors are recorded and skipping continues. The tokenizer
234
+ # advances its position for every such error (and never raises for an
235
+ # unexpected character in recover mode), so this loop always terminates.
236
+ while not is_('eof'):
237
+ try:
238
+ next()
239
+ except as e:
240
+ if is_recoverable_error(e):
241
+ record_recovery(e)
242
+ if is_('eof'):
243
+ return
244
+ continue
245
+ raise
246
+ if is_('eof') or is_('punc', '}') or S.token.nlb:
247
+ return
210
248
 
211
249
  def expect_token(type, val):
212
250
  if is_(type, val):
213
251
  return next()
214
- token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" +
215
- ", expected " + type + " «" + val + "»")
252
+ token_error(S.token, 'Unexpected token ' + S.token.type + ' «' + S.token.value + '»' +
253
+ ', expected ' + type + ' «' + val + '»')
216
254
 
217
255
  def expect(punc):
218
- return expect_token("punc", punc)
256
+ return expect_token('punc', punc)
219
257
 
220
258
  def semicolon():
221
- if is_("punc", ";"):
259
+ if is_('punc', ';'):
222
260
  next()
223
261
  S.token.nlb = True
224
262
 
225
263
  def embed_tokens(parser):
226
- def with_embedded_tokens():
264
+ async def with_embedded_tokens():
227
265
  start = S.token
228
- expr = parser()
266
+ expr = await parser()
229
267
  if expr is undefined:
230
268
  unexpected()
231
269
  end = prev()
@@ -243,7 +281,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
243
281
  if obj.name:
244
282
  ans.push(obj.name.name)
245
283
  else:
246
- token_error(obj.start, "Top-level functions must have names")
284
+ token_error(obj.start, 'Top-level functions must have names')
247
285
  else:
248
286
  # skip inner scopes
249
287
  if is_node_type(obj, AST_Scope):
@@ -262,7 +300,6 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
262
300
  if body.alternative:
263
301
  ans = ans.concat(scan_for_top_level_callables(body.alternative))
264
302
 
265
-
266
303
  return ans
267
304
 
268
305
  def scan_for_classes(body):
@@ -305,10 +342,10 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
305
342
 
306
343
  def add_assign_lhs(lhs):
307
344
  if is_node_type(lhs, AST_Seq):
308
- lhs = new AST_Array({'elements':lhs.to_array()})
345
+ lhs = new AST_Array({'elements': lhs.to_array()})
309
346
  if is_node_type(lhs, AST_Array):
310
347
  # assignment to an implicit tuple
311
- push("ρσ_unpack")
348
+ push('ρσ_unpack')
312
349
  scan_in_array(lhs.elements)
313
350
  elif lhs.name:
314
351
  # assignment to a single variable
@@ -317,7 +354,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
317
354
  def add_for_in(stmt):
318
355
  if is_node_type(stmt.init, AST_Array):
319
356
  # iteration via implicit tuple
320
- push("ρσ_unpack")
357
+ push('ρσ_unpack')
321
358
  scan_in_array(stmt.init.elements)
322
359
  else:
323
360
  # iteration via a single variable
@@ -395,30 +432,28 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
395
432
  if opt:
396
433
  vars = vars.concat(scan_for_nonlocal_defs(opt))
397
434
 
398
-
399
435
  elif body.body:
400
436
  vars = vars.concat(scan_for_nonlocal_defs(body.body))
401
437
  if body.alternative:
402
438
  vars = vars.concat(scan_for_nonlocal_defs(body.alternative))
403
439
 
404
-
405
440
  return vars
406
441
 
407
- def return_():
442
+ async def return_():
408
443
  if is_('punc', ';'):
409
444
  semicolon()
410
445
  value = None
411
446
  else:
412
- is_end_of_statement = S.token.nlb or is_("eof") or is_("punc", "}")
447
+ is_end_of_statement = S.token.nlb or is_('eof') or is_('punc', '}')
413
448
  if is_end_of_statement:
414
449
  value = None
415
450
  else:
416
- value = expression(True)
451
+ value = await expression(True)
417
452
  semicolon()
418
453
  return value
419
454
 
420
455
  @embed_tokens
421
- def statement():
456
+ async def statement():
422
457
  # From Kovid: The next three lines were a hack to try to support statements
423
458
  # starting with a regexp literal. However, it did not work, for example:
424
459
  # echo 'f=1\n/asd/.test()' | rs -> parse error
@@ -433,83 +468,104 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
433
468
  p = prev()
434
469
  if p and not S.token.nlb and ATOMIC_START_TOKEN[p.type] and not is_('punc', ':'):
435
470
  unexpected()
436
- if tmp_ is "string":
437
- return simple_statement()
438
- elif tmp_ is "shebang":
471
+ if tmp_ is 'string':
472
+ return await simple_statement()
473
+ elif tmp_ is 'shebang':
439
474
  tmp_ = S.token.value
440
475
  next()
441
476
  return new AST_Directive({
442
477
  'value': tmp_
443
478
  })
444
- elif tmp_ is "num" or tmp_ is "regexp" or tmp_ is "operator" or tmp_ is "atom" or tmp_ is "js":
445
- return simple_statement()
446
- elif tmp_ is "punc":
479
+ elif tmp_ is 'num' or tmp_ is 'regexp' or tmp_ is 'operator' or tmp_ is 'atom' or tmp_ is 'js':
480
+ return await simple_statement()
481
+ elif tmp_ is 'punc':
447
482
  tmp_ = S.token.value
448
- if tmp_ is ":":
483
+ if tmp_ is ':':
484
+ bs_body = await block_()
449
485
  return new AST_BlockStatement({
450
486
  'start': S.token,
451
- 'body': block_(),
487
+ 'body': bs_body,
452
488
  'end': prev()
453
489
  })
454
- elif tmp_ is "{" or tmp_ is "[" or tmp_ is "(":
455
- return simple_statement()
456
- elif tmp_ is ";":
490
+ elif tmp_ is '{' or tmp_ is '[' or tmp_ is '(':
491
+ return await simple_statement()
492
+ elif tmp_ is ';':
457
493
  next()
458
- return new AST_EmptyStatement({'stype':';', 'start':prev(), 'end':prev()})
494
+ return new AST_EmptyStatement({'stype': ';', 'start': prev(), 'end': prev()})
459
495
  else:
460
496
  unexpected()
461
- elif tmp_ is "name":
497
+ elif tmp_ is 'name':
462
498
  if (is_token(peek(), 'punc', ':')) token_error(peek(), 'invalid syntax, colon not allowed here')
463
- return simple_statement()
464
- elif tmp_ is "keyword":
499
+ return await simple_statement()
500
+ elif tmp_ is 'keyword':
465
501
  tmp_ = S.token.value
466
502
  next()
467
- if tmp_ is "break":
503
+ if tmp_ is 'break':
468
504
  return break_cont(AST_Break)
469
- elif tmp_ is "continue":
505
+ elif tmp_ is 'continue':
470
506
  return break_cont(AST_Continue)
471
- elif tmp_ is "debugger":
507
+ elif tmp_ is 'debugger':
472
508
  semicolon()
473
509
  return new AST_Debugger()
474
- elif tmp_ is "do":
510
+ elif tmp_ is 'do':
511
+ do_body = await in_loop(statement)
512
+ do_cond = await (async def ():
513
+ expect('.')
514
+ expect_token('keyword', 'while')
515
+ tmp = await expression(True)
516
+ if is_node_type(tmp, AST_Assign):
517
+ croak('Assignments in do loop conditions are not allowed')
518
+ semicolon()
519
+ return tmp
520
+ )()
475
521
  return new AST_Do({
476
- 'body': in_loop(statement),
477
- 'condition': (def():
478
- expect(".")
479
- expect_token("keyword", "while")
480
- tmp = expression(True)
481
- if is_node_type(tmp, AST_Assign):
482
- croak('Assignments in do loop conditions are not allowed')
483
- semicolon()
484
- return tmp
485
- )()
522
+ 'body': do_body,
523
+ 'condition': do_cond,
486
524
  })
487
- elif tmp_ is "while":
488
- while_cond = expression(True)
525
+ elif tmp_ is 'while':
526
+ while_cond = await expression(True)
489
527
  if is_node_type(while_cond, AST_Assign):
490
528
  croak('Assignments in while loop conditions are not allowed')
491
529
  if not is_('punc', ':'):
492
530
  croak('Expected a colon after the while statement')
531
+ while_body = await in_loop(statement)
493
532
  return new AST_While({
494
533
  'condition': while_cond,
495
- 'body': in_loop(statement)
534
+ 'body': while_body
496
535
  })
497
- elif tmp_ is "for":
536
+ elif tmp_ is 'for':
498
537
  if is_('js'):
499
- return for_js()
500
- return for_()
501
- elif tmp_ is "from":
502
- return import_(True)
503
- elif tmp_ is "import":
504
- return import_(False)
505
- elif tmp_ is "class":
506
- return class_()
507
- elif tmp_ is "def":
538
+ return await for_js()
539
+ return await for_()
540
+ elif tmp_ is 'from':
541
+ return await import_(True)
542
+ elif tmp_ is 'import':
543
+ return await import_(False)
544
+ elif tmp_ is 'class':
545
+ return await class_()
546
+ elif tmp_ is 'async':
547
+ if not is_('keyword', 'def'):
548
+ croak("Expected 'def' after 'async'")
549
+ next()
550
+ start = prev()
551
+ func = await function_(S.in_class[-1], False, True)
552
+ func.start = start
553
+ func.end = prev()
554
+ chain = await subscripts(func, True)
555
+ if chain is func:
556
+ return func
557
+ else:
558
+ return new AST_SimpleStatement({
559
+ 'start': start,
560
+ 'body': chain,
561
+ 'end': prev()
562
+ })
563
+ elif tmp_ is 'def':
508
564
  start = prev()
509
- func = function_(S.in_class[-1], False)
565
+ func = await function_(S.in_class[-1], False)
510
566
  func.start = start
511
567
  func.end = prev()
512
- chain = subscripts(func, True)
568
+ chain = await subscripts(func, True)
513
569
  if chain is func:
514
570
  return func
515
571
  else:
@@ -520,67 +576,75 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
520
576
  })
521
577
  elif tmp_ is 'assert':
522
578
  start = prev()
523
- cond = expression(False)
579
+ cond = await expression(False)
524
580
  msg = None
525
581
  if is_('punc', ','):
526
582
  next()
527
- msg = expression(False)
528
- return new AST_Assert({'start': start, 'condition':cond, 'message':msg, 'end':prev()})
529
- elif tmp_ is "if":
530
- return if_()
531
- elif tmp_ is "pass":
583
+ msg = await expression(False)
584
+ return new AST_Assert({'start': start, 'condition': cond, 'message': msg, 'end': prev()})
585
+ elif tmp_ is 'if':
586
+ return await if_()
587
+ elif tmp_ is 'pass':
532
588
  semicolon()
533
- return new AST_EmptyStatement({'stype':'pass', 'start':prev(), 'end':prev()})
534
- elif tmp_ is "return":
589
+ return new AST_EmptyStatement({'stype': 'pass', 'start': prev(), 'end': prev()})
590
+ elif tmp_ is 'return':
535
591
  if S.in_function is 0:
536
592
  croak("'return' outside of function")
537
593
  if S.functions[-1].is_generator:
538
594
  croak("'return' not allowed in a function with yield")
539
595
  S.functions[-1].is_generator = False
540
596
 
541
- return new AST_Return({'value':return_()})
542
- elif tmp_ is "yield":
543
- return yield_()
544
- elif tmp_ is "raise":
597
+ ret_val = await return_()
598
+ return new AST_Return({'value': ret_val})
599
+ elif tmp_ is 'await':
600
+ if S.in_function is 0 or not S.functions[-1].is_async:
601
+ croak("'await' outside of async function")
602
+ value = await expr_atom(True)
603
+ node = new AST_Await({'value': value})
604
+ semicolon()
605
+ return new AST_SimpleStatement({'body': node})
606
+ elif tmp_ is 'yield':
607
+ return await yield_()
608
+ elif tmp_ is 'raise':
545
609
  if S.token.nlb:
546
610
  return new AST_Throw({
547
611
  'value': new AST_SymbolCatch({
548
- 'name': "ρσ_Exception"
612
+ 'name': 'ρσ_Exception'
549
613
  })
550
614
  })
551
615
 
552
- tmp = expression(True)
616
+ tmp = await expression(True)
553
617
  semicolon()
554
618
  return new AST_Throw({
555
619
  'value': tmp
556
620
  })
557
- elif tmp_ is "try":
558
- return try_()
559
- elif tmp_ is "nonlocal":
560
- tmp = nonlocal_()
621
+ elif tmp_ is 'try':
622
+ return await try_()
623
+ elif tmp_ is 'nonlocal':
624
+ tmp = await nonlocal_()
561
625
  semicolon()
562
626
  return tmp
563
627
  elif tmp_ is 'global':
564
- tmp = nonlocal_(True)
628
+ tmp = await nonlocal_(True)
565
629
  semicolon()
566
630
  return tmp
567
- elif tmp_ is "with":
568
- return with_()
631
+ elif tmp_ is 'with':
632
+ return await with_()
569
633
  else:
570
634
  unexpected()
571
635
 
572
- def with_():
636
+ async def with_():
573
637
  clauses = v'[]'
574
638
  start = S.token
575
639
  while True:
576
640
  if is_('eof'):
577
641
  unexpected()
578
- expr = expression()
642
+ expr = await expression()
579
643
  alias = None
580
644
  if is_('keyword', 'as'):
581
645
  next()
582
646
  alias = as_symbol(AST_SymbolAlias)
583
- clauses.push(new AST_WithClause({'expression':expr, 'alias':alias}))
647
+ clauses.push(new AST_WithClause({'expression': expr, 'alias': alias}))
584
648
  if is_('punc', ','):
585
649
  next()
586
650
  continue
@@ -590,15 +654,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
590
654
 
591
655
  if not clauses.length:
592
656
  token_error(start, 'with statement must have at least one clause')
593
- body = statement()
657
+ body = await statement()
594
658
 
595
659
  return new AST_With({
596
660
  'clauses': clauses,
597
661
  'body': body
598
662
  })
599
663
 
600
- def simple_statement(tmp):
601
- tmp = expression(True)
664
+ async def simple_statement(tmp):
665
+ tmp = await expression(True)
602
666
  semicolon()
603
667
  return new AST_SimpleStatement({
604
668
  'body': tmp
@@ -606,26 +670,29 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
606
670
 
607
671
  def break_cont(t):
608
672
  if S.in_loop is 0:
609
- croak(t.name.slice(4) + " not inside a loop or switch")
673
+ croak(t.name.slice(4) + ' not inside a loop or switch')
610
674
  semicolon()
611
675
  return new t()
612
676
 
613
- def yield_():
677
+ async def yield_():
614
678
  if S.in_function is 0:
615
679
  croak("'yield' outside of function")
680
+ if S.functions[-1].is_async:
681
+ croak("'yield' not allowed in an async function")
616
682
  if S.functions[-1].is_generator is False:
617
683
  croak("'yield' not allowed in a function with return")
618
684
  S.functions[-1].is_generator = True
619
685
  is_yield_from = is_('keyword', 'from')
620
686
  if is_yield_from:
621
687
  next()
622
- return new AST_Yield({'is_yield_from':is_yield_from, 'value': return_()})
688
+ yld_val = await return_()
689
+ return new AST_Yield({'is_yield_from': is_yield_from, 'value': yld_val})
623
690
 
624
- def for_(list_comp):
691
+ async def for_(list_comp):
625
692
  # expect("(")
626
693
  init = None
627
- if not is_("punc", ";"):
628
- init = expression(True, True)
694
+ if not is_('punc', ';'):
695
+ init = await expression(True, True)
629
696
  # standardize AST_Seq into array now for consistency
630
697
  if is_node_type(init, AST_Seq):
631
698
  if is_node_type(init.car, AST_SymbolRef) and is_node_type(init.cdr, AST_SymbolRef):
@@ -639,17 +706,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
639
706
  'end': init.end
640
707
  })
641
708
 
642
- if is_("operator", "in"):
709
+ if is_('operator', 'in'):
643
710
  if is_node_type(init, AST_Var) and init.definitions.length > 1:
644
- croak("Only one variable declaration allowed in for..in loop")
711
+ croak('Only one variable declaration allowed in for..in loop')
645
712
  next()
646
- return for_in(init, list_comp)
713
+ return await for_in(init, list_comp)
647
714
 
648
715
  unexpected()
649
716
 
650
- def for_in(init, list_comp):
717
+ async def for_in(init, list_comp):
651
718
  lhs = init.definitions[0].name if is_node_type(init, AST_Var) else None
652
- obj = expression(True)
719
+ obj = await expression(True)
653
720
  # expect(")")
654
721
  if list_comp:
655
722
  return {
@@ -658,19 +725,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
658
725
  'object': obj
659
726
  }
660
727
 
728
+ fi_body = await in_loop(statement)
661
729
  return new AST_ForIn({
662
730
  'init': init,
663
731
  'name': lhs,
664
732
  'object': obj,
665
- 'body': in_loop(statement)
733
+ 'body': fi_body
666
734
  })
667
735
 
668
736
  # A native JavaScript for loop - for v"var i=0; i<5000; i++":
669
- def for_js():
737
+ async def for_js():
670
738
  condition = as_atom_node()
739
+ fj_body = await in_loop(statement)
671
740
  return new AST_ForJS({
672
741
  'condition': condition,
673
- 'body': in_loop(statement)
742
+ 'body': fj_body
674
743
  })
675
744
 
676
745
  # scan function/class body for nested class declarations
@@ -688,7 +757,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
688
757
  return ERROR_CLASSES[expr.name]
689
758
 
690
759
  # traverse in reverse to check local variables first
691
- for s in range(S.classes.length-1, -1, -1):
760
+ for s in range(S.classes.length - 1, -1, -1):
692
761
  if has_prop(S.classes[s], expr.name):
693
762
  return S.classes[s][expr.name]
694
763
 
@@ -703,7 +772,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
703
772
  # now 'referenced_path' should contain the full path of potential class
704
773
  if len(referenced_path) > 1:
705
774
  class_name = referenced_path.join('.')
706
- for s in range(S.classes.length-1, -1, -1):
775
+ for s in range(S.classes.length - 1, -1, -1):
707
776
  if has_prop(S.classes[s], class_name):
708
777
  return S.classes[s][class_name]
709
778
  return False
@@ -712,72 +781,145 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
712
781
  ctx = S.input.context()
713
782
  raise new ImportError(message, ctx.filename, ctx.tokline, ctx.tokcol, ctx.tokpos)
714
783
 
715
- def do_import(key):
784
+ async def do_import(key):
716
785
  if has_prop(imported_modules, key):
717
786
  return
787
+ # Circular import check must come BEFORE loading_promises so we detect cycles
788
+ # even when the same module is being loaded by a parallel chain.
718
789
  if has_prop(importing_modules, key) and importing_modules[key]:
719
790
  import_error('Detected a recursive import of: ' + key + ' while importing: ' + module_id)
791
+ # If a parallel chain is already loading this module, wait for it instead of
792
+ # starting a duplicate load. loading_promises is shared across the entire
793
+ # compilation via options.loading_promises.
794
+ loading_promises = options.loading_promises
795
+ if has_prop(loading_promises, key):
796
+ await loading_promises[key]
797
+ return
720
798
 
721
- # Ensure that the package containing this module is also imported
722
- package_module_id = key.split('.')[:-1].join('.')
723
- if len(package_module_id) > 0:
724
- do_import(package_module_id)
799
+ # Register this load BEFORE the first await so that any parallel caller
800
+ # that reaches this point sees the in-progress entry and waits.
801
+ # Use a single-element list to capture the resolve function from the Promise
802
+ # executor without needing nonlocal (mutating a container, not rebinding).
803
+ _rfn = v'[null]'
725
804
 
726
- if options.for_linting:
727
- imported_modules[key] = {'is_cached':True, 'classes':{}, 'module_id':key, 'exports':[],
728
- 'nonlocalvars':[], 'baselib':{}, 'outputs':{}, 'discard_asserts':options.discard_asserts}
729
- return
805
+ def _capture_resolve(resolve):
806
+ _rfn[0] = resolve
807
+ loading_promises[key] = new Promise(_capture_resolve)
730
808
 
731
- def safe_read(base_path):
732
- for i, path in enumerate([base_path + '.pyj', base_path + '/__init__.pyj']):
733
- try:
734
- return [readfile(path, "utf-8"), path] # noqa:undef
735
- except as e:
736
- if e.code is 'ENOENT' or e.code is 'EPERM' or e.code is 'EACCESS':
809
+ try:
810
+ # Ensure that the package containing this module is also imported
811
+ package_module_id = key.split('.')[:-1].join('.')
812
+ if len(package_module_id) > 0:
813
+ await do_import(package_module_id)
814
+
815
+ if options.for_linting:
816
+ imported_modules[key] = {'is_cached': True, 'classes': {}, 'module_id': key, 'exports': [],
817
+ 'nonlocalvars': [], 'baselib': {}, 'outputs': {}, 'discard_asserts': options.discard_asserts}
818
+ return
819
+
820
+ # stat_file returns {mtimeMs, content?}. For real filesystems mtimeMs is a
821
+ # number and content is absent; for virtual filesystems content holds the
822
+ # already-read source to avoid a double read.
823
+ async def safe_stat(base_path): # noqa
824
+ for i, path in enumerate([base_path + '.pyj', base_path + '/__init__.pyj']):
825
+ try:
826
+ st = await stat_file(path)
827
+ return [st.mtimeMs, path, st.content if has_prop(st, 'content') else None]
828
+ except as e:
829
+ if e.code is 'ENOENT' or e.code is 'EPERM' or e.code is 'EACCESS':
830
+ if i is 1:
831
+ return None, None, None
737
832
  if i is 1:
738
- return None, None
739
- if i is 1:
740
- raise
833
+ raise
741
834
 
742
- src_code = filename = None
743
- modpath = key.replace(/\./g, '/')
835
+ file_mtime = filename = prefetched_src = None
836
+ modpath = key.replace(/\./g, '/')
744
837
 
745
- for location in import_dirs:
746
- if location:
747
- data, filename = safe_read(location + '/' + modpath)
748
- if data is not None:
749
- src_code = data
750
- break
751
- if src_code is None:
752
- import_error("Failed Import: '" + key + "' module doesn't exist in any of the import directories: " + import_dirs.join(':'))
838
+ for location in import_dirs:
839
+ if location:
840
+ file_mtime, filename, prefetched_src = await safe_stat(location + '/' + modpath)
841
+ if filename is not None:
842
+ break
843
+ if filename is None:
844
+ import_error("Failed Import: '" + key + "' module doesn't exist in any of the import directories: " + import_dirs.join(':'))
753
845
 
754
- try:
755
- cached = JSON.parse(readfile(cache_file_name(filename, options.module_cache_dir), 'utf-8'))
756
- except:
757
- cached = None
846
+ # Read the cache file. I/O starts here; any CPU work done before the
847
+ # await can overlap with the disk read.
848
+ cache_read = readfile(cache_file_name(filename, options.module_cache_dir))
758
849
 
759
- srchash = sha1sum(src_code) # noqa:undef
760
- if cached and cached.version is COMPILER_VERSION and cached.signature is srchash and cached.discard_asserts is v'!!options.discard_asserts':
761
- for ikey in cached.imported_module_ids:
762
- do_import(ikey) # Ensure all modules imported by the cached module are also imported
763
- imported_modules[key] = {
764
- 'is_cached':True, 'classes':cached.classes, 'outputs':cached.outputs, 'module_id':key, 'import_order':Object.keys(imported_modules).length,
765
- 'nonlocalvars':cached.nonlocalvars, 'baselib':cached.baselib, 'exports':cached.exports, 'discard_asserts':options.discard_asserts,
766
- 'imported_module_ids':cached.imported_module_ids,
767
- }
768
- else:
769
- parse(src_code, {
770
- 'filename': filename,
771
- 'toplevel': None,
772
- 'basedir': options.basedir,
773
- 'libdir': options.libdir,
774
- 'import_dirs': options.import_dirs,
775
- 'module_id': key,
776
- 'imported_modules': imported_modules,
777
- 'importing_modules': importing_modules,
778
- 'discard_asserts': options.discard_asserts,
779
- 'module_cache_dir': options.module_cache_dir,
780
- }) # This function will add the module to imported_modules itself
850
+ cached = None
851
+ ast_json_str = None
852
+ try:
853
+ _split = decode_cache(await cache_read)
854
+ if _split is not None:
855
+ cached = JSON.parse(_split.meta)
856
+ ast_json_str = _split.ast
857
+ except:
858
+ cached = None
859
+
860
+ srchash = src_code = None
861
+ cache_ok = cached and cached.v is CACHE_VERSION and cached.compiler_version is COMPILER_VERSION
862
+ if cache_ok and file_mtime is not None and cached.mtime is not None and cached.mtime is file_mtime:
863
+ # Fast path: mtime matches — source file is unchanged, skip reading and hashing it
864
+ srchash = cached.signature
865
+ else:
866
+ # Need source content to validate with sha1sum (or to re-parse on miss)
867
+ src_code = prefetched_src or await readfile(filename, 'utf-8')
868
+ srchash = sha1sum(src_code)
869
+ if not cache_ok or cached.signature is not srchash:
870
+ cached = None
871
+
872
+ if cached is not None:
873
+ # Load all deps in parallel — cached module deps have been pre-validated as
874
+ # non-circular so Promise.all is safe. The loading_promises dict ensures
875
+ # that if two parallel chains try to load the same dep, only one actually
876
+ # does the work; the other waits.
877
+ pending = v'[]'
878
+ for ikey in cached.imported_module_ids:
879
+ if not has_prop(imported_modules, ikey):
880
+ pending.push(do_import(ikey))
881
+ if pending.length > 0:
882
+ await Promise.all(pending)
883
+ # Reconstruct classes dict from the simplified cache (no full AST nodes).
884
+ classes = {}
885
+ for cname in Object.keys(cached.classes or {}):
886
+ ci = cached.classes[cname]
887
+ classes[cname] = {'name': {'name': cname}, 'static': ci.static or {}, 'bound': ci.bound or [], 'classvars': ci.classvars or {}}
888
+ meta = {
889
+ 'module_id': key,
890
+ 'filename': filename,
891
+ 'import_order': Object.keys(imported_modules).length,
892
+ 'imports': imported_modules,
893
+ 'exports': [{'name': n} for n in cached.exported_names],
894
+ 'baselib': cached.baselib or {},
895
+ 'classes': classes,
896
+ }
897
+ if cached.has_side_effects is not undefined:
898
+ meta['_has_side_effects'] = cached.has_side_effects
899
+ if cached.index is not undefined:
900
+ meta['_cache_index'] = cached.index
901
+ module = make_lazy_ast_module(ast_json_str, meta)
902
+ imported_modules[key] = module
903
+ else:
904
+ await parse(src_code, {
905
+ 'filename': filename,
906
+ 'toplevel': None,
907
+ 'basedir': options.basedir,
908
+ 'libdir': options.libdir,
909
+ 'import_dirs': options.import_dirs,
910
+ 'module_id': key,
911
+ 'imported_modules': imported_modules,
912
+ 'importing_modules': importing_modules,
913
+ 'discard_asserts': options.discard_asserts,
914
+ 'module_cache_dir': options.module_cache_dir,
915
+ '_src_hash': srchash,
916
+ '_file_mtime': file_mtime,
917
+ 'loading_promises': loading_promises,
918
+ }) # This function will add the module to imported_modules itself
919
+ finally:
920
+ del loading_promises[key]
921
+ if _rfn[0]:
922
+ _rfn[0](None)
781
923
 
782
924
  imported_modules[key].srchash = srchash
783
925
 
@@ -785,7 +927,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
785
927
  baselib_items[bitem] = True
786
928
 
787
929
  def read_python_flags():
788
- expect_token("keyword", "import")
930
+ expect_token('keyword', 'import')
789
931
  bracketed = is_('punc', '(')
790
932
  if bracketed:
791
933
  next()
@@ -809,15 +951,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
809
951
  else:
810
952
  continue
811
953
  break
812
- return new AST_EmptyStatement({'stype':'scoped_flags', 'start':prev(), 'end':prev()})
954
+ return new AST_EmptyStatement({'stype': 'scoped_flags', 'start': prev(), 'end': prev()})
813
955
 
814
- def import_(from_import):
815
- ans = new AST_Imports({'imports':[]})
956
+ async def import_(from_import):
957
+ ans = new AST_Imports({'imports': []})
816
958
  while True:
817
- tok = tmp = name = last_tok = expression(False)
959
+ tok = tmp = name = last_tok = await expression(False)
818
960
  key = ''
819
961
  while is_node_type(tmp, AST_Dot):
820
- key = "." + tmp.property + key
962
+ key = '.' + tmp.property + key
821
963
  tmp = last_tok = tmp.expression
822
964
  key = tmp.name + key
823
965
  if from_import and key is '__python__':
@@ -830,8 +972,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
830
972
  'module': name,
831
973
  'key': key,
832
974
  'alias': alias,
833
- 'argnames':None,
834
- 'body':def():
975
+ 'argnames': None,
976
+ 'body': def ():
835
977
  return imported_modules[key]
836
978
  })
837
979
  aimp.start, aimp.end = tok.start, last_tok.end
@@ -844,12 +986,12 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
844
986
  break
845
987
 
846
988
  for imp in ans['imports']:
847
- do_import(imp.key)
989
+ await do_import(imp.key)
848
990
  if imported_module_ids.indexOf(imp.key) is -1:
849
991
  imported_module_ids.push(imp.key)
850
992
  classes = imported_modules[key].classes
851
993
  if from_import:
852
- expect_token("keyword", "import")
994
+ expect_token('keyword', 'import')
853
995
  imp.argnames = argnames = []
854
996
  bracketed = is_('punc', '(')
855
997
  if bracketed:
@@ -880,16 +1022,16 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
880
1022
  obj = classes[argvar.name]
881
1023
  if obj:
882
1024
  key = argvar.alias.name if argvar.alias else argvar.name
883
- S.classes[-1][key] = { "static": obj.static, 'bound': obj.bound, 'classvars': obj.classvars }
1025
+ S.classes[-1][key] = {'static': obj.static, 'bound': obj.bound, 'classvars': obj.classvars}
884
1026
  else:
885
1027
  for cname in Object.keys(classes):
886
1028
  obj = classes[cname]
887
1029
  key = imp.alias.name if imp.alias else imp.key
888
- S.classes[-1][key + '.' + obj.name.name] = { 'static': obj.static, 'bound': obj.bound, 'classvars': obj.classvars }
1030
+ S.classes[-1][key + '.' + obj.name.name] = {'static': obj.static, 'bound': obj.bound, 'classvars': obj.classvars}
889
1031
 
890
1032
  return ans
891
1033
 
892
- def class_():
1034
+ async def class_():
893
1035
  name = as_symbol(AST_SymbolDefun)
894
1036
  if not name:
895
1037
  unexpected()
@@ -898,7 +1040,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
898
1040
  externaldecorator = has_simple_decorator(S.decorators, 'external')
899
1041
 
900
1042
  class_details = {
901
- "static": {},
1043
+ 'static': {},
902
1044
  'bound': v'[]',
903
1045
  'classvars': {},
904
1046
  'processing': name.name,
@@ -908,7 +1050,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
908
1050
  class_parent = None
909
1051
 
910
1052
  # read the bases of the class, if any
911
- if is_("punc", "("):
1053
+ if is_('punc', '('):
912
1054
  S.in_parenthesized_expr = True
913
1055
  next()
914
1056
  while True:
@@ -916,7 +1058,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
916
1058
  S.in_parenthesized_expr = False
917
1059
  next()
918
1060
  break
919
- a = expr_atom(False)
1061
+ a = await expr_atom(False)
920
1062
  if class_parent is None:
921
1063
  class_parent = a
922
1064
  bases.push(a)
@@ -925,10 +1067,37 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
925
1067
  continue
926
1068
 
927
1069
  docstrings = v'[]'
1070
+ cls_decorators = (def ():
1071
+ d = []
1072
+ for decorator in S.decorators:
1073
+ d.push(new AST_Decorator({
1074
+ 'expression': decorator
1075
+ }))
1076
+ S.decorators = v'[]'
1077
+ return d
1078
+ )()
1079
+ cls_body = await (async def (loop, labels):
1080
+ # navigate to correct location in the module tree and append the class
1081
+ S.in_class.push(name.name)
1082
+ S.classes[S.classes.length - 1][name.name] = class_details
1083
+ S.classes.push({})
1084
+ S.scoped_flags.push()
1085
+ S.in_function += 1
1086
+ S.in_loop = 0
1087
+ S.labels = []
1088
+ a = await block_(docstrings)
1089
+ S.in_function -= 1
1090
+ S.scoped_flags.pop()
1091
+ S.classes.pop()
1092
+ S.in_class.pop()
1093
+ S.in_loop = loop
1094
+ S.labels = labels
1095
+ return a
1096
+ )(S.in_loop, S.labels)
928
1097
  definition = new AST_Class({
929
1098
  'name': name,
930
1099
  'docstrings': docstrings,
931
- 'module_id':module_id,
1100
+ 'module_id': module_id,
932
1101
  'dynamic_properties': Object.create(None),
933
1102
  'parent': class_parent,
934
1103
  'bases': bases,
@@ -938,33 +1107,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
938
1107
  'external': externaldecorator,
939
1108
  'bound': class_details.bound,
940
1109
  'statements': [],
941
- 'decorators': (def():
942
- d = []
943
- for decorator in S.decorators:
944
- d.push(new AST_Decorator({
945
- 'expression': decorator
946
- }))
947
- S.decorators = v'[]'
948
- return d
949
- )(),
950
- 'body': (def(loop, labels):
951
- # navigate to correct location in the module tree and append the class
952
- S.in_class.push(name.name)
953
- S.classes[S.classes.length - 1][name.name] = class_details
954
- S.classes.push({})
955
- S.scoped_flags.push()
956
- S.in_function += 1
957
- S.in_loop = 0
958
- S.labels = []
959
- a = block_(docstrings)
960
- S.in_function -= 1
961
- S.scoped_flags.pop()
962
- S.classes.pop()
963
- S.in_class.pop()
964
- S.in_loop = loop
965
- S.labels = labels
966
- return a
967
- )(S.in_loop, S.labels)
1110
+ 'decorators': cls_decorators,
1111
+ 'body': cls_body,
968
1112
  })
969
1113
  class_details.processing = False
970
1114
  # find the constructor
@@ -975,10 +1119,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
975
1119
  if not descriptor:
976
1120
  descriptor = definition.dynamic_properties[stmt.name.name] = {}
977
1121
  descriptor['getter' if stmt.is_getter else 'setter'] = stmt
978
- elif stmt.name.name is "__init__":
1122
+ elif stmt.name.name is '__init__':
979
1123
  definition.init = stmt
980
1124
  # find the class variables
981
1125
  class_var_names = {}
1126
+
982
1127
  # Ensure that if a class variable refers to another class variable in
983
1128
  # its initialization, the referenced variables' names is correctly
984
1129
  # mangled.
@@ -996,7 +1141,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
996
1141
  class_var_names[varname] = True
997
1142
  definition.classvars[varname] = True
998
1143
  elif is_node_type(node, AST_SymbolRef) and has_prop(class_var_names, node.name):
999
- node.thedef = new AST_SymbolDefun({'name':name.name + '.prototype.' + node.name})
1144
+ node.thedef = new AST_SymbolDefun({'name': name.name + '.prototype.' + node.name})
1000
1145
  if descend:
1001
1146
  descend.call(node)
1002
1147
  this._visit = visit_node
@@ -1008,7 +1153,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1008
1153
  definition.statements.push(stmt)
1009
1154
  return definition
1010
1155
 
1011
- def function_(in_class, is_expression):
1156
+ async def function_(in_class, is_expression, is_async=False):
1012
1157
  name = as_symbol(AST_SymbolDefun if in_class else AST_SymbolLambda) if is_('name') else None
1013
1158
  if in_class and not name:
1014
1159
  croak('Cannot use anonymous function as class methods')
@@ -1024,196 +1169,214 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1024
1169
  croak('A method cannot be both static and a property getter/setter')
1025
1170
  S.classes[S.classes.length - 2][in_class].static[name.name] = True
1026
1171
  staticmethod = True
1027
- elif name.name is not "__init__" and S.scoped_flags.get('bound_methods'):
1172
+ elif name.name is not '__init__' and S.scoped_flags.get('bound_methods'):
1028
1173
  S.classes[S.classes.length - 2][in_class].bound.push(name.name)
1029
1174
 
1030
- expect("(")
1175
+ expect('(')
1031
1176
  S.in_parenthesized_expr = True
1032
1177
  ctor = AST_Method if in_class else AST_Function
1033
1178
  return_annotation = None
1034
1179
  is_generator = v'[]'
1035
1180
  docstrings = v'[]'
1036
- definition = new ctor({
1037
- 'name': name,
1038
- 'is_expression': is_expression,
1039
- 'is_anonymous': is_anonymous,
1040
- 'argnames': (def(a):
1041
- defaults = {}
1042
- first = True
1043
- seen_names = {}
1044
- def_line = S.input.context().tokline
1045
- current_arg_name = None
1046
- name_token = None
1047
-
1048
- def get_arg():
1049
- nonlocal current_arg_name, name_token
1050
- current_arg_name = S.token.value
1051
- if has_prop(seen_names, current_arg_name):
1052
- token_error(prev(), "Can't repeat parameter names")
1053
- if current_arg_name is 'arguments':
1054
- token_error(prev(), "Can't use the name arguments as a parameter name, it is reserved by JavaScript")
1055
- seen_names[current_arg_name] = True
1056
- # save these in order to move back if we have an annotation
1057
- name_token = S.token
1058
- name_ctx = S.input.context()
1059
- # check if we have an argument annotation
1060
- ntok = peek()
1061
- if ntok.type is 'punc' and ntok.value is ':':
1062
- next()
1063
- expect(':')
1064
- annotation = maybe_conditional()
1065
-
1066
- # and now, do as_symbol without the next() at the end
1067
- # since we are already at the next comma (or end bracket)
1068
- if not is_token(name_token, "name"):
1069
- # assuming the previous context in case
1070
- # the annotation was over the line
1071
- croak("Name expected", name_ctx.tokline)
1072
- return None
1073
-
1074
- sym = new AST_SymbolFunarg({
1075
- 'name': name_token.value,
1076
- 'start': S.token,
1077
- 'end': S.token,
1078
- 'annotation': annotation
1079
- })
1080
- return sym
1081
- else:
1082
- if not is_("name"):
1083
- # there is no name, which is an error we should report on the
1084
- # same line as the definition, so move to that is we're not already there.
1085
- if S.input.context().tokline is not def_line:
1086
- croak("Name expected", def_line)
1087
- else:
1088
- croak("Name expected")
1089
- return None
1090
-
1091
- sym = new AST_SymbolFunarg({
1092
- 'name': current_arg_name,
1093
- 'start': S.token,
1094
- 'end': S.token,
1095
- 'annotation': None
1096
- })
1097
- next()
1098
- return sym
1099
1181
 
1100
- while not is_("punc", ")"):
1101
- if first:
1102
- first = False
1103
- else:
1104
- expect(",")
1105
- if is_('punc', ')'):
1106
- break
1107
- if is_('operator', '**'):
1108
- # **kwargs
1109
- next()
1110
- if a.kwargs:
1111
- token_error(name_token, "Can't define multiple **kwargs in function definition")
1112
- a.kwargs = get_arg()
1113
- elif is_('operator', '*'):
1114
- # *args
1182
+ fn_argnames = await (async def ():
1183
+ a = v'[]'
1184
+ starargs_sym = undefined
1185
+ kwargs_sym = undefined
1186
+ defaults = {}
1187
+ has_defaults = False
1188
+ first = True
1189
+ seen_names = {}
1190
+ def_line = S.input.context().tokline
1191
+ current_arg_name = None
1192
+ name_token = None
1193
+ async def get_arg():
1194
+ nonlocal current_arg_name, name_token
1195
+ current_arg_name = S.token.value
1196
+ if has_prop(seen_names, current_arg_name):
1197
+ token_error(prev(), "Can't repeat parameter names")
1198
+ if current_arg_name is 'arguments':
1199
+ token_error(prev(), "Can't use the name arguments as a parameter name, it is reserved by JavaScript")
1200
+ seen_names[current_arg_name] = True
1201
+ # save these in order to move back if we have an annotation
1202
+ name_token = S.token
1203
+ name_ctx = S.input.context()
1204
+ # check if we have an argument annotation
1205
+ ntok = peek()
1206
+ if ntok.type is 'punc' and ntok.value is ':':
1207
+ next()
1208
+ expect(':')
1209
+ annotation = await maybe_conditional()
1210
+ # and now, do as_symbol without the next() at the end
1211
+ # since we are already at the next comma (or end bracket)
1212
+ if not is_token(name_token, 'name'):
1213
+ # assuming the previous context in case
1214
+ # the annotation was over the line
1215
+ if S.input.context().tokline is not def_line:
1216
+ croak('Name expected', name_ctx.tokline)
1217
+ else:
1218
+ croak('Name expected')
1219
+ return None
1220
+ sym = new AST_SymbolFunarg({
1221
+ 'name': name_token.value,
1222
+ 'start': S.token,
1223
+ 'end': S.token,
1224
+ 'annotation': annotation
1225
+ })
1226
+ return sym
1227
+ else:
1228
+ if not is_('name'):
1229
+ # there is no name, which is an error we should report on the
1230
+ # same line as the definition, so move to that is we're not already there.
1231
+ if S.input.context().tokline is not def_line:
1232
+ croak('Name expected', def_line)
1233
+ else:
1234
+ croak('Name expected')
1235
+ return None
1236
+ sym = new AST_SymbolFunarg({
1237
+ 'name': current_arg_name,
1238
+ 'start': S.token,
1239
+ 'end': S.token,
1240
+ 'annotation': None
1241
+ })
1242
+ next()
1243
+ return sym
1244
+ while not is_('punc', ')'):
1245
+ if first:
1246
+ first = False
1247
+ else:
1248
+ expect(',')
1249
+ if is_('punc', ')'):
1250
+ break
1251
+ if is_('operator', '**'):
1252
+ # **kwargs
1253
+ next()
1254
+ if kwargs_sym:
1255
+ token_error(name_token, "Can't define multiple **kwargs in function definition")
1256
+ kwargs_sym = await get_arg()
1257
+ elif is_('operator', '*'):
1258
+ # *args
1259
+ next()
1260
+ if starargs_sym:
1261
+ token_error(name_token, "Can't define multiple *args in function definition")
1262
+ if kwargs_sym:
1263
+ token_error(name_token, "Can't define *args after **kwargs in function definition")
1264
+ starargs_sym = await get_arg()
1265
+ else:
1266
+ if starargs_sym or kwargs_sym:
1267
+ token_error(name_token, "Can't define a formal parameter after *args or **kwargs")
1268
+ a.push(await get_arg())
1269
+ if is_('operator', '='):
1270
+ if kwargs_sym:
1271
+ token_error(name_token, "Can't define an optional formal parameter after **kwargs")
1115
1272
  next()
1116
- if a.starargs:
1117
- token_error(name_token, "Can't define multiple *args in function definition")
1118
- if a.kwargs:
1119
- token_error(name_token, "Can't define *args after **kwargs in function definition")
1120
- a.starargs = get_arg()
1273
+ defaults[current_arg_name] = await expression(False)
1274
+ has_defaults = True
1121
1275
  else:
1122
- if a.starargs or a.kwargs:
1123
- token_error(name_token, "Can't define a formal parameter after *args or **kwargs")
1124
- a.push(get_arg())
1125
- if is_("operator", "="):
1126
- if a.kwargs:
1127
- token_error(name_token, "Can't define an optional formal parameter after **kwargs")
1128
- next()
1129
- defaults[current_arg_name] = expression(False)
1130
- a.has_defaults = True
1131
- else:
1132
- if a.has_defaults:
1133
- token_error(name_token, "Can't define required formal parameters after optional formal parameters")
1134
-
1276
+ if has_defaults:
1277
+ token_error(name_token, "Can't define required formal parameters after optional formal parameters")
1278
+ next()
1279
+ # check if we have a return type annotation
1280
+ if is_('punc', '->'):
1135
1281
  next()
1136
- # check if we have a return type annotation
1137
- if is_("punc", "->"):
1138
- next()
1139
- nonlocal return_annotation
1140
- return_annotation = maybe_conditional()
1141
- S.in_parenthesized_expr = False
1142
- a.defaults = defaults
1143
- a.is_simple_func = not a.starargs and not a.kwargs and not a.has_defaults
1144
- return a
1145
- )(v'[]'),
1282
+ nonlocal return_annotation
1283
+ return_annotation = await maybe_conditional()
1284
+ S.in_parenthesized_expr = False
1285
+ return new AST_ArgsDef({
1286
+ 'args': a,
1287
+ 'starargs': starargs_sym,
1288
+ 'kwargs': kwargs_sym,
1289
+ 'defaults': defaults,
1290
+ 'has_defaults': has_defaults,
1291
+ 'is_simple_func': not starargs_sym and not kwargs_sym and not has_defaults,
1292
+ })
1293
+ )()
1294
+
1295
+ fn_decorators = (def ():
1296
+ d = v'[]'
1297
+ for decorator in S.decorators:
1298
+ d.push(new AST_Decorator({
1299
+ 'expression': decorator
1300
+ }))
1301
+ S.decorators = v'[]'
1302
+ return d
1303
+ )()
1304
+
1305
+ fn_body = await (async def (loop, labels):
1306
+ S.in_class.push(False)
1307
+ S.classes.push({})
1308
+ S.scoped_flags.push()
1309
+ S.in_function += 1
1310
+ S.functions.push({'is_async': is_async})
1311
+ S.in_loop = 0
1312
+ S.labels = []
1313
+ a = await block_(docstrings)
1314
+ S.in_function -= 1
1315
+ S.scoped_flags.pop()
1316
+ is_generator.push(bool(S.functions.pop().is_generator))
1317
+ S.classes.pop()
1318
+ S.in_class.pop()
1319
+ S.in_loop = loop
1320
+ S.labels = labels
1321
+ return a
1322
+ )(S.in_loop, S.labels)
1323
+
1324
+ definition = new ctor({
1325
+ 'name': name,
1326
+ 'is_expression': is_expression,
1327
+ 'is_anonymous': is_anonymous,
1328
+ 'argnames': fn_argnames,
1146
1329
  'localvars': [],
1147
- 'decorators': (def():
1148
- d = v'[]'
1149
- for decorator in S.decorators:
1150
- d.push(new AST_Decorator({
1151
- 'expression': decorator
1152
- }))
1153
- S.decorators = v'[]'
1154
- return d
1155
- )(),
1330
+ 'decorators': fn_decorators,
1156
1331
  'docstrings': docstrings,
1157
- 'body': (def(loop, labels):
1158
- S.in_class.push(False)
1159
- S.classes.push({})
1160
- S.scoped_flags.push()
1161
- S.in_function += 1
1162
- S.functions.push({})
1163
- S.in_loop = 0
1164
- S.labels = []
1165
- a = block_(docstrings)
1166
- S.in_function -= 1
1167
- S.scoped_flags.pop()
1168
- is_generator.push(bool(S.functions.pop().is_generator))
1169
- S.classes.pop()
1170
- S.in_class.pop()
1171
- S.in_loop = loop
1172
- S.labels = labels
1173
- return a
1174
- )(S.in_loop, S.labels)
1332
+ 'body': fn_body,
1175
1333
  })
1176
1334
  definition.return_annotation = return_annotation
1177
1335
  definition.is_generator = is_generator[0]
1336
+ definition.is_async = is_async
1178
1337
  if is_node_type(definition, AST_Method):
1179
1338
  definition.static = staticmethod
1180
1339
  definition.is_getter = property_getter
1181
1340
  definition.is_setter = property_setter
1182
- if definition.argnames.length < 1 and not definition.static:
1341
+ if definition.argnames.args.length < 1 and not definition.static:
1183
1342
  croak('Methods of a class must have at least one argument, traditionally named self')
1184
1343
  if definition.name and definition.name.name is '__init__':
1185
1344
  if definition.is_generator:
1186
1345
  croak('The __init__ method of a class cannot be a generator (yield not allowed)')
1346
+ if definition.is_async:
1347
+ croak('The __init__ method of a class cannot be async')
1187
1348
  if property_getter or property_setter:
1188
1349
  croak('The __init__ method of a class cannot be a property getter/setter')
1350
+ if definition.is_async and definition.is_generator:
1351
+ croak('A function cannot be both async and a generator')
1189
1352
  if definition.is_generator:
1190
1353
  baselib_items['yield'] = True
1191
1354
 
1192
1355
  # detect local variables, strip function arguments
1193
1356
  assignments = scan_for_local_vars(definition.body)
1194
1357
  for i in range(assignments.length):
1195
- for j in range(definition.argnames.length+1):
1196
- if j is definition.argnames.length:
1358
+ for j in range(definition.argnames.args.length + 1):
1359
+ if j is definition.argnames.args.length:
1197
1360
  definition.localvars.push(new_symbol(AST_SymbolVar, assignments[i]))
1198
- elif j < definition.argnames.length and assignments[i] is definition.argnames[j].name:
1361
+ elif j < definition.argnames.args.length and assignments[i] is definition.argnames.args[j].name:
1199
1362
  break
1200
1363
 
1201
1364
  nonlocals = scan_for_nonlocal_defs(definition.body)
1202
1365
  nonlocals = {name for name in nonlocals}
1203
- definition.localvars = definition.localvars.filter(def(v): return not nonlocals.has(v.name);)
1366
+ definition.localvars = definition.localvars.filter(def (v): return not nonlocals.has(v.name);)
1204
1367
  return definition
1205
1368
 
1206
- def if_():
1207
- cond = expression(True)
1208
- body = statement()
1369
+ async def if_():
1370
+ cond = await expression(True)
1371
+ body = await statement()
1209
1372
  belse = None
1210
- if is_("keyword", "elif") or is_("keyword", "else"):
1211
- if is_("keyword", "else"):
1373
+ if is_('keyword', 'elif') or is_('keyword', 'else'):
1374
+ if is_('keyword', 'else'):
1212
1375
  next()
1213
1376
  else:
1214
- S.token.value = "if"
1377
+ S.token.value = 'if'
1215
1378
  # effectively converts 'elif' to 'else if'
1216
- belse = statement()
1379
+ belse = await statement()
1217
1380
 
1218
1381
  return new AST_If({
1219
1382
  'condition': cond,
@@ -1227,15 +1390,27 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1227
1390
  return stmt.body
1228
1391
  return False
1229
1392
 
1230
- def block_(docstrings):
1393
+ async def block_(docstrings):
1231
1394
  prev_whitespace = S.token.leading_whitespace
1232
- expect(":")
1395
+ expect(':')
1233
1396
  a = v'[]'
1234
1397
  if not S.token.nlb:
1235
1398
  while not S.token.nlb:
1236
- if is_("eof"):
1399
+ if is_('eof'):
1400
+ if options.recover_errors:
1401
+ break
1237
1402
  unexpected()
1238
- stmt = statement()
1403
+ if options.recover_errors:
1404
+ try:
1405
+ stmt = await statement()
1406
+ except as berr:
1407
+ if is_recoverable_error(berr):
1408
+ record_recovery(berr)
1409
+ await skip_to_recovery_point()
1410
+ continue
1411
+ raise
1412
+ else:
1413
+ stmt = await statement()
1239
1414
  if docstrings:
1240
1415
  ds = is_docstring(stmt)
1241
1416
  if ds:
@@ -1246,11 +1421,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1246
1421
  current_whitespace = S.token.leading_whitespace
1247
1422
  if current_whitespace.length is 0 or prev_whitespace is current_whitespace:
1248
1423
  croak('Expected an indented block')
1249
- while not is_("punc", "}"):
1250
- if is_("eof"):
1424
+ while not is_('punc', '}'):
1425
+ if is_('eof'):
1251
1426
  # end of file, terminate block automatically
1252
1427
  return a
1253
- stmt = statement()
1428
+ if options.recover_errors:
1429
+ try:
1430
+ stmt = await statement()
1431
+ except as berr:
1432
+ if is_recoverable_error(berr):
1433
+ record_recovery(berr)
1434
+ await skip_to_recovery_point()
1435
+ continue
1436
+ raise
1437
+ else:
1438
+ stmt = await statement()
1254
1439
  if docstrings:
1255
1440
  ds = is_docstring(stmt)
1256
1441
  if ds:
@@ -1260,79 +1445,89 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1260
1445
  next()
1261
1446
  return a
1262
1447
 
1263
- def try_():
1264
- body = block_()
1448
+ async def try_():
1449
+ body = await block_()
1265
1450
  bcatch = v'[]'
1266
1451
  bfinally = None
1267
1452
  belse = None
1268
- while is_("keyword", "except"):
1453
+ while is_('keyword', 'except'):
1269
1454
  start = S.token
1270
1455
  next()
1271
1456
  exceptions = []
1272
- if not is_("punc", ":") and not is_("keyword", "as"):
1457
+ if not is_('punc', ':') and not is_('keyword', 'as'):
1273
1458
  exceptions.push(as_symbol(AST_SymbolVar))
1274
- while is_("punc", ","):
1459
+ while is_('punc', ','):
1275
1460
  next()
1276
1461
  exceptions.push(as_symbol(AST_SymbolVar))
1277
1462
 
1278
1463
  name = None
1279
- if is_("keyword", "as"):
1464
+ if is_('keyword', 'as'):
1280
1465
  next()
1281
1466
  name = as_symbol(AST_SymbolCatch)
1282
1467
 
1468
+ except_body = await block_()
1283
1469
  bcatch.push(new AST_Except({
1284
1470
  'start': start,
1285
1471
  'argname': name,
1286
1472
  'errors': exceptions,
1287
- 'body': block_(),
1473
+ 'body': except_body,
1288
1474
  'end': prev()
1289
1475
  }))
1290
1476
 
1291
- if is_("keyword", "else"):
1477
+ if is_('keyword', 'else'):
1292
1478
  start = S.token
1293
1479
  next()
1480
+ belse_body = await block_()
1294
1481
  belse = new AST_Else({
1295
1482
  'start': start,
1296
- 'body': block_(),
1483
+ 'body': belse_body,
1297
1484
  'end': prev()
1298
1485
  })
1299
1486
 
1300
- if is_("keyword", "finally"):
1487
+ if is_('keyword', 'finally'):
1301
1488
  start = S.token
1302
1489
  next()
1490
+ bfinally_body = await block_()
1303
1491
  bfinally = new AST_Finally({
1304
1492
  'start': start,
1305
- 'body': block_(),
1493
+ 'body': bfinally_body,
1306
1494
  'end': prev()
1307
1495
  })
1308
1496
 
1309
1497
  if not bcatch.length and not bfinally:
1310
- croak("Missing except/finally blocks")
1498
+ croak('Missing except/finally blocks')
1311
1499
 
1312
1500
  return new AST_Try({
1313
1501
  'body': body,
1314
- 'bcatch': (new AST_Catch({ 'body': bcatch }) if bcatch.length else None),
1502
+ 'bcatch': (new AST_Catch({'body': bcatch}) if bcatch.length else None),
1315
1503
  'bfinally': bfinally,
1316
1504
  'belse': belse
1317
1505
  })
1318
1506
 
1319
- def vardefs(symbol_class):
1507
+ async def vardefs(symbol_class):
1320
1508
  a = []
1321
1509
  while True:
1510
+ vd_start = S.token
1511
+ vd_name = as_symbol(symbol_class)
1512
+ if is_('operator', '='):
1513
+ next()
1514
+ varval = await expression(False)
1515
+ else:
1516
+ varval = None
1322
1517
  a.push(new AST_VarDef({
1323
- 'start': S.token,
1324
- 'name': as_symbol(symbol_class),
1325
- 'value': (next(), expression(False)) if is_('operator', '=') else None,
1518
+ 'start': vd_start,
1519
+ 'name': vd_name,
1520
+ 'value': varval,
1326
1521
  'end': prev()
1327
1522
  }))
1328
- if not is_("punc", ","):
1523
+ if not is_('punc', ','):
1329
1524
  break
1330
1525
  next()
1331
1526
 
1332
1527
  return a
1333
1528
 
1334
- def nonlocal_(is_global):
1335
- defs = vardefs(AST_SymbolNonlocal)
1529
+ async def nonlocal_(is_global):
1530
+ defs = await vardefs(AST_SymbolNonlocal)
1336
1531
  if is_global:
1337
1532
  for vardef in defs:
1338
1533
  S.globals.push(vardef.name.name)
@@ -1342,19 +1537,19 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1342
1537
  'end': prev()
1343
1538
  })
1344
1539
 
1345
- def new_():
1540
+ async def new_():
1346
1541
  start = S.token
1347
- expect_token("operator", "new")
1348
- newexp = expr_atom(False)
1542
+ expect_token('operator', 'new')
1543
+ newexp = await expr_atom(False)
1349
1544
 
1350
- if is_("punc", "("):
1545
+ if is_('punc', '('):
1351
1546
  S.in_parenthesized_expr = True
1352
1547
  next()
1353
- args = func_call_list()
1548
+ args = await func_call_list()
1354
1549
  S.in_parenthesized_expr = False
1355
1550
  else:
1356
- args = func_call_list(True)
1357
- return subscripts(new AST_New({
1551
+ args = await func_call_list(True)
1552
+ return await subscripts(new AST_New({
1358
1553
  'start': start,
1359
1554
  'expression': newexp,
1360
1555
  'args': args,
@@ -1378,40 +1573,40 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1378
1573
  def token_as_atom_node():
1379
1574
  tok = S.token
1380
1575
  tmp_ = tok.type
1381
- if tmp_ is "name":
1576
+ if tmp_ is 'name':
1382
1577
  return token_as_symbol(tok, AST_SymbolRef)
1383
- elif tmp_ is "num":
1578
+ elif tmp_ is 'num':
1384
1579
  return new AST_Number({
1385
1580
  'start': tok,
1386
1581
  'end': tok,
1387
1582
  'value': tok.value
1388
1583
  })
1389
- elif tmp_ is "string":
1584
+ elif tmp_ is 'string':
1390
1585
  return string_()
1391
- elif tmp_ is "regexp":
1586
+ elif tmp_ is 'regexp':
1392
1587
  return new AST_RegExp({
1393
1588
  'start': tok,
1394
1589
  'end': tok,
1395
1590
  'value': tok.value
1396
1591
  })
1397
- elif tmp_ is "atom":
1592
+ elif tmp_ is 'atom':
1398
1593
  tmp__ = tok.value
1399
- if tmp__ is "False":
1594
+ if tmp__ is 'False':
1400
1595
  return new AST_False({
1401
1596
  'start': tok,
1402
1597
  'end': tok
1403
1598
  })
1404
- elif tmp__ is "True":
1599
+ elif tmp__ is 'True':
1405
1600
  return new AST_True({
1406
1601
  'start': tok,
1407
1602
  'end': tok
1408
1603
  })
1409
- elif tmp__ is "None":
1604
+ elif tmp__ is 'None':
1410
1605
  return new AST_Null({
1411
1606
  'start': tok,
1412
1607
  'end': tok
1413
1608
  })
1414
- elif tmp_ is "js":
1609
+ elif tmp_ is 'js':
1415
1610
  return new AST_Verbatim({
1416
1611
  'start': tok,
1417
1612
  'end': tok,
@@ -1424,22 +1619,22 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1424
1619
  next()
1425
1620
  return ret
1426
1621
 
1427
- def expr_atom(allow_calls):
1428
- if is_("operator", "new"):
1429
- return new_()
1622
+ async def expr_atom(allow_calls):
1623
+ if is_('operator', 'new'):
1624
+ return await new_()
1430
1625
 
1431
1626
  start = S.token
1432
- if is_("punc"):
1627
+ if is_('punc'):
1433
1628
  tmp_ = start.value
1434
- if tmp_ is "(":
1629
+ if tmp_ is '(':
1435
1630
  S.in_parenthesized_expr = True
1436
1631
  next()
1437
1632
  if is_('punc', ')'):
1438
1633
  next()
1439
- return new AST_Array({'elements':[]})
1440
- ex = expression(True)
1634
+ return new AST_Array({'elements': []})
1635
+ ex = await expression(True)
1441
1636
  if is_('keyword', 'for'):
1442
- ret = read_comprehension(new AST_GeneratorComprehension({'statement': ex}), ')')
1637
+ ret = await read_comprehension(new AST_GeneratorComprehension({'statement': ex}), ')')
1443
1638
  S.in_parenthesized_expr = False
1444
1639
  return ret
1445
1640
  ex.start = start
@@ -1447,153 +1642,168 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1447
1642
  if is_node_type(ex, AST_SymbolRef):
1448
1643
  ex.parens = True
1449
1644
  if not is_node_type(ex, AST_GeneratorComprehension):
1450
- expect(")")
1645
+ expect(')')
1451
1646
  if is_node_type(ex, AST_UnaryPrefix):
1452
1647
  ex.parenthesized = True
1453
1648
  S.in_parenthesized_expr = False
1454
- return subscripts(ex, allow_calls)
1455
- elif tmp_ is "[":
1456
- return subscripts(array_(), allow_calls)
1457
- elif tmp_ is "{":
1458
- return subscripts(object_(), allow_calls)
1649
+ return await subscripts(ex, allow_calls)
1650
+ elif tmp_ is '[':
1651
+ return await subscripts(await array_(), allow_calls)
1652
+ elif tmp_ is '{':
1653
+ return await subscripts(await object_(), allow_calls)
1459
1654
 
1460
1655
  unexpected()
1461
1656
 
1462
- if is_("keyword", "class"):
1657
+ if is_('keyword', 'class'):
1463
1658
  next()
1464
- cls = class_()
1659
+ cls = await class_()
1465
1660
  cls.start = start
1466
1661
  cls.end = prev()
1467
- return subscripts(cls, allow_calls)
1662
+ return await subscripts(cls, allow_calls)
1468
1663
 
1469
- if is_("keyword", "def"):
1664
+ if is_('keyword', 'async'):
1470
1665
  next()
1471
- func = function_(False, True)
1666
+ if not is_('keyword', 'def'):
1667
+ croak("Expected 'def' after 'async'")
1668
+ next()
1669
+ func = await function_(False, True, True)
1472
1670
  func.start = start
1473
1671
  func.end = prev()
1474
- return subscripts(func, allow_calls)
1672
+ return await subscripts(func, allow_calls)
1673
+
1674
+ if is_('keyword', 'def'):
1675
+ next()
1676
+ func = await function_(False, True)
1677
+ func.start = start
1678
+ func.end = prev()
1679
+ return await subscripts(func, allow_calls)
1680
+
1681
+ if is_('keyword', 'await'):
1682
+ next()
1683
+ if S.in_function is 0 or not S.functions[-1].is_async:
1684
+ croak("'await' outside of async function")
1685
+ value = await expr_atom(True)
1686
+ return new AST_Await({'value': value})
1475
1687
 
1476
1688
  if is_('keyword', 'yield'):
1477
1689
  next()
1478
- return yield_()
1690
+ return await yield_()
1479
1691
 
1480
1692
  if ATOMIC_START_TOKEN[S.token.type]:
1481
- return subscripts(as_atom_node(), allow_calls)
1693
+ return await subscripts(as_atom_node(), allow_calls)
1482
1694
 
1483
1695
  unexpected()
1484
1696
 
1485
- def expr_list(closing, allow_trailing_comma, allow_empty, func_call):
1697
+ async def expr_list(closing, allow_trailing_comma, allow_empty, func_call):
1486
1698
  first = True
1487
1699
  a = []
1488
1700
  saw_starargs = False
1489
- while not is_("punc", closing):
1701
+ while not is_('punc', closing):
1490
1702
  if saw_starargs:
1491
- token_error(prev(), "*args must be the last argument in a function call")
1703
+ token_error(prev(), '*args must be the last argument in a function call')
1492
1704
 
1493
1705
  if first:
1494
1706
  first = False
1495
1707
  else:
1496
- expect(",")
1497
- if allow_trailing_comma and is_("punc", closing):
1708
+ expect(',')
1709
+ if allow_trailing_comma and is_('punc', closing):
1498
1710
  break
1499
1711
 
1500
- if is_("operator", "*") and func_call:
1712
+ if is_('operator', '*') and func_call:
1501
1713
  saw_starargs = True
1502
1714
  next()
1503
1715
 
1504
- if is_("punc", ",") and allow_empty:
1716
+ if is_('punc', ',') and allow_empty:
1505
1717
  a.push(new AST_Hole({
1506
1718
  'start': S.token,
1507
1719
  'end': S.token
1508
1720
  }))
1509
1721
  else:
1510
- a.push(expression(False))
1722
+ a.push(await expression(False))
1511
1723
 
1724
+ next()
1512
1725
  if func_call:
1513
- tmp = []
1514
- tmp.kwargs = []
1726
+ pargs = []
1727
+ kwargs = []
1515
1728
  for arg in a:
1516
1729
  if is_node_type(arg, AST_Assign):
1517
- tmp.kwargs.push([arg.left, arg.right])
1730
+ kwargs.push([arg.left, arg.right])
1518
1731
  else:
1519
- tmp.push(arg)
1520
- a = tmp
1521
-
1522
- next()
1523
- if saw_starargs:
1524
- a.starargs = True
1732
+ pargs.push(arg)
1733
+ return new AST_CallArgs({'args': pargs, 'kwargs': kwargs, 'kwarg_items': v'[]', 'starargs': saw_starargs})
1525
1734
  return a
1526
1735
 
1527
- def func_call_list(empty):
1736
+ async def func_call_list(empty):
1528
1737
  a = v'[]'
1529
1738
  first = True
1530
- a.kwargs = v'[]'
1531
- a.kwarg_items = v'[]'
1532
- a.starargs = False
1739
+ kwargs = v'[]'
1740
+ kwarg_items = v'[]'
1741
+ starargs = False
1533
1742
  if empty:
1534
- return a
1743
+ return new AST_CallArgs({'args': a, 'kwargs': kwargs, 'kwarg_items': kwarg_items, 'starargs': starargs})
1535
1744
  single_comprehension = False
1536
- while not is_("punc", ')') and not is_('eof'):
1745
+ while not is_('punc', ')') and not is_('eof'):
1537
1746
  if not first:
1538
- expect(",")
1747
+ expect(',')
1539
1748
  if is_('punc', ')'):
1540
1749
  break
1541
1750
  if is_('operator', '*'):
1542
1751
  next()
1543
- arg = expression(False)
1544
- arg.is_array = True
1545
- a.push(arg)
1546
- a.starargs = True
1752
+ arg = await expression(False)
1753
+ a.push(new AST_CallArg({'value': arg, 'is_array': True}))
1754
+ starargs = True
1547
1755
  elif is_('operator', '**'):
1548
1756
  next()
1549
- a.kwarg_items.push(as_symbol(AST_SymbolRef, False))
1550
- a.starargs = True
1757
+ kwarg_items.push(as_symbol(AST_SymbolRef, False))
1758
+ starargs = True
1551
1759
  else:
1552
- arg = expression(False)
1760
+ arg = await expression(False)
1553
1761
  if is_node_type(arg, AST_Assign):
1554
- a.kwargs.push([arg.left, arg.right])
1762
+ kwargs.push([arg.left, arg.right])
1555
1763
  else:
1556
1764
  if is_('keyword', 'for'):
1557
1765
  if not first:
1558
1766
  croak('Generator expression must be parenthesized if not sole argument')
1559
- a.push(read_comprehension(new AST_GeneratorComprehension({'statement': arg}), ')'))
1767
+ comp = await read_comprehension(new AST_GeneratorComprehension({'statement': arg}), ')')
1768
+ a.push(new AST_CallArg({'value': comp, 'is_array': False}))
1560
1769
  single_comprehension = True
1561
1770
  break
1562
- a.push(arg)
1771
+ a.push(new AST_CallArg({'value': arg, 'is_array': False}))
1563
1772
  first = False
1564
1773
  if not single_comprehension:
1565
1774
  next()
1566
- return a
1775
+ return new AST_CallArgs({'args': a, 'kwargs': kwargs, 'kwarg_items': kwarg_items, 'starargs': starargs})
1567
1776
 
1568
1777
  @embed_tokens
1569
- def array_():
1570
- expect("[")
1778
+ async def array_():
1779
+ expect('[')
1571
1780
  expr = []
1572
- if not is_("punc", "]"):
1573
- expr.push(expression(False))
1574
- if is_("keyword", "for"):
1781
+ if not is_('punc', ']'):
1782
+ expr.push(await expression(False))
1783
+ if is_('keyword', 'for'):
1575
1784
  # list comprehension
1576
- return read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
1785
+ return await read_comprehension(new AST_ListComprehension({'statement': expr[0]}), ']')
1577
1786
 
1578
- if not is_("punc", "]"):
1579
- expect(",")
1787
+ if not is_('punc', ']'):
1788
+ expect(',')
1580
1789
 
1790
+ arr_rest = await expr_list(']', True, True)
1581
1791
  return new AST_Array({
1582
- 'elements': expr.concat(expr_list("]", True, True))
1792
+ 'elements': expr.concat(arr_rest)
1583
1793
  })
1584
1794
 
1585
1795
  @embed_tokens
1586
- def object_():
1587
- expect("{")
1796
+ async def object_():
1797
+ expect('{')
1588
1798
  first = True
1589
1799
  has_non_const_keys = False
1590
1800
  is_pydict = S.scoped_flags.get('dict_literals', False)
1591
1801
  is_jshash = S.scoped_flags.get('hash_literals', False)
1592
1802
  a = []
1593
- while not is_("punc", "}"):
1803
+ while not is_('punc', '}'):
1594
1804
  if not first:
1595
- expect(",")
1596
- if is_("punc", "}"):
1805
+ expect(',')
1806
+ if is_('punc', '}'):
1597
1807
  # allow trailing comma
1598
1808
  break
1599
1809
  first = False
@@ -1603,26 +1813,27 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1603
1813
  orig = ctx.expecting_object_literal_key
1604
1814
  ctx.expecting_object_literal_key = True
1605
1815
  try:
1606
- left = expression(False)
1816
+ left = await expression(False)
1607
1817
  finally:
1608
1818
  ctx.expecting_object_literal_key = orig
1609
1819
  if is_('keyword', 'for'):
1610
1820
  # is_pydict is irrelevant here
1611
- return read_comprehension(new AST_SetComprehension({'statement':left}), '}')
1821
+ return await read_comprehension(new AST_SetComprehension({'statement': left}), '}')
1612
1822
  if a.length is 0 and (is_('punc', ',') or is_('punc', '}')):
1613
1823
  end = prev()
1614
- return set_(start, end, left)
1824
+ return await set_(start, end, left)
1615
1825
  if not is_node_type(left, AST_Constant):
1616
1826
  has_non_const_keys = True
1617
- expect(":")
1827
+ expect(':')
1828
+ obj_val = await expression(False)
1618
1829
  a.push(new AST_ObjectKeyVal({
1619
1830
  'start': start,
1620
1831
  'key': left,
1621
- 'value': expression(False),
1832
+ 'value': obj_val,
1622
1833
  'end': prev()
1623
1834
  }))
1624
1835
  if a.length is 1 and is_('keyword', 'for'):
1625
- return dict_comprehension(a, is_pydict, is_jshash)
1836
+ return await dict_comprehension(a, is_pydict, is_jshash)
1626
1837
 
1627
1838
  next()
1628
1839
  return new (AST_ExpressiveObject if has_non_const_keys else AST_Object)({
@@ -1631,50 +1842,58 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1631
1842
  'is_jshash': is_jshash,
1632
1843
  })
1633
1844
 
1634
- def set_(start, end, expr):
1845
+ async def set_(start, end, expr):
1635
1846
  ostart = start
1636
- a = [new AST_SetItem({'start':start, 'end':end, 'value':expr})]
1637
- while not is_("punc", "}"):
1638
- expect(",")
1847
+ a = [new AST_SetItem({'start': start, 'end': end, 'value': expr})]
1848
+ while not is_('punc', '}'):
1849
+ expect(',')
1639
1850
  start = S.token
1640
- if is_("punc", "}"):
1851
+ if is_('punc', '}'):
1641
1852
  # allow trailing comma
1642
1853
  break
1643
- a.push(new AST_SetItem({'start':start, 'value':expression(False), 'end':prev()}))
1854
+ set_val = await expression(False)
1855
+ a.push(new AST_SetItem({'start': start, 'value': set_val, 'end': prev()}))
1644
1856
  next()
1645
- return new AST_Set({'items':a, 'start':ostart, 'end':prev()})
1857
+ return new AST_Set({'items': a, 'start': ostart, 'end': prev()})
1646
1858
 
1647
- def read_comprehension(obj, terminator):
1859
+ async def read_comprehension(obj, terminator):
1648
1860
  if is_node_type(obj, AST_GeneratorComprehension):
1649
1861
  baselib_items['yield'] = True
1650
1862
  S.in_comprehension = True
1651
1863
  S.in_parenthesized_expr = False # in case we are already in a parenthesized expression
1652
1864
  expect_token('keyword', 'for')
1653
- forloop = for_(True)
1865
+ forloop = await for_(True)
1654
1866
  obj.init = forloop.init
1655
1867
  obj.name = forloop.name
1656
1868
  obj.object = forloop.object
1657
- obj.condition = None if is_('punc', terminator) else (expect_token("keyword", "if"), expression(True))
1869
+ if is_('punc', terminator):
1870
+ obj.condition = None
1871
+ else:
1872
+ expect_token('keyword', 'if')
1873
+ obj.condition = await expression(True)
1658
1874
  expect(terminator)
1659
1875
  S.in_comprehension = False
1660
1876
  return obj
1661
1877
 
1662
- def dict_comprehension(a, is_pydict, is_jshash):
1878
+ async def dict_comprehension(a, is_pydict, is_jshash):
1663
1879
  if a.length:
1664
1880
  left, right = a[0].key, a[0].value
1665
1881
  else:
1666
- left = expression(False)
1882
+ left = await expression(False)
1667
1883
  if not is_('punc', ':'):
1668
- return read_comprehension(new AST_SetComprehension({'statement':left}), '}')
1884
+ return await read_comprehension(new AST_SetComprehension({'statement': left}), '}')
1669
1885
  expect(':')
1670
- right = expression(False)
1671
- return read_comprehension(new AST_DictComprehension({'statement':left, 'value_statement':right, 'is_pydict':is_pydict, 'is_jshash':is_jshash}), '}')
1886
+ right = await expression(False)
1887
+ return await read_comprehension(
1888
+ new AST_DictComprehension({'statement': left, 'value_statement': right, 'is_pydict': is_pydict, 'is_jshash': is_jshash}),
1889
+ '}'
1890
+ )
1672
1891
 
1673
1892
  def as_name():
1674
1893
  tmp = S.token
1675
1894
  next()
1676
1895
  tmp_ = tmp.type
1677
- if tmp_ is "name" or tmp_ is "operator" or tmp_ is "keyword" or tmp_ is "atom":
1896
+ if tmp_ is 'name' or tmp_ is 'operator' or tmp_ is 'keyword' or tmp_ is 'atom':
1678
1897
  return tmp.value
1679
1898
  else:
1680
1899
  unexpected()
@@ -1690,9 +1909,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1690
1909
  })
1691
1910
 
1692
1911
  def as_symbol(ttype, noerror):
1693
- if not is_("name"):
1912
+ if not is_('name'):
1694
1913
  if not noerror:
1695
- croak("Name expected")
1914
+ croak('Name expected')
1696
1915
  return None
1697
1916
 
1698
1917
  sym = token_as_symbol(S.token, ttype)
@@ -1714,49 +1933,50 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1714
1933
  else:
1715
1934
  return False
1716
1935
 
1717
- def getitem(expr, allow_calls):
1936
+ async def getitem(expr, allow_calls):
1718
1937
  start = expr.start
1719
1938
  next()
1720
1939
  is_py_sub = S.scoped_flags.get('overload_getitem', False)
1721
1940
  slice_bounds = v'[]'
1722
1941
  is_slice = False
1723
- if is_("punc", ":"):
1942
+ if is_('punc', ':'):
1724
1943
  # slice [:n]
1725
1944
  slice_bounds.push(None)
1726
1945
  else:
1727
- slice_bounds.push(expression(False))
1946
+ slice_bounds.push(await expression(False))
1728
1947
 
1729
- if is_("punc", ":"):
1948
+ if is_('punc', ':'):
1730
1949
  # slice [n:m?]
1731
1950
  is_slice = True
1732
1951
  next()
1733
- if is_("punc", ":"):
1952
+ if is_('punc', ':'):
1734
1953
  slice_bounds.push(None)
1735
- elif not is_("punc", "]"):
1736
- slice_bounds.push(expression(False))
1954
+ elif not is_('punc', ']'):
1955
+ slice_bounds.push(await expression(False))
1737
1956
 
1738
- if is_("punc", ":"):
1957
+ if is_('punc', ':'):
1739
1958
  # slice [n:m:o?]
1740
1959
  next()
1741
- if is_("punc", "]"):
1960
+ if is_('punc', ']'):
1742
1961
  unexpected()
1743
1962
  else:
1744
- slice_bounds.push(expression(False))
1963
+ slice_bounds.push(await expression(False))
1745
1964
 
1746
- expect("]")
1965
+ expect(']')
1747
1966
 
1748
1967
  if is_slice:
1749
- if is_("operator", '='):
1968
+ if is_('operator', '='):
1750
1969
  # splice-assignment (arr[start:end] = ...)
1751
1970
  next() # swallow the assignment
1752
- return subscripts(new AST_Splice({
1971
+ splice_assign = await expression(True)
1972
+ return await subscripts(new AST_Splice({
1753
1973
  'start': start,
1754
1974
  'expression': expr,
1755
1975
  'property': slice_bounds[0] or new AST_Number({
1756
1976
  'value': 0
1757
1977
  }),
1758
1978
  'property2': slice_bounds[1],
1759
- 'assignment': expression(True),
1979
+ 'assignment': splice_assign,
1760
1980
  'end': prev()
1761
1981
  }), allow_calls)
1762
1982
  elif slice_bounds.length is 3:
@@ -1768,34 +1988,46 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1768
1988
  slice_bounds.pop()
1769
1989
  elif not slice_bounds[-2]:
1770
1990
  slice_bounds[-2] = new AST_Undefined()
1771
- return subscripts(new AST_Call({
1991
+ _ca = v'[]'
1992
+ _ca.push(new AST_CallArg({'value': expr, 'is_array': False}))
1993
+ for _sb in slice_bounds:
1994
+ _ca.push(new AST_CallArg({'value': _sb, 'is_array': False}))
1995
+ return await subscripts(new AST_Call({
1772
1996
  'start': start,
1773
1997
  'expression': new AST_SymbolRef({
1774
- 'name': 'ρσ_delslice' if S.in_delete else "ρσ_eslice"
1998
+ 'name': 'ρσ_delslice' if S.in_delete else 'ρσ_eslice'
1775
1999
  }),
1776
- 'args': [expr].concat(slice_bounds),
2000
+ 'args': new AST_CallArgs({'args': _ca, 'kwargs': v'[]', 'kwarg_items': v'[]', 'starargs': False}),
1777
2001
  'end': prev()
1778
2002
  }), allow_calls)
1779
2003
  else:
1780
2004
  # regular slice (arr[start:end])
1781
- slice_bounds = [new AST_Number({'value':0}) if i is None else i for i in slice_bounds]
2005
+ slice_bounds = [new AST_Number({'value': 0}) if i is None else i for i in slice_bounds]
1782
2006
  if S.in_delete:
1783
- return subscripts(new AST_Call({
2007
+ _ca2 = v'[]'
2008
+ _ca2.push(new AST_CallArg({'value': expr, 'is_array': False}))
2009
+ _ca2.push(new AST_CallArg({'value': new AST_Number({'value': 1}), 'is_array': False}))
2010
+ for _sb in slice_bounds:
2011
+ _ca2.push(new AST_CallArg({'value': _sb, 'is_array': False}))
2012
+ return await subscripts(new AST_Call({
1784
2013
  'start': start,
1785
2014
  'expression': new AST_SymbolRef({'name': 'ρσ_delslice'}),
1786
- 'args': [expr, new AST_Number({'value':1})].concat(slice_bounds),
2015
+ 'args': new AST_CallArgs({'args': _ca2, 'kwargs': v'[]', 'kwarg_items': v'[]', 'starargs': False}),
1787
2016
  'end': prev()
1788
2017
  }), allow_calls)
1789
2018
 
1790
- return subscripts(new AST_Call({
2019
+ _ca3 = v'[]'
2020
+ for _sb in slice_bounds:
2021
+ _ca3.push(new AST_CallArg({'value': _sb, 'is_array': False}))
2022
+ return await subscripts(new AST_Call({
1791
2023
  'start': start,
1792
2024
  'expression': new AST_Dot({
1793
2025
  'start': start,
1794
2026
  'expression': expr,
1795
- 'property': "slice",
2027
+ 'property': 'slice',
1796
2028
  'end': prev()
1797
2029
  }),
1798
- 'args': slice_bounds,
2030
+ 'args': new AST_CallArgs({'args': _ca3, 'kwargs': v'[]', 'kwarg_items': v'[]', 'starargs': False}),
1799
2031
  'end': prev()
1800
2032
  }), allow_calls)
1801
2033
  else:
@@ -1803,22 +2035,22 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1803
2035
  if is_py_sub:
1804
2036
  assignment = None
1805
2037
  assign_operator = ''
1806
- if is_("operator") and ASSIGNMENT[S.token.value]:
2038
+ if is_('operator') and ASSIGNMENT[S.token.value]:
1807
2039
  assign_operator = S.token.value[:-1]
1808
2040
  next()
1809
- assignment = expression(True)
1810
- return subscripts(new AST_ItemAccess({
2041
+ assignment = await expression(True)
2042
+ return await subscripts(new AST_ItemAccess({
1811
2043
  'start': start,
1812
2044
  'expression': expr,
1813
2045
  'property': slice_bounds[0] or new AST_Number({
1814
2046
  'value': 0
1815
2047
  }),
1816
- 'assignment':assignment,
2048
+ 'assignment': assignment,
1817
2049
  'assign_operator': assign_operator,
1818
2050
  'end': prev()
1819
2051
  }), allow_calls)
1820
2052
 
1821
- return subscripts(new AST_Sub({
2053
+ return await subscripts(new AST_Sub({
1822
2054
  'start': start,
1823
2055
  'expression': expr,
1824
2056
  'property': slice_bounds[0] or new AST_Number({
@@ -1827,16 +2059,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1827
2059
  'end': prev()
1828
2060
  }), allow_calls)
1829
2061
 
1830
- def call_(expr):
2062
+ async def call_(expr):
1831
2063
  start = expr.start
1832
2064
  S.in_parenthesized_expr = True
1833
2065
  next()
1834
2066
  if not expr.parens and get_class_in_scope(expr):
1835
2067
  # this is an object being created using a class
1836
- ret = subscripts(new AST_New({
2068
+ call_args = await func_call_list()
2069
+ ret = await subscripts(new AST_New({
1837
2070
  'start': start,
1838
2071
  'expression': expr,
1839
- 'args': func_call_list(),
2072
+ 'args': call_args,
1840
2073
  'end': prev()
1841
2074
  }), True)
1842
2075
  S.in_parenthesized_expr = False
@@ -1849,52 +2082,55 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1849
2082
  # generate class call
1850
2083
  funcname = expr
1851
2084
 
1852
- ret = subscripts(new AST_ClassCall({
2085
+ classcall_args = await func_call_list()
2086
+ ret = await subscripts(new AST_ClassCall({
1853
2087
  'start': start,
1854
- "class": expr.expression,
2088
+ 'class': expr.expression,
1855
2089
  'method': funcname.property,
1856
- "static": is_static_method(c, funcname.property),
1857
- 'args': func_call_list(),
2090
+ 'static': is_static_method(c, funcname.property),
2091
+ 'args': classcall_args,
1858
2092
  'end': prev()
1859
2093
  }), True)
1860
2094
  S.in_parenthesized_expr = False
1861
2095
  return ret
1862
2096
  elif is_node_type(expr, AST_SymbolRef):
1863
2097
  tmp_ = expr.name
1864
- if tmp_ is "jstype":
2098
+ if tmp_ is 'jstype':
2099
+ jstype_args = await func_call_list()
1865
2100
  ret = new AST_UnaryPrefix({
1866
2101
  'start': start,
1867
- 'operator': "typeof",
1868
- 'expression': func_call_list()[0],
2102
+ 'operator': 'typeof',
2103
+ 'expression': jstype_args.args[0].value,
1869
2104
  'end': prev()
1870
2105
  })
1871
2106
  S.in_parenthesized_expr = False
1872
2107
  return ret
1873
- elif tmp_ is "isinstance":
1874
- args = func_call_list()
1875
- if args.length is not 2:
2108
+ elif tmp_ is 'isinstance':
2109
+ args = await func_call_list()
2110
+ if args.args.length is not 2:
1876
2111
  croak('isinstance() must be called with exactly two arguments')
1877
2112
  ret = new AST_Binary({
1878
2113
  'start': start,
1879
- 'left': args[0],
2114
+ 'left': args.args[0].value,
1880
2115
  'operator': 'instanceof',
1881
- 'right': args[1],
2116
+ 'right': args.args[1].value,
1882
2117
  'end': prev()
1883
2118
  })
1884
2119
  S.in_parenthesized_expr = False
1885
2120
  return ret
1886
2121
 
1887
2122
  # fall-through to basic function call
1888
- ret = subscripts(new AST_Call({
2123
+ basic_args = await func_call_list()
2124
+ ret = await subscripts(new AST_Call({
1889
2125
  'start': start,
1890
2126
  'expression': expr,
1891
- 'args': func_call_list(),
2127
+ 'args': basic_args,
1892
2128
  'end': prev()
1893
2129
  }), True)
1894
2130
  S.in_parenthesized_expr = False
1895
2131
  return ret
1896
2132
 
1897
- def get_attr(expr, allow_calls):
2133
+ async def get_attr(expr, allow_calls):
1898
2134
  next()
1899
2135
  prop = as_name()
1900
2136
  c = get_class_in_scope(expr)
@@ -1903,15 +2139,15 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1903
2139
  if classvars and v'classvars[prop]':
1904
2140
  prop = 'prototype.' + prop
1905
2141
 
1906
- return subscripts(new AST_Dot({
2142
+ return await subscripts(new AST_Dot({
1907
2143
  'start': expr.start,
1908
2144
  'expression': expr,
1909
2145
  'property': prop,
1910
2146
  'end': prev()
1911
2147
  }), allow_calls)
1912
2148
 
1913
- def existential(expr, allow_calls):
1914
- ans = new AST_Existential({'start':expr.start, 'end':S.token, 'expression':expr})
2149
+ async def existential(expr, allow_calls):
2150
+ ans = new AST_Existential({'start': expr.start, 'end': S.token, 'expression': expr})
1915
2151
  next()
1916
2152
  ttype = S.token.type
1917
2153
  val = S.token.value
@@ -1931,49 +2167,49 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1931
2167
  else:
1932
2168
  ans.after = None
1933
2169
  return ans
1934
- return subscripts(ans, allow_calls)
2170
+ return await subscripts(ans, allow_calls)
1935
2171
 
1936
- ans.after = expression()
2172
+ ans.after = await expression()
1937
2173
  return ans
1938
2174
 
1939
- def subscripts(expr, allow_calls):
1940
- if is_("punc", "."):
1941
- return get_attr(expr, allow_calls)
2175
+ async def subscripts(expr, allow_calls):
2176
+ if is_('punc', '.'):
2177
+ return await get_attr(expr, allow_calls)
1942
2178
 
1943
- if is_("punc", "[") and not S.token.nlb:
1944
- return getitem(expr, allow_calls)
2179
+ if is_('punc', '[') and not S.token.nlb:
2180
+ return await getitem(expr, allow_calls)
1945
2181
 
1946
- if allow_calls and is_("punc", "(") and not S.token.nlb:
1947
- return call_(expr)
2182
+ if allow_calls and is_('punc', '(') and not S.token.nlb:
2183
+ return await call_(expr)
1948
2184
 
1949
2185
  if is_('punc', '?'):
1950
- return existential(expr, allow_calls)
2186
+ return await existential(expr, allow_calls)
1951
2187
 
1952
2188
  return expr
1953
2189
 
1954
- def maybe_unary(allow_calls):
2190
+ async def maybe_unary(allow_calls):
1955
2191
  start = S.token
1956
2192
  if is_('operator', '@'):
1957
2193
  if S.parsing_decorator:
1958
2194
  croak('Nested decorators are not allowed')
1959
2195
  next()
1960
2196
  S.parsing_decorator = True
1961
- expr = expression()
2197
+ expr = await expression()
1962
2198
  S.parsing_decorator = False
1963
2199
  S.decorators.push(expr)
1964
- return new AST_EmptyStatement({'stype':'@', 'start':prev(), 'end':prev()})
1965
- if is_("operator") and UNARY_PREFIX[start.value]:
2200
+ return new AST_EmptyStatement({'stype': '@', 'start': prev(), 'end': prev()})
2201
+ if is_('operator') and UNARY_PREFIX[start.value]:
1966
2202
  next()
1967
2203
  is_parenthesized = is_('punc', '(')
1968
2204
  S.in_delete = start.value is 'delete'
1969
- expr = maybe_unary(allow_calls)
2205
+ expr = await maybe_unary(allow_calls)
1970
2206
  S.in_delete = False
1971
2207
  ex = make_unary(AST_UnaryPrefix, start.value, expr, is_parenthesized)
1972
2208
  ex.start = start
1973
2209
  ex.end = prev()
1974
2210
  return ex
1975
2211
 
1976
- val = expr_atom(allow_calls)
2212
+ val = await expr_atom(allow_calls)
1977
2213
  return val
1978
2214
 
1979
2215
  def make_unary(ctor, op, expr, is_parenthesized):
@@ -1983,19 +2219,19 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1983
2219
  'parenthesized': is_parenthesized
1984
2220
  })
1985
2221
 
1986
- def expr_op(left, min_prec, no_in):
2222
+ async def expr_op(left, min_prec, no_in):
1987
2223
  op = S.token.value if is_('operator') else None
1988
- if op is "!" and peek().type is "operator" and peek().value is "in":
2224
+ if op is '!' and peek().type is 'operator' and peek().value is 'in':
1989
2225
  next()
1990
2226
  S.token.value = op = 'nin'
1991
2227
 
1992
- if no_in and (op is "in" or op is 'nin'):
2228
+ if no_in and (op is 'in' or op is 'nin'):
1993
2229
  op = None
1994
2230
 
1995
2231
  prec = PRECEDENCE[op] if op is not None else None
1996
2232
  if prec is not None and prec > min_prec:
1997
2233
  next()
1998
- right = expr_op(maybe_unary(True), prec, no_in)
2234
+ right = await expr_op(await maybe_unary(True), prec, no_in)
1999
2235
  ret = new AST_Binary({
2000
2236
  'start': left.start,
2001
2237
  'left': left,
@@ -2003,24 +2239,25 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2003
2239
  'right': right,
2004
2240
  'end': right.end
2005
2241
  })
2006
- return expr_op(ret, min_prec, no_in)
2242
+ return await expr_op(ret, min_prec, no_in)
2007
2243
  return left
2008
2244
 
2009
- def expr_ops(no_in):
2010
- return expr_op(maybe_unary(True), 0, no_in)
2245
+ async def expr_ops(no_in):
2246
+ return await expr_op(await maybe_unary(True), 0, no_in)
2011
2247
 
2012
- def maybe_conditional(no_in):
2248
+ async def maybe_conditional(no_in):
2013
2249
  start = S.token
2014
- expr = expr_ops(no_in)
2250
+ expr = await expr_ops(no_in)
2015
2251
  if (is_('keyword', 'if') and (S.in_parenthesized_expr or (S.statement_starting_token is not S.token and not S.in_comprehension and not S.token.nlb))):
2016
2252
  next()
2017
- ne = expression(False)
2253
+ ne = await expression(False)
2018
2254
  expect_token('keyword', 'else')
2255
+ cond_alt = await expression(False, no_in)
2019
2256
  conditional = new AST_Conditional({
2020
2257
  'start': start,
2021
2258
  'condition': ne,
2022
2259
  'consequent': expr,
2023
- 'alternative': expression(False, no_in),
2260
+ 'alternative': cond_alt,
2024
2261
  'end': peek()
2025
2262
  })
2026
2263
  return conditional
@@ -2044,28 +2281,30 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2044
2281
  c.provisional_classvars[ans.left.name] = True
2045
2282
  return ans
2046
2283
 
2047
- def maybe_assign(no_in, only_plain_assignment):
2284
+ async def maybe_assign(no_in, only_plain_assignment):
2048
2285
  start = S.token
2049
- left = maybe_conditional(no_in)
2286
+ left = await maybe_conditional(no_in)
2050
2287
  val = S.token.value
2051
- if is_("operator") and ASSIGNMENT[val]:
2288
+ if is_('operator') and ASSIGNMENT[val]:
2052
2289
  if only_plain_assignment and val is not '=':
2053
2290
  croak('Invalid assignment operator for chained assignment: ' + val)
2054
2291
  next()
2292
+ assign_right = await maybe_assign(no_in, True)
2055
2293
  return create_assign({
2056
2294
  'start': start,
2057
2295
  'left': left,
2058
2296
  'operator': val,
2059
- 'right': maybe_assign(no_in, True),
2297
+ 'right': assign_right,
2060
2298
  'end': prev()
2061
2299
  })
2062
2300
  return left
2063
2301
 
2064
- def expression(commas, no_in):
2302
+ async def expression(commas, no_in):
2065
2303
  # if there is an assignment, we want the sequences to pivot
2066
2304
  # around it to allow for tuple packing/unpacking
2067
2305
  start = S.token
2068
- expr = maybe_assign(no_in)
2306
+ expr = await maybe_assign(no_in)
2307
+
2069
2308
  def build_seq(a):
2070
2309
  if a.length is 1:
2071
2310
  return a[0]
@@ -2078,10 +2317,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2078
2317
  })
2079
2318
  if commas:
2080
2319
  left = v'[ expr ]'
2081
- while is_("punc", ",") and not peek().nlb:
2320
+ while is_('punc', ',') and (not peek().nlb or S.in_parenthesized_expr):
2082
2321
  next()
2083
2322
  if is_node_type(expr, AST_Assign):
2084
2323
  left[-1] = left[-1].left
2324
+ seq_cdr = await expression(True, no_in)
2085
2325
  return create_assign({
2086
2326
  'start': start,
2087
2327
  'left': (left[0] if left.length is 1 else new AST_Array({
@@ -2090,12 +2330,12 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2090
2330
  'operator': expr.operator,
2091
2331
  'right': new AST_Seq({
2092
2332
  'car': expr.right,
2093
- 'cdr': expression(True, no_in)
2333
+ 'cdr': seq_cdr
2094
2334
  }),
2095
2335
  'end': peek()
2096
2336
  })
2097
2337
 
2098
- expr = maybe_assign(no_in)
2338
+ expr = await maybe_assign(no_in)
2099
2339
  left.push(expr)
2100
2340
 
2101
2341
  # if last one was an assignment, fix it
@@ -2114,20 +2354,30 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2114
2354
  return build_seq(left)
2115
2355
  return expr
2116
2356
 
2117
- def in_loop(cont):
2357
+ async def in_loop(cont):
2118
2358
  S.in_loop += 1
2119
- ret = cont()
2359
+ ret = await cont()
2120
2360
  S.in_loop -= 1
2121
2361
  return ret
2122
2362
 
2123
- def run_parser():
2363
+ async def run_parser():
2124
2364
  start = S.token = next()
2125
2365
  body = v'[]'
2126
2366
  docstrings = v'[]'
2127
2367
  first_token = True
2128
2368
  toplevel = options.toplevel
2129
- while not is_("eof"):
2130
- element = statement()
2369
+ while not is_('eof'):
2370
+ if options.recover_errors:
2371
+ try:
2372
+ element = await statement()
2373
+ except as rerr:
2374
+ if is_recoverable_error(rerr):
2375
+ record_recovery(rerr)
2376
+ await skip_to_recovery_point()
2377
+ continue
2378
+ raise
2379
+ else:
2380
+ element = await statement()
2131
2381
  if first_token and is_node_type(element, AST_Directive) and element.value.indexOf('#!') is 0:
2132
2382
  shebang = element.value
2133
2383
  else:
@@ -2152,6 +2402,18 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2152
2402
  'docstrings': docstrings,
2153
2403
  })
2154
2404
 
2405
+ if options.recover_errors:
2406
+ # Combine parser-level recovered errors with any collected by the
2407
+ # tokenizer (e.g. skipped unexpected characters) for the LSP to report.
2408
+ recovered = S.recovered_errors
2409
+ try:
2410
+ tok_ctx = S.input.context()
2411
+ if tok_ctx and tok_ctx.recovered_errors and tok_ctx.recovered_errors.length:
2412
+ recovered = recovered.concat(tok_ctx.recovered_errors)
2413
+ except:
2414
+ pass
2415
+ toplevel.recovered_errors = recovered
2416
+
2155
2417
  toplevel.nonlocalvars = scan_for_nonlocal_defs(toplevel.body).concat(S.globals)
2156
2418
  toplevel.localvars = []
2157
2419
  toplevel.exports = []
@@ -2182,21 +2444,102 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2182
2444
  toplevel.scoped_flags = S.scoped_flags.stack[0]
2183
2445
  importing_modules[module_id] = False
2184
2446
  toplevel.comments_after = S.token.comments_before or v'[]'
2447
+
2448
+ if options._src_hash and options.filename and module_id is not '__main__':
2449
+ try:
2450
+ # Simplified class info (no full AST nodes) for lazy-shell reconstruction.
2451
+ classes_cache = {}
2452
+ for cname in Object.keys(toplevel.classes or {}):
2453
+ cls = toplevel.classes[cname]
2454
+ classes_cache[cname] = {'static': cls.static or {}, 'bound': cls.bound or [], 'classvars': cls.classvars or {}}
2455
+ # Single pass: compute has_side_effects + tree-shaking index.
2456
+ # The index lets the tree-shaker resolve the fixpoint iteration
2457
+ # (steps 4-5) without accessing mod.body — no AST deserialization
2458
+ # until step 6 (pruning), which needs actual nodes anyway.
2459
+ has_se = False
2460
+ idx_exec_refs_set = {}
2461
+ idx_import_bindings = {}
2462
+ idx_top_defs = {}
2463
+
2464
+ def _collect_refs_into(node, refs_set): # noqa
2465
+ def _rv(n): # noqa
2466
+ if is_node_type(n, AST_SymbolRef):
2467
+ refs_set[n.name] = True
2468
+ node.walk(new TreeWalker(_rv))
2469
+
2470
+ for idx_stmt in toplevel.body:
2471
+ if is_node_type(idx_stmt, AST_Function) or is_node_type(idx_stmt, AST_Class):
2472
+ if idx_stmt.name:
2473
+ idx_pinned = False
2474
+ if idx_stmt.decorators:
2475
+ for idx_dec in idx_stmt.decorators:
2476
+ if idx_dec.expression and is_node_type(idx_dec.expression, AST_SymbolRef) and idx_dec.expression.name == 'no_prune':
2477
+ idx_pinned = True
2478
+ break
2479
+ idx_def_refs = {}
2480
+ _collect_refs_into(idx_stmt, idx_def_refs)
2481
+ idx_top_defs[idx_stmt.name.name] = {'pinned': idx_pinned, 'refs': Object.keys(idx_def_refs)}
2482
+ elif is_node_type(idx_stmt, AST_Imports):
2483
+ for idx_imp in idx_stmt.imports:
2484
+ if idx_imp.argnames and idx_imp.argnames.length:
2485
+ for idx_arg in idx_imp.argnames:
2486
+ idx_local = idx_arg.alias.name if idx_arg.alias else idx_arg.name
2487
+ idx_import_bindings[idx_local] = {'source': idx_imp.key, 'imported_name': idx_arg.name, 'is_namespace': False}
2488
+ else:
2489
+ idx_local = idx_imp.alias.name if idx_imp.alias else idx_imp.key.split('.')[0]
2490
+ idx_import_bindings[idx_local] = {'source': idx_imp.key, 'imported_name': None, 'is_namespace': True}
2491
+ else:
2492
+ if not has_se:
2493
+ se_found = v'[false]'
2494
+
2495
+ def se_check(se_node, se_descend): # noqa
2496
+ if is_node_type(se_node, AST_Call):
2497
+ se_found[0] = True
2498
+ return True
2499
+ idx_stmt.walk(new TreeWalker(se_check))
2500
+ if se_found[0]:
2501
+ has_se = True
2502
+ _collect_refs_into(idx_stmt, idx_exec_refs_set)
2503
+
2504
+ cache_meta = {
2505
+ 'v': CACHE_VERSION,
2506
+ 'signature': options._src_hash,
2507
+ 'compiler_version': COMPILER_VERSION,
2508
+ 'imported_module_ids': toplevel.imported_module_ids,
2509
+ 'exported_names': [sym.name for sym in toplevel.exports],
2510
+ 'baselib': toplevel.baselib or {},
2511
+ 'classes': classes_cache,
2512
+ 'has_side_effects': has_se,
2513
+ 'index': {
2514
+ 'exec_refs': Object.keys(idx_exec_refs_set),
2515
+ 'import_bindings': idx_import_bindings,
2516
+ 'top_defs': idx_top_defs,
2517
+ },
2518
+ }
2519
+ if options._file_mtime is not None and options._file_mtime is not undefined:
2520
+ cache_meta['mtime'] = options._file_mtime
2521
+ # Binary format: metadata JSON followed by newline and binary AST pool.
2522
+ await writefile(cache_file_name(options.filename, options.module_cache_dir), encode_cache(JSON.stringify(cache_meta), toplevel))
2523
+ except as e:
2524
+ console.error('Failed to write AST cache file:', options.filename, 'with error:', e)
2525
+
2185
2526
  return toplevel
2186
2527
 
2187
2528
  return run_parser
2188
2529
 
2189
- def parse(text, options):
2530
+
2531
+ async def parse(text, options):
2190
2532
  options = defaults(options, {
2191
- 'filename': None, # name of the file being parsed
2192
- 'module_id':'__main__', # The id of the module being parsed
2533
+ 'filename': None, # name of the file being parsed
2534
+ 'module_id': '__main__', # The id of the module being parsed
2193
2535
  'toplevel': None,
2194
- 'for_linting': False, # If True certain actions are not performed, such as importing modules
2536
+ 'for_linting': False, # If True certain actions are not performed, such as importing modules
2195
2537
  'import_dirs': v'[]',
2196
- 'classes': undefined, # Map of class names to AST_Class that are available in the global namespace (used by the REPL)
2197
- 'scoped_flags': {}, # Global scoped flags (used by the REPL)
2538
+ 'classes': undefined, # Map of class names to AST_Class that are available in the global namespace (used by the REPL)
2539
+ 'scoped_flags': {}, # Global scoped flags (used by the REPL)
2198
2540
  'discard_asserts': False,
2199
2541
  'module_cache_dir': '',
2542
+ 'recover_errors': False, # When True, continue parsing past syntax errors (used by the LSP). See run_parser/block_.
2200
2543
  })
2201
2544
  import_dirs = [x for x in options.import_dirs]
2202
2545
  for location in v'[options.libdir, options.basedir]':
@@ -2207,11 +2550,16 @@ def parse(text, options):
2207
2550
  imported_module_ids = []
2208
2551
  imported_modules = options.imported_modules or {}
2209
2552
  importing_modules = options.importing_modules or {}
2553
+ # loading_promises tracks modules currently being loaded across the entire compilation.
2554
+ # It is shared by passing it through options so parallel sub-parse() calls all see it.
2555
+ if not options.loading_promises:
2556
+ options.loading_promises = {}
2210
2557
  importing_modules[module_id] = True
2211
2558
 
2212
2559
  # The internal state of the parser
2213
2560
  S = {
2214
- 'input': tokenizer(text, options.filename) if jstype(text) is 'string' else text,
2561
+ 'input': tokenizer(text, options.filename, options.recover_errors) if jstype(text) is 'string' else text,
2562
+ 'recovered_errors': v'[]', # syntax errors swallowed during recovery (recover_errors mode only)
2215
2563
  'token': None,
2216
2564
  'prev': None,
2217
2565
  'peeked': [],
@@ -2221,9 +2569,9 @@ def parse(text, options):
2221
2569
  'in_parenthesized_expr': False,
2222
2570
  'in_delete': False,
2223
2571
  'in_loop': 0,
2224
- 'in_class': [ False ],
2225
- 'classes': [ {} ],
2226
- 'functions': [ {} ],
2572
+ 'in_class': [False],
2573
+ 'classes': [{}],
2574
+ 'functions': [{}],
2227
2575
  'labels': [],
2228
2576
  'decorators': v'[]',
2229
2577
  'parsing_decorator': False,
@@ -2247,6 +2595,9 @@ def parse(text, options):
2247
2595
  if options.classes:
2248
2596
  for cname in options.classes:
2249
2597
  obj = options.classes[cname]
2250
- S.classes[0][cname] = { 'static':obj.static, 'bound':obj.bound, 'classvars': obj.classvars }
2598
+ S.classes[0][cname] = {'static': obj.static, 'bound': obj.bound, 'classvars': obj.classvars}
2599
+
2600
+ if jstype(text) is 'string' and not options._src_hash:
2601
+ options._src_hash = sha1sum(text)
2251
2602
 
2252
- return create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_ids, imported_modules, importing_modules, options)()
2603
+ return await create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_ids, imported_modules, importing_modules, options)()