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
@@ -5,10 +5,10 @@ from __future__ import annotations
5
5
  import keyword
6
6
  import logging
7
7
  import os
8
- from typing import Callable, TYPE_CHECKING, TypeAlias, TypeVar
8
+ from typing import Callable, Sequence, TYPE_CHECKING, TypeAlias, TypeVar, cast
9
9
 
10
10
  import jaclang.compiler.unitree as uni
11
- from jaclang.compiler import jac_lark as jl # type: ignore
11
+ from jaclang.compiler import jac_lark as jl
12
12
  from jaclang.compiler.constant import EdgeDir, Tokens as Tok
13
13
  from jaclang.compiler.passes.main import Transform
14
14
  from jaclang.vendor.lark import Lark, Transformer, Tree, logger
@@ -17,6 +17,7 @@ if TYPE_CHECKING:
17
17
  from jaclang.compiler.program import JacProgram
18
18
 
19
19
  T = TypeVar("T", bound=uni.UniNode)
20
+ TL = TypeVar("TL", bound=(uni.UniNode | list))
20
21
 
21
22
 
22
23
  class JacParser(Transform[uni.Source, uni.Module]):
@@ -183,7 +184,13 @@ class JacParser(Transform[uni.Source, uni.Module]):
183
184
  # Parser Helper functions. #
184
185
  # ******************************************************************* #
185
186
 
186
- def match(self, ty: type[T]) -> T | None:
187
+ def extract_from_list(
188
+ self, nd_list: list[uni.UniNode], ty: type[T] | tuple[type[T], ...]
189
+ ) -> list[T]:
190
+ """Extract a list of nodes of type 'ty' from the current nodes."""
191
+ return cast(list[T], [node for node in nd_list if isinstance(node, ty)])
192
+
193
+ def match(self, ty: type[TL]) -> TL | None:
187
194
  """Return a node matching type 'ty' if possible from the current nodes."""
188
195
  if (self.node_idx < len(self.cur_nodes)) and isinstance(
189
196
  self.cur_nodes[self.node_idx], ty
@@ -192,7 +199,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
192
199
  return self.cur_nodes[self.node_idx - 1] # type: ignore[return-value]
193
200
  return None
194
201
 
195
- def consume(self, ty: type[T]) -> T:
202
+ def consume(self, ty: type[TL]) -> TL:
196
203
  """Consume and return the specified type, if it's not exists, will be an internal compiler error."""
197
204
  if node := self.match(ty):
198
205
  return node
@@ -228,6 +235,17 @@ class JacParser(Transform[uni.Source, uni.Module]):
228
235
  nodes.append(node)
229
236
  return nodes # type: ignore[return-value]
230
237
 
238
+ @property
239
+ def flat_cur_nodes(self) -> list[uni.UniNode]:
240
+ """Flatten the current nodes."""
241
+ flat_nodes: list[uni.UniNode] = []
242
+ for node in self.cur_nodes:
243
+ if isinstance(node, list):
244
+ flat_nodes.extend(node)
245
+ else:
246
+ flat_nodes.append(node)
247
+ return flat_nodes
248
+
231
249
  # ******************************************************************* #
232
250
  # Parsing Rules #
233
251
  # ******************************************************************* #
@@ -293,12 +311,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
293
311
  """
294
312
  is_frozen = self.consume(uni.Token).name == Tok.KW_LET
295
313
  access_tag = self.match(uni.SubTag)
296
- assignments = self.consume(uni.SubNodeList)
314
+ assignments_list = self.consume(list)
297
315
  return uni.GlobalVars(
298
316
  access=access_tag,
299
- assignments=assignments,
317
+ assignments=self.extract_from_list(assignments_list, uni.Assignment),
300
318
  is_frozen=is_frozen,
301
- kid=self.cur_nodes,
319
+ kid=self.flat_cur_nodes,
302
320
  )
303
321
 
304
322
  def access_tag(self, _: None) -> uni.SubTag[uni.Token]:
@@ -318,11 +336,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
318
336
  # Q(thakee): Why the name should be KW_TEST if no name present?
319
337
  test_tok = self.consume_token(Tok.KW_TEST)
320
338
  name = self.match(uni.Name) or test_tok
321
- codeblock = self.consume(uni.SubNodeList)
339
+ codeblock = self.consume(list)
322
340
  return uni.Test(
323
341
  name=name,
324
- body=codeblock,
325
- kid=self.cur_nodes,
342
+ body=self.extract_from_list(codeblock, uni.CodeBlockStmt),
343
+ kid=self.flat_cur_nodes,
326
344
  )
327
345
 
328
346
  def free_code(self, _: None) -> uni.ModuleCode:
@@ -335,11 +353,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
335
353
  name = None
336
354
  if self.match_token(Tok.COLON):
337
355
  name = self.consume(uni.Name)
338
- codeblock = self.consume(uni.SubNodeList)
356
+ codeblock = self.consume(list)
339
357
  return uni.ModuleCode(
340
358
  name=name,
341
- body=codeblock,
342
- kid=self.cur_nodes,
359
+ body=self.extract_from_list(codeblock, uni.CodeBlockStmt),
360
+ kid=self.flat_cur_nodes,
343
361
  )
344
362
 
345
363
  def py_code_block(self, _: None) -> uni.PyInlineCode:
@@ -357,26 +375,17 @@ class JacParser(Transform[uni.Source, uni.Module]):
357
375
  """Grammar rule.
358
376
 
359
377
  import_stmt: KW_IMPORT KW_FROM from_path LBRACE import_items RBRACE
360
- | KW_IMPORT KW_FROM from_path COMMA import_items SEMI //Deprecated
361
378
  | KW_IMPORT import_path (COMMA import_path)* SEMI
362
379
  | KW_INCLUDE import_path SEMI
363
380
  """
364
- # TODO: kid will be removed so let's keep as it is for now.
365
- kid = self.cur_nodes
366
-
367
381
  if self.match_token(Tok.KW_INCLUDE):
368
382
  # Handle include statement
369
383
  import_path_obj = self.consume(uni.ModulePath)
370
- items = uni.SubNodeList[uni.ModulePath](
371
- items=[import_path_obj], delim=Tok.COMMA, kid=[import_path_obj]
372
- )
373
- kid = (kid[:1]) + [items] + kid[-1:] # TODO: Will be removed.
374
- self.consume_token(Tok.SEMI)
375
384
  return uni.Import(
376
385
  from_loc=None,
377
- items=items,
386
+ items=[import_path_obj],
378
387
  is_absorb=True,
379
- kid=kid,
388
+ kid=self.cur_nodes,
380
389
  )
381
390
 
382
391
  from_path: uni.ModulePath | None = None
@@ -385,31 +394,25 @@ class JacParser(Transform[uni.Source, uni.Module]):
385
394
  if self.match_token(Tok.KW_FROM):
386
395
  from_path = self.consume(uni.ModulePath)
387
396
  self.consume(uni.Token) # LBRACE or COMMA
388
- items = self.consume(uni.SubNodeList)
389
- if self.consume(uni.Token).name == Tok.SEMI: # RBRACE or SEMI
390
- self.parse_ref.log_warning(
391
- "Deprecated syntax, use braces for multiple imports (e.g, import from mymod {a, b, c})",
392
- )
397
+ items = self.extract_from_list(self.consume(list), uni.ModuleItem)
398
+ self.consume(uni.Token)
399
+ return uni.Import(
400
+ from_loc=from_path,
401
+ items=items,
402
+ is_absorb=False,
403
+ kid=self.flat_cur_nodes,
404
+ )
393
405
  else:
394
406
  paths = [self.consume(uni.ModulePath)]
395
407
  while self.match_token(Tok.COMMA):
396
408
  paths.append(self.consume(uni.ModulePath))
397
409
  self.consume_token(Tok.SEMI)
398
- items = uni.SubNodeList[uni.ModulePath](
410
+ return uni.Import(
411
+ from_loc=from_path,
399
412
  items=paths,
400
- delim=Tok.COMMA,
401
- # TODO: kid will be removed so let's keep as it is for now.
402
- kid=self.cur_nodes[1:-1],
413
+ is_absorb=False,
414
+ kid=self.flat_cur_nodes,
403
415
  )
404
- kid = kid[:1] + [items] + kid[-1:]
405
-
406
- is_absorb = False
407
- return uni.Import(
408
- from_loc=from_path,
409
- items=items,
410
- is_absorb=is_absorb,
411
- kid=kid,
412
- )
413
416
 
414
417
  def from_path(self, _: None) -> uni.ModulePath:
415
418
  """Grammar rule.
@@ -443,44 +446,28 @@ class JacParser(Transform[uni.Source, uni.Module]):
443
446
 
444
447
  import_path: dotted_name (KW_AS NAME)?
445
448
  """
446
- valid_path = self.consume(uni.SubNodeList)
449
+ valid_path = self.extract_from_list(self.consume(list), uni.Name)
447
450
  alias = self.consume(uni.Name) if self.match_token(Tok.KW_AS) else None
448
451
  return uni.ModulePath(
449
452
  path=valid_path,
450
453
  level=0,
451
454
  alias=alias,
452
- kid=self.cur_nodes,
455
+ kid=self.flat_cur_nodes,
453
456
  )
454
457
 
455
- def dotted_name(self, _: None) -> uni.SubNodeList[uni.Name]:
458
+ def dotted_name(self, _: None) -> list[uni.UniNode]:
456
459
  """Grammar rule.
457
460
 
458
461
  dotted_name: named_ref (DOT named_ref)*
459
462
  """
460
- valid_path = [self.consume(uni.Name)]
461
- while self.match_token(Tok.DOT):
462
- valid_path.append(self.consume(uni.Name))
463
- return uni.SubNodeList[uni.Name](
464
- items=valid_path,
465
- delim=Tok.DOT,
466
- kid=self.cur_nodes,
467
- )
463
+ return self.cur_nodes
468
464
 
469
- def import_items(self, _: None) -> uni.SubNodeList[uni.ModuleItem]:
465
+ def import_items(self, _: None) -> list[uni.UniNode]:
470
466
  """Grammar rule.
471
467
 
472
468
  import_items: (import_item COMMA)* import_item COMMA?
473
469
  """
474
- items = [self.consume(uni.ModuleItem)]
475
- while self.match_token(Tok.COMMA):
476
- if module_item := self.match(uni.ModuleItem):
477
- items.append(module_item)
478
- ret = uni.SubNodeList[uni.ModuleItem](
479
- items=items,
480
- delim=Tok.COMMA,
481
- kid=self.cur_nodes,
482
- )
483
- return ret
470
+ return self.flat_cur_nodes
484
471
 
485
472
  def import_item(self, _: None) -> uni.ModuleItem:
486
473
  """Grammar rule.
@@ -504,12 +491,13 @@ class JacParser(Transform[uni.Source, uni.Module]):
504
491
  """
505
492
  archspec: uni.ArchSpec | uni.Enum | None = None
506
493
 
507
- decorators = self.match(uni.SubNodeList)
494
+ decorators_node = self.match(list)
508
495
  is_async = self.match_token(Tok.KW_ASYNC)
509
- if decorators is not None:
496
+ if decorators_node is not None:
510
497
  archspec = self.consume(uni.ArchSpec)
498
+ decorators = self.extract_from_list(decorators_node, uni.Expr)
511
499
  archspec.decorators = decorators
512
- archspec.add_kids_left([decorators])
500
+ archspec.add_kids_left(decorators_node)
513
501
  else:
514
502
  archspec = self.match(uni.ArchSpec) or self.consume(uni.Enum)
515
503
  if is_async and isinstance(archspec, uni.ArchSpec):
@@ -527,37 +515,46 @@ class JacParser(Transform[uni.Source, uni.Module]):
527
515
 
528
516
  impl_def: decorators? KW_IMPL dotted_name impl_spec? impl_tail
529
517
  """
530
- decorators = self.match(uni.SubNodeList)
518
+ decorators_node = self.match(list)
531
519
  self.consume_token(Tok.KW_IMPL)
532
- target = self.consume(uni.SubNodeList)
520
+ target = self.extract_from_list(self.consume(list), uni.NameAtom)
533
521
  spec = (
534
- self.match(uni.SubNodeList)
522
+ self.match(list)
535
523
  or self.match(uni.FuncSignature)
536
524
  or self.match(uni.EventSignature)
537
525
  )
538
- tail = self.match(uni.SubNodeList) or self.match(uni.FuncCall)
526
+ tail = self.match(list) or self.match(uni.FuncCall)
539
527
  valid_tail = spec if tail is None else tail
540
528
  valid_spec = None if tail is None else spec
541
- assert isinstance(valid_tail, (uni.SubNodeList, uni.FuncCall))
542
-
543
529
  impl = uni.ImplDef(
544
- decorators=decorators,
530
+ body=(
531
+ self.extract_from_list(
532
+ valid_tail,
533
+ (uni.EnumBlockStmt, uni.CodeBlockStmt), # type: ignore[arg-type]
534
+ )
535
+ if isinstance(valid_tail, list)
536
+ else valid_tail
537
+ ),
545
538
  target=target,
539
+ decorators=(
540
+ self.extract_from_list(decorators_node, uni.Expr)
541
+ if decorators_node
542
+ else None
543
+ ),
546
544
  spec=valid_spec,
547
- body=valid_tail,
548
- kid=self.cur_nodes,
545
+ kid=self.flat_cur_nodes,
549
546
  )
550
547
  return impl
551
548
 
552
549
  def impl_spec(
553
550
  self, _: None
554
- ) -> uni.SubNodeList[uni.Expr] | uni.FuncSignature | uni.EventSignature:
551
+ ) -> Sequence[uni.Expr] | uni.FuncSignature | uni.EventSignature:
555
552
  """Grammar rule.
556
553
 
557
554
  impl_spec: inherited_archs | func_decl | event_clause
558
555
  """
