jaclang 0.8.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

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.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (77) hide show
  1. jaclang/cli/cli.py +11 -9
  2. jaclang/compiler/jac.lark +2 -12
  3. jaclang/compiler/larkparse/jac_parser.py +1 -1
  4. jaclang/compiler/parser.py +360 -521
  5. jaclang/compiler/passes/main/cfg_build_pass.py +2 -2
  6. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  7. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  8. jaclang/compiler/passes/main/import_pass.py +3 -3
  9. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +196 -218
  11. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  13. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  14. jaclang/compiler/passes/main/sym_tab_link_pass.py +4 -4
  15. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  16. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  17. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +4 -2
  18. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +197 -120
  19. jaclang/compiler/program.py +2 -7
  20. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  21. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  22. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  23. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  24. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  25. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +11 -0
  26. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +7 -0
  27. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  28. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  29. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  30. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  31. jaclang/compiler/tests/test_importer.py +20 -0
  32. jaclang/compiler/tests/test_parser.py +1 -0
  33. jaclang/compiler/unitree.py +456 -304
  34. jaclang/langserve/engine.jac +498 -0
  35. jaclang/langserve/sem_manager.jac +309 -0
  36. jaclang/langserve/server.jac +186 -0
  37. jaclang/langserve/tests/server_test/test_lang_serve.py +6 -7
  38. jaclang/langserve/tests/server_test/utils.py +4 -1
  39. jaclang/langserve/tests/session.jac +294 -0
  40. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  41. jaclang/langserve/tests/test_server.py +12 -7
  42. jaclang/langserve/utils.jac +51 -30
  43. jaclang/runtimelib/archetype.py +1 -1
  44. jaclang/runtimelib/builtin.py +17 -14
  45. jaclang/runtimelib/importer.py +26 -8
  46. jaclang/runtimelib/machine.py +96 -55
  47. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  48. jaclang/runtimelib/utils.py +3 -3
  49. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  50. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  51. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  52. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  53. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  54. jaclang/tests/fixtures/concurrency.jac +1 -1
  55. jaclang/tests/fixtures/edge_ability.jac +49 -0
  56. jaclang/tests/fixtures/guess_game.jac +1 -1
  57. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  58. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  59. jaclang/tests/fixtures/node_del.jac +30 -36
  60. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  61. jaclang/tests/test_cli.py +12 -7
  62. jaclang/tests/test_language.py +91 -16
  63. jaclang/utils/helpers.py +14 -6
  64. jaclang/utils/lang_tools.py +2 -3
  65. jaclang/utils/tests/test_lang_tools.py +2 -1
  66. jaclang/utils/treeprinter.py +3 -4
  67. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/METADATA +4 -3
  68. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/RECORD +71 -55
  69. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/WHEEL +1 -1
  70. jaclang/langserve/engine.py +0 -553
  71. jaclang/langserve/sem_manager.py +0 -383
  72. jaclang/langserve/server.py +0 -167
  73. jaclang/langserve/tests/session.py +0 -255
  74. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  75. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  76. /jaclang/langserve/{__init__.py → __init__.jac} +0 -0
  77. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/entry_points.txt +0 -0
@@ -62,17 +62,10 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
62
62
  """Extract with entry from a body."""
63
63
 
64
64
  def gen_mod_code(with_entry_body: list[uni.CodeBlockStmt]) -> uni.ModuleCode:
65
- with_entry_subnodelist = uni.SubNodeList[uni.CodeBlockStmt](
66
- items=with_entry_body,
67
- delim=Tok.WS,
68
- kid=with_entry_body,
69
- left_enc=self.operator(Tok.LBRACE, "{"),
70
- right_enc=self.operator(Tok.RBRACE, "}"),
71
- )
72
65
  return uni.ModuleCode(
73
66
  name=None,
74
- body=with_entry_subnodelist,
75
- kid=[with_entry_subnodelist],
67
+ body=with_entry_body,
68
+ kid=with_entry_body,
76
69
  doc=None,
77
70
  )
78
71
 
@@ -172,33 +165,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
172
165
  ):
173
166
  self.convert_to_doc(valid[0].expr)
174
167
  doc = valid[0].expr
175
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
176
- items=valid[1:],
177
- delim=Tok.WS,
178
- kid=valid[1:] + [doc],
179
- left_enc=self.operator(Tok.LBRACE, "{"),
180
- right_enc=self.operator(Tok.RBRACE, "}"),
181
- )
168
+ valid_body = valid[1:]
182
169
  else:
183
170
  doc = None
184
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
185
- items=valid,
186
- delim=Tok.WS,
187
- kid=valid,
188
- left_enc=self.operator(Tok.LBRACE, "{"),
189
- right_enc=self.operator(Tok.RBRACE, "}"),
190
- )
171
+ valid_body = valid
191
172
  decorators = [self.convert(i) for i in node.decorator_list]
192
173
  valid_dec = [i for i in decorators if isinstance(i, uni.Expr)]
193
174
  if len(valid_dec) != len(decorators):
194
175
  raise self.ice("Length mismatch in decorators on function")
195
- valid_decorators = (
196
- uni.SubNodeList[uni.Expr](
197
- items=valid_dec, delim=Tok.DECOR_OP, kid=decorators
198
- )
199
- if len(valid_dec)
200
- else None
201
- )
176
+ valid_decorators = valid_dec if valid_dec else None
202
177
  res = self.convert(node.args)
