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
@@ -224,21 +224,15 @@ class PyastGenPass(UniPass):
224
224
 
225
225
  def resolve_stmt_block(
226
226
  self,
227
- node: (
228
- uni.SubNodeList[uni.CodeBlockStmt]
229
- | uni.SubNodeList[uni.ArchBlockStmt]
230
- | uni.SubNodeList[uni.EnumBlockStmt]
231
- | None
232
- ),
227
+ node: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None,
233
228
  doc: Optional[uni.String] = None,
234
229
  ) -> list[ast3.AST]:
235
230
  """Unwind codeblock."""
236
- valid_stmts = (
237
- [i for i in node.items if not isinstance(i, uni.Semi)] if node else []
238
- )
231
+ items = list(node) if node else []
232
+ valid_stmts = [i for i in items if not isinstance(i, uni.Semi)]
239
233
  ret: list[ast3.AST] = (
240
- [self.sync(ast3.Pass(), node)]
241
- if isinstance(node, uni.SubNodeList) and not valid_stmts
234
+ [self.sync(ast3.Pass())]
235
+ if isinstance(node, Sequence) and not valid_stmts
242
236
  else (
243
237
  self.flatten(
244
238
  [
@@ -247,7 +241,7 @@ class PyastGenPass(UniPass):
247
241
  if not isinstance(x, uni.ImplDef)
248
242
  ]
249
243
  )
250
- if node
244
+ if node is not None
251
245
  else []
252
246
  )
253
247
  )
@@ -287,9 +281,6 @@ class PyastGenPass(UniPass):
287
281
  def exit_sub_tag(self, node: uni.SubTag[uni.T]) -> None:
288
282
  node.gen.py_ast = node.tag.gen.py_ast
289
283
 
290
- def exit_sub_node_list(self, node: uni.SubNodeList[uni.T]) -> None:
291
- node.gen.py_ast = self.flatten([i.gen.py_ast for i in node.items])
292
-
293
284
  def exit_module(self, node: uni.Module) -> None:
294
285
  clean_body = [i for i in node.body if not isinstance(i, uni.ImplDef)]
295
286
  pre_body: list[uni.UniNode] = []
@@ -332,14 +323,15 @@ class PyastGenPass(UniPass):
332
323
  ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
333
324
  jac_node=node.doc,
334
325
  )
335
- if isinstance(doc, ast3.AST) and isinstance(
336
- node.assignments.gen.py_ast, list
337
- ):
338
- node.gen.py_ast = [doc] + node.assignments.gen.py_ast
326
+ assigns_ast: list[ast3.AST] = self.flatten(
327
+ [a.gen.py_ast for a in node.assignments]
328
+ )
329
+ if isinstance(doc, ast3.AST):
330
+ node.gen.py_ast = [doc] + assigns_ast
339
331
  else:
340
332
  raise self.ice()
341
333
  else:
342
- node.gen.py_ast = node.assignments.gen.py_ast
334
+ node.gen.py_ast = self.flatten([a.gen.py_ast for a in node.assignments])
343
335
 
344
336
  def exit_test(self, node: uni.Test) -> None:
345
337
  test_name = node.name.sym_name
@@ -382,7 +374,7 @@ class PyastGenPass(UniPass):
382
374
  ast3.keyword(
383
375
  arg="file_loc",
384
376
  value=self.sync(
385
- ast3.Constant(value=node.body.loc.mod_path)
377
+ ast3.Constant(value=node.loc.mod_path)
386
378
  ),
387
379
  )
388
380
  ),
@@ -439,7 +431,7 @@ class PyastGenPass(UniPass):
439
431
  )
440
432
  imp_from = {}
441
433
  if node.items:
442
- for item in node.items.items:
434
+ for item in node.items:
443
435
  if isinstance(item, uni.ModuleItem):
444
436
  imp_from[item.name.sym_name] = (
445
437
  item.alias.sym_name if item.alias else None
@@ -710,7 +702,7 @@ class PyastGenPass(UniPass):
710
702
  )
711
703
  )
712
704
  if node.is_absorb:
713
- source = node.items.items[0]
705
+ source = node.items[0]
714
706
  if not isinstance(source, uni.ModulePath):
715
707
  raise self.ice()