559
556
  spec = (
560
- self.match(uni.SubNodeList) # inherited_archs
557
+ self.match(list) # inherited_archs
561
558
  or self.match(uni.FuncSignature) # func_decl
562
559
  or self.consume(uni.EventSignature) # event_clause
563
560
  )
@@ -565,16 +562,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
565
562
 
566
563
  def impl_tail(
567
564
  self, _: None
568
- ) -> uni.SubNodeList[uni.CodeBlockStmt] | uni.FuncCall:
565
+ ) -> Sequence[uni.EnumBlockStmt] | list[uni.CodeBlockStmt] | uni.FuncCall:
569
566
  """Grammar rule.
570
567
 
571
568
  impl_tail: enum_block | block_tail
572
569
  """
573
- tail = (
574
- self.match(uni.SubNodeList) # enum_block
575
- or self.match(uni.SubNodeList) # block_tail (code_block)
576
- or self.consume(uni.FuncCall) # block_tail (KW_BY atomic_call)
577
- )
570
+ tail = self.match(list) or self.consume( # enum_block or code_block
571
+ uni.FuncCall
572
+ ) # block_tail (KW_BY atomic_call)
578
573
  return tail
579
574
 
580
575
  def archetype_decl(self, _: None) -> uni.ArchSpec:
@@ -585,22 +580,23 @@ class JacParser(Transform[uni.Source, uni.Module]):
585
580
  arch_type = self.consume(uni.Token)
586
581
  access = self.match(uni.SubTag)
587
582
  name = self.consume(uni.Name)
588
- sub_list1 = self.match(uni.SubNodeList)
589
- sub_list2 = self.match(uni.SubNodeList)
590
- if self.match_token(Tok.SEMI):
591
- inh, body = sub_list1, None
583
+ inh_sn = self.match(list)
584
+ body_list = self.match(list)
585
+ body: list[uni.ArchBlockStmt] | None
586
+ if body_list is None and self.match_token(Tok.SEMI):
587
+ body = None
588
+ elif body_list is None:
589
+ body = self.extract_from_list(inh_sn or [], uni.ArchBlockStmt)
590
+ inh_sn = None
592
591
  else:
593
- body = (
594
- sub_list2 or sub_list1
595
- ) # if sub_list2 is None then body is sub_list1
596
- inh = sub_list2 and sub_list1 # if sub_list2 is None then inh is None.
592
+ body = self.extract_from_list(body_list or [], uni.ArchBlockStmt)
597
593
  return uni.Archetype(
598
594
  arch_type=arch_type,
599
595
  name=name,
600
596
  access=access,
601
- base_classes=inh,
597
+ base_classes=self.extract_from_list(inh_sn or [], uni.Expr) or [],
602
598
  body=body,
603
- kid=self.cur_nodes,
599
+ kid=self.flat_cur_nodes,
604
600
  )
605
601
 
606
602
  def arch_type(self, _: None) -> uni.Token:
@@ -613,30 +609,19 @@ class JacParser(Transform[uni.Source, uni.Module]):
613
609
  """
614
610
  return self.consume(uni.Token)
615
611
 
616
- def decorators(self, _: None) -> uni.SubNodeList[uni.Expr]:
612
+ def decorators(self, _: None) -> list[uni.UniNode]:
617
613
  """Grammar rule.
618
614
 
619
615
  decorators: (DECOR_OP atomic_chain)+
620
616
  """
621
- self.consume_token(Tok.DECOR_OP)
622
- return uni.SubNodeList[uni.Expr](
623
- items=self.consume_many(uni.Expr),
624
- delim=Tok.DECOR_OP,
625
- kid=self.cur_nodes,
626
- )
617
+ return self.cur_nodes
627
618
 
628
- def inherited_archs(self, kid: list[uni.UniNode]) -> uni.SubNodeList[uni.Expr]:
619
+ def inherited_archs(self, kid: list[uni.UniNode]) -> list[uni.UniNode]:
629
620
  """Grammar rule.
630
621
 
631
622
  inherited_archs: LPAREN (atomic_chain COMMA)* atomic_chain RPAREN
632
623
  """
633
- self.match_token(Tok.LPAREN)
634
- items: list = []
635
- while inherited_arch := self.match(uni.Expr):
636
- items.append(inherited_arch)
637
- self.match_token(Tok.COMMA)
638
- self.match_token(Tok.RPAREN)
639
- return uni.SubNodeList[uni.Expr](items=items, delim=Tok.COMMA, kid=kid)
624
+ return self.flat_cur_nodes
640
625
 
641
626
  def named_ref(self, _: None) -> uni.NameAtom:
642
627
  """Grammar rule.
@@ -666,10 +651,10 @@ class JacParser(Transform[uni.Source, uni.Module]):
666
651
  enum: decorators? enum_decl
667
652
  | enum_def
668
653
  """
669
- if decorator := self.match(uni.SubNodeList):
654
+ if decorator := self.match(list):
670
655
  enum_decl = self.consume(uni.Enum)
671
- enum_decl.decorators = decorator
672
- enum_decl.add_kids_left([decorator])
656
+ enum_decl.decorators = self.extract_from_list(decorator, uni.Expr)
657
+ enum_decl.add_kids_left(decorator)
673
658
  return enum_decl
674
659
  return self.consume(uni.Enum)
675
660
 
@@ -681,48 +666,48 @@ class JacParser(Transform[uni.Source, uni.Module]):
681
666
  self.consume_token(Tok.KW_ENUM)
682
667
  access = self.match(uni.SubTag)
683
668
  name = self.consume(uni.Name)
684
- sub_list1 = self.match(uni.SubNodeList)
685
- sub_list2 = self.match(uni.SubNodeList)
686
- if self.match_token(Tok.SEMI):
669
+ sub_list1 = self.match(list)
670
+ enum_body = self.match(list)
671
+ body: list[uni.UniNode] | None = None
672
+ if enum_body is None and self.match_token(Tok.SEMI):
687
673
  inh, body = sub_list1, None
674
+ elif enum_body is None:
675
+ body = self.extract_from_list(sub_list1 or [], uni.EnumBlockStmt)
676
+ inh = None
688
677
  else:
689
- body = sub_list2 or sub_list1
690
- inh = sub_list2 and sub_list1
678
+ body = enum_body
679
+ inh = sub_list1
691
680
  return uni.Enum(
692
681
  name=name,
693
682
  access=access,
694
- base_classes=inh,
695
- body=body,
696
- kid=self.cur_nodes,
683
+ base_classes=self.extract_from_list(inh or [], uni.Expr) or [],
684
+ body=self.extract_from_list(body, uni.EnumBlockStmt) if body else None,
685
+ kid=self.flat_cur_nodes,
697
686
  )
698
687
 
699
- def enum_block(self, _: None) -> uni.SubNodeList[uni.EnumBlockStmt]:
688
+ def enum_block(self, _: None) -> list[uni.UniNode]:
700
689
  """Grammar rule.