203
178
  sig: Optional[uni.FuncSignature] = (
204
179
  res if isinstance(res, uni.FuncSignature) else None
@@ -206,12 +181,12 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
206
181
  ret_sig = self.convert(node.returns) if node.returns else None
207
182
  if isinstance(ret_sig, uni.Expr):
208
183
  if not sig:
209
- sig = uni.FuncSignature(params=None, return_type=ret_sig, kid=[ret_sig])
184
+ sig = uni.FuncSignature(params=[], return_type=ret_sig, kid=[ret_sig])
210
185
  else:
211
186
  sig.return_type = ret_sig
212
187
  sig.add_kids_right([sig.return_type])
213
188
  kid = ([doc] if doc else []) + (
214
- [name, sig, valid_body] if sig else [name, valid_body]
189
+ [name, sig, *valid_body] if sig else [name, *valid_body]
215
190
  )
216
191
  if not sig:
217
192
  raise self.ice("Function signature not found")
@@ -309,11 +284,9 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
309
284
  and isinstance(body_stmt.signature, uni.FuncSignature)
310
285
  and body_stmt.signature.params
311
286
  ):
312
- body_stmt.signature.params.items = [
313
- param
314
- for param in body_stmt.signature.params.items
315
- if param.name.value != "self"
316
- ]
287
+ for param in body_stmt.signature.params:
288
+ if param.name.value == "self":
289
+ param.type_tag = uni.SubTag[uni.Expr](name, kid=[name])
317
290
  doc = (
318
291
  body[0].expr
319
292
  if isinstance(body[0], uni.ExprStmt)
@@ -331,48 +304,31 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
331
304
  self.operator(Tok.LBRACE, "{"),
332
305
  self.operator(Tok.RBRACE, "}"),
333
306
  ]
334
- valid_body = uni.SubNodeList[uni.ArchBlockStmt](
335
- items=valid,
336
- delim=Tok.WS,
337
- kid=(valid if valid else empty_block),
338
- left_enc=self.operator(Tok.LBRACE, "{"),
339
- right_enc=self.operator(Tok.RBRACE, "}"),
340
- )
307
+ valid_body = valid if valid else empty_block
341
308
  converted_base_classes = [self.convert(base) for base in node.bases]
342
309
  base_classes: list[uni.Expr] = [
343
310
  base for base in converted_base_classes if isinstance(base, uni.Expr)
344
311
  ]
345
- valid_bases = (
346
- uni.SubNodeList[uni.Expr](
347
- items=base_classes, delim=Tok.COMMA, kid=base_classes
348
- )
349
- if base_classes
350
- else None
351
- )
352
312
  converted_decorators_list = [self.convert(i) for i in node.decorator_list]
353
313
  decorators = [i for i in converted_decorators_list if isinstance(i, uni.Expr)]
354
- valid_decorators = (
355
- uni.SubNodeList[uni.Expr](
356
- items=decorators, delim=Tok.DECOR_OP, kid=decorators
357
- )
358
- if decorators
359
- else None
360
- )
314
+ if len(decorators) != len(converted_decorators_list):
315
+ raise self.ice("Length mismatch in decorators on class")
316
+ valid_decorators = decorators if decorators else None
361
317
  kid = (
362
- [name, valid_bases, valid_body, doc]
363
- if doc and valid_bases
318
+ [name, *base_classes, *valid_body, doc]
319
+ if doc and base_classes
364
320
  else (
365
- [name, valid_bases, valid_body]
366
- if valid_bases
367
- else [name, valid_body, doc] if doc else [name, valid_body]
321
+ [name, *base_classes, *valid_body]
322
+ if base_classes
323
+ else [name, *valid_body, doc] if doc else [name, *valid_body]
368
324
  )
369
325
  )
370
326
  return uni.Archetype(
371
327
  arch_type=arch_type,
372
328
  name=name,
373
329
  access=None,
374
- base_classes=valid_bases,
375
- body=valid_body,
330
+ base_classes=base_classes,
331
+ body=valid,
376
332
  kid=kid,
377
333
  doc=doc,
378
334
  decorators=valid_decorators,
@@ -406,17 +362,14 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
406
362
  valid_exprs = [expr for expr in exprs if isinstance(expr, uni.Expr)]
407
363
  if not len(valid_exprs) or len(valid_exprs) != len(exprs):
408
364
  raise self.ice("Length mismatch in delete targets")
409
- target = uni.SubNodeList[uni.Expr | uni.KWPair](
410
- items=[*valid_exprs], delim=Tok.COMMA, kid=exprs
411
- )
412
365
  target_1 = (
413
366
  valid_exprs[0]
414
367
  if len(valid_exprs) > 1
415
- else uni.TupleVal(values=target, kid=[target])
368
+ else uni.TupleVal(values=valid_exprs, kid=[*valid_exprs])
416
369
  )
417
370
  return uni.DeleteStmt(
418
371
  target=target_1,
419
- kid=[target],
372
+ kid=[*valid_exprs],
420
373
  )
421
374
 
422
375
  def proc_assign(self, node: py_ast.Assign) -> uni.Assignment:
@@ -429,18 +382,14 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
429
382
  value = self.convert(node.value)
430
383
  targets = [self.convert(target) for target in node.targets]
431
384
  valid = [target for target in targets if isinstance(target, uni.Expr)]
432
- if len(valid) == len(targets):
433
- valid_targets = uni.SubNodeList[uni.Expr](
434
- items=valid, delim=Tok.EQ, kid=valid
435
- )
436
- else:
385
+ if not len(valid) == len(targets):
437
386
  raise self.ice("Length mismatch in assignment targets")
438
387
  if isinstance(value, uni.Expr):
439
388
  return uni.Assignment(
440
- target=valid_targets,
389
+ target=valid,
441
390
  value=value,
442
391
  type_tag=None,
443
- kid=[valid_targets, value],
392
+ kid=[*valid, value],
444
393
  )
445
394
  else:
446
395
  raise self.ice()
@@ -466,11 +415,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
466
415
  and isinstance(target, uni.Expr)
467
416
  and isinstance(op, uni.Token)
468
417
  ):
