jaclang 0.8.0__py3-none-any.whl → 0.8.2__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 (124) hide show
  1. jaclang/__init__.py +6 -0
  2. jaclang/cli/cli.py +23 -50
  3. jaclang/compiler/codeinfo.py +0 -1
  4. jaclang/compiler/jac.lark +14 -22
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +378 -531
  7. jaclang/compiler/passes/main/__init__.py +0 -14
  8. jaclang/compiler/passes/main/annex_pass.py +2 -8
  9. jaclang/compiler/passes/main/cfg_build_pass.py +39 -13
  10. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  11. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  12. jaclang/compiler/passes/main/import_pass.py +6 -14
  13. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +428 -799
  15. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  16. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  18. jaclang/compiler/passes/main/sym_tab_link_pass.py +6 -9
  19. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  20. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  21. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +5 -9
  22. jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
  23. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
  24. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
  25. jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
  26. jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
  27. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +425 -216
  28. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
  29. jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
  30. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
  31. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
  32. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
  33. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
  34. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +18 -3
  35. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
  36. jaclang/compiler/program.py +22 -66
  37. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  38. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  39. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  40. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  41. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  42. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +5 -0
  43. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +3 -0
  44. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  45. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  46. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  47. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  48. jaclang/compiler/tests/test_importer.py +30 -13
  49. jaclang/compiler/tests/test_parser.py +1 -0
  50. jaclang/compiler/unitree.py +488 -320
  51. jaclang/langserve/__init__.jac +1 -0
  52. jaclang/langserve/engine.jac +503 -0
  53. jaclang/langserve/sem_manager.jac +309 -0
  54. jaclang/langserve/server.jac +201 -0
  55. jaclang/langserve/tests/server_test/test_lang_serve.py +139 -48
  56. jaclang/langserve/tests/server_test/utils.py +35 -6
  57. jaclang/langserve/tests/session.jac +294 -0
  58. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  59. jaclang/langserve/tests/test_server.py +8 -7
  60. jaclang/langserve/utils.jac +51 -30
  61. jaclang/runtimelib/archetype.py +128 -6
  62. jaclang/runtimelib/builtin.py +17 -14
  63. jaclang/runtimelib/importer.py +51 -76
  64. jaclang/runtimelib/machine.py +469 -305
  65. jaclang/runtimelib/meta_importer.py +86 -0
  66. jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
  67. jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
  68. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  69. jaclang/runtimelib/tests/test_jaseci.py +3 -1
  70. jaclang/runtimelib/utils.py +3 -3
  71. jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
  72. jaclang/tests/fixtures/async_ability.jac +43 -10
  73. jaclang/tests/fixtures/async_function.jac +18 -0
  74. jaclang/tests/fixtures/async_walker.jac +17 -12
  75. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  76. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  77. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  78. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  79. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  80. jaclang/tests/fixtures/concurrency.jac +1 -1
  81. jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
  82. jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
  83. jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
  84. jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
  85. jaclang/tests/fixtures/deep/one_lev.jac +2 -2
  86. jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
  87. jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
  88. jaclang/tests/fixtures/edge_ability.jac +49 -0
  89. jaclang/tests/fixtures/foo.jac +14 -22
  90. jaclang/tests/fixtures/guess_game.jac +1 -1
  91. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  92. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  93. jaclang/tests/fixtures/jac_from_py.py +1 -1
  94. jaclang/tests/fixtures/jp_importer.jac +6 -6
  95. jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
  96. jaclang/tests/fixtures/node_del.jac +30 -36
  97. jaclang/tests/fixtures/unicode_strings.jac +24 -0
  98. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  99. jaclang/tests/fixtures/walker_update.jac +5 -7
  100. jaclang/tests/test_cli.py +12 -7
  101. jaclang/tests/test_language.py +218 -145
  102. jaclang/tests/test_reference.py +9 -4
  103. jaclang/tests/test_typecheck.py +13 -26
  104. jaclang/utils/helpers.py +14 -6
  105. jaclang/utils/lang_tools.py +9 -8
  106. jaclang/utils/module_resolver.py +23 -0
  107. jaclang/utils/tests/test_lang_tools.py +2 -1
  108. jaclang/utils/treeprinter.py +3 -4
  109. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/METADATA +4 -3
  110. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/RECORD +112 -94
  111. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/WHEEL +1 -1
  112. jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
  113. jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
  114. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
  115. jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
  116. jaclang/langserve/__init__.py +0 -1
  117. jaclang/langserve/engine.py +0 -553
  118. jaclang/langserve/sem_manager.py +0 -383
  119. jaclang/langserve/server.py +0 -167
  120. jaclang/langserve/tests/session.py +0 -255
  121. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  122. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  123. jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
  124. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/entry_points.txt +0 -0
@@ -31,6 +31,63 @@ from jaclang.settings import settings
31
31
 
32
32
  T = TypeVar("T", bound=ast3.AST)
33
33
 
34
+ # Mapping of Jac tokens to corresponding Python AST operator classes. This
35
+ # helps keep the implementation of ``exit_token`` concise and easier to
36
+ # maintain.
37
+ TOKEN_AST_MAP: dict[Tok, type[ast3.AST]] = {
38
+ Tok.KW_AND: ast3.And,
39
+ Tok.KW_OR: ast3.Or,
40
+ Tok.PLUS: ast3.Add,
41
+ Tok.ADD_EQ: ast3.Add,
42
+ Tok.BW_AND: ast3.BitAnd,
43
+ Tok.BW_AND_EQ: ast3.BitAnd,
44
+ Tok.BW_OR: ast3.BitOr,
45
+ Tok.BW_OR_EQ: ast3.BitOr,
46
+ Tok.BW_XOR: ast3.BitXor,
47
+ Tok.BW_XOR_EQ: ast3.BitXor,
48
+ Tok.DIV: ast3.Div,
49
+ Tok.DIV_EQ: ast3.Div,
50
+ Tok.FLOOR_DIV: ast3.FloorDiv,
51
+ Tok.FLOOR_DIV_EQ: ast3.FloorDiv,
52
+ Tok.LSHIFT: ast3.LShift,
53
+ Tok.LSHIFT_EQ: ast3.LShift,
54
+ Tok.MOD: ast3.Mod,
55
+ Tok.MOD_EQ: ast3.Mod,
56
+ Tok.STAR_MUL: ast3.Mult,
57
+ Tok.MUL_EQ: ast3.Mult,
58
+ Tok.DECOR_OP: ast3.MatMult,
59
+ Tok.MATMUL_EQ: ast3.MatMult,
60
+ Tok.STAR_POW: ast3.Pow,
61
+ Tok.STAR_POW_EQ: ast3.Pow,
62
+ Tok.RSHIFT: ast3.RShift,
63
+ Tok.RSHIFT_EQ: ast3.RShift,
64
+ Tok.MINUS: ast3.Sub,
65
+ Tok.SUB_EQ: ast3.Sub,
66
+ Tok.BW_NOT: ast3.Invert,
67
+ Tok.BW_NOT_EQ: ast3.Invert,
68
+ Tok.NOT: ast3.Not,
69
+ Tok.EQ: ast3.NotEq,
70
+ Tok.EE: ast3.Eq,
71
+ Tok.GT: ast3.Gt,
72
+ Tok.GTE: ast3.GtE,
73
+ Tok.KW_IN: ast3.In,
74
+ Tok.KW_IS: ast3.Is,
75
+ Tok.KW_ISN: ast3.IsNot,
76
+ Tok.LT: ast3.Lt,
77
+ Tok.LTE: ast3.LtE,
78
+ Tok.NE: ast3.NotEq,
79
+ Tok.KW_NIN: ast3.NotIn,
80
+ }
81
+
82
+ # Mapping of unary operator tokens to their Python AST counterparts used in
83
+ # ``exit_unary_expr``.
84
+ UNARY_OP_MAP: dict[Tok, type[ast3.unaryop]] = {
85
+ Tok.NOT: ast3.Not,
86
+ Tok.BW_NOT: ast3.Invert,
87
+ Tok.PLUS: ast3.UAdd,
88
+ Tok.MINUS: ast3.USub,
89
+ }
90
+
34
91
 
35
92
  class PyastGenPass(UniPass):
36
93
  """Jac blue transpilation to python pass."""
@@ -114,69 +171,55 @@ class PyastGenPass(UniPass):
114
171
  )
115
172
  )
116
173
 