701
690
 
702
691
  enum_block: LBRACE assignment_list COMMA? (py_code_block | free_code)* RBRACE
703
692
  """
704
693
  left_enc = self.consume_token(Tok.LBRACE)
705
- assignments = self.consume(uni.SubNodeList)
694
+ assignments = self.consume(list)
706
695
  self.match_token(Tok.COMMA)
707
696
  while item := self.match(uni.EnumBlockStmt):
708
- assignments.add_kids_right([item])
709
- assignments.items.append(item)
697
+ item.is_enum_stmt = True
698
+ assignments.append(item)
710
699
  right_enc = self.consume_token(Tok.RBRACE)
711
- assignments.add_kids_left([left_enc])
712
- assignments.add_kids_right([right_enc])
713
- assignments.left_enc = left_enc
714
- assignments.right_enc = right_enc
715
- for i in assignments.kid:
700
+ for i in assignments:
716
701
  if isinstance(i, uni.Assignment):
717
702
  i.is_enum_stmt = True
718
- return assignments
703
+ return [left_enc, *assignments, right_enc]
719
704
 
720
705
  def ability(self, _: None) -> uni.Ability | uni.FuncCall:
721
706
  """Grammar rule.
722
707
 
723
708
  ability: decorators? KW_ASYNC? (ability_decl | function_decl)
724
709
  """
725
- decorators = self.match(uni.SubNodeList)
710
+ decorators_node = self.match(list)
726
711
  is_async = self.match_token(Tok.KW_ASYNC)
727
712
 
728
713
  # Try to match ability_decl or function_decl
@@ -731,8 +716,9 @@ class JacParser(Transform[uni.Source, uni.Module]):
731
716
  ability.is_async = True
732
717
  ability.add_kids_left([is_async])
733
718
 
734
- if decorators:
735
- for dec in decorators.items:
719
+ if decorators_node:
720
+ decorators = self.extract_from_list(decorators_node, uni.Expr)
721
+ for dec in decorators[:]:
736
722
  if (
737
723
  isinstance(dec, uni.NameAtom)
738
724
  and dec.sym_name == "staticmethod"
@@ -742,7 +728,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
742
728
  static_kw.line_no = dec.loc.first_line
743
729
  static_kw.c_start = dec.loc.col_start
744
730
  static_kw.c_end = static_kw.c_start + len(static_kw.name)
745
- decorators.items.remove(dec) # noqa: B038
731
+ decorators.remove(dec) # noqa: B038
746
732
  if not ability.is_static:
747
733
  ability.is_static = True
748
734
  if not ability.is_override:
@@ -750,9 +736,9 @@ class JacParser(Transform[uni.Source, uni.Module]):
750
736
  else:
751
737
  ability.insert_kids_at_pos([static_kw], 1)
752
738
  break
753
- if decorators.items:
739
+ if decorators:
754
740
  ability.decorators = decorators
755
- ability.add_kids_left([decorators])
741
+ ability.add_kids_left(decorators_node)
756
742
 
757
743
  return ability
758
744
 
@@ -770,12 +756,18 @@ class JacParser(Transform[uni.Source, uni.Module]):
770
756
  signature = self.consume(uni.EventSignature)
771
757
 
772
758
  # Handle block_tail
773
- body = self.match(uni.SubNodeList) or self.match(uni.FuncCall)
774
- if body is None:
759
+ body_sn_or_call = self.match(list) or self.match(uni.FuncCall)
760
+ if body_sn_or_call is None:
775
761
  is_abstract = self.match_token(Tok.KW_ABSTRACT) is not None
776
762
  self.consume_token(Tok.SEMI)
763
+ body = None
777
764
  else:
778
765
  is_abstract = False
766
+ body = (
767
+ self.extract_from_list(body_sn_or_call, uni.CodeBlockStmt)
768
+ if isinstance(body_sn_or_call, list)
769
+ else body_sn_or_call
770
+ )
779
771
 
780
772
  return uni.Ability(
781
773
  name_ref=name,
@@ -786,7 +778,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
786
778
  access=access,
787
779
  signature=signature,
788
780
  body=body,
789
- kid=self.cur_nodes,
781
+ kid=self.flat_cur_nodes,
790
782
  )
791
783
 
792
784
  def function_decl(self, _: None) -> uni.Ability:
@@ -804,12 +796,18 @@ class JacParser(Transform[uni.Source, uni.Module]):
804
796
  signature = self.match(uni.FuncSignature)
805
797
 
806
798
  # Handle block_tail
807
- body = self.match(uni.SubNodeList) or self.match(uni.FuncCall)
808
- if body is None:
799
+ body_sn_or_call = self.match(list) or self.match(uni.FuncCall)
800
+ if body_sn_or_call is None:
809
801
  is_abstract = self.match_token(Tok.KW_ABSTRACT) is not None
810
802
  self.consume_token(Tok.SEMI)
803
+ body = None
811
804
  else:
812
805
  is_abstract = False
806
+ body = (
807
+ self.extract_from_list(body_sn_or_call, uni.CodeBlockStmt)
808
+ if isinstance(body_sn_or_call, list)
809
+ else body_sn_or_call
810
+ )
813
811
 
814
812
  return uni.Ability(
815
813
  name_ref=name,
@@ -820,7 +818,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
820
818
  access=access,
821
819
  signature=signature,
822
820
  body=body,
823
- kid=self.cur_nodes,
821
+ kid=self.flat_cur_nodes,
824
822
  )
825
823
 
826
824
  def func_decl(self, _: None) -> uni.FuncSignature:
@@ -829,44 +827,38 @@ class JacParser(Transform[uni.Source, uni.Module]):
829
827
  func_decl: (LPAREN func_decl_params? RPAREN) (RETURN_HINT expression)?
830
828
  | (RETURN_HINT expression)
831
829
  """
832
- params: uni.SubNodeList | None = None
830
+ params: list[uni.UniNode] | None = None
833
831
  return_spec: uni.Expr | None = None
834
832
 
835
833
  # Check if starting with RETURN_HINT
836
834
  if self.match_token(Tok.RETURN_HINT):
837
835
  return_spec = self.consume(uni.Expr)
838
836
  return uni.FuncSignature(
839
- params=None,
837
+ params=[],
840
838
  return_type=return_spec,
841
- kid=self.cur_nodes,
839
+ kid=self.flat_cur_nodes,
842
840
  )
843
841
  # Otherwise, parse the traditional parameter list form
844
842
  else:
845
843
  self.consume_token(Tok.LPAREN)
846
- params = self.match(uni.SubNodeList)
844
+ params = self.match(list)
847
845
  self.consume_token(Tok.RPAREN)
848
846
  if self.match_token(Tok.RETURN_HINT):
849
847
  return_spec = self.consume(uni.Expr)
850
848
  return uni.FuncSignature(
851
- params=params,
849
+ params=(
850
+ self.extract_from_list(params, uni.ParamVar) if params else []
851
+ ),
852
852
  return_type=return_spec,
853
- kid=self.cur_nodes,
853
+ kid=self.flat_cur_nodes,
854
854
  )
855
855
 
856
- def func_decl_params(self, _: None) -> uni.SubNodeList[uni.ParamVar]:
856
+ def func_decl_params(self, _: None) -> list[uni.UniNode]:
857
857
  """Grammar rule.
858
858
 
859
859
  func_decl_params: (param_var COMMA)* param_var COMMA?
860
860
  """
861
- paramvar: list = []
862
- while param_stmt := self.match(uni.ParamVar):
863
- paramvar.append(param_stmt)
864
- self.match_token(Tok.COMMA)
865
- return uni.SubNodeList[uni.ParamVar](
866
- items=paramvar,
867
- delim=Tok.COMMA,
868
- kid=self.cur_nodes,
869
- )
861
+ return self.cur_nodes
870
862
 
871
863
  def param_var(self, _: None) -> uni.ParamVar:
872
864
  """Grammar rule.
@@ -885,22 +877,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
885
877
  kid=self.cur_nodes,
886
878
  )
887
879
 
888
- def member_block(self, _: None) -> uni.SubNodeList[uni.ArchBlockStmt]:
880
+ def member_block(self, _: None) -> list[uni.UniNode]:
889
881
  """Grammar rule.
890
882
 
891
883
  member_block: LBRACE member_stmt* RBRACE
892
884
  """
893
- left_enc = self.consume_token(Tok.LBRACE)
894
- items = self.match_many(uni.ArchBlockStmt)
895
- right_enc = self.consume_token(Tok.RBRACE)
896
- ret = uni.SubNodeList[uni.ArchBlockStmt](
897
- items=items,
898
- delim=Tok.WS,
899
- kid=self.cur_nodes,
900
- )
901
- ret.left_enc = left_enc
902
- ret.right_enc = right_enc
903
- return ret
885
+ return self.cur_nodes
904
886
 
905
887
  def member_stmt(self, _: None) -> uni.ArchBlockStmt:
906
888
  """Grammar rule.
@@ -929,35 +911,24 @@ class JacParser(Transform[uni.Source, uni.Module]):
929
911
  access = chomp[0] if isinstance(chomp[0], uni.SubTag) else None
930
912
  chomp = chomp[1:] if access else chomp
931
913
  assign = chomp[0]