469
- targ = uni.SubNodeList[uni.Expr](
470
- items=[target], delim=Tok.COMMA, kid=[target]
471
- )
472
418
  return uni.Assignment(
473
- target=targ,
419
+ target=[target],
474
420
  type_tag=None,
475
421
  mutable=True,
476
422
  aug_op=op,
@@ -502,11 +448,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
502
448
  and isinstance(annotation, uni.Expr)
503
449
  and isinstance(target, uni.Expr)
504
450
  ):
505
- target = uni.SubNodeList[uni.Expr](
506
- items=[target], delim=Tok.EQ, kid=[target]
507
- )
508
451
  return uni.Assignment(
509
- target=target,
452
+ target=[target],
510
453
  value=value if isinstance(value, (uni.Expr, uni.YieldExpr)) else None,
511
454
  type_tag=annotation_subtag,
512
455
  kid=(
@@ -535,32 +478,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
535
478
  if len(val_body) != len(body):
536
479
  raise self.ice("Length mismatch in for body")
537
480
 
538
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
539
- items=val_body,
540
- delim=Tok.WS,
541
- kid=val_body,
542
- left_enc=self.operator(Tok.LBRACE, "{"),
543
- right_enc=self.operator(Tok.RBRACE, "}"),
544
- )
481
+ valid_body = val_body
545
482
  orelse = [self.convert(i) for i in node.orelse]
546
483
  val_orelse = [i for i in orelse if isinstance(i, uni.CodeBlockStmt)]
547
484
  if len(val_orelse) != len(orelse):
548
485
  raise self.ice("Length mismatch in for orelse")
549
486
  if orelse:
550
- valid_orelse = uni.SubNodeList[uni.CodeBlockStmt](
551
- items=val_orelse,
552
- delim=Tok.WS,
553
- kid=orelse,
554
- left_enc=self.operator(Tok.LBRACE, "{"),
555
- right_enc=self.operator(Tok.RBRACE, "}"),
556
- )
487
+ fin_orelse = uni.ElseStmt(body=val_orelse, kid=val_orelse)
557
488
  else:
558
- valid_orelse = None
559
- fin_orelse = (
560
- uni.ElseStmt(body=valid_orelse, kid=[valid_orelse])
561
- if valid_orelse
562
- else None
563
- )
489
+ fin_orelse = None
564
490
  if isinstance(target, uni.Expr) and isinstance(iter, uni.Expr):
565
491
  return uni.InForStmt(
566
492
  target=target,
@@ -569,9 +495,9 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
569
495
  body=valid_body,
570
496
  else_body=fin_orelse,
571
497
  kid=(
572
- [target, iter, valid_body, fin_orelse]
498
+ [target, iter, *valid_body, fin_orelse]
573
499
  if fin_orelse
574
- else [target, iter, valid_body]
500
+ else [target, iter, *valid_body]
575
501
  ),
576
502
  )
577
503
  else:
@@ -594,32 +520,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
594
520
  if len(val_body) != len(body):
595
521
  raise self.ice("Length mismatch in for body")
596
522
 
597
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
598
- items=val_body,
599
- delim=Tok.WS,
600
- kid=val_body,
601
- left_enc=self.operator(Tok.LBRACE, "{"),
602
- right_enc=self.operator(Tok.RBRACE, "}"),
603
- )
523
+ valid_body = val_body
604
524
  orelse = [self.convert(i) for i in node.orelse]
605
525
  val_orelse = [i for i in orelse if isinstance(i, uni.CodeBlockStmt)]
606
526
  if len(val_orelse) != len(orelse):
607
527
  raise self.ice("Length mismatch in for orelse")
608
528
  if orelse:
609
- valid_orelse = uni.SubNodeList[uni.CodeBlockStmt](
610
- items=val_orelse,
611
- delim=Tok.WS,
612
- kid=orelse,
613
- left_enc=self.operator(Tok.LBRACE, "{"),
614
- right_enc=self.operator(Tok.RBRACE, "}"),
615
- )
529
+ fin_orelse = uni.ElseStmt(body=val_orelse, kid=val_orelse)
616
530
  else:
617
- valid_orelse = None
618
- fin_orelse = (
619
- uni.ElseStmt(body=valid_orelse, kid=[valid_orelse])
620
- if valid_orelse
621
- else None
622
- )
531
+ fin_orelse = None
623
532
  if isinstance(target, uni.Expr) and isinstance(iter, uni.Expr):
624
533
  return uni.InForStmt(
625
534
  target=target,
@@ -628,9 +537,9 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
628
537
  body=valid_body,
629
538
  else_body=fin_orelse,
630
539
  kid=(
631
- [target, iter, valid_body, fin_orelse]
540
+ [target, iter, *valid_body, fin_orelse]
632
541
  if fin_orelse
633
- else [target, iter, valid_body]
542
+ else [target, iter, *valid_body]
634
543
  ),
635
544
  )