716
708
  typecheck_nodes.append(
@@ -727,7 +719,11 @@ class PyastGenPass(UniPass):
727
719
  typecheck_nodes.append(
728
720
  self.sync(
729
721
  ast3.Import(
730
- names=[cast(ast3.alias, x) for x in node.items.gen.py_ast]
722
+ names=[
723
+ cast(ast3.alias, x)
724
+ for item in node.items
725
+ for x in item.gen.py_ast
726
+ ]
731
727
  )
732
728
  )
733
729
  )
@@ -740,7 +736,11 @@ class PyastGenPass(UniPass):
740
736
  if node.from_loc
741
737
  else None
742
738
  ),
743
- names=[cast(ast3.alias, i) for i in node.items.gen.py_ast],
739
+ names=[
740
+ cast(ast3.alias, i)
741
+ for item in node.items
742
+ for i in item.gen.py_ast
743
+ ],
744
744
  level=0,
745
745
  )
746
746
  )
@@ -781,16 +781,19 @@ class PyastGenPass(UniPass):
781
781
  self.traverse(node.body)
782
782
 
783
783
  def exit_archetype(self, node: uni.Archetype) -> None:
784
- body = self.resolve_stmt_block(
785
- (
786
- node.body.body
787
- if isinstance(node.body, uni.ImplDef)
788
- and isinstance(node.body.body, uni.SubNodeList)
789
- else node.body if isinstance(node.body, uni.SubNodeList) else None
790
- ),
791
- doc=node.doc,
792
- )
793
-
784
+ inner = None
785
+ if isinstance(node.body, uni.ImplDef):
786
+ inner = (
787
+ node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
788
+ )
789
+ elif not isinstance(node.body, uni.FuncCall):
790
+ inner = node.body
791
+ body = self.resolve_stmt_block(inner, doc=node.doc)
792
+ if not body and not isinstance(node.body, uni.FuncCall):
793
+ self.log_error(
794
+ "Archetype has no body. Perhaps an impl must be imported.", node
795
+ )
796
+ body = [self.sync(ast3.Pass(), node)]
794
797
  if node.is_async:
795
798
  body.insert(
796
799
  0,
@@ -805,12 +808,11 @@ class PyastGenPass(UniPass):
805
808
  )
806
809
 
807
810
  decorators = (
808
- node.decorators.gen.py_ast
809
- if isinstance(node.decorators, uni.SubNodeList)
811
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
812
+ if node.decorators
810
813
  else []
811
814
  )
812
-
813
- base_classes = node.base_classes.gen.py_ast if node.base_classes else []
815
+ base_classes = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.base_classes]
814
816
  if node.arch_type.name != Tok.KW_CLASS:
815
817
  base_classes.append(self.jaclib_obj(node.arch_type.value.capitalize()))
816
818
 
@@ -833,25 +835,21 @@ class PyastGenPass(UniPass):
833
835
 
834
836
  def exit_enum(self, node: uni.Enum) -> None:
835
837
  self.needs_enum()
836
- body = self.resolve_stmt_block(
837
- (
838
- node.body.body
839
- if isinstance(node.body, uni.ImplDef)
840
- and isinstance(node.body.body, uni.SubNodeList)
841
- else node.body if isinstance(node.body, uni.SubNodeList) else None
842
- ),
843
- doc=node.doc,
844
- )
838
+ inner = None
839
+ if isinstance(node.body, uni.ImplDef):
840
+ inner = (
841
+ node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
842
+ )
843
+ elif not isinstance(node.body, uni.FuncCall):
844
+ inner = node.body
845
+ body = self.resolve_stmt_block(inner, doc=node.doc)
845
846
  decorators = (
846
- node.decorators.gen.py_ast
847
- if isinstance(node.decorators, uni.SubNodeList)
847
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
848
+ if node.decorators
848
849
  else []
849
850
  )