932
- if isinstance(assign, uni.SubNodeList):
914
+ if isinstance(assign, list):
915
+ assigns = self.extract_from_list(assign, uni.HasVar)
933
916
  return uni.ArchHas(
934
- vars=assign,
917
+ vars=assigns,
935
918
  is_static=is_static,
936
919
  is_frozen=is_freeze,
937
920
  access=access,
938
- kid=kid,
921
+ kid=self.flat_cur_nodes,
939
922
  )
940
923
  else:
941
924
  raise self.ice()
942
925
 
943
- def has_assign_list(self, _: None) -> uni.SubNodeList[uni.HasVar]:
926
+ def has_assign_list(self, _: None) -> list[uni.UniNode]:
944
927
  """Grammar rule.
945
928
 
946
929
  has_assign_list: (has_assign_list COMMA)? typed_has_clause
947
930
  """
948
- if consume := self.match(uni.SubNodeList):
949
- comma = self.consume_token(Tok.COMMA)
950
- assign = self.consume(uni.HasVar)
951
- new_kid = [*consume.kid, comma, assign]
952
- else:
953
- assign = self.consume(uni.HasVar)
954
- new_kid = [assign]
955
- valid_kid = [i for i in new_kid if isinstance(i, uni.HasVar)]
956
- return uni.SubNodeList[uni.HasVar](
957
- items=valid_kid,
958
- delim=Tok.COMMA,
959
- kid=new_kid,
960
- )
931
+ return self.flat_cur_nodes
961
932
 
962
933
  def typed_has_clause(self, _: None) -> uni.HasVar:
963
934
  """Grammar rule.
@@ -1017,26 +988,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
1017
988
  pos_end=token.pos_end,
1018
989
  )
1019
990
 
1020
- def code_block(
1021
- self, kid: list[uni.UniNode]
1022
- ) -> uni.SubNodeList[uni.CodeBlockStmt]:
991
+ def code_block(self, kid: list[uni.UniNode]) -> list[uni.UniNode]:
1023
992
  """Grammar rule.
1024
993
 
1025
994
  code_block: LBRACE statement* RBRACE
1026
995
  """
1027
- left_enc = kid[0] if isinstance(kid[0], uni.Token) else None
1028
- right_enc = kid[-1] if isinstance(kid[-1], uni.Token) else None
1029
- valid_stmt = [i for i in kid if isinstance(i, uni.CodeBlockStmt)]
1030
- if len(valid_stmt) == len(kid) - 2:
1031
- return uni.SubNodeList[uni.CodeBlockStmt](
1032
- items=valid_stmt,
1033
- delim=Tok.WS,
1034
- left_enc=left_enc,
1035
- right_enc=right_enc,
1036
- kid=kid,
1037
- )
1038
- else:
1039
- raise self.ice()
996
+ return self.flat_cur_nodes
1040
997
 
1041
998
  def statement(self, kid: list[uni.UniNode]) -> uni.CodeBlockStmt:
1042
999
  """Grammar rule.
@@ -1102,11 +1059,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
1102
1059
  """
1103
1060
  self.consume_token(Tok.RETURN_HINT)
1104
1061
  ctx = self.consume(uni.Expr)
1105
- body = self.consume(uni.SubNodeList)
1062
+ body = self.consume(list)
1106
1063
  return uni.TypedCtxBlock(
1107
1064
  type_ctx=ctx,
1108
- body=body,
1109
- kid=self.cur_nodes,
1065
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1066
+ kid=self.flat_cur_nodes,
1110
1067
  )
1111
1068
 
1112
1069
  def if_stmt(self, _: None) -> uni.IfStmt:
@@ -1116,13 +1073,13 @@ class JacParser(Transform[uni.Source, uni.Module]):
1116
1073
  """
1117
1074
  self.consume_token(Tok.KW_IF)
1118
1075
  condition = self.consume(uni.Expr)
1119
- body = self.consume(uni.SubNodeList)
1076
+ body = self.consume(list)
1120
1077
  else_body = self.match(uni.ElseStmt) or self.match(uni.ElseIf)
1121
1078
  return uni.IfStmt(
1122
1079
  condition=condition,
1123
- body=body,
1080
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1124
1081
  else_body=else_body,
1125
- kid=self.cur_nodes,
1082
+ kid=self.flat_cur_nodes,
1126
1083
  )
1127
1084
 
1128
1085
  def elif_stmt(self, _: None) -> uni.ElseIf:
@@ -1132,13 +1089,13 @@ class JacParser(Transform[uni.Source, uni.Module]):
1132
1089
  """
1133
1090
  self.consume_token(Tok.KW_ELIF)
1134
1091
  condition = self.consume(uni.Expr)
1135
- body = self.consume(uni.SubNodeList)
1092
+ body = self.consume(list)
1136
1093
  else_body = self.match(uni.ElseStmt) or self.match(uni.ElseIf)
1137
1094
  return uni.ElseIf(
1138
1095
  condition=condition,
1139
- body=body,
1096
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1140
1097
  else_body=else_body,
1141
- kid=self.cur_nodes,
1098
+ kid=self.flat_cur_nodes,
1142
1099
  )
1143
1100
 
1144
1101
  def else_stmt(self, _: None) -> uni.ElseStmt:
@@ -1147,10 +1104,10 @@ class JacParser(Transform[uni.Source, uni.Module]):
1147
1104
  else_stmt: KW_ELSE code_block
1148
1105
  """
1149
1106
  self.consume_token(Tok.KW_ELSE)
1150
- body = self.consume(uni.SubNodeList)
1107
+ body = self.consume(list)
1151
1108
  return uni.ElseStmt(
1152
- body=body,
1153
- kid=self.cur_nodes,
1109
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1110
+ kid=self.flat_cur_nodes,
1154
1111
  )
1155
1112
 
1156
1113
  def try_stmt(self, _: None) -> uni.TryStmt:
@@ -1159,31 +1116,28 @@ class JacParser(Transform[uni.Source, uni.Module]):
1159
1116
  try_stmt: KW_TRY code_block except_list? else_stmt? finally_stmt?
1160
1117
  """
1161
1118
  self.consume_token(Tok.KW_TRY)
1162
- block = self.consume(uni.SubNodeList)
1163
- except_list = self.match(uni.SubNodeList)
1119
+ block = self.consume(list)
1120
+ except_list = self.match(list)
1164
1121
  else_stmt = self.match(uni.ElseStmt)
1165
1122
  finally_stmt = self.match(uni.FinallyStmt)
1166
1123
  return uni.TryStmt(
1167
- body=block,
1168
- excepts=except_list,
1124
+ body=self.extract_from_list(block, uni.CodeBlockStmt),
1125
+ excepts=(
1126
+ self.extract_from_list(except_list, uni.Except)
1127
+ if except_list
1128
+ else []
1129
+ ),
1169
1130
  else_body=else_stmt,
1170
1131
  finally_body=finally_stmt,
1171
- kid=self.cur_nodes,
1132
+ kid=self.flat_cur_nodes,
1172
1133
  )
1173
1134
 
1174
- def except_list(self, _: None) -> uni.SubNodeList[uni.Except]:
1135
+ def except_list(self, _: None) -> list[uni.UniNode]:
1175
1136
  """Grammar rule.
1176
1137
 
1177
1138
  except_list: except_def+
1178
1139
  """
1179
- items = [self.consume(uni.Except)]
1180
- while expt := self.match(uni.Except):
1181
- items.append(expt)
1182
- return uni.SubNodeList[uni.Except](
1183
- items=items,
1184
- delim=Tok.WS,
1185
- kid=self.cur_nodes,
1186
- )
1140
+ return self.flat_cur_nodes
1187
1141
 
1188
1142
  def except_def(self, _: None) -> uni.Except:
1189
1143
  """Grammar rule.
@@ -1195,12 +1149,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
1195
1149
  ex_type = self.consume(uni.Expr)
1196
1150
  if self.match_token(Tok.KW_AS):
1197
1151
  name = self.consume(uni.Name)
1198
- body = self.consume(uni.SubNodeList)
1152
+ body_node = self.consume(list)
1199
1153
  return uni.Except(
1200
1154
  ex_type=ex_type,
1201
1155
  name=name,
1202
- body=body,
1203
- kid=self.cur_nodes,
1156
+ body=self.extract_from_list(body_node, uni.CodeBlockStmt),
1157
+ kid=self.flat_cur_nodes,
1204
1158
  )
1205
1159
 
1206
1160
  def finally_stmt(self, _: None) -> uni.FinallyStmt:
@@ -1209,10 +1163,10 @@ class JacParser(Transform[uni.Source, uni.Module]):
1209
1163
  finally_stmt: KW_FINALLY code_block
1210
1164
  """
1211
1165
  self.consume_token(Tok.KW_FINALLY)
1212
- body = self.consume(uni.SubNodeList)
1166
+ body = self.consume(list)
1213
1167
  return uni.FinallyStmt(
1214
- body=body,
1215
- kid=self.cur_nodes,
1168
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1169
+ kid=self.flat_cur_nodes,
1216
1170
  )
1217
1171
 
1218
1172
  def for_stmt(self, _: None) -> uni.IterForStmt | uni.InForStmt:
@@ -1228,29 +1182,29 @@ class JacParser(Transform[uni.Source, uni.Module]):
1228
1182
  condition = self.consume(uni.Expr)
1229
1183
  self.consume_token(Tok.KW_BY)
1230
1184
  count_by = self.consume(uni.Assignment)
1231
- body = self.consume(uni.SubNodeList)
1185
+ body = self.consume(list)
1232
1186
  else_body = self.match(uni.ElseStmt)
1233
1187
  return uni.IterForStmt(
1234
1188
  is_async=is_async,
1235
1189
  iter=iter,
1236
1190
  condition=condition,
1237
1191
  count_by=count_by,
1238
- body=body,
1192
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1239
1193
  else_body=else_body,
1240
- kid=self.cur_nodes,
1194
+ kid=self.flat_cur_nodes,
1241
1195
  )
1242
1196
  target = self.consume(uni.Expr)
1243
1197
  self.consume_token(Tok.KW_IN)
1244
1198
  collection = self.consume(uni.Expr)
1245
- body = self.consume(uni.SubNodeList)
1199
+ body = self.consume(list)
1246
1200
  else_body = self.match(uni.ElseStmt)
1247
1201
  return uni.InForStmt(
1248
1202
  is_async=is_async,
1249
1203
  target=target,
1250
1204
  collection=collection,
1251
- body=body,
1205
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1252
1206
  else_body=else_body,
1253
- kid=self.cur_nodes,
1207
+ kid=self.flat_cur_nodes,
1254
1208
  )
1255
1209
 
1256
1210
  def while_stmt(self, _: None) -> uni.WhileStmt:
@@ -1260,11 +1214,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
1260
1214
  """