636
545
  else:
@@ -650,18 +559,11 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
650
559
  valid_body = [stmt for stmt in body if isinstance(stmt, uni.CodeBlockStmt)]
651
560
  if len(valid_body) != len(body):
652
561
  raise self.ice("Length mismatch in while body")
653
- fin_body = uni.SubNodeList[uni.CodeBlockStmt](
654
- items=valid_body,
655
- delim=Tok.WS,
656
- kid=valid_body,
657
- left_enc=self.operator(Tok.LBRACE, "{"),
658
- right_enc=self.operator(Tok.RBRACE, "}"),
659
- )
660
562
  if isinstance(test, uni.Expr):
661
563
  return uni.WhileStmt(
662
564
  condition=test,
663
- body=fin_body,
664
- kid=[test, fin_body],
565
+ body=valid_body,
566
+ kid=[test, *valid_body],
665
567
  )
666
568
  else:
667
569
  raise self.ice()
@@ -680,13 +582,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
680
582
  valid_body = [stmt for stmt in body if isinstance(stmt, uni.CodeBlockStmt)]
681
583
  if len(valid_body) != len(body):
682
584
  self.log_error("Length mismatch in async for body")
683
- body2 = uni.SubNodeList[uni.CodeBlockStmt](
684
- items=valid_body,
685
- delim=Tok.WS,
686
- kid=body,
687
- left_enc=self.operator(Tok.LBRACE, "{"),
688
- right_enc=self.operator(Tok.RBRACE, "}"),
689
- )
585
+ body2 = valid_body
690
586
 
691
587
  orelse = [self.convert(stmt) for stmt in node.orelse]
692
588
  valid_orelse = [
@@ -702,14 +598,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
702
598
  kid=first_elm.kid,
703
599
  )
704
600
  else:
705
- orelse2 = uni.SubNodeList[uni.CodeBlockStmt](
706
- items=valid_orelse,
707
- delim=Tok.WS,
708
- kid=orelse,
709
- left_enc=self.operator(Tok.LBRACE, "{"),
710
- right_enc=self.operator(Tok.RBRACE, "}"),
711
- )
712
- else_body = uni.ElseStmt(body=orelse2, kid=[orelse2])
601
+ else_body = uni.ElseStmt(body=valid_orelse, kid=valid_orelse)
713
602
  else:
714
603
  else_body = None
715
604
  if isinstance(test, uni.Expr):
@@ -717,7 +606,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
717
606
  condition=test,
718
607
  body=body2,
719
608
  else_body=else_body,
720
- kid=([test, body2, else_body] if else_body else [test, body2]),
609
+ kid=([test, *body2, else_body] if else_body else [test, *body2]),
721
610
  )
722
611
  else:
723
612
  raise self.ice()
@@ -735,22 +624,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
735
624
  valid_items = [item for item in items if isinstance(item, uni.ExprAsItem)]
736
625
  if len(valid_items) != len(items):
737
626
  raise self.ice("Length mismatch in with items")
738
- items_sub = uni.SubNodeList[uni.ExprAsItem](
739
- items=valid_items, delim=Tok.COMMA, kid=items
740
- )
741
627
  body = [self.convert(stmt) for stmt in node.body]
742
628
  valid_body = [stmt for stmt in body if isinstance(stmt, uni.CodeBlockStmt)]
743
629
  if len(valid_body) != len(body):
744
630
  raise self.ice("Length mismatch in async for body")
745
- body_sub = uni.SubNodeList[uni.CodeBlockStmt](
746
- items=valid_body,
747
- delim=Tok.WS,
748
- kid=body,
749
- left_enc=self.operator(Tok.LBRACE, "{"),
750
- right_enc=self.operator(Tok.RBRACE, "}"),
751
- )
752
631
  return uni.WithStmt(
753
- is_async=False, exprs=items_sub, body=body_sub, kid=[items_sub, body_sub]
632
+ is_async=False,
633
+ exprs=valid_items,
634
+ body=valid_body,
635
+ kid=[*valid_items, *valid_body],
754
636
  )
755
637
 
756
638
  def proc_async_with(self, node: py_ast.AsyncWith) -> uni.WithStmt:
@@ -765,22 +647,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
765
647
  valid_items = [item for item in items if isinstance(item, uni.ExprAsItem)]
766
648
  if len(valid_items) != len(items):
767
649
  raise self.ice("Length mismatch in with items")
768
- items_sub = uni.SubNodeList[uni.ExprAsItem](
769
- items=valid_items, delim=Tok.COMMA, kid=items
770
- )
771
650
  body = [self.convert(stmt) for stmt in node.body]
772
651
  valid_body = [stmt for stmt in body if isinstance(stmt, uni.CodeBlockStmt)]
773
652
  if len(valid_body) != len(body):
774
653
  raise self.ice("Length mismatch in async for body")
775
- body_sub = uni.SubNodeList[uni.CodeBlockStmt](
776
- items=valid_body,
777
- delim=Tok.WS,
778
- kid=body,
779
- left_enc=self.operator(Tok.LBRACE, "{"),
780
- right_enc=self.operator(Tok.RBRACE, "}"),
781
- )
782
654
  return uni.WithStmt(
783
- is_async=True, exprs=items_sub, body=body_sub, kid=[items_sub, body_sub]
655
+ is_async=True,
656
+ exprs=valid_items,
657
+ body=valid_body,
658
+ kid=[*valid_items, *valid_body],
784
659
  )