117
- def needs_typing(self) -> None:
118
- """Check if enum is needed."""
119
- if self.needs_typing.__name__ in self.already_added:
174
+ def _add_preamble_once(self, key: str, node: ast3.AST) -> None:
175
+ """Append an import statement to the preamble once."""
176
+ if key in self.already_added:
120
177
  return
121
- self.preamble.append(
122
- self.sync(
123
- ast3.Import(
124
- names=[
125
- self.sync(
126
- ast3.alias(name="typing"),
127
- jac_node=self.ir_out,
128
- ),
129
- ]
130
- ),
131
- jac_node=self.ir_out,
132
- )
178
+ self.preamble.append(self.sync(node, jac_node=self.ir_out))
179
+ self.already_added.append(key)
180
+
181
+ def needs_typing(self) -> None:
182
+ """Ensure typing is imported only once."""
183
+ self._add_preamble_once(
184
+ self.needs_typing.__name__,
185
+ ast3.Import(
186
+ names=[self.sync(ast3.alias(name="typing"), jac_node=self.ir_out)]
187
+ ),
133
188
  )
134
- self.already_added.append(self.needs_typing.__name__)
135
189
 
136
190
  def needs_enum(self) -> None:
137
- """Check if enum is needed."""
138
- if self.needs_enum.__name__ in self.already_added:
139
- return
140
- self.preamble.append(
141
- self.sync(
142
- ast3.ImportFrom(
143
- module="enum",
144
- names=[
145
- self.sync(ast3.alias(name="Enum", asname=None)),
146
- self.sync(ast3.alias(name="auto", asname=None)),
147
- ],
148
- level=0,
149
- ),
150
- jac_node=self.ir_out,
151
- )
191
+ """Ensure Enum utilities are imported only once."""
192
+ self._add_preamble_once(
193
+ self.needs_enum.__name__,
194
+ ast3.ImportFrom(
195
+ module="enum",
196
+ names=[
197
+ self.sync(ast3.alias(name="Enum", asname=None)),
198
+ self.sync(ast3.alias(name="auto", asname=None)),
199
+ ],
200
+ level=0,
201
+ ),
152
202
  )
153
- self.already_added.append(self.needs_enum.__name__)
154
203
 
155
204
  def needs_future(self) -> None:
156
- """Check if enum is needed."""
157
- if self.needs_future.__name__ in self.already_added:
158
- return
159
- self.preamble.append(
160
- self.sync(
161
- ast3.ImportFrom(
162
- module="concurrent.futures",
163
- names=[
164
- self.sync(ast3.alias(name="Future", asname=None)),
165
- ],
166
- level=0,
167
- ),
168
- jac_node=self.ir_out,
169
- )
205
+ """Ensure concurrent Future is imported only once."""
206
+ self._add_preamble_once(
207
+ self.needs_future.__name__,
208
+ ast3.ImportFrom(
209
+ module="concurrent.futures",
210
+ names=[self.sync(ast3.alias(name="Future", asname=None))],
211
+ level=0,
212
+ ),
170
213
  )
171
- self.already_added.append(self.needs_future.__name__)
172
214
 
173
215
  def flatten(self, body: list[T | list[T] | None]) -> list[T]:
174
- new_body = []
175
- for i in body:
176
- if isinstance(i, list):
177
- new_body += i
178
- elif i is not None:
179
- new_body.append(i) if i else None
216
+ """Flatten a list of items or lists into a single list."""
217
+ new_body: list[T] = []
218
+ for item in body:
219
+ if isinstance(item, list):
220
+ new_body.extend(item)
221
+ elif item is not None:
222
+ new_body.append(item)
180
223
  return new_body
181
224
 
182
225
  def sync(
@@ -224,21 +267,15 @@ class PyastGenPass(UniPass):
224
267
 
225
268
  def resolve_stmt_block(
226
269
  self,
227
- node: (
228
- uni.SubNodeList[uni.CodeBlockStmt]
229
- | uni.SubNodeList[uni.ArchBlockStmt]
230
- | uni.SubNodeList[uni.EnumBlockStmt]
231
- | None
232
- ),
270
+ node: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None,
233
271
  doc: Optional[uni.String] = None,
234
272
  ) -> list[ast3.AST]:
235
273
  """Unwind codeblock."""
236
- valid_stmts = (
237
- [i for i in node.items if not isinstance(i, uni.Semi)] if node else []
238
- )
274
+ items = list(node) if node else []
275
+ valid_stmts = [i for i in items if not isinstance(i, uni.Semi)]
239
276
  ret: list[ast3.AST] = (
240
- [self.sync(ast3.Pass(), node)]
241
- if isinstance(node, uni.SubNodeList) and not valid_stmts
277
+ [self.sync(ast3.Pass())]
278
+ if isinstance(node, Sequence) and not valid_stmts
242
279
  else (
243
280
  self.flatten(
244
281
  [
@@ -247,7 +284,7 @@ class PyastGenPass(UniPass):
247
284
  if not isinstance(x, uni.ImplDef)
248
285
  ]
249
286
  )
250
- if node
287
+ if node is not None
251
288
  else []
252
289
  )
253
290
  )
@@ -273,23 +310,16 @@ class PyastGenPass(UniPass):
273
310
  attr_node: ast3.Name | ast3.Attribute = self.sync(
274
311
  ast3.Name(id=attribute_list[0], ctx=ast3.Load()), sync_node_list[0]
275
312
  )
276
- for i in range(len(attribute_list)):
277
- if i == 0:
278
- continue
313
+ for attr, sync_node in zip(attribute_list[1:], sync_node_list[1:]):
279
314
  attr_node = self.sync(
280
- ast3.Attribute(
281
- value=attr_node, attr=attribute_list[i], ctx=ast3.Load()
282
- ),
283
- sync_node_list[i],
315
+ ast3.Attribute(value=attr_node, attr=attr, ctx=ast3.Load()),
316
+ sync_node,
284
317
  )
285
318
  return attr_node
286
319
 
287
320
  def exit_sub_tag(self, node: uni.SubTag[uni.T]) -> None:
288
321
  node.gen.py_ast = node.tag.gen.py_ast
289
322
 
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
323
  def exit_module(self, node: uni.Module) -> None:
294
324
  clean_body = [i for i in node.body if not isinstance(i, uni.ImplDef)]
295
325
  pre_body: list[uni.UniNode] = []
@@ -332,14 +362,15 @@ class PyastGenPass(UniPass):
332
362
  ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
333
363
  jac_node=node.doc,
334
364
  )
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
365
+ assigns_ast: list[ast3.AST] = self.flatten(
366
+ [a.gen.py_ast for a in node.assignments]
367
+ )
368
+ if isinstance(doc, ast3.AST):
369
+ node.gen.py_ast = [doc] + assigns_ast
339
370
  else:
340
371
  raise self.ice()
341
372
  else:
342
- node.gen.py_ast = node.assignments.gen.py_ast
373
+ node.gen.py_ast = self.flatten([a.gen.py_ast for a in node.assignments])
343
374
 
344
375
  def exit_test(self, node: uni.Test) -> None:
345
376
  test_name = node.name.sym_name
@@ -382,7 +413,7 @@ class PyastGenPass(UniPass):
382
413
  ast3.keyword(
383
414
  arg="file_loc",
384
415
  value=self.sync(
385
- ast3.Constant(value=node.body.loc.mod_path)
416
+ ast3.Constant(value=node.loc.mod_path)
386
417
  ),
387
418
  )
388
419
  ),
@@ -434,40 +465,8 @@ class PyastGenPass(UniPass):
434
465
  )
435
466
 
436
467
  def exit_import(self, node: uni.Import) -> None:
437
- path_alias: dict[str, Optional[str]] = (
438
- {node.from_loc.dot_path_str: None} if node.from_loc else {}
439
- )
440
- imp_from = {}
441
- if node.items:
442
- for item in node.items.items:
443
- if isinstance(item, uni.ModuleItem):
444
- imp_from[item.name.sym_name] = (
445
- item.alias.sym_name if item.alias else None
446
- )
447
- elif isinstance(item, uni.ModulePath):
448
- path_alias[item.dot_path_str] = (
449
- item.alias.sym_name if item.alias else None
450
- )
451
-
452
- item_names: list[ast3.expr] = []
453
- item_keys: list[ast3.Constant] = []
454
- item_values: list[ast3.Constant] = []
455
- for k, v in imp_from.items():
456
- item_keys.append(self.sync(ast3.Constant(value=k)))
457
- item_values.append(self.sync(ast3.Constant(value=v)))
458
- item_names.append(
459
- self.sync(
460
- ast3.Name(
461
- id=v or k,
462
- ctx=ast3.Store(),
463
- )
464
- )
465
- )
466
- path_named_value: str
468
+ """Exit import node."""
467
469
  py_nodes: list[ast3.AST] = []
468
- typecheck_nodes: list[ast3.AST] = []
469
- runtime_nodes: list[ast3.AST] = []
470
-
471
470
  if node.doc:
472
471
  py_nodes.append(
473
472
  self.sync(
@@ -476,284 +475,58 @@ class PyastGenPass(UniPass):
476
475
  )
477
476
  )
478
477
 
479
- for path, alias in path_alias.items():
480
- path_named_value = ("_jac_inc_" if node.is_absorb else "") + (
481
- alias if alias else path
482
- ).lstrip(".").split(".")[0]
483
- # target_named_value = ""
484
- # for i in path.split("."):
485
- # target_named_value += i if i else "."
486
- # if i:
487
- # break
488
-
489
- args = [
490
- self.sync(
491
- ast3.Constant(value=path),
492
- ),
493
- self.sync(
494
- ast3.Name(
495
- id="__file__",
496
- ctx=ast3.Load(),
497
- )
498
- ),
499
- ]
500
- keywords = []
501
-
502
- if node.is_absorb:
503
- args.append(self.sync(ast3.Constant(value=node.is_absorb)))
504
-
505
- if alias is not None:
506
- keywords.append(
507
- self.sync(
508
- ast3.keyword(
509
- arg="mdl_alias",
510
- value=self.sync(
511
- ast3.Constant(value=alias),
512
- ),
513
- )
514
- )
515
- )
516
-
517
- if item_keys and item_values:
518
- keywords.append(
519
- self.sync(
520
- ast3.keyword(
521
- arg="items",
522
- value=self.sync(
523
- ast3.Dict(
524
- keys=cast(list[ast3.expr | None], item_keys),
525
- values=cast(list[ast3.expr], item_values),
526
- ),
527
- ),
528
- )
529
- )
530
- )
531
-
532
- runtime_nodes.append(
533
- self.sync(
534
- ast3.Assign(
535
- targets=(
536
- [
537
- self.sync(
538
- ast3.Tuple(
539
- elts=(
540
- item_names
541
- or [
542
- self.sync(
543
- ast3.Name(
544
- id=path_named_value,
545
- ctx=ast3.Store(),
546
- )
547
- )
548
- ]
549
- ),
550
- ctx=ast3.Store(),
551
- )
552
- )
553
- ]
554
- ),
555
- value=self.sync(
556
- ast3.Call(
557
- func=self.jaclib_obj("py_jac_import"),
558
- args=args,
559
- keywords=keywords,
560
- )
561
- ),
562
- ),
563
- ),
564
- )
565
478
  if node.is_absorb:
566
- absorb_exec = f"={path_named_value}.__dict__['"
567
- runtime_nodes.append(
568
- self.sync(
569
- ast3.For(
570
- target=self.sync(ast3.Name(id="i", ctx=ast3.Store())),
571
- iter=self.sync(
572
- ast3.IfExp(
573
- test=self.sync(
574
- ast3.Compare(
575
- left=self.sync(ast3.Constant(value="__all__")),
576
- ops=[self.sync(ast3.In())],
577
- comparators=[
578
- self.sync(
579
- ast3.Attribute(
580
- value=self.sync(
581
- ast3.Name(
582
- id=path_named_value,
583
- ctx=ast3.Load(),
584
- )
585
- ),
586
- attr="__dict__",
587
- ctx=ast3.Load(),
588
- )
589
- )
590
- ],
591
- )
592
- ),
593
- body=self.sync(
594
- ast3.Attribute(
595
- value=self.sync(
596
- ast3.Name(
597
- id=path_named_value, ctx=ast3.Load()
598
- )
599
- ),
600
- attr="__all__",
601
- ctx=ast3.Load(),
602
- )
603
- ),
604
- orelse=self.sync(
605
- ast3.Attribute(
606
- value=self.sync(
607
- ast3.Name(
608
- id=path_named_value, ctx=ast3.Load()
609
- )
610
- ),
611
- attr="__dict__",
612
- ctx=ast3.Load(),
613
- )
614
- ),
615
- )
616
- ),
617
- body=[
618
- self.sync(
619
- ast3.If(
620
- test=self.sync(
621
- ast3.UnaryOp(
622
- op=self.sync(ast3.Not()),
623
- operand=self.sync(
624
- ast3.Call(
625
- func=self.sync(
626
- ast3.Attribute(
627
- value=self.sync(
628
- ast3.Name(
629
- id="i",
630
- ctx=ast3.Load(),
631
- )
632
- ),
633
- attr="startswith",
634
- ctx=ast3.Load(),
635
- )
636
- ),
637
- args=[
638
- self.sync(
639
- ast3.Constant(value="_")
640
- )
641
- ],
642
- keywords=[],
643
- )
644
- ),
645
- )
646
- ),
647
- body=[
648
- self.sync(
649
- ast3.Expr(
650
- value=self.sync(
651
- ast3.Call(
652
- func=self.sync(
653
- ast3.Name(
654
- id="exec",
655
- ctx=ast3.Load(),
656
- )
657
- ),
658
- args=[
659
- self.sync(
660
- ast3.JoinedStr(
661
- values=[
662
- self.sync(
663
- ast3.FormattedValue(
664
- value=self.sync(
665
- ast3.Name(
666
- id="i",
667
- ctx=ast3.Load(),
668
- )
669
- ),
670
- conversion=-1,
671
- )
672
- ),
673
- self.sync(
674
- ast3.Constant(
675
- value=absorb_exec
676
- )
677
- ),
678
- self.sync(
679
- ast3.FormattedValue(
680
- value=self.sync(
681
- ast3.Name(
682
- id="i",
683
- ctx=ast3.Load(),
684
- )
685
- ),
686
- conversion=-1,
687
- )
688
- ),
689
- self.sync(
690
- ast3.Constant(
691
- value="']"
692
- )
693
- ),
694
- ]
695
- )
696
- )
697
- ],
698
- keywords=[],
699
- )
700
- )
701
- )
702
- )
703
- ],
704
- orelse=[],
705
- )
706
- )
707
- ],
708
- orelse=[],
709
- )
710
- )
711
- )
712
- if node.is_absorb:
713
- source = node.items.items[0]
479
+ # This is `include "module_name";` which becomes `from module_name import *`
480
+ source = node.items[0]
714
481
  if not isinstance(source, uni.ModulePath):
715
482
  raise self.ice()
716
- typecheck_nodes.append(
483
+
484
+ module_name_parts = [p.value for p in source.path] if source.path else []
485
+ module_name = ".".join(module_name_parts) if module_name_parts else None
486
+
487
+ py_nodes.append(
717
488
  self.sync(
718
489
  py_node=ast3.ImportFrom(
719
- module=(source.dot_path_str.lstrip(".") if source else None),
490
+ module=module_name,
720
491
  names=[self.sync(ast3.alias(name="*"), node)],
721
- level=0,
492
+ level=source.level,
722
493
  ),
723
494
  jac_node=node,
724
495
  )
725
496
  )
726
497
  elif not node.from_loc:
727
- typecheck_nodes.append(
498
+ # This is `import module1, module2 as alias;`
499
+ py_nodes.append(
728
500
  self.sync(
729
501
  ast3.Import(
730
- names=[cast(ast3.alias, x) for x in node.items.gen.py_ast]
502
+ names=[
503
+ cast(ast3.alias, x)
504
+ for item in node.items
505
+ for x in item.gen.py_ast
506
+ ]
731
507
  )
732
508
  )
733
509
  )
734
510
  else:
735
- typecheck_nodes.append(
511
+ # This is `from module import item1, item2 as alias;`
512
+ module_name_parts = (
513
+ [p.value for p in node.from_loc.path] if node.from_loc.path else []
514
+ )
515
+ module_name = ".".join(module_name_parts) if module_name_parts else None
516
+
517
+ py_nodes.append(
736
518
  self.sync(
737
519
  ast3.ImportFrom(
738
- module=(
739
- node.from_loc.dot_path_str.lstrip(".")
740
- if node.from_loc
741
- else None
742
- ),
743
- names=[cast(ast3.alias, i) for i in node.items.gen.py_ast],
744
- level=0,
520
+ module=module_name,
521
+ names=[
522
+ cast(ast3.alias, i)
523
+ for item in node.items
524
+ for i in item.gen.py_ast
525
+ ],
526
+ level=node.from_loc.level,
745
527
  )
746
528
  )
747
529
  )
748
- py_nodes.append(
749
- self.sync(
750
- ast3.If(
751
- test=self.jaclib_obj("TYPE_CHECKING"),
752
- body=[cast(ast3.stmt, node) for node in typecheck_nodes],
753
- orelse=[cast(ast3.stmt, node) for node in runtime_nodes],
754
- )
755
- )
756
- )
757
530
  node.gen.py_ast = py_nodes
758
531
 
759
532
  def exit_module_path(self, node: uni.ModulePath) -> None:
@@ -781,16 +554,19 @@ class PyastGenPass(UniPass):
781
554
  self.traverse(node.body)
782
555
 
783
556
  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
-
557
+ inner = None
558
+ if isinstance(node.body, uni.ImplDef):
559
+ inner = (
560
+ node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
561
+ )
562
+ elif not isinstance(node.body, uni.FuncCall):
563
+ inner = node.body
564
+ body = self.resolve_stmt_block(inner, doc=node.doc)
565
+ if not body and not isinstance(node.body, uni.FuncCall):
566
+ self.log_error(
567
+ "Archetype has no body. Perhaps an impl must be imported.", node
568
+ )
569
+ body = [self.sync(ast3.Pass(), node)]
794
570
  if node.is_async:
795
571
  body.insert(
796
572
  0,
@@ -805,12 +581,11 @@ class PyastGenPass(UniPass):
805
581
  )
806
582
 
807
583
  decorators = (
808
- node.decorators.gen.py_ast
809
- if isinstance(node.decorators, uni.SubNodeList)
584
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
585
+ if node.decorators
810
586
  else []
811
587
  )
812
-
813
- base_classes = node.base_classes.gen.py_ast if node.base_classes else []
588
+ base_classes = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.base_classes]
814
589
  if node.arch_type.name != Tok.KW_CLASS:
815
590
  base_classes.append(self.jaclib_obj(node.arch_type.value.capitalize()))
816
591
 
@@ -833,25 +608,21 @@ class PyastGenPass(UniPass):
833
608
 
834
609
  def exit_enum(self, node: uni.Enum) -> None:
835
610
  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
- )
611
+ inner = None
612
+ if isinstance(node.body, uni.ImplDef):
613
+ inner = (
614
+ node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
615
+ )
616
+ elif not isinstance(node.body, uni.FuncCall):
617
+ inner = node.body
618
+ body = self.resolve_stmt_block(inner, doc=node.doc)
845
619
  decorators = (
846
- node.decorators.gen.py_ast
847
- if isinstance(node.decorators, uni.SubNodeList)
620
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
621
+ if node.decorators
848
622
  else []
849
623
  )
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()
624
+ base_classes = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.base_classes]
625
+ base_classes.append(self.sync(ast3.Name(id="Enum", ctx=ast3.Load())))
855
626
  node.gen.py_ast = [
856
627
  self.sync(
857
628
  ast3.ClassDef(
@@ -869,20 +640,42 @@ class PyastGenPass(UniPass):
869
640
  if isinstance(node.body, uni.ImplDef):
870
641
  self.traverse(node.body)
871
642
 
643
+ def gen_llm_call_override(self, node: uni.FuncCall) -> list[ast3.AST]:
644
+ """Generate python ast nodes for llm function body override syntax.
645
+
646
+ example:
647
+ foo() by llm();
648
+ """
649
+ # to Avoid circular import
650
+ from jaclang.runtimelib.machine import JacMachineInterface
651
+
652
+ return JacMachineInterface.gen_llm_call_override(self, node)
653
+
872
654
  def gen_llm_body(self, node: uni.Ability) -> list[ast3.AST]:
873
655
  """Generate the by LLM body."""
874
656
  # to Avoid circular import
875
657
  from jaclang.runtimelib.machine import JacMachineInterface
876
658
 
877
- return JacMachineInterface.gen_llm_body(self, node)
659
+ body: list[ast3.AST] = JacMachineInterface.gen_llm_body(self, node)
660
+ if node.doc:
661
+ body.insert(
662
+ 0,
663
+ self.sync(
664
+ ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
665
+ jac_node=node.doc,
666
+ ),
667
+ )
668
+ return body
878
669
 
879
670
  def exit_ability(self, node: uni.Ability) -> None:
880
671
  func_type = ast3.AsyncFunctionDef if node.is_async else ast3.FunctionDef
881
672
  body = (
882
673
  self.gen_llm_body(node)
883
674
  if isinstance(node.body, uni.FuncCall)
884
- or isinstance(node.body, uni.ImplDef)
885
- and isinstance(node.body.body, uni.FuncCall)
675
+ or (
676
+ isinstance(node.body, uni.ImplDef)
677
+ and isinstance(node.body.body, uni.FuncCall)
678
+ )
886
679
  else (
887
680
  [
888
681
  self.sync(
@@ -899,8 +692,12 @@ class PyastGenPass(UniPass):
899
692
  (
900
693
  node.body.body
901
694
  if isinstance(node.body, uni.ImplDef)
902
- and isinstance(node.body.body, uni.SubNodeList)
903
- else node.body
695
+ and not isinstance(node.body.body, uni.FuncCall)
696
+ else (
697
+ node.body
698
+ if not isinstance(node.body, uni.FuncCall)
699
+ else None
700
+ )
904
701
  ),
905
702
  doc=node.doc,
906
703
  )
@@ -912,7 +709,11 @@ class PyastGenPass(UniPass):
912
709
  f"Abstract ability {node.sym_name} should not have a body.",
913
710
  node,
914
711
  )
915
- decorator_list = node.decorators.gen.py_ast if node.decorators else []
712
+ decorator_list = (
713
+ [cast(ast3.expr, i.gen.py_ast[0]) for i in node.decorators]
714
+ if node.decorators
715
+ else []
716
+ )
916
717
  if isinstance(node.signature, uni.EventSignature):
917
718
  decorator_list.append(
918
719
  self.jaclib_obj(
@@ -971,11 +772,7 @@ class PyastGenPass(UniPass):
971
772
  ),
972
773
  body=[cast(ast3.stmt, i) for i in body],
973
774
  decorator_list=[cast(ast3.expr, i) for i in decorator_list],
974
- returns=(
975
- cast(ast3.expr, node.signature.return_type.gen.py_ast[0])
976
- if node.signature and node.signature.return_type
977
- else self.sync(ast3.Constant(value=None))
978
- ),
775
+ returns=self.sync(ast3.Constant(value=None)),
979
776
  type_params=[],
980
777
  )
981
778
  )
@@ -995,23 +792,18 @@ class PyastGenPass(UniPass):
995
792
  )
996
793
  vararg = None
997
794
  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
- )
795
+ for i in node.params:
796
+ if i.unpack and i.unpack.value == "*":
797
+ vararg = i.gen.py_ast[0]
798
+ elif i.unpack and i.unpack.value == "**":
799
+ kwarg = i.gen.py_ast[0]
800
+ else:
801
+ (
802
+ params.append(i.gen.py_ast[0])
803
+ if isinstance(i.gen.py_ast[0], ast3.arg)
804
+ else self.ice("This list should only be Args")
805
+ )
806
+ defaults = [x.value.gen.py_ast[0] for x in node.params if x.value]
1015
807
  node.gen.py_ast = [
1016
808
  self.sync(
1017
809
  ast3.arguments(
@@ -1027,9 +819,10 @@ class PyastGenPass(UniPass):
1027
819
  ]
1028
820
 
1029
821
  def exit_event_signature(self, node: uni.EventSignature) -> None:
1030
- here = self.sync(
822
+ arch_kw = Con.HERE.value if node.from_walker else Con.VISITOR.value
823
+ arch_arg = self.sync(
1031
824
  ast3.arg(
1032
- arg=f"{Con.HERE.value}",
825
+ arg=f"{arch_kw}",
1033
826
  annotation=(
1034
827
  cast(ast3.expr, node.arch_tag_info.gen.py_ast[0])
1035
828
  if node.arch_tag_info
@@ -1043,10 +836,10 @@ class PyastGenPass(UniPass):
1043
836
  ast3.arguments(
1044
837
  posonlyargs=[],
1045
838
  args=(
1046
- [self.sync(ast3.arg(arg="self", annotation=None)), here]
839
+ [self.sync(ast3.arg(arg="self", annotation=None)), arch_arg]
1047
840
  if (abl := node.find_parent_of_type(uni.Ability))
1048
841
  and abl.is_method
1049
- else [here]
842
+ else [arch_arg]
1050
843
  ),
1051
844
  kwonlyargs=[],
1052
845
  vararg=None,
@@ -1090,43 +883,31 @@ class PyastGenPass(UniPass):
1090
883
  ]
1091
884
 
1092
885
  def exit_arch_has(self, node: uni.ArchHas) -> None:
886
+ vars_py: list[ast3.AST] = self.flatten([v.gen.py_ast for v in node.vars])
1093
887
  if node.doc:
1094
888
  doc = self.sync(
1095
889
  ast3.Expr(value=cast(ast3.expr, node.doc.gen.py_ast[0])),
1096
890
  jac_node=node.doc,
1097
891
  )
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
892
+ if isinstance(doc, ast3.AST):
893
+ node.gen.py_ast = [doc] + vars_py
1100
894
  else:
1101
895
  raise self.ice()
1102
896
  else:
1103
- node.gen.py_ast = node.vars.gen.py_ast # TODO: This is a list
897
+ node.gen.py_ast = vars_py
1104
898
 
1105
899
  def exit_has_var(self, node: uni.HasVar) -> None:
1106
900
  annotation = node.type_tag.gen.py_ast[0] if node.type_tag else None
1107
901
 
1108
902
  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
903
+ (haspar := node.find_parent_of_type(uni.ArchHas))
904
+ and haspar
905
+ and haspar.is_static
1113
906
  )
1114
-
1115
907
  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
- )
908
+ (archpar := node.find_parent_of_type(uni.Archetype))
909
+ and archpar
910
+ and archpar.arch_type.name == Tok.KW_CLASS
1130
911
  )
1131
912
 
1132
913
  value = None
@@ -1313,11 +1094,9 @@ class PyastGenPass(UniPass):
1313
1094
  self.sync(
1314
1095
  ast3.Try(
1315
1096
  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
- ),
1097
+ handlers=[
1098
+ cast(ast3.ExceptHandler, i.gen.py_ast[0]) for i in node.excepts
1099
+ ],
1321
1100
  orelse=(
1322
1101
  [cast(ast3.stmt, i) for i in node.else_body.gen.py_ast]
1323
1102
  if node.else_body
@@ -1355,7 +1134,7 @@ class PyastGenPass(UniPass):
1355
1134
 
1356
1135
  def exit_iter_for_stmt(self, node: uni.IterForStmt) -> None:
1357
1136
  py_nodes: list[ast3.AST] = []
1358
- body = node.body.gen.py_ast
1137
+ body = self.resolve_stmt_block(node.body)
1359
1138
  if (
1360
1139
  isinstance(body, list)
1361
1140
  and isinstance(node.count_by.gen.py_ast[0], ast3.AST)
@@ -1419,7 +1198,9 @@ class PyastGenPass(UniPass):
1419
1198
  node.gen.py_ast = [
1420
1199
  self.sync(
1421
1200
  with_node(
1422
- items=[cast(ast3.withitem, item) for item in node.exprs.gen.py_ast],
1201
+ items=[
1202
+ cast(ast3.withitem, item.gen.py_ast[0]) for item in node.exprs
1203
+ ],
1423
1204
  body=[
1424
1205
  cast(ast3.stmt, stmt)
1425
1206
  for stmt in self.resolve_stmt_block(node.body)
@@ -1510,13 +1291,11 @@ class PyastGenPass(UniPass):
1510
1291
  node: uni.FuncCall,
1511
1292
  ) -> CheckNodeIsinstanceCallResult:
1512
1293
 
1513
- # Ensure the type of the FuncCall node is SubNodeList[Expr]
1514
- # since the type can be: Optional[SubNodeList[Expr | KWPair]].
1294
+ # Ensure the func call has exactly two expression parameters
1515
1295
  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)
1296
+ len(node.params) == 2
1297
+ and isinstance(node.params[0], uni.Expr)
1298
+ and isinstance(node.params[1], uni.Expr)
1520
1299
  ):
1521
1300
  return CheckNodeIsinstanceCallResult()
1522
1301
 
@@ -1526,8 +1305,8 @@ class PyastGenPass(UniPass):
1526
1305
 
1527
1306
  return CheckNodeIsinstanceCallResult(
1528
1307
  True,
1529
- node.params.items[0].gen.py_ast[0],
1530
- node.params.items[1].gen.py_ast[0],
1308
+ node.params[0].gen.py_ast[0],
1309
+ node.params[1].gen.py_ast[0],
1531
1310
  )
1532
1311
 
1533
1312
  # By default the check expression will become assertTrue(<expr>), unless any pattern detected.
@@ -1613,9 +1392,8 @@ class PyastGenPass(UniPass):
1613
1392
  if isinstance(func, uni.Name) and func.value == "almostEqual":
1614
1393
  assert_func_name = "assertAlmostEqual"
1615
1394
  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])
1395
+ for param in node.target.params:
1396
+ assert_args_list.append(param.gen.py_ast[0])
1619
1397
 
1620
1398
  # assert_func_expr = "Con.JAC_CHECK.value.assertXXX"
1621
1399
  assert_func_expr: ast3.Attribute = self.sync(
@@ -1746,7 +1524,7 @@ class PyastGenPass(UniPass):
1746
1524
  walker = self.sync(
1747
1525
  ast3.Name(id="self", ctx=ast3.Load())
1748
1526
  if node.from_walker
1749
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1527
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1750
1528
  )
1751
1529
 
1752
1530
  node.gen.py_ast = [
@@ -1769,7 +1547,7 @@ class PyastGenPass(UniPass):
1769
1547
  loc = self.sync(
1770
1548
  ast3.Name(id="self", ctx=ast3.Load())
1771
1549
  if node.from_walker
1772
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1550
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1773
1551
  )
1774
1552
 
1775
1553
  visit_call = self.sync(
@@ -1780,6 +1558,16 @@ class PyastGenPass(UniPass):
1780
1558
  )
1781
1559
  )
1782
1560
 
1561
+ if node.insert_loc is not None:
1562
+ visit_call.keywords.append(
1563
+ self.sync(
1564
+ ast3.keyword(
1565
+ arg="insert_loc",
1566
+ value=cast(ast3.expr, node.insert_loc.gen.py_ast[0]),
1567
+ )
1568
+ )
1569
+ )
1570
+
1783
1571
  node.gen.py_ast = [
1784
1572
  (
1785
1573
  self.sync(
@@ -1803,7 +1591,7 @@ class PyastGenPass(UniPass):
1803
1591
  loc = self.sync(
1804
1592
  ast3.Name(id="self", ctx=ast3.Load())
1805
1593
  if node.from_walker
1806
- else ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
1594
+ else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
1807
1595
  )
1808
1596
  node.gen.py_ast = [
1809
1597
  self.sync(
@@ -1821,29 +1609,13 @@ class PyastGenPass(UniPass):
1821
1609
  ]
1822
1610
 
1823
1611
  def exit_await_expr(self, node: uni.AwaitExpr) -> None:
1824
- parent_node = node.parent
1825
- while parent_node and (parent_node := parent_node.parent):
1826
- if hasattr(parent_node, "is_async") and parent_node.is_async:
1827
- node.gen.py_ast = [
1828
- self.sync(
1829
- ast3.Await(value=cast(ast3.expr, node.target.gen.py_ast[0]))
1830
- )
1831
- ]
1832
- break
1833
- else:
1834
- node.gen.py_ast = [
1835
- self.sync(
1836
- ast3.Call(
1837
- func=self.jaclib_obj("await_obj"),
1838
- args=[cast(ast3.expr, node.target.gen.py_ast[0])],
1839
- keywords=[],
1840
- )
1841
- )
1842
- ]
1612
+ node.gen.py_ast = [
1613
+ self.sync(ast3.Await(value=cast(ast3.expr, node.target.gen.py_ast[0])))
1614
+ ]
1843
1615
 
1844
1616
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
1845
1617
  py_nodes = []
1846
- for x in node.target.items:
1618
+ for x in node.target:
1847
1619
  py_nodes.append(
1848
1620
  self.sync(
1849
1621
  ast3.Global(names=[x.sym_name]),
@@ -1854,7 +1626,7 @@ class PyastGenPass(UniPass):
1854
1626
 
1855
1627
  def exit_non_local_stmt(self, node: uni.NonLocalStmt) -> None:
1856
1628
  py_nodes = []
1857
- for x in node.target.items:
1629
+ for x in node.target:
1858
1630
  py_nodes.append(
1859
1631
  self.sync(
1860
1632
  ast3.Nonlocal(names=[x.sym_name]),
@@ -1879,18 +1651,20 @@ class PyastGenPass(UniPass):
1879
1651
  else None if node.type_tag else self.ice()
1880
1652
  )
1881
1653
  )
1654
+ targets_ast = [cast(ast3.expr, t.gen.py_ast[0]) for t in node.target]
1655
+
1882
1656
  if node.type_tag:
1883
1657
  node.gen.py_ast = [
1884
1658
  self.sync(
1885
1659
  ast3.AnnAssign(
1886
- target=cast(ast3.Name, node.target.items[0].gen.py_ast[0]),
1660
+ target=cast(ast3.Name, targets_ast[0]),
1887
1661
  annotation=cast(ast3.expr, node.type_tag.gen.py_ast[0]),
1888
1662
  value=(
1889
1663
  cast(ast3.expr, node.value.gen.py_ast[0])
1890
1664
  if node.value
1891
1665
  else None
1892
1666
  ),
1893
- simple=int(isinstance(node.target.gen.py_ast[0], ast3.Name)),
1667
+ simple=int(isinstance(targets_ast[0], ast3.Name)),
1894
1668
  )
1895
1669
  )
1896
1670
  ]
@@ -1898,7 +1672,7 @@ class PyastGenPass(UniPass):
1898
1672
  node.gen.py_ast = [
1899
1673
  self.sync(
1900
1674
  ast3.AugAssign(
1901
- target=cast(ast3.Name, node.target.items[0].gen.py_ast[0]),
1675
+ target=cast(ast3.Name, targets_ast[0]),
1902
1676
  op=cast(ast3.operator, node.aug_op.gen.py_ast[0]),
1903
1677
  value=(
1904
1678
  cast(ast3.expr, value)
@@ -1912,7 +1686,7 @@ class PyastGenPass(UniPass):
1912
1686
  node.gen.py_ast = [
1913
1687
  self.sync(
1914
1688
  ast3.Assign(
1915
- targets=cast(list[ast3.expr], node.target.gen.py_ast),
1689
+ targets=cast(list[ast3.expr], targets_ast),
1916
1690
  value=(
1917
1691
  cast(ast3.expr, value)
1918
1692
  if isinstance(value, ast3.expr)
@@ -2078,11 +1852,9 @@ class PyastGenPass(UniPass):
2078
1852
  func_node = uni.FuncCall(
2079
1853
  target=node.right,
2080
1854
  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
- )
1855
+ list(node.left.values)
1856
+ if isinstance(node.left, uni.TupleVal) and node.left.values
1857
+ else [node.left]
2086
1858
  ),
2087
1859
  genai_call=None,
2088
1860
  kid=node.kid,
@@ -2110,11 +1882,9 @@ class PyastGenPass(UniPass):
2110
1882
  func_node = uni.FuncCall(
2111
1883
  target=node.left,
2112
1884
  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
- )
1885
+ list(node.right.values)
1886
+ if isinstance(node.right, uni.TupleVal) and node.right.values
1887
+ else [node.right]
2118
1888
  ),
2119
1889
  genai_call=None,
2120
1890
  kid=node.kid,
@@ -2174,42 +1944,17 @@ class PyastGenPass(UniPass):
2174
1944
  ]
2175
1945
 
2176
1946
  def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
2177
- if node.op.name == Tok.NOT:
1947
+ op_cls = UNARY_OP_MAP.get(node.op.name)
1948
+ if op_cls:
2178
1949
  node.gen.py_ast = [
2179
1950
  self.sync(
2180
1951
  ast3.UnaryOp(
2181
- op=self.sync(ast3.Not()),
2182
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2183
- )
2184
- )
2185
- ]
2186
- elif node.op.name == Tok.BW_NOT:
2187
- node.gen.py_ast = [
2188
- self.sync(
2189
- ast3.UnaryOp(
2190
- op=self.sync(ast3.Invert()),
2191
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2192
- )
2193
- )
2194
- ]
2195
- elif node.op.name == Tok.PLUS:
2196
- node.gen.py_ast = [
2197
- self.sync(
2198
- ast3.UnaryOp(
2199
- op=self.sync(ast3.UAdd()),
2200
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2201
- )
2202
- )
2203
- ]
2204
- elif node.op.name == Tok.MINUS:
2205
- node.gen.py_ast = [
2206
- self.sync(
2207
- ast3.UnaryOp(
2208
- op=self.sync(ast3.USub()),
1952
+ op=self.sync(op_cls()),
2209
1953
  operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2210
1954
  )
2211
1955
  )
2212
1956
  ]
1957
+ return
2213
1958
  elif node.op.name in [Tok.PIPE_FWD, Tok.KW_SPAWN, Tok.A_PIPE_FWD]:
2214
1959
  node.gen.py_ast = [
2215
1960
  self.sync(
@@ -2274,9 +2019,11 @@ class PyastGenPass(UniPass):
2274
2019
  if isinstance(i, uni.String):
2275
2020
  pieces.append(i.lit_value)
2276
2021
  elif isinstance(i, uni.FString):
2277
- pieces.extend(get_pieces(i.parts.items)) if i.parts else None
2022
+ pieces.extend(get_pieces(i.parts)) if i.parts else None
2278
2023
  elif isinstance(i, uni.ExprStmt):
2279
2024
  pieces.append(i.gen.py_ast[0])
2025
+ elif isinstance(i, uni.Token) and i.name in [Tok.LBRACE, Tok.RBRACE]:
2026
+ continue
2280
2027
  else:
2281
2028
  raise self.ice("Multi string made of something weird.")
2282
2029
  return pieces
@@ -2313,62 +2060,30 @@ class PyastGenPass(UniPass):
2313
2060
  node.gen.py_ast = [combined_multi[0]]
2314
2061
 
2315
2062
  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
- )
2063
+ py_parts: list[list[ast3.AST]] = [
2064
+ cast(list[ast3.AST], p.gen.py_ast) for p in node.parts
2065
+ ]
2066
+ parts = self.flatten(cast(list[list[ast3.AST] | ast3.AST | None], py_parts))
2067
+ node.gen.py_ast = parts if parts else [self.sync(ast3.Constant(value=""))]
2321
2068
 
2322
2069
  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
- ]
2070
+ elts = [cast(ast3.expr, v.gen.py_ast[0]) for v in node.values]
2071
+ ctx = (
2072
+ ast3.Load()
2073
+ if isinstance(node.py_ctx_func(), ast3.Load)
2074
+ else cast(ast3.expr_context, node.py_ctx_func())
2075
+ )
2076
+ node.gen.py_ast = [self.sync(ast3.List(elts=elts, ctx=ctx))]
2349
2077
 
2350
2078
  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
- ]
2079
+ elts = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.values]
2080
+ node.gen.py_ast = [self.sync(ast3.Set(elts=elts))]
2362
2081
 
2363
2082
  def exit_tuple_val(self, node: uni.TupleVal) -> None:
2364
2083
  node.gen.py_ast = [
2365
2084
  self.sync(
2366
2085
  ast3.Tuple(
2367
- elts=(
2368
- cast(list[ast3.expr], node.values.gen.py_ast)
2369
- if node.values
2370
- else []
2371
- ),
2086
+ elts=[cast(ast3.expr, i.gen.py_ast[0]) for i in node.values],
2372
2087
  ctx=cast(ast3.expr_context, node.py_ctx_func()),
2373
2088
  )
2374
2089
  )
@@ -2597,12 +2312,14 @@ class PyastGenPass(UniPass):
2597
2312
 
2598
2313
  return JacMachineInterface.get_by_llm_call_args(self, node)
2599
2314
 
2600
- def exit_func_call(self, node: uni.FuncCall) -> None:
2601
- func = node.target.gen.py_ast[0]
2315
+ def gen_call_args(
2316
+ self, node: uni.FuncCall
2317
+ ) -> tuple[list[ast3.expr], list[ast3.keyword]]:
2318
+ """Generate the arguments for a function call."""
2602
2319
  args = []
2603
2320
  keywords = []
2604
- if node.params and len(node.params.items) > 0:
2605
- for x in node.params.items:
2321
+ if node.params:
2322
+ for x in node.params:
2606
2323
  if isinstance(x, uni.UnaryExpr) and x.op.name == Tok.STAR_POW:
2607
2324
  keywords.append(
2608
2325
  self.sync(
@@ -2620,10 +2337,21 @@ class PyastGenPass(UniPass):
2620
2337
  keywords.append(x.gen.py_ast[0])
2621
2338
  else:
2622
2339
  self.ice("Invalid Parameter")
2623
- if node.genai_call:
2340
+ return args, keywords
2341
+
2342
+ def exit_func_call(self, node: uni.FuncCall) -> None:
2343
+ if node.body_genai_call:
2344
+ node.gen.py_ast = self.gen_llm_call_override(node)
2345
+
2346
+ # TODO: This needs to be changed to only generate parameters no the body.
2347
+ elif node.genai_call:
2624
2348
  by_llm_call_args = self.get_by_llm_call_args(node)
2625
2349
  node.gen.py_ast = [self.sync(self.by_llm_call(**by_llm_call_args))]
2350
+
2626
2351
  else:
2352
+ func = node.target.gen.py_ast[0]
2353
+ args, keywords = self.gen_call_args(node)
2354
+
2627
2355
  node.gen.py_ast = [
2628
2356
  self.sync(
2629
2357
  ast3.Call(
@@ -2723,135 +2451,115 @@ class PyastGenPass(UniPass):
2723
2451
  ]
2724
2452
 
2725
2453
  def exit_edge_ref_trailer(self, node: uni.EdgeRefTrailer) -> None:
2726
- pynode = node.chain[0].gen.py_ast[0]
2727
- chomp = [*node.chain]
2728
- last_edge = None
2729
- if node.edges_only:
2730
- for i in node.chain:
2731
- if isinstance(i, uni.EdgeOpRef):
2732
- last_edge = i
2733
- while len(chomp):
2734
- cur = chomp[0]
2735
- chomp = chomp[1:]
2736
- if len(chomp) == len(node.chain) - 1 and not isinstance(cur, uni.EdgeOpRef):
2737
- continue
2738
- next_i = chomp[0] if chomp else None
2739
- if isinstance(cur, uni.EdgeOpRef) and (
2740
- not next_i or not isinstance(next_i, uni.EdgeOpRef)
2741
- ):
2742
- pynode = self.translate_edge_op_ref(
2743
- loc=pynode,
2744
- node=cur,
2745
- targ=(
2746
- next_i.gen.py_ast[0]
2747
- if next_i and not isinstance(next_i, uni.FilterCompr)
2748
- else None
2749
- ),
2750
- edges_only=node.edges_only and cur == last_edge,
2751
- )
2752
- if next_i and isinstance(next_i, uni.FilterCompr):
2753
- pynode = self.sync(
2754
- ast3.Call(
2755
- func=self.jaclib_obj("filter"),
2756
- args=[],
2757
- keywords=[
2758
- self.sync(
2759
- ast3.keyword(
2760
- arg="items",
2761
- value=cast(ast3.expr, pynode),
2762
- )
2763
- ),
2764
- self.sync(
2765
- ast3.keyword(
2766
- arg="func",
2767
- value=cast(ast3.expr, next_i.gen.py_ast[0]),
2768
- )
2769
- ),
2770
- ],
2771
- )
2772
- )
2773
- chomp = chomp[1:] if next_i else chomp
2774
- elif isinstance(cur, uni.EdgeOpRef) and isinstance(next_i, uni.EdgeOpRef):
2775
- pynode = self.translate_edge_op_ref(
2776
- pynode,
2777
- cur,
2778
- targ=None,
2779
- edges_only=node.edges_only and cur == last_edge,
2780
- )
2781
- else:
2782
- raise self.ice("Invalid edge ref trailer")
2454
+ origin = None
2455
+ cur = node.chain[0]
2456
+ chomp = [*node.chain[1:]]
2457
+ from_visit = bool(isinstance(node.parent, uni.VisitStmt))
2783
2458
 
2784
- node.gen.py_ast = [pynode]
2459
+ if not isinstance(cur, uni.EdgeOpRef):
2460
+ origin = cur.gen.py_ast[0]
2461
+ cur = cast(uni.EdgeOpRef, chomp.pop(0))
2785
2462
 
2786
- def exit_edge_op_ref(self, node: uni.EdgeOpRef) -> None:
2787
- loc = self.sync(
2788
- ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
2789
- if node.from_walker
2790
- else ast3.Name(id="self", ctx=ast3.Load())
2463
+ pynode = self.sync(
2464
+ ast3.Call(
2465
+ func=self.jaclib_obj("Path"),
2466
+ args=[cast(ast3.expr, origin or cur.gen.py_ast[0])],
2467
+ keywords=[],
2468
+ )
2791
2469
  )
2792
- node.gen.py_ast = [loc]
2793
2470
 
2794
- def translate_edge_op_ref(
2795
- self,
2796
- loc: ast3.AST,
2797
- node: uni.EdgeOpRef,
2798
- targ: ast3.AST | None,
2799
- edges_only: bool,
2800
- ) -> ast3.AST:
2801
- """Generate ast for edge op ref call."""
2802
- keywords = [self.sync(ast3.keyword(arg="sources", value=cast(ast3.expr, loc)))]
2803
-
2804
- if targ:
2805
- keywords.append(
2806
- self.sync(ast3.keyword(arg="targets", value=cast(ast3.expr, targ)))
2807
- )
2471
+ while True:
2472
+ keywords = []
2473
+ if cur.filter_cond:
2474
+ keywords.append(
2475
+ self.sync(
2476
+ ast3.keyword(
2477
+ arg="edge",
2478
+ value=cast(
2479
+ ast3.expr, self.sync(cur.filter_cond.gen.py_ast[0])
2480
+ ),
2481
+ )
2482
+ )
2483
+ )
2808
2484
 
2809
- if node.edge_dir != EdgeDir.OUT:
2810
- keywords.append(
2811
- self.sync(
2812
- ast3.keyword(
2813
- arg="dir",
2814
- value=self.sync(
2815
- ast3.Attribute(
2816
- value=self.jaclib_obj("EdgeDir"),
2817
- attr=node.edge_dir.name,
2818
- ctx=ast3.Load(),
2819
- )
2820
- ),
2485
+ if chomp and not isinstance(chomp[0], uni.EdgeOpRef):
2486
+ filt = chomp.pop(0)
2487
+ keywords.append(
2488
+ self.sync(
2489
+ ast3.keyword(
2490
+ arg="node",
2491
+ value=cast(ast3.expr, self.sync(filt.gen.py_ast[0])),
2492
+ )
2821
2493
  )
2822
2494
  )
2495
+
2496
+ pynode = self.sync(
2497
+ ast3.Call(
2498
+ func=self.sync(
2499
+ ast3.Attribute(
2500
+ value=pynode,
2501
+ attr=f"_{cur.edge_dir.name.lower()}",
2502
+ ctx=ast3.Load(),
2503
+ )
2504
+ ),
2505
+ args=[],
2506
+ keywords=keywords,
2507
+ )
2823
2508
  )
2824
2509
 
2825
- if node.filter_cond:
2826
- keywords.append(
2827
- self.sync(
2828
- ast3.keyword(
2829
- arg="filter",
2830
- value=cast(
2831
- ast3.expr, self.sync(node.filter_cond.gen.py_ast[0])
2832
- ),
2833
- )
2510
+ if chomp:
2511
+ cur = cast(uni.EdgeOpRef, chomp.pop(0))
2512
+ else:
2513
+ break
2514
+
2515
+ if node.edges_only:
2516
+ pynode = self.sync(
2517
+ ast3.Call(
2518
+ func=self.sync(
2519
+ ast3.Attribute(
2520
+ value=pynode,
2521
+ attr="edge",
2522
+ ctx=ast3.Load(),
2523
+ )
2524
+ ),
2525
+ args=[],
2526
+ keywords=[],
2834
2527
  )
2835
2528
  )
2836
2529
 
2837
- if edges_only:
2838
- keywords.append(
2839
- self.sync(
2840
- ast3.keyword(
2841
- arg="edges_only",
2842
- value=self.sync(ast3.Constant(value=edges_only)),
2843
- )
2530
+ if from_visit:
2531
+ pynode = self.sync(
2532
+ ast3.Call(
2533
+ func=self.sync(
2534
+ ast3.Attribute(
2535
+ value=pynode,
2536
+ attr="visit",
2537
+ ctx=ast3.Load(),
2538
+ )
2539
+ ),
2540
+ args=[],
2541
+ keywords=[],
2844
2542
  )
2845
2543
  )
2846
2544
 
2847
- return self.sync(
2545
+ pynode = self.sync(
2848
2546
  ast3.Call(
2849
2547
  func=self.jaclib_obj("refs"),
2850
- args=[],
2851
- keywords=keywords,
2548
+ args=[pynode],
2549
+ keywords=[],
2852
2550
  )
2853
2551
  )
2854
2552
 
2553
+ node.gen.py_ast = [pynode]
2554
+
2555
+ def exit_edge_op_ref(self, node: uni.EdgeOpRef) -> None:
2556
+ loc = self.sync(
2557
+ ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
2558
+ if node.from_walker
2559
+ else ast3.Name(id="self", ctx=ast3.Load())
2560
+ )
2561
+ node.gen.py_ast = [loc]
2562
+
2855
2563
  def exit_disconnect_op(self, node: uni.DisconnectOp) -> None:
2856
2564
  node.gen.py_ast = node.edge_spec.gen.py_ast
2857
2565
 
@@ -2949,7 +2657,7 @@ class PyastGenPass(UniPass):
2949
2657
  ),
2950
2658
  jac_node=x,
2951
2659
  )
2952
- for x in (node.compares.items if node.compares else [])
2660
+ for x in node.compares
2953
2661
  if isinstance(x.gen.py_ast[0], ast3.Compare)
2954
2662
  and isinstance(x.gen.py_ast[0].left, ast3.Name)
2955
2663
  )
@@ -2984,7 +2692,7 @@ class PyastGenPass(UniPass):
2984
2692
  def exit_assign_compr(self, node: uni.AssignCompr) -> None:
2985
2693
  keys = []
2986
2694
  values = []
2987
- for i in node.assigns.items:
2695
+ for i in node.assigns:
2988
2696
  if i.key: # TODO: add support for **kwargs in assign_compr
2989
2697
  keys.append(self.sync(ast3.Constant(i.key.sym_name)))
2990
2698
  values.append(i.value.gen.py_ast[0])
@@ -3099,92 +2807,27 @@ class PyastGenPass(UniPass):
3099
2807
  self.sync(
3100
2808
  ast3.MatchClass(
3101
2809
  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
- ),
2810
+ patterns=[
2811
+ cast(ast3.pattern, x.gen.py_ast[0])
2812
+ for x in (node.arg_patterns or [])
2813
+ ],
2814
+ kwd_attrs=[
2815
+ x.key.sym_name
2816
+ for x in (node.kw_patterns or [])
2817
+ if isinstance(x.key, uni.NameAtom)
2818
+ ],
2819
+ kwd_patterns=[
2820
+ cast(ast3.pattern, x.value.gen.py_ast[0])
2821
+ for x in (node.kw_patterns or [])
2822
+ ],
3127
2823
  )
3128
2824
  )
3129
2825
  ]
3130
2826
 
3131
2827
  def exit_token(self, node: uni.Token) -> None:
3132
- if node.name == Tok.KW_AND:
3133
- node.gen.py_ast = [self.sync(ast3.And())]
3134
- elif node.name == Tok.KW_OR:
3135
- node.gen.py_ast = [self.sync(ast3.Or())]
3136
- elif node.name in [Tok.PLUS, Tok.ADD_EQ]:
3137
- node.gen.py_ast = [self.sync(ast3.Add())]
3138
- elif node.name in [Tok.BW_AND, Tok.BW_AND_EQ]:
3139
- node.gen.py_ast = [self.sync(ast3.BitAnd())]
3140
- elif node.name in [Tok.BW_OR, Tok.BW_OR_EQ]:
3141
- node.gen.py_ast = [self.sync(ast3.BitOr())]
3142
- elif node.name in [Tok.BW_XOR, Tok.BW_XOR_EQ]:
3143
- node.gen.py_ast = [self.sync(ast3.BitXor())]
3144
- elif node.name in [Tok.DIV, Tok.DIV_EQ]:
3145
- node.gen.py_ast = [self.sync(ast3.Div())]
3146
- elif node.name in [Tok.FLOOR_DIV, Tok.FLOOR_DIV_EQ]:
3147
- node.gen.py_ast = [self.sync(ast3.FloorDiv())]
3148
- elif node.name in [Tok.LSHIFT, Tok.LSHIFT_EQ]:
3149
- node.gen.py_ast = [self.sync(ast3.LShift())]
3150
- elif node.name in [Tok.MOD, Tok.MOD_EQ]:
3151
- node.gen.py_ast = [self.sync(ast3.Mod())]
3152
- elif node.name in [Tok.STAR_MUL, Tok.MUL_EQ]:
3153
- node.gen.py_ast = [self.sync(ast3.Mult())]
3154
- elif node.name in [Tok.DECOR_OP, Tok.MATMUL_EQ]:
3155
- node.gen.py_ast = [self.sync(ast3.MatMult())]
3156
- elif node.name in [Tok.STAR_POW, Tok.STAR_POW_EQ]:
3157
- node.gen.py_ast = [self.sync(ast3.Pow())]
3158
- elif node.name in [Tok.RSHIFT, Tok.RSHIFT_EQ]:
3159
- node.gen.py_ast = [self.sync(ast3.RShift())]
3160
- elif node.name in [Tok.MINUS, Tok.SUB_EQ]:
3161
- node.gen.py_ast = [self.sync(ast3.Sub())]
3162
- elif node.name in [Tok.BW_NOT, Tok.BW_NOT_EQ]:
3163
- node.gen.py_ast = [self.sync(ast3.Invert())]
3164
- elif node.name in [Tok.NOT]:
3165
- node.gen.py_ast = [self.sync(ast3.Not())]
3166
- elif node.name in [Tok.EQ]:
3167
- node.gen.py_ast = [self.sync(ast3.NotEq())]
3168
- elif node.name == Tok.EE:
3169
- node.gen.py_ast = [self.sync(ast3.Eq())]
3170
- elif node.name == Tok.GT:
3171
- node.gen.py_ast = [self.sync(ast3.Gt())]
3172
- elif node.name == Tok.GTE:
3173
- node.gen.py_ast = [self.sync(ast3.GtE())]
3174
- elif node.name == Tok.KW_IN:
3175
- node.gen.py_ast = [self.sync(ast3.In())]
3176
- elif node.name == Tok.KW_IS:
3177
- node.gen.py_ast = [self.sync(ast3.Is())]
3178
- elif node.name == Tok.KW_ISN:
3179
- node.gen.py_ast = [self.sync(ast3.IsNot())]
3180
- elif node.name == Tok.LT:
3181
- node.gen.py_ast = [self.sync(ast3.Lt())]
3182
- elif node.name == Tok.LTE:
3183
- node.gen.py_ast = [self.sync(ast3.LtE())]
3184
- elif node.name == Tok.NE:
3185
- node.gen.py_ast = [self.sync(ast3.NotEq())]
3186
- elif node.name == Tok.KW_NIN:
3187
- node.gen.py_ast = [self.sync(ast3.NotIn())]
2828
+ op_cls = TOKEN_AST_MAP.get(node.name)
2829
+ if op_cls:
2830
+ node.gen.py_ast = [self.sync(op_cls())]
3188
2831
 
3189
2832
  def exit_name(self, node: uni.Name) -> None:
3190
2833
  node.gen.py_ast = [
@@ -3195,21 +2838,7 @@ class PyastGenPass(UniPass):
3195
2838
  node.gen.py_ast = [self.sync(ast3.Constant(value=float(node.value)))]
3196
2839
 
3197
2840
  def exit_int(self, node: uni.Int) -> None:
3198
- def handle_node_value(value: str) -> int:
3199
- if value.startswith(("0x", "0X")):
3200
- return int(value, 16)
3201
- elif value.startswith(("0b", "0B")):
3202
- return int(value, 2)
3203
- elif value.startswith(("0o", "0O")):
3204
- return int(value, 8)
3205
- else:
3206
- return int(value)
3207
-
3208
- node.gen.py_ast = [
3209
- self.sync(
3210
- ast3.Constant(value=handle_node_value(str(node.value)), kind=None)
3211
- )
3212
- ]
2841
+ node.gen.py_ast = [self.sync(ast3.Constant(value=int(node.value, 0)))]
3213
2842
 
3214
2843
  def exit_string(self, node: uni.String) -> None:
3215
2844
  node.gen.py_ast = [self.sync(ast3.Constant(value=node.lit_value))]