1261
1215
  self.consume_token(Tok.KW_WHILE)
1262
1216
  condition = self.consume(uni.Expr)
1263
- body = self.consume(uni.SubNodeList)
1217
+ body = self.consume(list)
1264
1218
  return uni.WhileStmt(
1265
1219
  condition=condition,
1266
- body=body,
1267
- kid=self.cur_nodes,
1220
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1221
+ kid=self.flat_cur_nodes,
1268
1222
  )
1269
1223
 
1270
1224
  def with_stmt(self, _: None) -> uni.WithStmt:
@@ -1274,28 +1228,21 @@ class JacParser(Transform[uni.Source, uni.Module]):
1274
1228
  """
1275
1229
  is_async = bool(self.match_token(Tok.KW_ASYNC))
1276
1230
  self.consume_token(Tok.KW_WITH)
1277
- exprs = self.consume(uni.SubNodeList)
1278
- body = self.consume(uni.SubNodeList)
1231
+ exprs_node = self.extract_from_list(self.consume(list), uni.ExprAsItem)
1232
+ body = self.consume(list)
1279
1233
  return uni.WithStmt(
1280
1234
  is_async=is_async,
1281
- exprs=exprs,
1282
- body=body,
1283
- kid=self.cur_nodes,
1235
+ exprs=exprs_node,
1236
+ body=self.extract_from_list(body, uni.CodeBlockStmt),
1237
+ kid=self.flat_cur_nodes,
1284
1238
  )
1285
1239
 
1286
- def expr_as_list(self, _: None) -> uni.SubNodeList[uni.ExprAsItem]:
1240
+ def expr_as_list(self, _: None) -> list[uni.UniNode]:
1287
1241
  """Grammar rule.
1288
1242
 
1289
1243
  expr_as_list: (expr_as COMMA)* expr_as
1290
1244
  """
1291
- items = [self.consume(uni.ExprAsItem)]
1292
- while self.match_token(Tok.COMMA):
1293
- items.append(self.consume(uni.ExprAsItem))
1294
- return uni.SubNodeList[uni.ExprAsItem](
1295
- items=items,
1296
- delim=Tok.COMMA,
1297
- kid=self.cur_nodes,
1298
- )
1245
+ return self.cur_nodes
1299
1246
 
1300
1247
  def expr_as(self, _: None) -> uni.ExprAsItem:
1301
1248
  """Grammar rule.
@@ -1466,10 +1413,10 @@ class JacParser(Transform[uni.Source, uni.Module]):
1466
1413
  global_ref: GLOBAL_OP name_list
1467
1414
  """
1468
1415
  self.consume_token(Tok.GLOBAL_OP)
1469
- target = self.consume(uni.SubNodeList)
1416
+ target = self.consume(list)
1470
1417
  return uni.GlobalStmt(
1471
- target=target,
1472
- kid=self.cur_nodes,
1418
+ target=self.extract_from_list(target, uni.Name),
1419
+ kid=self.flat_cur_nodes,
1473
1420
  )
1474
1421
 
1475
1422
  def nonlocal_ref(self, _: None) -> uni.NonLocalStmt:
@@ -1478,10 +1425,10 @@ class JacParser(Transform[uni.Source, uni.Module]):
1478
1425
  nonlocal_ref: NONLOCAL_OP name_list
1479
1426
  """
1480
1427
  self.consume_token(Tok.NONLOCAL_OP)
1481
- target = self.consume(uni.SubNodeList)
1428
+ target = self.consume(list)
1482
1429
  return uni.NonLocalStmt(
1483
- target=target,
1484
- kid=self.cur_nodes,
1430
+ target=self.extract_from_list(target, uni.Name),
1431
+ kid=self.flat_cur_nodes,
1485
1432
  )
1486
1433
 
1487
1434
  def assignment(self, _: None) -> uni.Assignment:
@@ -1516,28 +1463,21 @@ class JacParser(Transform[uni.Source, uni.Module]):
1516
1463
  value = self.consume(uni.Expr) if self.match_token(Tok.EQ) else None
1517
1464
 
1518
1465
  valid_assignees = [i for i in assignees if isinstance(i, (uni.Expr))]
1519
- new_targ = uni.SubNodeList[uni.Expr](
1520
- items=valid_assignees,
1521
- delim=Tok.EQ,
1522
- kid=assignees,
1523
- )
1524
- kid = [x for x in self.cur_nodes if x not in assignees]
1525
- kid.insert(1, new_targ) if is_frozen else kid.insert(0, new_targ)
1526
1466
  if is_aug:
1527
1467
  return uni.Assignment(
1528
- target=new_targ,
1468
+ target=valid_assignees,
1529
1469
  type_tag=type_tag,
1530
1470
  value=value,
1531
1471
  mutable=is_frozen,
1532
1472
  aug_op=is_aug,
1533
- kid=kid,
1473
+ kid=self.flat_cur_nodes,
1534
1474
  )
1535
1475
  return uni.Assignment(
1536
- target=new_targ,
1476
+ target=valid_assignees,
1537
1477
  type_tag=type_tag,
1538
1478
  value=value,
1539
1479
  mutable=is_frozen,
1540
- kid=kid,
1480
+ kid=self.flat_cur_nodes,
1541
1481
  )
1542
1482
 
1543
1483
  def expression(self, _: None) -> uni.Expr:
@@ -1591,18 +1531,20 @@ class JacParser(Transform[uni.Source, uni.Module]):
1591
1531
  return_type: uni.Expr | None = None
1592
1532
  sig_kid: list[uni.UniNode] = []
1593
1533
  self.consume_token(Tok.KW_LAMBDA)
1594
- params = self.match(uni.SubNodeList)
1534
+ params = self.match(list)
1595
1535
  if self.match_token(Tok.RETURN_HINT):
1596
1536
  return_type = self.consume(uni.Expr)
1597
1537
  self.consume_token(Tok.COLON)
1598
1538
  body = self.consume(uni.Expr)
1599
1539
  if params:
1600
- sig_kid.append(params)
1540
+ sig_kid.extend(params)
1601
1541
  if return_type:
1602
1542
  sig_kid.append(return_type)