785
660
 
786
661
  def proc_raise(self, node: py_ast.Raise) -> uni.RaiseStmt:
@@ -961,11 +836,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
961
836
  op = self.convert(node.op)
962
837
  values = [self.convert(value) for value in node.values]
963
838
  valid = [value for value in values if isinstance(value, uni.Expr)]
964
- valid_values = uni.SubNodeList[uni.Expr](
965
- items=valid, delim=Tok.COMMA, kid=values
966
- )
967
839
  if isinstance(op, uni.Token) and len(valid) == len(values):
968
- expr = uni.BoolExpr(op=op, values=valid, kid=[op, valid_values])
840
+ expr = uni.BoolExpr(op=op, values=valid, kid=[op, *valid])
969
841
  return uni.AtomUnit(
970
842
  value=expr,
971
843
  kid=[
@@ -1014,17 +886,15 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1014
886
  if isinstance(i, uni.KWPair):
1015
887
  params_in.append(i)
1016
888
  if len(params_in) != 0:
1017
- params_in2 = uni.SubNodeList[uni.Expr | uni.KWPair](
1018
- items=params_in, delim=Tok.COMMA, kid=params_in
1019
- )
889
+ kids = [func, *params_in]
1020
890
  else:
1021
- params_in2 = None
891
+ kids = [func]
1022
892
  if isinstance(func, uni.Expr):
1023
893
  return uni.FuncCall(
1024
894
  target=func,
1025
- params=params_in2,
895
+ params=params_in,
1026
896
  genai_call=None,
1027
- kid=[func, params_in2] if params_in2 else [func],
897
+ kid=kids,
1028
898
  )
1029
899
  else:
1030
900
  raise self.ice()
@@ -1044,14 +914,10 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1044
914
  valid_comparators = [
1045
915
  comparator for comparator in comparators if isinstance(comparator, uni.Expr)
1046
916
  ]
1047
- comparators2 = uni.SubNodeList[uni.Expr](
1048
- items=valid_comparators, delim=Tok.COMMA, kid=comparators
1049
- )
1050
917
  ops = [self.convert(op) for op in node.ops]
1051
918
  valid_ops = [op for op in ops if isinstance(op, uni.Token)]
1052
- ops2 = uni.SubNodeList[uni.Token](items=valid_ops, delim=Tok.COMMA, kid=ops)
1053
919
 
1054
- kids = [left, ops2, comparators2]
920
+ kids = [left, *valid_ops, *valid_comparators]
1055
921
  if (
1056
922
  isinstance(left, uni.Expr)
1057
923
  and len(ops) == len(valid_ops)
@@ -1192,8 +1058,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1192
1058
  valid = [i for i in generators if isinstance(i, (uni.InnerCompr))]
1193
1059
  if len(valid) != len(generators):
1194
1060
  raise self.ice("Length mismatch in dict compr generators")
1195
- compr = uni.SubNodeList[uni.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1196
- return uni.DictCompr(kv_pair=kv_pair, compr=valid, kid=[kv_pair, compr])
1061
+ return uni.DictCompr(kv_pair=kv_pair, compr=valid, kid=[kv_pair, *valid])
1197
1062
 
1198
1063
  def proc_ellipsis(self, node: py_ast.Ellipsis) -> None:
1199
1064
  """Process python node."""
@@ -1263,19 +1128,12 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1263
1128
  valid = [i for i in body if isinstance(i, (uni.CodeBlockStmt))]
1264
1129
  if len(valid) != len(body):
1265
1130
  raise self.ice("Length mismatch in except handler body")
1266
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
1267
- items=valid,
1268
- delim=Tok.WS,
1269
- kid=valid,
1270
- left_enc=self.operator(Tok.LBRACE, "{"),
1271
- right_enc=self.operator(Tok.RBRACE, "}"),
1272
- )
1273
- kid = [item for item in [type, name, valid_body] if item]
1131
+ kid = [item for item in [type, name, *valid] if item]
1274
1132
  if isinstance(type, uni.Expr) and (isinstance(name, uni.Name) or not name):
1275
1133
  return uni.Except(
1276
1134
  ex_type=type,
1277
1135
  name=name,
1278
- body=valid_body,
1136
+ body=valid,
1279
1137
  kid=kid,
1280
1138
  )
1281
1139
  else:
@@ -1329,9 +1187,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1329
1187
  valid = [gen for gen in generators if isinstance(gen, uni.InnerCompr)]
1330
1188
  if len(generators) != len(valid):
1331
1189
  raise self.ice("Length mismatch in list comp generators")
1332
- compr = uni.SubNodeList[uni.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1333
1190
  if isinstance(elt, uni.Expr):
1334
- return uni.GenCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1191
+ return uni.GenCompr(out_expr=elt, compr=valid, kid=[elt, *valid])
1335
1192
  else:
1336
1193
  raise self.ice()
1337
1194
 
@@ -1356,8 +1213,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1356
1213
  pos_end=0,
1357
1214
  )
1358
1215
  )
1359
- target = uni.SubNodeList[uni.NameAtom](items=names, delim=Tok.COMMA, kid=names)
1360
- return uni.GlobalStmt(target=target, kid=[target])
1216
+ return uni.GlobalStmt(target=names, kid=names)
1361
1217
 
1362
1218
  def proc_if_exp(self, node: py_ast.IfExp) -> uni.IfElseExpr:
1363
1219
  """Process python node.
@@ -1398,9 +1254,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1398
1254
  ):
1399
1255
  paths.append(
1400
1256
  uni.ModulePath(
1401
- path=uni.SubNodeList[uni.Name](
1402
- items=[name.expr], delim=Tok.DOT, kid=[name.expr]
1403
- ),
1257
+ path=[name.expr],
1404
1258
  level=0,
1405
1259
  alias=name.alias,
1406
1260
  kid=[i for i in name.kid if i],
@@ -1409,12 +1263,11 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1409
1263
  # Need to unravel atom trailers
1410
1264
  else:
1411
1265
  raise self.ice()
1412
- items = uni.SubNodeList[uni.ModulePath](items=paths, delim=Tok.COMMA, kid=paths)
1413
1266
  ret = uni.Import(
1414
1267
  from_loc=None,
1415
- items=items,
1268
+ items=paths,
1416
1269
  is_absorb=False,
1417
- kid=[items],
1270
+ kid=paths,
1418
1271
  )
1419
1272
  return ret
1420
1273
 
@@ -1456,11 +1309,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1456
1309
  moddots = [self.operator(Tok.DOT, ".") for _ in range(node.level)]
1457
1310
  modparts = moddots + modpaths
1458
1311
  path = uni.ModulePath(
1459
- path=(
1460
- uni.SubNodeList[uni.Name](items=modpaths, delim=Tok.DOT, kid=modpaths)
1461
- if modpaths
1462
- else None
1463
- ),
1312
+ path=modpaths if modpaths else None,
1464
1313
  level=node.level,
1465
1314
  alias=None,
1466
1315
  kid=modparts,
@@ -1483,32 +1332,23 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1483
1332
  )
1484
1333
  else:
1485
1334
  raise self.ice()
1486
- items = (
1487
- uni.SubNodeList[uni.ModuleItem](
1488
- items=valid_names, delim=Tok.COMMA, kid=valid_names
1489
- )
1490
- if valid_names
1491
- else None
1492
- )
1335
+ items = valid_names
1493
1336
  if not items:
1494
1337
  raise self.ice("No valid names in import from")
1495
1338
  pytag = uni.SubTag[uni.Name](tag=lang, kid=[lang])
1496
1339
  if len(node.names) == 1 and node.names[0].name == "*":
1497
- path_in = uni.SubNodeList[uni.ModulePath](
1498
- items=[path], delim=Tok.COMMA, kid=[path]
1499
- )
1500
1340
  ret = uni.Import(
1501
1341
  from_loc=None,
1502
- items=path_in,
1342
+ items=[path],
1503
1343
  is_absorb=True,
1504
- kid=[pytag, path_in],
1344
+ kid=[pytag, path],
1505
1345
  )
1506
1346
  return ret
1507
1347
  ret = uni.Import(
1508
1348
  from_loc=path,
1509
1349
  items=items,
1510
1350
  is_absorb=False,
1511
- kid=[pytag, path, items],
1351
+ kid=[pytag, path, *items],
1512
1352
  )
1513
1353
  return ret
1514
1354
 
@@ -1524,10 +1364,10 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1524
1364
  valid = [
1525
1365
  value for value in values if isinstance(value, (uni.String, uni.ExprStmt))
1526
1366
  ]
1527
- valid_values = uni.SubNodeList[uni.String | uni.ExprStmt](
1528
- items=valid, delim=None, kid=valid
1367
+ return uni.FString(
1368
+ parts=valid,
1369
+ kid=[*valid] if valid else [uni.EmptyToken()],
1529
1370
  )
1530
- return uni.FString(parts=valid_values, kid=[valid_values])
1531
1371
 
1532
1372
  def proc_lambda(self, node: py_ast.Lambda) -> uni.LambdaExpr:
1533
1373
  """Process python node.
@@ -1557,13 +1397,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1557
1397
  l_square = self.operator(Tok.LSQUARE, "[")
1558
1398
  r_square = self.operator(Tok.RSQUARE, "]")
1559
1399
  return uni.ListVal(
1560
- values=(
1561
- uni.SubNodeList[uni.Expr](
1562
- items=valid_elts, delim=Tok.COMMA, kid=valid_elts
1563
- )
1564
- if valid_elts
1565
- else None
1566
- ),
1400
+ values=valid_elts,
1567
1401
  kid=[*valid_elts] if valid_elts else [l_square, r_square],
1568
1402
  )
1569
1403
 
@@ -1579,9 +1413,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1579
1413
  valid = [gen for gen in generators if isinstance(gen, uni.InnerCompr)]
1580
1414
  if len(generators) != len(valid):
1581
1415
  raise self.ice("Length mismatch in list comp generators")
1582
- compr = uni.SubNodeList[uni.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1583
1416
  if isinstance(elt, uni.Expr):
1584
- return uni.ListCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1417
+ return uni.ListCompr(out_expr=elt, compr=valid, kid=[elt, *valid])
1585
1418
  else:
1586
1419
  raise self.ice()
1587
1420
 
@@ -1652,14 +1485,12 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1652
1485
  patterns = [self.convert(i) for i in node.patterns]
1653
1486
  valid_patterns = [i for i in patterns if isinstance(i, uni.MatchPattern)]
1654
1487
  if len(patterns) == len(valid_patterns):
1655
- patterns_sub = uni.SubNodeList[uni.MatchPattern](
1656
- items=valid_patterns, delim=Tok.COMMA, kid=valid_patterns
1657
- )
1658
- kid.append(patterns_sub)
1488
+ kid.extend(valid_patterns)
1489
+ patterns_list = valid_patterns
1659
1490
  else:
1660
1491
  raise self.ice()
1661
1492
  else:
1662
- patterns_sub = None
1493
+ patterns_list = None
1663
1494
 
1664
1495
  if len(node.kwd_patterns):
1665
1496
  names: list[uni.Name] = []
@@ -1690,15 +1521,17 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1690
1521
  kid=[names[i], valid_kwd_patterns[i]],
1691
1522
  )
1692
1523
  )
1693
- kw_patterns = uni.SubNodeList[uni.MatchKVPair](
1694
- items=kv_pairs, delim=Tok.COMMA, kid=kv_pairs
1695
- )
1696
- kid.append(kw_patterns)
1524
+
1525
+ kid.extend(kv_pairs)
1526
+ kw_patterns_list = kv_pairs
1697
1527
  else:
1698
- kw_patterns = None
1528
+ kw_patterns_list = None
1699
1529
  if isinstance(cls, (uni.NameAtom, uni.AtomTrailer)):
1700
1530
  return uni.MatchArch(
1701
- name=cls, arg_patterns=patterns_sub, kw_patterns=kw_patterns, kid=kid
1531
+ name=cls,
1532
+ arg_patterns=patterns_list,
1533
+ kw_patterns=kw_patterns_list,
1534
+ kid=kid,
1702
1535
  )
1703
1536
  else:
1704
1537
  raise self.ice()
@@ -1905,8 +1738,7 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1905
1738
  pos_end=0,
1906
1739
  )