850
- base_classes = node.base_classes.gen.py_ast if node.base_classes else []
851
- if isinstance(base_classes, list):
852
- base_classes.append(self.sync(ast3.Name(id="Enum", ctx=ast3.Load())))
853
- else:
854
- raise self.ice()
851
+ base_classes = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.base_classes]
852
+ base_classes.append(self.sync(ast3.Name(id="Enum", ctx=ast3.Load())))
855
853
  node.gen.py_ast = [
856
854
  self.sync(
857
855
  ast3.ClassDef(
@@ -874,15 +872,26 @@ class PyastGenPass(UniPass):
874
872
  # to Avoid circular import
875
873
  from jaclang.runtimelib.machine import JacMachineInterface
876
874
 
877
- return JacMachineInterface.gen_llm_body(self, node)
875
+ body: list[ast3.AST] = JacMachineInterface.gen_llm_body(self, node)
876
+ if node.doc:
877
+ body.insert(
878
+ 0,
879
+ self.sync(
880
+ ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
881
+ jac_node=node.doc,
882
+ ),
883
+ )
884
+ return body
878
885
 
879
886
  def exit_ability(self, node: uni.Ability) -> None:
880
887
  func_type = ast3.AsyncFunctionDef if node.is_async else ast3.FunctionDef
881
888
  body = (
882
889
  self.gen_llm_body(node)
883
890
  if isinstance(node.body, uni.FuncCall)
884
- or isinstance(node.body, uni.ImplDef)
885
- and isinstance(node.body.body, uni.FuncCall)
891
+ or (
892
+ isinstance(node.body, uni.ImplDef)
893
+ and isinstance(node.body.body, uni.FuncCall)
894
+ )
886
895
  else (
887
896
  [
888
897
  self.sync(
@@ -899,8 +908,12 @@ class PyastGenPass(UniPass):
899
908
  (
900
909
  node.body.body
901
910
  if isinstance(node.body, uni.ImplDef)
902
- and isinstance(node.body.body, uni.SubNodeList)
903
- else node.body
911
+ and not isinstance(node.body.body, uni.FuncCall)
912
+ else (
913
+ node.body
914
+ if not isinstance(node.body, uni.FuncCall)
915
+ else None
916
+ )
904
917
  ),
905
918
  doc=node.doc,
906
919
  )
@@ -912,7 +925,11 @@ class PyastGenPass(UniPass):
912
925
  f"Abstract ability {node.sym_name} should not have a body.",
913
926
  node,
914
927
  )
915
- decorator_list = node.decorators.gen.py_ast if node.decorators else []
928
+ decorator_list = (
929
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
930
+ if node.decorators
931
+ else []
932
+ )
916
933
  if isinstance(node.signature, uni.EventSignature):
917
934
  decorator_list.append(
918
935
  self.jaclib_obj(
@@ -995,23 +1012,18 @@ class PyastGenPass(UniPass):
995
1012
  )
996
1013
  vararg = None
997
1014
  kwarg = None
998
- if isinstance(node.params, uni.SubNodeList):
999
- for i in node.params.items:
1000
- if i.unpack and i.unpack.value == "*":
1001
- vararg = i.gen.py_ast[0]
1002
- elif i.unpack and i.unpack.value == "**":
1003
- kwarg = i.gen.py_ast[0]
1004
- else:
1005
- (
1006
- params.append(i.gen.py_ast[0])
1007
- if isinstance(i.gen.py_ast[0], ast3.arg)
1008
- else self.ice("This list should only be Args")
1009
- )
1010
- defaults = (
1011
- [x.value.gen.py_ast[0] for x in node.params.items if x.value]
1012
- if node.params
1013
- else []
1014
- )
1015
+ for i in node.params:
1016
+ if i.unpack and i.unpack.value == "*":
1017
+ vararg = i.gen.py_ast[0]
1018
+ elif i.unpack and i.unpack.value == "**":
1019
+ kwarg = i.gen.py_ast[0]
1020
+ else:
1021
+ (
1022
+ params.append(i.gen.py_ast[0])
1023
+ if isinstance(i.gen.py_ast[0], ast3.arg)
1024
+ else self.ice("This list should only be Args")
1025
+ )
1026
+ defaults = [x.value.gen.py_ast[0] for x in node.params if x.value]
1015
1027
  node.gen.py_ast = [
1016
1028
  self.sync(
1017
1029
  ast3.arguments(
@@ -1027,9 +1039,10 @@ class PyastGenPass(UniPass):
1027
1039
  ]
1028
1040
 
1029
1041
  def exit_event_signature(self, node: uni.EventSignature) -> None:
1030
- here = self.sync(
1042
+ arch_kw = Con.HERE.value if node.from_walker else Con.VISITOR.value
1043
+ arch_arg = self.sync(
1031
1044
  ast3.arg(
1032
- arg=f"{Con.HERE.value}",
1045
+ arg=f"{arch_kw}",
1033
1046
  annotation=(
1034
1047
  cast(ast3.expr, node.arch_tag_info.gen.py_ast[0])
1035
1048
  if node.arch_tag_info
@@ -1043,10 +1056,10 @@ class PyastGenPass(UniPass):
1043
1056
  ast3.arguments(
1044
1057
  posonlyargs=[],
1045
1058
  args=(
1046
- [self.sync(ast3.arg(arg="self", annotation=None)), here]
1059
+ [self.sync(ast3.arg(arg="self", annotation=None)), arch_arg]
1047
1060
  if (abl := node.find_parent_of_type(uni.Ability))
1048
1061
  and abl.is_method
1049
- else [here]
1062
+ else [arch_arg]
1050
1063
  ),
1051
1064
  kwonlyargs=[],
1052
1065
  vararg=None,
@@ -1090,43 +1103,31 @@ class PyastGenPass(UniPass):
1090
1103
  ]
1091
1104
 
1092
1105
  def exit_arch_has(self, node: uni.ArchHas) -> None:
1106
+ vars_py: list[ast3.AST] = self.flatten([v.gen.py_ast for v in node.vars])
1093
1107
  if node.doc:
1094
1108
  doc = self.sync(
1095
1109
  ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
1096
1110
  jac_node=node.doc,
1097
1111
  )
1098
- if isinstance(doc, ast3.AST) and isinstance(node.vars.gen.py_ast, list):
1099
- node.gen.py_ast = [doc] + node.vars.gen.py_ast
1112
+ if isinstance(doc, ast3.AST):
1113
+ node.gen.py_ast = [doc] + vars_py
1100
1114
  else:
1101
1115
  raise self.ice()
1102
1116
  else:
1103
- node.gen.py_ast = node.vars.gen.py_ast # TODO: This is a list
1117
+ node.gen.py_ast = vars_py
1104
1118
 
1105
1119
  def exit_has_var(self, node: uni.HasVar) -> None:
1106
1120
  annotation = node.type_tag.gen.py_ast[0] if node.type_tag else None
1107
1121
 
1108
1122
  is_static_var = (
1109
- node.parent
1110
- and node.parent.parent
1111
- and isinstance(node.parent.parent, uni.ArchHas)
1112
- and node.parent.parent.is_static
1123
+ (haspar := node.find_parent_of_type(uni.ArchHas))
1124
+ and haspar
1125
+ and haspar.is_static
1113
1126
  )
1114
-
1115
1127
  is_in_class = (
1116
- node.parent
1117
- and node.parent.parent
1118
- and node.parent.parent.parent
1119
- and (
1120
- (
1121
- isinstance(node.parent.parent.parent, uni.Archetype)
1122
- and node.parent.parent.parent.arch_type.name == Tok.KW_CLASS
1123
- )
1124
- or (
1125
- node.parent.parent.parent.parent
1126
- and isinstance(node.parent.parent.parent.parent, uni.Archetype)
1127
- and node.parent.parent.parent.parent.arch_type.name == Tok.KW_CLASS
1128
- )
1129
- )
1128
+ (archpar := node.find_parent_of_type(uni.Archetype))
1129
+ and archpar
1130
+ and archpar.arch_type.name == Tok.KW_CLASS
1130
1131
  )
1131
1132
 
1132
1133
  value = None
@@ -1313,11 +1314,9 @@ class PyastGenPass(UniPass):
1313
1314
  self.sync(
1314
1315
  ast3.Try(
1315
1316
  body=cast(list[ast3.stmt], self.resolve_stmt_block(node.body)),
1316
- handlers=(
1317
- [cast(ast3.ExceptHandler, i) for i in node.excepts.gen.py_ast]
1318
- if node.excepts
1319
- else []
1320
- ),
1317
+ handlers=[
1318
+ cast(ast3.ExceptHandler, i.gen.py_ast[0]) for i in node.excepts
1319
+ ],
1321
1320
  orelse=(
1322
1321
  [cast(ast3.stmt, i) for i in node.else_body.gen.py_ast]
1323
1322
  if node.else_body
@@ -1355,7 +1354,7 @@ class PyastGenPass(UniPass):
1355
1354
 
1356
1355
  def exit_iter_for_stmt(self, node: uni.IterForStmt) -> None:
1357
1356
  py_nodes: list[ast3.AST] = []
1358
- body = node.body.gen.py_ast
1357
+ body = self.resolve_stmt_block(node.body)
1359
1358
  if (
1360
1359
  isinstance(body, list)
1361
1360
  and isinstance(node.count_by.gen.py_ast[0], ast3.AST)
@@ -1419,7 +1418,9 @@ class PyastGenPass(UniPass):
1419
1418
  node.gen.py_ast = [
1420
1419
  self.sync(
1421
1420
  with_node(
1422
- items=[cast(ast3.withitem, item) for item in node.exprs.gen.py_ast],
1421
+ items=[
1422
+ cast(ast3.withitem, item.gen.py_ast[0]) for item in node.exprs
1423
+ ],
1423
1424
  body=[
1424
1425
  cast(ast3.stmt, stmt)
1425
1426
  for stmt in self.resolve_stmt_block(node.body)
@@ -1510,13 +1511,11 @@ class PyastGenPass(UniPass):
1510
1511
  node: uni.FuncCall,
1511
1512
  ) -> CheckNodeIsinstanceCallResult:
1512
1513
 
1513
- # Ensure the type of the FuncCall node is SubNodeList[Expr]
1514
- # since the type can be: Optional[SubNodeList[Expr | KWPair]].
1514
+ # Ensure the func call has exactly two expression parameters
1515
1515
  if not (
1516
- node.params is not None
1517
- and len(node.params.items) == 2
1518
- and isinstance(node.params.items[0], uni.Expr)
1519
- and isinstance(node.params.items[1], uni.Expr)
1516
+ len(node.params) == 2
1517
+ and isinstance(node.params[0], uni.Expr)
1518
+ and isinstance(node.params[1], uni.Expr)
1520
1519
  ):
1521
1520
  return CheckNodeIsinstanceCallResult()
1522
1521
 
@@ -1526,8 +1525,8 @@ class PyastGenPass(UniPass):
1526
1525
 
1527
1526
  return CheckNodeIsinstanceCallResult(
1528
1527
  True,
1529
- node.params.items[0].gen.py_ast[0],
1530
- node.params.items[1].gen.py_ast[0],
1528
+ node.params[0].gen.py_ast[0],
1529
+ node.params[1].gen.py_ast[0],
1531
1530
  )
1532
1531
 
1533
1532
  # By default the check expression will become assertTrue(<expr>), unless any pattern detected.
@@ -1613,9 +1612,8 @@ class PyastGenPass(UniPass):
1613
1612
  if isinstance(func, uni.Name) and func.value == "almostEqual":
1614
1613
  assert_func_name = "assertAlmostEqual"
1615
1614
  assert_args_list = []
1616
- if node.target.params is not None:
1617
- for param in node.target.params.items:
1618
- assert_args_list.append(param.gen.py_ast[0])
1615
+ for param in node.target.params:
1616
+ assert_args_list.append(param.gen.py_ast[0])
1619
1617
 
1620
1618
  # assert_func_expr = "Con.JAC_CHECK.value.assertXXX"
1621
1619
  assert_func_expr: ast3.Attribute = self.sync(
@@ -1746,7 +1744,7 @@ class PyastGenPass(UniPass):
1746
1744
  walker = self.sync(
1747
1745
  ast3.Name(id="self", ctx=ast3.Load())
1748
1746
  if node.from_walker
1749
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1747
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1750
1748
  )
1751
1749
 
1752
1750
  node.gen.py_ast = [
@@ -1769,7 +1767,7 @@ class PyastGenPass(UniPass):
1769
1767
  loc = self.sync(
1770
1768
  ast3.Name(id="self", ctx=ast3.Load())
1771
1769
  if node.from_walker
1772
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1770
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1773
1771
  )
1774
1772
 
1775
1773
  visit_call = self.sync(
@@ -1780,6 +1778,16 @@ class PyastGenPass(UniPass):
1780
1778
  )
1781
1779
  )
1782
1780
 
1781
+ if node.insert_loc is not None:
1782
+ visit_call.keywords.append(
1783
+ self.sync(
1784
+ ast3.keyword(
1785
+ arg="insert_loc",
1786
+ value=cast(ast3.expr, node.insert_loc.gen.py_ast[0]),
1787
+ )
1788
+ )
1789
+ )
1790
+
1783
1791
  node.gen.py_ast = [
1784
1792
  (
1785
1793
  self.sync(
@@ -1803,7 +1811,7 @@ class PyastGenPass(UniPass):
1803
1811
  loc = self.sync(
1804
1812
  ast3.Name(id="self", ctx=ast3.Load())
1805
1813
  if node.from_walker
1806
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1814
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1807
1815
  )
1808
1816
  node.gen.py_ast = [
1809
1817
  self.sync(
@@ -1843,7 +1851,7 @@ class PyastGenPass(UniPass):
1843
1851
 
1844
1852
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
1845
1853
  py_nodes = []
1846
- for x in node.target.items:
1854
+ for x in node.target:
1847
1855
  py_nodes.append(
1848
1856
  self.sync(
1849
1857
  ast3.Global(names=[x.sym_name]),
@@ -1854,7 +1862,7 @@ class PyastGenPass(UniPass):
1854
1862
 
1855
1863
  def exit_non_local_stmt(self, node: uni.NonLocalStmt) -> None:
1856
1864
  py_nodes = []
1857
- for x in node.target.items:
1865
+ for x in node.target:
1858
1866
  py_nodes.append(
1859
1867
  self.sync(
1860
1868
  ast3.Nonlocal(names=[x.sym_name]),
@@ -1879,18 +1887,20 @@ class PyastGenPass(UniPass):
1879
1887
  else None if node.type_tag else self.ice()
1880
1888
  )
1881
1889
  )
1890
+ targets_ast = [cast(ast3.expr, t.gen.py_ast[0]) for t in node.target]
1891
+
1882
1892
  if node.type_tag:
1883
1893
  node.gen.py_ast = [
1884
1894
  self.sync(
1885
1895
  ast3.AnnAssign(
1886
- target=cast(ast3.Name, node.target.items[0].gen.py_ast[0]),
1896
+ target=cast(ast3.Name, targets_ast[0]),
1887
1897
  annotation=cast(ast3.expr, node.type_tag.gen.py_ast[0]),
1888
1898
  value=(
1889
1899
  cast(ast3.expr, node.value.gen.py_ast[0])
1890
1900
  if node.value
1891
1901
  else None
1892
1902
  ),
1893
- simple=int(isinstance(node.target.gen.py_ast[0], ast3.Name)),
1903
+ simple=int(isinstance(targets_ast[0], ast3.Name)),
1894
1904
  )
1895
1905
  )
1896
1906
  ]
@@ -1898,7 +1908,7 @@ class PyastGenPass(UniPass):
1898
1908
  node.gen.py_ast = [
1899
1909
  self.sync(
1900
1910
  ast3.AugAssign(
1901
- target=cast(ast3.Name, node.target.items[0].gen.py_ast[0]),
1911
+ target=cast(ast3.Name, targets_ast[0]),
1902
1912
  op=cast(ast3.operator, node.aug_op.gen.py_ast[0]),
1903
1913
  value=(
1904
1914
  cast(ast3.expr, value)
@@ -1912,7 +1922,7 @@ class PyastGenPass(UniPass):
1912
1922
  node.gen.py_ast = [
1913
1923
  self.sync(
1914
1924
  ast3.Assign(
1915
- targets=cast(list[ast3.expr], node.target.gen.py_ast),
1925
+ targets=cast(list[ast3.expr], targets_ast),
1916
1926
  value=(
1917
1927
  cast(ast3.expr, value)
1918
1928
  if isinstance(value, ast3.expr)
@@ -2078,11 +2088,9 @@ class PyastGenPass(UniPass):
2078
2088
  func_node = uni.FuncCall(
2079
2089
  target=node.right,
2080
2090
  params=(
2081
- node.left.values
2082
- if isinstance(node.left, uni.TupleVal)
2083
- else uni.SubNodeList(
2084
- items=[node.left], delim=Tok.COMMA, kid=[node.left]
2085
- )
2091
+ list(node.left.values)
2092
+ if isinstance(node.left, uni.TupleVal) and node.left.values
2093
+ else [node.left]
2086
2094
  ),
2087
2095
  genai_call=None,
2088
2096
  kid=node.kid,
@@ -2110,11 +2118,9 @@ class PyastGenPass(UniPass):
2110
2118
  func_node = uni.FuncCall(
2111
2119
  target=node.left,
2112
2120
  params=(
2113
- node.right.values
2114
- if isinstance(node.right, uni.TupleVal)
2115
- else uni.SubNodeList(
2116
- items=[node.right], delim=Tok.COMMA, kid=[node.right]
2117
- )
2121
+ list(node.right.values)
2122
+ if isinstance(node.right, uni.TupleVal) and node.right.values
2123
+ else [node.right]
2118
2124
  ),
2119
2125
  genai_call=None,
2120
2126
  kid=node.kid,
@@ -2274,9 +2280,11 @@ class PyastGenPass(UniPass):
2274
2280
  if isinstance(i, uni.String):
2275
2281
  pieces.append(i.lit_value)
2276
2282
  elif isinstance(i, uni.FString):
2277
- pieces.extend(get_pieces(i.parts.items)) if i.parts else None
2283
+ pieces.extend(get_pieces(i.parts)) if i.parts else None
2278
2284
  elif isinstance(i, uni.ExprStmt):
2279
2285
  pieces.append(i.gen.py_ast[0])
2286
+ elif isinstance(i, uni.Token) and i.name in [Tok.LBRACE, Tok.RBRACE]:
2287
+ continue
2280
2288
  else:
2281
2289
  raise self.ice("Multi string made of something weird.")
2282
2290
  return pieces
@@ -2313,62 +2321,30 @@ class PyastGenPass(UniPass):
2313
2321
  node.gen.py_ast = [combined_multi[0]]
2314
2322
 
2315
2323
  def exit_f_string(self, node: uni.FString) -> None:
2316
- node.gen.py_ast = (
2317
- node.parts.gen.py_ast
2318
- if node.parts
2319
- else [self.sync(ast3.Constant(value=""))]
2320
- )
2324
+ py_parts: list[list[ast3.AST]] = [
2325
+ cast(list[ast3.AST], p.gen.py_ast) for p in node.parts
2326
+ ]
2327
+ parts = self.flatten(cast(list[list[ast3.AST] | ast3.AST | None], py_parts))
2328
+ node.gen.py_ast = parts if parts else [self.sync(ast3.Constant(value=""))]
2321
2329
 
2322
2330
  def exit_list_val(self, node: uni.ListVal) -> None:
2323
- if isinstance(node.py_ctx_func(), ast3.Load):
2324
- node.gen.py_ast = [
2325
- self.sync(
2326
- ast3.List(
2327
- elts=(
2328
- cast(list[ast3.expr], node.values.gen.py_ast)
2329
- if node.values
2330
- else []
2331
- ),
2332
- ctx=ast3.Load(),
2333
- )
2334
- )
2335
- ]
2336
- else:
2337
- node.gen.py_ast = [
2338
- self.sync(
2339
- ast3.List(
2340
- elts=(
2341
- [cast(ast3.expr, item) for item in node.values.gen.py_ast]
2342
- if node.values and node.values.gen.py_ast
2343
- else []
2344
- ),
2345
- ctx=cast(ast3.expr_context, node.py_ctx_func()),
2346
- )
2347
- )
2348
- ]
2331
+ elts = [cast(ast3.expr, v.gen.py_ast[0]) for v in node.values]
2332
+ ctx = (
2333
+ ast3.Load()
2334
+ if isinstance(node.py_ctx_func(), ast3.Load)
2335
+ else cast(ast3.expr_context, node.py_ctx_func())
2336
+ )
2337
+ node.gen.py_ast = [self.sync(ast3.List(elts=elts, ctx=ctx))]
2349
2338
 
2350
2339
  def exit_set_val(self, node: uni.SetVal) -> None:
2351
- node.gen.py_ast = [
2352
- self.sync(
2353
- ast3.Set(
2354
- elts=(
2355
- [cast(ast3.expr, i) for i in node.values.gen.py_ast]
2356
- if node.values
2357
- else []
2358
- ),
2359
- )
2360
- )
2361
- ]
2340
+ elts = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.values]
2341
+ node.gen.py_ast = [self.sync(ast3.Set(elts=elts))]
2362
2342
 
2363
2343
  def exit_tuple_val(self, node: uni.TupleVal) -> None:
2364
2344
  node.gen.py_ast = [
2365
2345
  self.sync(
2366
2346
  ast3.Tuple(
2367
- elts=(
2368
- cast(list[ast3.expr], node.values.gen.py_ast)
2369
- if node.values
2370
- else []
2371
- ),
2347
+ elts=[cast(ast3.expr, i.gen.py_ast[0]) for i in node.values],
2372
2348
  ctx=cast(ast3.expr_context, node.py_ctx_func()),
2373
2349
  )
2374
2350
  )
@@ -2601,8 +2577,8 @@ class PyastGenPass(UniPass):
2601
2577
  func = node.target.gen.py_ast[0]
2602
2578
  args = []
2603
2579
  keywords = []
2604
- if node.params and len(node.params.items) > 0:
2605
- for x in node.params.items:
2580
+ if node.params:
2581
+ for x in node.params:
2606
2582
  if isinstance(x, uni.UnaryExpr) and x.op.name == Tok.STAR_POW:
2607
2583
  keywords.append(
2608
2584
  self.sync(
@@ -2726,6 +2702,7 @@ class PyastGenPass(UniPass):
2726
2702
  pynode = node.chain[0].gen.py_ast[0]
2727
2703
  chomp = [*node.chain]
2728
2704
  last_edge = None
2705
+ from_visit = bool(isinstance(node.parent, uni.VisitStmt))
2729
2706
  if node.edges_only:
2730
2707
  for i in node.chain:
2731
2708
  if isinstance(i, uni.EdgeOpRef):
@@ -2748,6 +2725,7 @@ class PyastGenPass(UniPass):
2748
2725
  else None
2749
2726
  ),
2750
2727
  edges_only=node.edges_only and cur == last_edge,
2728
+ from_visit=from_visit,
2751
2729
  )
2752
2730
  if next_i and isinstance(next_i, uni.FilterCompr):
2753
2731
  pynode = self.sync(
@@ -2777,6 +2755,7 @@ class PyastGenPass(UniPass):
2777
2755
  cur,
2778
2756
  targ=None,
2779
2757
  edges_only=node.edges_only and cur == last_edge,
2758
+ from_visit=from_visit,
2780
2759
  )
2781
2760
  else:
2782
2761
  raise self.ice("Invalid edge ref trailer")
@@ -2797,6 +2776,7 @@ class PyastGenPass(UniPass):
2797
2776
  node: uni.EdgeOpRef,
2798
2777
  targ: ast3.AST | None,
2799
2778
  edges_only: bool,
2779
+ from_visit: bool,
2800
2780
  ) -> ast3.AST:
2801
2781
  """Generate ast for edge op ref call."""
2802
2782
  keywords = [self.sync(ast3.keyword(arg="sources", value=cast(ast3.expr, loc)))]
@@ -2844,6 +2824,16 @@ class PyastGenPass(UniPass):
2844
2824
  )
2845
2825
  )
2846
2826
 
2827
+ if from_visit:
2828
+ keywords.append(
2829
+ self.sync(
2830
+ ast3.keyword(
2831
+ arg="from_visit",
2832
+ value=self.sync(ast3.Constant(value=from_visit)),
2833
+ )
2834
+ )
2835
+ )
2836
+
2847
2837
  return self.sync(
2848
2838
  ast3.Call(
2849
2839
  func=self.jaclib_obj("refs"),
@@ -2949,7 +2939,7 @@ class PyastGenPass(UniPass):
2949
2939
  ),
2950
2940
  jac_node=x,
2951
2941
  )
2952
- for x in (node.compares.items if node.compares else [])
2942
+ for x in node.compares
2953
2943
  if isinstance(x.gen.py_ast[0], ast3.Compare)
2954
2944
  and isinstance(x.gen.py_ast[0].left, ast3.Name)
2955
2945
  )
@@ -2984,7 +2974,7 @@ class PyastGenPass(UniPass):
2984
2974
  def exit_assign_compr(self, node: uni.AssignCompr) -> None:
2985
2975
  keys = []
2986
2976
  values = []
2987
- for i in node.assigns.items:
2977
+ for i in node.assigns:
2988
2978
  if i.key: # TODO: add support for **kwargs in assign_compr
2989
2979
  keys.append(self.sync(ast3.Constant(i.key.sym_name)))
2990
2980
  values.append(i.value.gen.py_ast[0])
@@ -3099,31 +3089,19 @@ class PyastGenPass(UniPass):
3099
3089
  self.sync(
3100
3090
  ast3.MatchClass(
3101
3091
  cls=cast(ast3.expr, node.name.gen.py_ast[0]),
3102
- patterns=(
3103
- [
3104
- cast(ast3.pattern, x.gen.py_ast[0])
3105
- for x in node.arg_patterns.items
3106
- ]
3107
- if node.arg_patterns
3108
- else []
3109
- ),
3110
- kwd_attrs=(
3111
- [
3112
- x.key.sym_name
3113
- for x in node.kw_patterns.items
3114
- if isinstance(x.key, uni.NameAtom)
3115
- ]
3116
- if node.kw_patterns
3117
- else []
3118
- ),
3119
- kwd_patterns=(
3120
- [
3121
- cast(ast3.pattern, x.value.gen.py_ast[0])
3122
- for x in node.kw_patterns.items
3123
- ]
3124
- if node.kw_patterns
3125
- else []
3126
- ),
3092
+ patterns=[
3093
+ cast(ast3.pattern, x.gen.py_ast[0])
3094
+ for x in (node.arg_patterns or [])
3095
+ ],
3096
+ kwd_attrs=[
3097
+ x.key.sym_name
3098
+ for x in (node.kw_patterns or [])
3099
+ if isinstance(x.key, uni.NameAtom)
3100
+ ],
3101
+ kwd_patterns=[
3102
+ cast(ast3.pattern, x.value.gen.py_ast[0])
3103
+ for x in (node.kw_patterns or [])
3104
+ ],
3127
3105
  )
3128
3106
  )
3129
3107
  ]