1603
1543
  signature = (
1604
1544
  uni.FuncSignature(
1605
- params=params,
1545
+ params=(
1546
+ self.extract_from_list(params, uni.ParamVar) if params else []
1547
+ ),
1606
1548
  return_type=return_type,
1607
1549
  kid=sig_kid,
1608
1550
  )
@@ -1925,15 +1867,19 @@ class JacParser(Transform[uni.Source, uni.Module]):
1925
1867
  genai_call: uni.FuncCall | None = None
1926
1868
  target = self.consume(uni.Expr)
1927
1869
  self.consume_token(Tok.LPAREN)
1928
- params = self.match(uni.SubNodeList)
1870
+ params_sn = self.match(list)
1929
1871
  if self.match_token(Tok.KW_BY):
1930
1872
  genai_call = self.consume(uni.FuncCall)
1931
1873
  self.consume_token(Tok.RPAREN)
1932
1874
  return uni.FuncCall(
1933
1875
  target=target,
1934
- params=params,
1876
+ params=(
1877
+ self.extract_from_list(params_sn, (uni.Expr, uni.KWPair)) # type: ignore[arg-type]
1878
+ if params_sn
1879
+ else []
1880
+ ),
1935
1881
  genai_call=genai_call,
1936
- kid=self.cur_nodes,
1882
+ kid=self.flat_cur_nodes,
1937
1883
  )
1938
1884
 
1939
1885
  def index_slice(self, _: None) -> uni.IndexSlice:
@@ -1947,16 +1893,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
1947
1893
  """
1948
1894
  if len(self.cur_nodes) == 1:
1949
1895
  index = self.consume(uni.ListVal)
1950
- if not index.values:
1951
- raise self.ice()
1952
- if len(index.values.items) == 1:
1953
- expr = index.values.items[0] if index.values else None
1896
+ if len(index.values) == 1:
1897
+ expr = index.values[0]
1954
1898
  kid = self.cur_nodes
1955
1899
  else:
1956
- sublist = uni.SubNodeList[uni.Expr | uni.KWPair](
1957
- items=[*index.values.items], delim=Tok.COMMA, kid=index.kid
1958
- )
1959
- expr = uni.TupleVal(values=sublist, kid=[sublist])
1900
+ expr = uni.TupleVal(values=index.values, kid=index.kid)
1960
1901
  kid = [expr]
1961
1902
  return uni.IndexSlice(
1962
1903
  slices=[uni.IndexSlice.Slice(start=expr, stop=None, step=None)],
@@ -2062,52 +2003,54 @@ class JacParser(Transform[uni.Source, uni.Module]):
2062
2003
  | FSTR_SQ_START fstr_sq_parts FSTR_SQ_END
2063
2004
  """
2064
2005
  self.match_token(Tok.FSTR_START) or self.consume_token(Tok.FSTR_SQ_START)
2065
- target = self.match(uni.SubNodeList)
2006
+ target = self.match(list)
2066
2007
  self.match_token(Tok.FSTR_END) or self.consume_token(Tok.FSTR_SQ_END)
2067
2008
  return uni.FString(
2068
- parts=target,
2069
- kid=self.cur_nodes,
2009
+ parts=(
2010
+ self.extract_from_list(target, (uni.String, uni.ExprStmt))
2011
+ if target
2012
+ else []
2013
+ ),
2014
+ kid=self.flat_cur_nodes,
2070
2015
  )
2071
2016
 
2072
- def fstr_parts(self, _: None) -> uni.SubNodeList[uni.String | uni.ExprStmt]:
2017
+ def fstr_parts(self, _: None) -> list[uni.UniNode]:
2073
2018
  """Grammar rule.
2074
2019
 
2075
2020
  fstr_parts: (FSTR_PIECE | FSTR_BESC | LBRACE expression RBRACE )*
2076
2021
  """
2077
- valid_parts: list[uni.String | uni.ExprStmt] = [
2022
+ valid_parts: list[uni.UniNode] = [
2078
2023
  (
2079
2024
  i
2080
2025
  if isinstance(i, uni.String)
2081
- else uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
2026
+ else (
2027
+ uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
2028
+ if isinstance(i, uni.Expr)
2029
+ else i
2030
+ )
2082
2031
  )
2083
2032
  for i in self.cur_nodes
2084
- if isinstance(i, uni.Expr)
2085
2033
  ]
2086
- return uni.SubNodeList[uni.String | uni.ExprStmt](
2087
- items=valid_parts,
2088
- delim=None,
2089
- kid=valid_parts,
2090
- )
2034
+ return valid_parts
2091
2035
 
2092
- def fstr_sq_parts(self, _: None) -> uni.SubNodeList[uni.String | uni.ExprStmt]:
2036
+ def fstr_sq_parts(self, _: None) -> list[uni.UniNode]:
2093
2037
  """Grammar rule.
2094
2038
 
2095
2039
  fstr_sq_parts: (FSTR_SQ_PIECE | FSTR_BESC | LBRACE expression RBRACE )*
2096
2040
  """
2097
- valid_parts: list[uni.String | uni.ExprStmt] = [
2041
+ valid_parts: list[uni.UniNode] = [
2098
2042
  (
2099
2043
  i
2100
2044
  if isinstance(i, uni.String)
2101
- else uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
2045
+ else (
2046
+ uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
2047
+ if isinstance(i, uni.Expr)
2048
+ else i
2049
+ )
2102
2050
  )
2103
2051
  for i in self.cur_nodes
2104
- if isinstance(i, uni.Expr)
2105
2052
  ]
2106
- return uni.SubNodeList[uni.String | uni.ExprStmt](
2107
- items=valid_parts,
2108
- delim=None,
2109
- kid=valid_parts,
2110
- )
2053
+ return valid_parts
2111
2054
 
2112
2055
  def list_val(self, _: None) -> uni.ListVal:
2113
2056
  """Grammar rule.
@@ -2115,12 +2058,15 @@ class JacParser(Transform[uni.Source, uni.Module]):
2115
2058
  list_val: LSQUARE (expr_list COMMA?)? RSQUARE
2116
2059
  """
2117
2060
  self.consume_token(Tok.LSQUARE)
2118
- values = self.match(uni.SubNodeList)
2061
+ values_node = self.match(list)
2119
2062
  self.match_token(Tok.COMMA)
2120
2063
  self.consume_token(Tok.RSQUARE)
2064
+ values = (
2065
+ self.extract_from_list(values_node, uni.Expr) if values_node else []
2066
+ )
2121
2067
  return uni.ListVal(
2122
2068
  values=values,
2123
- kid=self.cur_nodes,
2069
+ kid=self.flat_cur_nodes,
2124
2070
  )
2125
2071
 
2126
2072
  def tuple_val(self, _: None) -> uni.TupleVal:
@@ -2129,11 +2075,15 @@ class JacParser(Transform[uni.Source, uni.Module]):
2129
2075
  tuple_val: LPAREN tuple_list? RPAREN
2130
2076
  """
2131
2077
  self.consume_token(Tok.LPAREN)
2132
- target = self.match(uni.SubNodeList)
2078
+ target = self.match(list)
2133
2079
  self.consume_token(Tok.RPAREN)
2134
2080
  return uni.TupleVal(
2135
- values=target,
2136
- kid=self.cur_nodes,
2081
+ values=(
2082
+ self.extract_from_list(target, (uni.Expr, uni.KWPair))
2083
+ if target
2084
+ else []
2085
+ ),
2086
+ kid=self.flat_cur_nodes,
2137
2087
  )
2138
2088
 
2139
2089
  def set_val(self, _: None) -> uni.SetVal:
@@ -2142,50 +2092,28 @@ class JacParser(Transform[uni.Source, uni.Module]):
2142
2092
  set_val: LBRACE expr_list COMMA? RBRACE
2143
2093
  """
2144
2094
  self.match_token(Tok.LBRACE)
2145
- expr_list = self.match(uni.SubNodeList)
2095
+ expr_list = self.match(list)
2146
2096
  self.match_token(Tok.COMMA)
2147
2097
  self.match_token(Tok.RBRACE)
2098
+ values = self.extract_from_list(expr_list, uni.Expr) if expr_list else []
2148
2099
  return uni.SetVal(
2149
- values=expr_list,
2150
- kid=self.cur_nodes,
2100
+ values=values,
2101
+ kid=self.flat_cur_nodes,
2151
2102
  )
2152
2103
 
2153
- def expr_list(self, kid: list[uni.UniNode]) -> uni.SubNodeList[uni.Expr]:
2104
+ def expr_list(self, kid: list[uni.UniNode]) -> list[uni.UniNode]:
2154
2105
  """Grammar rule.
2155
2106
 
2156
2107
  expr_list: (expr_list COMMA)? expression
2157
2108
  """
2158
- new_kid: list = []
2159
- if consume := self.match(uni.SubNodeList):
2160
- comma = self.consume_token(Tok.COMMA)
2161
- new_kid.extend([*consume.kid, comma])
2162
- expr = self.consume(uni.Expr)
2163
- new_kid.extend([expr])
2164
- valid_kid = [i for i in new_kid if isinstance(i, uni.Expr)]
2165
- return uni.SubNodeList[uni.Expr](
2166
- items=valid_kid,
2167
- delim=Tok.COMMA,
2168
- kid=new_kid,
2169
- )
2109
+ return self.flat_cur_nodes
2170
2110
 
2171
- def kw_expr_list(self, kid: list[uni.UniNode]) -> uni.SubNodeList[uni.KWPair]:
2111
+ def kw_expr_list(self, kid: list[uni.UniNode]) -> list[uni.UniNode]:
2172
2112
  """Grammar rule.
2173
2113
 
2174
2114
  kw_expr_list: (kw_expr_list COMMA)? kw_expr
2175
2115
  """
2176
- if consume := self.match(uni.SubNodeList):
2177
- comma = self.consume_token(Tok.COMMA)
2178
- expr = self.consume(uni.KWPair)
2179
- new_kid = [*consume.kid, comma, expr]
2180
- else:
2181
- expr = self.consume(uni.KWPair)
2182
- new_kid = [expr]
2183
- valid_kid = [i for i in new_kid if isinstance(i, uni.KWPair)]
2184
- return uni.SubNodeList[uni.KWPair](
2185
- items=valid_kid,
2186
- delim=Tok.COMMA,
2187
- kid=new_kid,
2188
- )
2116
+ return self.flat_cur_nodes
2189
2117
 
2190
2118
  def kw_expr(self, _: None) -> uni.KWPair:
2191
2119
  """Grammar rule.
@@ -2205,21 +2133,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
2205
2133
  kid=self.cur_nodes,
2206
2134
  )
2207
2135
 
2208
- def name_list(self, _: None) -> uni.SubNodeList[uni.Name]:
2136
+ def name_list(self, _: None) -> list[uni.UniNode]:
2209
2137
  """Grammar rule.
2210
2138
 
2211
2139
  name_list: (named_ref COMMA)* named_ref
2212
2140
  """
2213
- valid_kid = [self.consume(uni.Name)]
2214
- while self.match_token(Tok.COMMA):
2215
- valid_kid.append(self.consume(uni.Name))
2216
- return uni.SubNodeList[uni.Name](
2217
- items=valid_kid,
2218
- delim=Tok.COMMA,
2219
- kid=self.cur_nodes,
2220
- )
2141
+ return self.flat_cur_nodes
2221
2142
 
2222
- def tuple_list(self, _: None) -> uni.SubNodeList[uni.Expr | uni.KWPair]:
2143
+ def tuple_list(self, _: None) -> list[uni.UniNode]:
2223
2144
  """Grammar rule.
2224
2145
 
2225
2146
  tuple_list: expression COMMA expr_list COMMA kw_expr_list COMMA?
@@ -2228,29 +2149,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2228
2149
  | expression COMMA
2229
2150
  | kw_expr_list COMMA?
2230
2151
  """
2231
- if first_expr := self.match(uni.SubNodeList):
2232
- comma = self.match_token(Tok.COMMA)
2233
- if comma:
2234
- first_expr.kid.append(comma)
2235
- return first_expr
2236
- expr = self.consume(uni.Expr)
2237
- self.consume_token(Tok.COMMA)
2238
- second_expr = self.match(uni.SubNodeList)
2239
- self.match_token(Tok.COMMA)
2240
- kw_expr_list = self.match(uni.SubNodeList)
2241
- self.match_token(Tok.COMMA)
2242
- expr_list: list = []
2243
- if second_expr:
2244
- expr_list = second_expr.kid
2245
- if kw_expr_list:
2246
- expr_list = [*expr_list, *kw_expr_list.kid]
2247
- expr_list = [expr, *expr_list]
2248
- valid_kid = [i for i in expr_list if isinstance(i, (uni.Expr, uni.KWPair))]
2249
- return uni.SubNodeList[uni.Expr | uni.KWPair](
2250
- items=valid_kid,
2251
- delim=Tok.COMMA,
2252
- kid=self.cur_nodes,
2253
- )
2152
+ return self.flat_cur_nodes
2254
2153
 
2255
2154
  def dict_val(self, _: None) -> uni.DictVal:
2256
2155
  """Grammar rule.
@@ -2370,67 +2269,36 @@ class JacParser(Transform[uni.Source, uni.Module]):
2370
2269
  kid=self.cur_nodes,
2371
2270
  )
2372
2271
 
2373
- def param_list(self, _: None) -> uni.SubNodeList[uni.Expr | uni.KWPair]:
2272
+ def param_list(self, _: None) -> list[uni.UniNode]:
2374
2273
  """Grammar rule.
2375
2274
 
2376
2275
  param_list: expr_list COMMA kw_expr_list COMMA?
2377
2276
  | kw_expr_list COMMA?
2378
2277
  | expr_list COMMA?
2379
2278
  """
2380
- kw_expr_list: uni.SubNodeList | None = None
2381
- expr_list = self.consume(uni.SubNodeList)
2382
- if len(self.cur_nodes) > 2:
2383
- self.consume_token(Tok.COMMA)
2384
- kw_expr_list = self.consume(uni.SubNodeList)
2385
- ends_comma = self.match_token(Tok.COMMA)
2386
- if kw_expr_list:
2387
- valid_kid = [
2388
- i
2389
- for i in [*expr_list.items, *kw_expr_list.items]
2390
- if isinstance(i, (uni.Expr, uni.KWPair))
2391
- ]
2392
- return uni.SubNodeList[uni.Expr | uni.KWPair](
2393
- items=valid_kid,
2394
- delim=Tok.COMMA,
2395
- kid=self.cur_nodes,
2396
- )
2397
- else:
2398
- if ends_comma:
2399
- expr_list.kid.append(ends_comma)
2400
- return expr_list
2279
+ return self.flat_cur_nodes
2401
2280
 
2402
- def assignment_list(self, _: None) -> uni.SubNodeList[uni.Assignment]:
2281
+ def assignment_list(self, _: None) -> list[uni.UniNode]:
2403
2282
  """Grammar rule.
2404
2283
 
2405
2284
  assignment_list: (assignment_list COMMA)? (assignment | NAME)
2406
2285
  """
2407
2286
 
2408
2287
  def name_to_assign(name_consume: uni.NameAtom) -> uni.Assignment:
2409
- target = uni.SubNodeList[uni.Expr](
2410
- items=[name_consume], delim=Tok.EQ, kid=[name_consume]
2411
- )
2412
2288
  return uni.Assignment(
2413
- target=target, value=None, type_tag=None, kid=[target]
2289
+ target=[name_consume], value=None, type_tag=None, kid=[name_consume]
2414
2290
  )
2415
2291
 
2416
- if consume := self.match(uni.SubNodeList):
2417
- comma = self.consume_token(Tok.COMMA)
2418
- assign = self.match(uni.Assignment) or self.consume(uni.NameAtom)
2419
- if isinstance(assign, uni.NameAtom):
2420
- assign = name_to_assign(assign)
2421
- new_kid = [*consume.kid, comma, assign]
2292
+ if self.match(list):
2293
+ self.consume_token(Tok.COMMA)
2294
+ if self.match(uni.Assignment):
2295
+ pass
2422
2296
  elif name_consume := self.match(uni.NameAtom):
2423
- name_assign = name_to_assign(name_consume)
2424
- new_kid = [name_assign]
2297
+ self.cur_nodes[self.node_idx - 1] = name_to_assign(name_consume)
2425
2298
  else:
2426
2299
  assign = self.consume(uni.Assignment)
2427
- new_kid = [assign]
2428
- valid_kid = [i for i in new_kid if isinstance(i, uni.Assignment)]
2429
- return uni.SubNodeList[uni.Assignment](
2430
- items=valid_kid,
2431
- delim=Tok.COMMA,
2432
- kid=new_kid,
2433
- )
2300
+ self.cur_nodes[self.node_idx - 1] = assign
2301
+ return self.flat_cur_nodes
2434
2302
 
2435
2303
  def type_ref(self, kid: list[uni.UniNode]) -> uni.TypeRef:
2436
2304
  """Grammar rule.
@@ -2542,19 +2410,20 @@ class JacParser(Transform[uni.Source, uni.Module]):
2542
2410
  connect_to: CARROW_R | CARROW_R_P1 expression (COLON kw_expr_list)? CARROW_R_P2
2543
2411
  """
2544
2412
  conn_type: uni.Expr | None = None
2545
- conn_assign_sub: uni.SubNodeList | None = None
2413
+ conn_assign_sub: list[uni.UniNode] | None = None
2546
2414
  if self.match_token(Tok.CARROW_R_P1):
2547
2415
  conn_type = self.consume(uni.Expr)
2548
2416
  conn_assign_sub = (
2549
- self.consume(uni.SubNodeList)
2550
- if self.match_token(Tok.COLON)
2551
- else None
2417
+ self.consume(list) if self.match_token(Tok.COLON) else None
2552
2418
  )
2553
2419
  self.consume_token(Tok.CARROW_R_P2)
2554
2420
  else:
2555
2421
  self.consume_token(Tok.CARROW_R)
2556
2422
  conn_assign = (
2557
- uni.AssignCompr(assigns=conn_assign_sub, kid=[conn_assign_sub])
2423
+ uni.AssignCompr(
2424
+ assigns=self.extract_from_list(conn_assign_sub, uni.KWPair),
2425
+ kid=conn_assign_sub,
2426
+ )
2558
2427
  if conn_assign_sub
2559
2428
  else None
2560
2429
  )
@@ -2564,7 +2433,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2564
2433
  conn_type=conn_type,
2565
2434
  conn_assign=conn_assign,
2566
2435
  edge_dir=EdgeDir.OUT,
2567
- kid=self.cur_nodes,
2436
+ kid=self.flat_cur_nodes,
2568
2437
  )
2569
2438
 
2570
2439
  def connect_from(self, _: None) -> uni.ConnectOp:
@@ -2573,19 +2442,20 @@ class JacParser(Transform[uni.Source, uni.Module]):
2573
2442
  connect_from: CARROW_L | CARROW_L_P1 expression (COLON kw_expr_list)? CARROW_L_P2
2574
2443
  """
2575
2444
  conn_type: uni.Expr | None = None
2576
- conn_assign_sub: uni.SubNodeList | None = None
2445
+ conn_assign_sub: list[uni.UniNode] | None = None
2577
2446
  if self.match_token(Tok.CARROW_L_P1):
2578
2447
  conn_type = self.consume(uni.Expr)
2579
2448
  conn_assign_sub = (
2580
- self.consume(uni.SubNodeList)
2581
- if self.match_token(Tok.COLON)
2582
- else None
2449
+ self.consume(list) if self.match_token(Tok.COLON) else None
2583
2450
  )
2584
2451
  self.consume_token(Tok.CARROW_L_P2)
2585
2452
  else:
2586
2453
  self.consume_token(Tok.CARROW_L)
2587
2454
  conn_assign = (
2588
- uni.AssignCompr(assigns=conn_assign_sub, kid=[conn_assign_sub])
2455
+ uni.AssignCompr(
2456
+ assigns=self.extract_from_list(conn_assign_sub, uni.KWPair),
2457
+ kid=conn_assign_sub,
2458
+ )
2589
2459
  if conn_assign_sub
2590
2460
  else None
2591
2461
  )
@@ -2595,7 +2465,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2595
2465
  conn_type=conn_type,
2596
2466
  conn_assign=conn_assign,
2597
2467
  edge_dir=EdgeDir.IN,
2598
- kid=self.cur_nodes,
2468
+ kid=self.flat_cur_nodes,
2599
2469
  )
2600
2470
 
2601
2471
  def connect_any(self, _: None) -> uni.ConnectOp:
@@ -2604,19 +2474,20 @@ class JacParser(Transform[uni.Source, uni.Module]):
2604
2474
  connect_any: CARROW_BI | CARROW_L_P1 expression (COLON kw_expr_list)? CARROW_R_P2
2605
2475
  """
2606
2476
  conn_type: uni.Expr | None = None
2607
- conn_assign_sub: uni.SubNodeList | None = None
2477
+ conn_assign_sub: list[uni.UniNode] | None = None
2608
2478
  if self.match_token(Tok.CARROW_L_P1):
2609
2479
  conn_type = self.consume(uni.Expr)
2610
2480
  conn_assign_sub = (
2611
- self.consume(uni.SubNodeList)
2612
- if self.match_token(Tok.COLON)
2613
- else None
2481
+ self.consume(list) if self.match_token(Tok.COLON) else None
2614
2482
  )
2615
2483
  self.consume_token(Tok.CARROW_R_P2)
2616
2484
  else:
2617
2485
  self.consume_token(Tok.CARROW_BI)
2618
2486
  conn_assign = (
2619
- uni.AssignCompr(assigns=conn_assign_sub, kid=[conn_assign_sub])
2487
+ uni.AssignCompr(
2488
+ assigns=self.extract_from_list(conn_assign_sub, uni.KWPair),
2489
+ kid=conn_assign_sub,
2490
+ )
2620
2491
  if conn_assign_sub
2621
2492
  else None
2622
2493
  )
@@ -2626,7 +2497,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2626
2497
  conn_type=conn_type,
2627
2498
  conn_assign=conn_assign,
2628
2499
  edge_dir=EdgeDir.ANY,
2629
- kid=self.cur_nodes,
2500
+ kid=self.flat_cur_nodes,
2630
2501
  )