1907
1740
  )
1908
- target = uni.SubNodeList[uni.NameAtom](items=names, delim=Tok.COMMA, kid=names)
1909
- return uni.NonLocalStmt(target=target, kid=names)
1741
+ return uni.NonLocalStmt(target=names, kid=names)
1910
1742
 
1911
1743
  def proc_pass(self, node: py_ast.Pass) -> uni.Semi:
1912
1744
  """Process python node."""
@@ -1933,16 +1765,13 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1933
1765
  valid = [i for i in elts if isinstance(i, (uni.Expr))]
1934
1766
  if len(valid) != len(elts):
1935
1767
  raise self.ice("Length mismatch in set body")
1936
- valid_elts = uni.SubNodeList[uni.Expr](
1937
- items=valid, delim=Tok.COMMA, kid=valid
1938
- )
1939
1768
  kid: list[uni.UniNode] = [*valid]
1940
1769
  else:
1941
- valid_elts = None
1770
+ valid = []
1942
1771
  l_brace = self.operator(Tok.LBRACE, "{")
1943
1772
  r_brace = self.operator(Tok.RBRACE, "}")
1944
1773
  kid = [l_brace, r_brace]
1945
- return uni.SetVal(values=valid_elts, kid=kid)
1774
+ return uni.SetVal(values=valid, kid=kid)
1946
1775
 