2631
2502
 
2632
2503
  def filter_compr(self, _: None) -> uni.FilterCompr:
@@ -2645,43 +2516,35 @@ class JacParser(Transform[uni.Source, uni.Module]):
2645
2516
  self.consume_token(Tok.RPAREN)
2646
2517
  return f_type
2647
2518
  self.consume_token(Tok.NULL_OK)
2648
- compares = self.consume(uni.SubNodeList)
2519
+ compares_list = self.consume(list)
2649
2520
  self.consume_token(Tok.RPAREN)
2650
2521
  return uni.FilterCompr(
2651
- compares=compares,
2522
+ compares=self.extract_from_list(compares_list, uni.CompareExpr),
2652
2523
  f_type=None,
2653
- kid=self.cur_nodes,
2524
+ kid=self.flat_cur_nodes,
2654
2525
  )
2655
2526
 
2656
- def filter_compare_list(self, _: None) -> uni.SubNodeList[uni.CompareExpr]:
2527
+ def filter_compare_list(self, _: None) -> list[uni.UniNode]:
2657
2528
  """Grammar rule.
2658
2529
 
2659
2530
  filter_compare_list: (filter_compare_list COMMA)? filter_compare_item
2660
2531
  """
2661
- if consume := self.match(uni.SubNodeList):
2662
- comma = self.consume_token(Tok.COMMA)
2663
- expr = self.consume(uni.CompareExpr)
2664
- new_kid = [*consume.kid, comma, expr]
2665
- else:
2666
- expr = self.consume(uni.CompareExpr)
2667
- new_kid = [expr]
2668
- valid_kid = [i for i in new_kid if isinstance(i, uni.CompareExpr)]
2669
- return uni.SubNodeList[uni.CompareExpr](
2670
- items=valid_kid,
2671
- delim=Tok.COMMA,
2672
- kid=new_kid,
2673
- )
2532
+ return self.flat_cur_nodes
2674
2533
 
2675
2534
  def typed_filter_compare_list(self, _: None) -> uni.FilterCompr:
2676
2535
  """Grammar rule.
2677
2536
 
2678
2537
  typed_filter_compare_list: expression (COLON filter_compare_list)?
2679
2538
  """
2680
- compares: uni.SubNodeList | None = None
2539
+ compares_list: list[uni.UniNode] | None = None
2681
2540
  expr = self.consume(uni.Expr)
2682
2541
  if self.match_token(Tok.COLON):
2683
- compares = self.consume(uni.SubNodeList)
2684
- return uni.FilterCompr(compares=compares, f_type=expr, kid=self.cur_nodes)
2542
+ compares_list = self.consume(list)
2543
+ return uni.FilterCompr(
2544
+ compares=self.extract_from_list(compares_list or [], uni.CompareExpr),
2545
+ f_type=expr,
2546
+ kid=self.flat_cur_nodes,
2547
+ )
2685
2548
 
2686
2549
  def filter_compare_item(self, _: None) -> uni.CompareExpr:
2687
2550
  """Grammar rule.
@@ -2702,9 +2565,9 @@ class JacParser(Transform[uni.Source, uni.Module]):
2702
2565
  """
2703
2566
  self.consume_token(Tok.LPAREN)
2704
2567
  self.consume_token(Tok.EQ)
2705
- assigns = self.consume(uni.SubNodeList)
2568
+ assigns_sn = self.extract_from_list(self.consume(list), uni.KWPair)
2706
2569
  self.consume_token(Tok.RPAREN)
2707
- return uni.AssignCompr(assigns=assigns, kid=self.cur_nodes)
2570
+ return uni.AssignCompr(assigns=assigns_sn, kid=self.flat_cur_nodes)
2708
2571
 
2709
2572
  def match_stmt(self, _: None) -> uni.MatchStmt:
2710
2573
  """Grammar rule.
@@ -2900,11 +2763,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
2900
2763
  class_pattern: NAME (DOT NAME)* LPAREN kw_pattern_list? RPAREN
2901
2764
  | NAME (DOT NAME)* LPAREN pattern_list (COMMA kw_pattern_list)? RPAREN
2902
2765
  """
2766
+ name_idx = 0
2903
2767
  cur_element = self.consume(uni.NameAtom)
2768
+ name_idx += 1
2904
2769
  trailer: uni.AtomTrailer | None = None
2905
2770
  while dot := self.match_token(Tok.DOT):
2906
2771
  target = trailer if trailer else cur_element
2907
- right = self.consume(uni.Expr)
2772
+ right = self.consume(uni.NameAtom)
2773
+ name_idx += 2
2908
2774
  trailer = uni.AtomTrailer(
2909
2775
  target=target,
2910
2776
  right=right,
@@ -2917,82 +2783,55 @@ class JacParser(Transform[uni.Source, uni.Module]):
2917
2783
  raise TypeError(
2918
2784
  f"Expected name to be either NameAtom or AtomTrailer, got {type(name)}"
2919
2785
  )
2920
- lparen = self.consume_token(Tok.LPAREN)
2921
- first = self.match(uni.SubNodeList)
2922
- second = (
2923
- self.consume(uni.SubNodeList)
2924
- if (comma := self.match_token(Tok.COMMA))
2925
- else None
2926
- )
2927
- rparen = self.consume_token(Tok.RPAREN)
2928
- arg = (
2929
- first
2930
- if (first and isinstance(first.items[0], uni.MatchPattern))
2931
- else None
2932
- )
2933
- kw = (
2934
- second
2935
- if (second and isinstance(second.items[0], uni.MatchKVPair))
2936
- else (
2937
- first
2938
- if (first and isinstance(first.items[0], uni.MatchKVPair))
2939
- else None
2940
- )
2941
- )
2942
- kid_nodes: list = [name, lparen]
2943
- if arg:
2944
- kid_nodes.append(arg)
2945
- if kw:
2946
- kid_nodes.extend([comma, kw]) if comma else kid_nodes.append(kw)
2947
- elif kw:
2948
- kid_nodes.append(kw)
2949
- kid_nodes.append(rparen)
2786
+ self.consume_token(Tok.LPAREN)
2787
+ first = self.match(list)
2788
+ second: list[uni.UniNode] | None = None
2789
+ has_kw = bool(first and any(isinstance(i, uni.MatchKVPair) for i in first))
2790
+ if first and not has_kw and self.match_token(Tok.COMMA):
2791
+ second = self.consume(list)
2792
+ self.consume_token(Tok.RPAREN)
2793
+ if has_kw:
2794
+ arg = None
2795
+ kw_list = first
2796
+ else:
2797
+ arg = first
2798
+ kw_list = second
2950
2799
  return uni.MatchArch(
2951
2800
  name=name,
2952
- arg_patterns=arg,
2953
- kw_patterns=kw,
2954
- kid=kid_nodes,
2801
+ arg_patterns=(
2802
+ self.extract_from_list(arg, uni.MatchPattern) if arg else None
2803
+ ),
2804
+ kw_patterns=(
2805
+ self.extract_from_list(kw_list, uni.MatchKVPair)
2806
+ if kw_list
2807
+ else None
2808
+ ),
2809
+ kid=[name, *self.flat_cur_nodes[name_idx:]],
2955
2810
  )
2956
2811
 
2957
- def pattern_list(self, _: None) -> uni.SubNodeList[uni.MatchPattern]:
2812
+ def pattern_list(self, _: None) -> list[uni.UniNode]:
2958
2813
  """Grammar rule.
2959
2814
 
2960
2815
  pattern_list: (pattern_list COMMA)? pattern_seq
2961
2816
  """
2962
- if consume := self.match(uni.SubNodeList):
2963
- comma = self.consume_token(Tok.COMMA)
2964
- pattern = self.consume(uni.MatchPattern)
2965
- else:
2966
- pattern = self.consume(uni.MatchPattern)
2967
- new_kid = [*consume.kid, comma, pattern] if consume else [pattern]
2968
- valid_kid = [i for i in new_kid if isinstance(i, uni.MatchPattern)]
2969
- return uni.SubNodeList[uni.MatchPattern](
2970
- items=valid_kid,
2971
- delim=Tok.COMMA,
2972
- kid=new_kid,
2973
- )
2817
+ return self.flat_cur_nodes
2974
2818
 
2975
- def kw_pattern_list(self, _: None) -> uni.SubNodeList[uni.MatchKVPair]:
2819
+ def kw_pattern_list(self, _: None) -> list[uni.UniNode]:
2976
2820
  """Grammar rule.
2977
2821
 
2978
2822
  kw_pattern_list: (kw_pattern_list COMMA)? named_ref EQ pattern_seq
2979
2823
  """
2980
- new_kid: list = []
2981
- if consume := self.match(uni.SubNodeList):
2824
+ new_kid: list[uni.UniNode] = []
2825
+ if consume := self.match(list):
2982
2826
  comma = self.consume_token(Tok.COMMA)
2983
- new_kid.extend([*consume.kid, comma])
2827
+ new_kid.extend([*consume, comma])
2984
2828
  name = self.consume(uni.NameAtom)
2985
2829
  eq = self.consume_token(Tok.EQ)
2986
2830
  value = self.consume(uni.MatchPattern)
2987
- new_kid.extend(
2988
- [uni.MatchKVPair(key=name, value=value, kid=[name, eq, value])]
2989
- )
2990
- valid_kid = [i for i in new_kid if isinstance(i, uni.MatchKVPair)]
2991
- return uni.SubNodeList[uni.MatchKVPair](
2992
- items=valid_kid,
2993
- delim=Tok.COMMA,
2994
- kid=new_kid,
2831
+ new_kid.append(
2832
+ uni.MatchKVPair(key=name, value=value, kid=[name, eq, value])
2995
2833
  )
2834
+ return new_kid
2996
2835
 
2997
2836
  def __default_token__(self, token: jl.Token) -> uni.Token:
2998
2837
  """Token handler."""
@@ -3072,13 +2911,13 @@ class JacParser(Transform[uni.Source, uni.Module]):
3072
2911
  kid=self.cur_nodes,
3073
2912
  )
3074
2913
 
3075
- def block_tail(self, _: None) -> uni.SubNodeList | uni.FuncCall:
2914
+ def block_tail(self, _: None) -> list[uni.CodeBlockStmt] | uni.FuncCall:
3076
2915
  """Grammar rule.
3077
2916
 
3078
2917
  block_tail: code_block | KW_BY atomic_call SEMI | KW_ABSTRACT? SEMI
3079
2918
  """
3080
2919
  # Try to match code_block first
3081
- if code_block := self.match(uni.SubNodeList):
2920
+ if code_block := self.match(list):
3082
2921
  return code_block
3083
2922
 
3084
2923
  # Otherwise, it must be KW_BY atomic_call SEMI