1947
1776
  def proc_set_comp(self, node: py_ast.SetComp) -> uni.ListCompr:
1948
1777
  """Process python node.
@@ -1956,9 +1785,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
1956
1785
  valid = [gen for gen in generators if isinstance(gen, uni.InnerCompr)]
1957
1786
  if len(generators) != len(valid):
1958
1787
  raise self.ice("Length mismatch in list comp generators")
1959
- compr = uni.SubNodeList[uni.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1960
1788
  if isinstance(elt, uni.Expr):
1961
- return uni.SetCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1789
+ return uni.SetCompr(out_expr=elt, compr=valid, kid=[elt, *valid])
1962
1790
  else:
1963
1791
  raise self.ice()
1964
1792
 
@@ -2022,11 +1850,11 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
2022
1850
  if (
2023
1851
  not isinstance(slice, uni.IndexSlice)
2024
1852
  and isinstance(slice, uni.TupleVal)
2025
- and slice.values is not None
1853
+ and slice.values
2026
1854
  ):
2027
1855
 
2028
1856
  slices: list[uni.IndexSlice.Slice] = []
2029
- for index_slice in slice.values.items:
1857
+ for index_slice in slice.values:
2030
1858
  if not isinstance(index_slice, uni.IndexSlice):
2031
1859
  raise self.ice()
2032
1860
  slices.append(index_slice.slices[0])
@@ -2061,41 +1889,27 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
2061
1889
  valid = [i for i in body if isinstance(i, (uni.CodeBlockStmt))]
2062
1890
  if len(valid) != len(body):
2063
1891
  raise self.ice("Length mismatch in try body")
2064
- valid_body = uni.SubNodeList[uni.CodeBlockStmt](
2065
- items=valid,
2066
- delim=Tok.WS,
2067
- kid=valid,
2068
- left_enc=self.operator(Tok.LBRACE, "{"),
2069
- right_enc=self.operator(Tok.RBRACE, "}"),
2070
- )
2071
- kid: list[uni.UniNode] = [valid_body]
1892
+ valid_body = valid
1893
+ kid: list[uni.UniNode] = [*valid_body]
2072
1894
 
2073
1895
  if len(node.handlers) != 0:
2074
1896
  handlers = [self.convert(i) for i in node.handlers]
2075
1897
  valid_handlers = [i for i in handlers if isinstance(i, (uni.Except))]
2076
1898
  if len(handlers) != len(valid_handlers):
2077
1899
  raise self.ice("Length mismatch in try handlers")
2078
- excepts = uni.SubNodeList[uni.Except](
2079
- items=valid_handlers, delim=Tok.WS, kid=valid_handlers
2080
- )
2081
- kid.append(excepts)
1900
+ excepts = valid_handlers
1901
+ kid.extend(valid_handlers)
2082
1902
  else:
2083
- excepts = None
1903
+ excepts = []
2084
1904
 
2085
1905
  if len(node.orelse) != 0:
2086
1906
  orelse = [self.convert(i) for i in node.orelse]
2087
1907
  valid_orelse = [i for i in orelse if isinstance(i, (uni.CodeBlockStmt))]
2088
1908
  if len(orelse) != len(valid_orelse):
2089
1909
  raise self.ice("Length mismatch in try orelse")
2090
- else_body = uni.SubNodeList[uni.CodeBlockStmt](
2091
- items=valid_orelse,
2092
- delim=Tok.WS,
2093
- kid=valid_orelse,
2094
- left_enc=self.operator(Tok.LBRACE, "{"),
2095
- right_enc=self.operator(Tok.RBRACE, "}"),
2096
- )
2097
- elsestmt = uni.ElseStmt(body=else_body, kid=[else_body])
2098
- kid.append(else_body)
1910
+ else_body = valid_orelse
1911
+ elsestmt = uni.ElseStmt(body=else_body, kid=else_body)
1912
+ kid.extend(else_body)
2099
1913
  else:
2100
1914
  else_body = None
2101
1915
 
@@ -2106,23 +1920,20 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
2106
1920
  ]
2107
1921
  if len(finalbody) != len(valid_finalbody):
2108
1922
  raise self.ice("Length mismatch in try finalbody")
2109
- finally_body = uni.SubNodeList[uni.CodeBlockStmt](
2110
- items=valid_finalbody,
2111
- delim=Tok.WS,
2112
- kid=valid_finalbody,
2113
- left_enc=self.operator(Tok.LBRACE, "{"),
2114
- right_enc=self.operator(Tok.RBRACE, "}"),
1923
+ finally_stmt_obj: Optional[uni.FinallyStmt] = (
1924
+ fin_append := uni.FinallyStmt(
1925
+ body=valid_finalbody,
1926
+ kid=valid_finalbody,
1927
+ )
2115
1928
  )
2116
- finally_stmt = uni.FinallyStmt(body=finally_body, kid=[finally_body])
2117
-
2118
- kid.append(finally_stmt)
1929
+ kid.append(fin_append)
2119
1930
  else:
2120
- finally_body = None
1931
+ finally_stmt_obj = None
2121
1932
  ret = uni.TryStmt(
2122
1933
  body=valid_body,
2123
1934
  excepts=excepts,
2124
1935
  else_body=elsestmt if else_body else None,
2125
- finally_body=finally_stmt if finally_body else None,
1936
+ finally_body=finally_stmt_obj,
2126
1937
  kid=kid,
2127
1938
  )
2128
1939
  return ret
@@ -2147,17 +1958,14 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
2147
1958
  """
2148
1959
  elts = [self.convert(elt) for elt in node.elts]
2149
1960
  if len(node.elts) != 0:
2150
- valid = [i for i in elts if isinstance(i, (uni.Expr, uni.KWPair))]
2151
- if len(elts) != len(valid):
1961
+ valid_elts = [i for i in elts if isinstance(i, (uni.Expr, uni.KWPair))]
1962
+ if len(elts) != len(valid_elts):
2152
1963
  raise self.ice("Length mismatch in tuple elts")
2153
- valid_elts = uni.SubNodeList[uni.Expr | uni.KWPair](
2154
- items=valid, delim=Tok.COMMA, kid=valid
2155
- )
2156
1964
  kid = elts
2157
1965
  else:
2158
1966
  l_paren = self.operator(Tok.LPAREN, "(")
2159
1967
  r_paren = self.operator(Tok.RPAREN, ")")
2160
- valid_elts = None
1968
+ valid_elts = []
2161
1969
  kid = [l_paren, r_paren]
2162
1970
  return uni.TupleVal(values=valid_elts, kid=kid)
2163
1971
 
@@ -2339,21 +2147,17 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
2339
2147
 
2340
2148
  valid_params = [param for param in params if isinstance(param, uni.ParamVar)]
2341
2149
  if valid_params:
2342
- fs_params = uni.SubNodeList[uni.ParamVar](
2343
- items=valid_params, delim=Tok.COMMA, kid=valid_params
2344
- )
2150
+ fs_params = valid_params
2345
2151
  return uni.FuncSignature(
2346
2152
  params=fs_params,
2347
2153
  return_type=None,
2348
- kid=[fs_params],
2154
+ kid=fs_params,
2349
2155
  )
2350
2156
  else:
2351
- _lparen = self.operator(Tok.LPAREN, "(")
2352
- _rparen = self.operator(Tok.RPAREN, ")")
2353
2157
  return uni.FuncSignature(
2354
- params=None,
2158
+ params=[],
2355
2159
  return_type=None,
2356
- kid=[_lparen, _rparen],
2160
+ kid=[self.operator(Tok.LPAREN, "("), self.operator(Tok.RPAREN, ")")],
2357
2161
  )
2358
2162
 
2359
2163
  def operator(self, tok: Tok, value: str) -> uni.Token: