jaclang 0.8.1__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 (77) hide show
  1. jaclang/__init__.py +6 -0
  2. jaclang/cli/cli.py +21 -50
  3. jaclang/compiler/codeinfo.py +0 -1
  4. jaclang/compiler/jac.lark +12 -10
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +18 -10
  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 +38 -12
  10. jaclang/compiler/passes/main/import_pass.py +3 -11
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +243 -592
  12. jaclang/compiler/passes/main/sym_tab_link_pass.py +2 -5
  13. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +2 -8
  14. jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
  15. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
  16. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
  17. jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
  18. jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
  19. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +237 -105
  20. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
  21. jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
  23. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
  25. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
  26. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +18 -3
  27. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
  28. jaclang/compiler/program.py +21 -60
  29. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +2 -8
  30. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +1 -5
  31. jaclang/compiler/tests/test_importer.py +10 -13
  32. jaclang/compiler/unitree.py +32 -16
  33. jaclang/langserve/__init__.jac +1 -1
  34. jaclang/langserve/engine.jac +113 -108
  35. jaclang/langserve/server.jac +17 -2
  36. jaclang/langserve/tests/server_test/test_lang_serve.py +138 -46
  37. jaclang/langserve/tests/server_test/utils.py +35 -9
  38. jaclang/langserve/tests/test_sem_tokens.py +1 -1
  39. jaclang/langserve/tests/test_server.py +3 -7
  40. jaclang/runtimelib/archetype.py +127 -5
  41. jaclang/runtimelib/importer.py +51 -94
  42. jaclang/runtimelib/machine.py +391 -268
  43. jaclang/runtimelib/meta_importer.py +86 -0
  44. jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
  45. jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
  46. jaclang/runtimelib/tests/test_jaseci.py +3 -1
  47. jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
  48. jaclang/tests/fixtures/async_ability.jac +43 -10
  49. jaclang/tests/fixtures/async_function.jac +18 -0
  50. jaclang/tests/fixtures/async_walker.jac +17 -12
  51. jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
  52. jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
  53. jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
  54. jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
  55. jaclang/tests/fixtures/deep/one_lev.jac +2 -2
  56. jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
  57. jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
  58. jaclang/tests/fixtures/foo.jac +14 -22
  59. jaclang/tests/fixtures/jac_from_py.py +1 -1
  60. jaclang/tests/fixtures/jp_importer.jac +6 -6
  61. jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
  62. jaclang/tests/fixtures/unicode_strings.jac +24 -0
  63. jaclang/tests/fixtures/walker_update.jac +5 -7
  64. jaclang/tests/test_language.py +138 -140
  65. jaclang/tests/test_reference.py +9 -4
  66. jaclang/tests/test_typecheck.py +13 -26
  67. jaclang/utils/lang_tools.py +7 -5
  68. jaclang/utils/module_resolver.py +23 -0
  69. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/METADATA +1 -1
  70. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/RECORD +72 -70
  71. jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
  72. jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
  73. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
  74. jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
  75. jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
  76. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/WHEEL +0 -0
  77. {jaclang-0.8.1.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(
@@ -267,14 +310,10 @@ class PyastGenPass(UniPass):
267
310
  attr_node: ast3.Name | ast3.Attribute = self.sync(
268
311
  ast3.Name(id=attribute_list[0], ctx=ast3.Load()), sync_node_list[0]
269
312
  )
270
- for i in range(len(attribute_list)):
271
- if i == 0:
272
- continue
313
+ for attr, sync_node in zip(attribute_list[1:], sync_node_list[1:]):
273
314
  attr_node = self.sync(
274
- ast3.Attribute(
275
- value=attr_node, attr=attribute_list[i], ctx=ast3.Load()
276
- ),
277
- sync_node_list[i],
315
+ ast3.Attribute(value=attr_node, attr=attr, ctx=ast3.Load()),
316
+ sync_node,
278
317
  )
279
318
  return attr_node
280
319
 
@@ -426,40 +465,8 @@ class PyastGenPass(UniPass):
426
465
  )
427
466
 
428
467
  def exit_import(self, node: uni.Import) -> None:
429
- path_alias: dict[str, Optional[str]] = (
430
- {node.from_loc.dot_path_str: None} if node.from_loc else {}
431
- )
432
- imp_from = {}
433
- if node.items:
434
- for item in node.items:
435
- if isinstance(item, uni.ModuleItem):
436
- imp_from[item.name.sym_name] = (
437
- item.alias.sym_name if item.alias else None
438
- )
439
- elif isinstance(item, uni.ModulePath):
440
- path_alias[item.dot_path_str] = (
441
- item.alias.sym_name if item.alias else None
442
- )
443
-
444
- item_names: list[ast3.expr] = []
445
- item_keys: list[ast3.Constant] = []
446
- item_values: list[ast3.Constant] = []
447
- for k, v in imp_from.items():
448
- item_keys.append(self.sync(ast3.Constant(value=k)))
449
- item_values.append(self.sync(ast3.Constant(value=v)))
450
- item_names.append(
451
- self.sync(
452
- ast3.Name(
453
- id=v or k,
454
- ctx=ast3.Store(),
455
- )
456
- )
457
- )
458
- path_named_value: str
468
+ """Exit import node."""
459
469
  py_nodes: list[ast3.AST] = []
460
- typecheck_nodes: list[ast3.AST] = []
461
- runtime_nodes: list[ast3.AST] = []
462
-
463
470
  if node.doc:
464
471
  py_nodes.append(
465
472
  self.sync(
@@ -468,255 +475,28 @@ class PyastGenPass(UniPass):
468
475
  )
469
476
  )
470
477
 
471
- for path, alias in path_alias.items():
472
- path_named_value = ("_jac_inc_" if node.is_absorb else "") + (
473
- alias if alias else path
474
- ).lstrip(".").split(".")[0]
475
- # target_named_value = ""
476
- # for i in path.split("."):
477
- # target_named_value += i if i else "."
478
- # if i:
479
- # break
480
-
481
- args = [
482
- self.sync(
483
- ast3.Constant(value=path),
484
- ),
485
- self.sync(
486
- ast3.Name(
487
- id="__file__",
488
- ctx=ast3.Load(),
489
- )
490
- ),
491
- ]
492
- keywords = []
493
-
494
- if node.is_absorb:
495
- args.append(self.sync(ast3.Constant(value=node.is_absorb)))
496
-
497
- if alias is not None:
498
- keywords.append(
499
- self.sync(
500
- ast3.keyword(
501
- arg="mdl_alias",
502
- value=self.sync(
503
- ast3.Constant(value=alias),
504
- ),
505
- )
506
- )
507
- )
508
-
509
- if item_keys and item_values:
510
- keywords.append(
511
- self.sync(
512
- ast3.keyword(
513
- arg="items",
514
- value=self.sync(
515
- ast3.Dict(
516
- keys=cast(list[ast3.expr | None], item_keys),
517
- values=cast(list[ast3.expr], item_values),
518
- ),
519
- ),
520
- )
521
- )
522
- )
523
-
524
- runtime_nodes.append(
525
- self.sync(
526
- ast3.Assign(
527
- targets=(
528
- [
529
- self.sync(
530
- ast3.Tuple(
531
- elts=(
532
- item_names
533
- or [
534
- self.sync(
535
- ast3.Name(
536
- id=path_named_value,
537
- ctx=ast3.Store(),
538
- )
539
- )
540
- ]
541
- ),
542
- ctx=ast3.Store(),
543
- )
544
- )
545
- ]
546
- ),
547
- value=self.sync(
548
- ast3.Call(
549
- func=self.jaclib_obj("py_jac_import"),
550
- args=args,
551
- keywords=keywords,
552
- )
553
- ),
554
- ),
555
- ),
556
- )
557
- if node.is_absorb:
558
- absorb_exec = f"={path_named_value}.__dict__['"
559
- runtime_nodes.append(
560
- self.sync(
561
- ast3.For(
562
- target=self.sync(ast3.Name(id="i", ctx=ast3.Store())),
563
- iter=self.sync(
564
- ast3.IfExp(
565
- test=self.sync(
566
- ast3.Compare(
567
- left=self.sync(ast3.Constant(value="__all__")),
568
- ops=[self.sync(ast3.In())],
569
- comparators=[
570
- self.sync(
571
- ast3.Attribute(
572
- value=self.sync(
573
- ast3.Name(
574
- id=path_named_value,
575
- ctx=ast3.Load(),
576
- )
577
- ),
578
- attr="__dict__",
579
- ctx=ast3.Load(),
580
- )
581
- )
582
- ],
583
- )
584
- ),
585
- body=self.sync(
586
- ast3.Attribute(
587
- value=self.sync(
588
- ast3.Name(
589
- id=path_named_value, ctx=ast3.Load()
590
- )
591
- ),
592
- attr="__all__",
593
- ctx=ast3.Load(),
594
- )
595
- ),
596
- orelse=self.sync(
597
- ast3.Attribute(
598
- value=self.sync(
599
- ast3.Name(
600
- id=path_named_value, ctx=ast3.Load()
601
- )
602
- ),
603
- attr="__dict__",
604
- ctx=ast3.Load(),
605
- )
606
- ),
607
- )
608
- ),
609
- body=[
610
- self.sync(
611
- ast3.If(
612
- test=self.sync(
613
- ast3.UnaryOp(
614
- op=self.sync(ast3.Not()),
615
- operand=self.sync(
616
- ast3.Call(
617
- func=self.sync(
618
- ast3.Attribute(
619
- value=self.sync(
620
- ast3.Name(
621
- id="i",
622
- ctx=ast3.Load(),
623
- )
624
- ),
625
- attr="startswith",
626
- ctx=ast3.Load(),
627
- )
628
- ),
629
- args=[
630
- self.sync(
631
- ast3.Constant(value="_")
632
- )
633
- ],
634
- keywords=[],
635
- )
636
- ),
637
- )
638
- ),
639
- body=[
640
- self.sync(
641
- ast3.Expr(
642
- value=self.sync(
643
- ast3.Call(
644
- func=self.sync(
645
- ast3.Name(
646
- id="exec",
647
- ctx=ast3.Load(),
648
- )
649
- ),
650
- args=[
651
- self.sync(
652
- ast3.JoinedStr(
653
- values=[
654
- self.sync(
655
- ast3.FormattedValue(
656
- value=self.sync(
657
- ast3.Name(
658
- id="i",
659
- ctx=ast3.Load(),
660
- )
661
- ),
662
- conversion=-1,
663
- )
664
- ),
665
- self.sync(
666
- ast3.Constant(
667
- value=absorb_exec
668
- )
669
- ),
670
- self.sync(
671
- ast3.FormattedValue(
672
- value=self.sync(
673
- ast3.Name(
674
- id="i",
675
- ctx=ast3.Load(),
676
- )
677
- ),
678
- conversion=-1,
679
- )
680
- ),
681
- self.sync(
682
- ast3.Constant(
683
- value="']"
684
- )
685
- ),
686
- ]
687
- )
688
- )
689
- ],
690
- keywords=[],
691
- )
692
- )
693
- )
694
- )
695
- ],
696
- orelse=[],
697
- )
698
- )
699
- ],
700
- orelse=[],
701
- )
702
- )
703
- )
704
478
  if node.is_absorb:
479
+ # This is `include "module_name";` which becomes `from module_name import *`
705
480
  source = node.items[0]
706
481
  if not isinstance(source, uni.ModulePath):
707
482
  raise self.ice()
708
- 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(
709
488
  self.sync(
710
489
  py_node=ast3.ImportFrom(
711
- module=(source.dot_path_str.lstrip(".") if source else None),
490
+ module=module_name,
712
491
  names=[self.sync(ast3.alias(name="*"), node)],
713
- level=0,
492
+ level=source.level,
714
493
  ),
715
494
  jac_node=node,
716
495
  )
717
496
  )
718
497
  elif not node.from_loc:
719
- typecheck_nodes.append(
498
+ # This is `import module1, module2 as alias;`
499
+ py_nodes.append(
720
500
  self.sync(
721
501
  ast3.Import(
722
502
  names=[
@@ -728,32 +508,25 @@ class PyastGenPass(UniPass):
728
508
  )
729
509
  )
730
510
  else:
731
- 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(
732
518
  self.sync(
733
519
  ast3.ImportFrom(
734
- module=(
735
- node.from_loc.dot_path_str.lstrip(".")
736
- if node.from_loc
737
- else None
738
- ),
520
+ module=module_name,
739
521
  names=[
740
522
  cast(ast3.alias, i)
741
523
  for item in node.items
742
524
  for i in item.gen.py_ast
743
525
  ],
744
- level=0,
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:
@@ -867,6 +640,17 @@ class PyastGenPass(UniPass):
867
640
  if isinstance(node.body, uni.ImplDef):
868
641
  self.traverse(node.body)
869
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
+
870
654
  def gen_llm_body(self, node: uni.Ability) -> list[ast3.AST]:
871
655
  """Generate the by LLM body."""
872
656
  # to Avoid circular import
@@ -988,11 +772,7 @@ class PyastGenPass(UniPass):
988
772
  ),
989
773
  body=[cast(ast3.stmt, i) for i in body],
990
774
  decorator_list=[cast(ast3.expr, i) for i in decorator_list],
991
- returns=(
992
- cast(ast3.expr, node.signature.return_type.gen.py_ast[0])
993
- if node.signature and node.signature.return_type
994
- else self.sync(ast3.Constant(value=None))
995
- ),
775
+ returns=self.sync(ast3.Constant(value=None)),
996
776
  type_params=[],
997
777
  )
998
778
  )
@@ -1829,25 +1609,9 @@ class PyastGenPass(UniPass):
1829
1609
  ]
1830
1610
 
1831
1611
  def exit_await_expr(self, node: uni.AwaitExpr) -> None:
1832
- parent_node = node.parent
1833
- while parent_node and (parent_node := parent_node.parent):
1834
- if hasattr(parent_node, "is_async") and parent_node.is_async:
1835
- node.gen.py_ast = [
1836
- self.sync(
1837
- ast3.Await(value=cast(ast3.expr, node.target.gen.py_ast[0]))
1838
- )
1839
- ]
1840
- break
1841
- else:
1842
- node.gen.py_ast = [
1843
- self.sync(
1844
- ast3.Call(
1845
- func=self.jaclib_obj("await_obj"),
1846
- args=[cast(ast3.expr, node.target.gen.py_ast[0])],
1847
- keywords=[],
1848
- )
1849
- )
1850
- ]
1612
+ node.gen.py_ast = [
1613
+ self.sync(ast3.Await(value=cast(ast3.expr, node.target.gen.py_ast[0])))
1614
+ ]
1851
1615
 
1852
1616
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
1853
1617
  py_nodes = []
@@ -2180,42 +1944,17 @@ class PyastGenPass(UniPass):
2180
1944
  ]
2181
1945
 
2182
1946
  def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
2183
- if node.op.name == Tok.NOT:
1947
+ op_cls = UNARY_OP_MAP.get(node.op.name)
1948
+ if op_cls:
2184
1949
  node.gen.py_ast = [
2185
1950
  self.sync(
2186
1951
  ast3.UnaryOp(
2187
- op=self.sync(ast3.Not()),
2188
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2189
- )
2190
- )
2191
- ]
2192
- elif node.op.name == Tok.BW_NOT:
2193
- node.gen.py_ast = [
2194
- self.sync(
2195
- ast3.UnaryOp(
2196
- op=self.sync(ast3.Invert()),
2197
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2198
- )
2199
- )
2200
- ]
2201
- elif node.op.name == Tok.PLUS:
2202
- node.gen.py_ast = [
2203
- self.sync(
2204
- ast3.UnaryOp(
2205
- op=self.sync(ast3.UAdd()),
2206
- operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2207
- )
2208
- )
2209
- ]
2210
- elif node.op.name == Tok.MINUS:
2211
- node.gen.py_ast = [
2212
- self.sync(
2213
- ast3.UnaryOp(
2214
- op=self.sync(ast3.USub()),
1952
+ op=self.sync(op_cls()),
2215
1953
  operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2216
1954
  )
2217
1955
  )
2218
1956
  ]
1957
+ return
2219
1958
  elif node.op.name in [Tok.PIPE_FWD, Tok.KW_SPAWN, Tok.A_PIPE_FWD]:
2220
1959
  node.gen.py_ast = [
2221
1960
  self.sync(
@@ -2573,8 +2312,10 @@ class PyastGenPass(UniPass):
2573
2312
 
2574
2313
  return JacMachineInterface.get_by_llm_call_args(self, node)
2575
2314
 
2576
- def exit_func_call(self, node: uni.FuncCall) -> None:
2577
- 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."""
2578
2319
  args = []
2579
2320
  keywords = []
2580
2321
  if node.params:
@@ -2596,10 +2337,21 @@ class PyastGenPass(UniPass):
2596
2337
  keywords.append(x.gen.py_ast[0])
2597
2338
  else:
2598
2339
  self.ice("Invalid Parameter")
2599
- 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:
2600
2348
  by_llm_call_args = self.get_by_llm_call_args(node)
2601
2349
  node.gen.py_ast = [self.sync(self.by_llm_call(**by_llm_call_args))]
2350
+
2602
2351
  else:
2352
+ func = node.target.gen.py_ast[0]
2353
+ args, keywords = self.gen_call_args(node)
2354
+
2603
2355
  node.gen.py_ast = [
2604
2356
  self.sync(
2605
2357
  ast3.Call(
@@ -2699,149 +2451,115 @@ class PyastGenPass(UniPass):
2699
2451
  ]
2700
2452
 
2701
2453
  def exit_edge_ref_trailer(self, node: uni.EdgeRefTrailer) -> None:
2702
- pynode = node.chain[0].gen.py_ast[0]
2703
- chomp = [*node.chain]
2704
- last_edge = None
2454
+ origin = None
2455
+ cur = node.chain[0]
2456
+ chomp = [*node.chain[1:]]
2705
2457
  from_visit = bool(isinstance(node.parent, uni.VisitStmt))
2706
- if node.edges_only:
2707
- for i in node.chain:
2708
- if isinstance(i, uni.EdgeOpRef):
2709
- last_edge = i
2710
- while len(chomp):
2711
- cur = chomp[0]
2712
- chomp = chomp[1:]
2713
- if len(chomp) == len(node.chain) - 1 and not isinstance(cur, uni.EdgeOpRef):
2714
- continue
2715
- next_i = chomp[0] if chomp else None
2716
- if isinstance(cur, uni.EdgeOpRef) and (
2717
- not next_i or not isinstance(next_i, uni.EdgeOpRef)
2718
- ):
2719
- pynode = self.translate_edge_op_ref(
2720
- loc=pynode,
2721
- node=cur,
2722
- targ=(
2723
- next_i.gen.py_ast[0]
2724
- if next_i and not isinstance(next_i, uni.FilterCompr)
2725
- else None
2726
- ),
2727
- edges_only=node.edges_only and cur == last_edge,
2728
- from_visit=from_visit,
2729
- )
2730
- if next_i and isinstance(next_i, uni.FilterCompr):
2731
- pynode = self.sync(
2732
- ast3.Call(
2733
- func=self.jaclib_obj("filter"),
2734
- args=[],
2735
- keywords=[
2736
- self.sync(
2737
- ast3.keyword(
2738
- arg="items",
2739
- value=cast(ast3.expr, pynode),
2740
- )
2741
- ),
2742
- self.sync(
2743
- ast3.keyword(
2744
- arg="func",
2745
- value=cast(ast3.expr, next_i.gen.py_ast[0]),
2746
- )
2747
- ),
2748
- ],
2749
- )
2750
- )
2751
- chomp = chomp[1:] if next_i else chomp
2752
- elif isinstance(cur, uni.EdgeOpRef) and isinstance(next_i, uni.EdgeOpRef):
2753
- pynode = self.translate_edge_op_ref(
2754
- pynode,
2755
- cur,
2756
- targ=None,
2757
- edges_only=node.edges_only and cur == last_edge,
2758
- from_visit=from_visit,
2759
- )
2760
- else:
2761
- raise self.ice("Invalid edge ref trailer")
2762
-
2763
- node.gen.py_ast = [pynode]
2764
-
2765
- def exit_edge_op_ref(self, node: uni.EdgeOpRef) -> None:
2766
- loc = self.sync(
2767
- ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
2768
- if node.from_walker
2769
- else ast3.Name(id="self", ctx=ast3.Load())
2770
- )
2771
- node.gen.py_ast = [loc]
2772
2458
 
2773
- def translate_edge_op_ref(
2774
- self,
2775
- loc: ast3.AST,
2776
- node: uni.EdgeOpRef,
2777
- targ: ast3.AST | None,
2778
- edges_only: bool,
2779
- from_visit: bool,
2780
- ) -> ast3.AST:
2781
- """Generate ast for edge op ref call."""
2782
- keywords = [self.sync(ast3.keyword(arg="sources", value=cast(ast3.expr, loc)))]
2459
+ if not isinstance(cur, uni.EdgeOpRef):
2460
+ origin = cur.gen.py_ast[0]
2461
+ cur = cast(uni.EdgeOpRef, chomp.pop(0))
2783
2462
 
2784
- if targ:
2785
- keywords.append(
2786
- self.sync(ast3.keyword(arg="targets", value=cast(ast3.expr, targ)))
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=[],
2787
2468
  )
2469
+ )
2788
2470
 
2789
- if node.edge_dir != EdgeDir.OUT:
2790
- keywords.append(
2791
- self.sync(
2792
- ast3.keyword(
2793
- arg="dir",
2794
- value=self.sync(
2795
- ast3.Attribute(
2796
- value=self.jaclib_obj("EdgeDir"),
2797
- attr=node.edge_dir.name,
2798
- ctx=ast3.Load(),
2799
- )
2800
- ),
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
+ )
2801
2482
  )
2802
2483
  )
2803
- )
2804
2484
 
2805
- if node.filter_cond:
2806
- keywords.append(
2807
- self.sync(
2808
- ast3.keyword(
2809
- arg="filter",
2810
- value=cast(
2811
- ast3.expr, self.sync(node.filter_cond.gen.py_ast[0])
2812
- ),
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
+ )
2813
2493
  )
2814
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
+ )
2815
2508
  )
2816
2509
 
2817
- if edges_only:
2818
- keywords.append(
2819
- self.sync(
2820
- ast3.keyword(
2821
- arg="edges_only",
2822
- value=self.sync(ast3.Constant(value=edges_only)),
2823
- )
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=[],
2824
2527
  )
2825
2528
  )
2826
2529
 
2827
2530
  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
- )
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=[],
2834
2542
  )
2835
2543
  )
2836
2544
 
2837
- return self.sync(
2545
+ pynode = self.sync(
2838
2546
  ast3.Call(
2839
2547
  func=self.jaclib_obj("refs"),
2840
- args=[],
2841
- keywords=keywords,
2548
+ args=[pynode],
2549
+ keywords=[],
2842
2550
  )
2843
2551
  )
2844
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
+
2845
2563
  def exit_disconnect_op(self, node: uni.DisconnectOp) -> None:
2846
2564
  node.gen.py_ast = node.edge_spec.gen.py_ast
2847
2565
 
@@ -3107,62 +2825,9 @@ class PyastGenPass(UniPass):
3107
2825
  ]
3108
2826
 
3109
2827
  def exit_token(self, node: uni.Token) -> None:
3110
- if node.name == Tok.KW_AND:
3111
- node.gen.py_ast = [self.sync(ast3.And())]
3112
- elif node.name == Tok.KW_OR:
3113
- node.gen.py_ast = [self.sync(ast3.Or())]
3114
- elif node.name in [Tok.PLUS, Tok.ADD_EQ]:
3115
- node.gen.py_ast = [self.sync(ast3.Add())]
3116
- elif node.name in [Tok.BW_AND, Tok.BW_AND_EQ]:
3117
- node.gen.py_ast = [self.sync(ast3.BitAnd())]
3118
- elif node.name in [Tok.BW_OR, Tok.BW_OR_EQ]:
3119
- node.gen.py_ast = [self.sync(ast3.BitOr())]
3120
- elif node.name in [Tok.BW_XOR, Tok.BW_XOR_EQ]:
3121
- node.gen.py_ast = [self.sync(ast3.BitXor())]
3122
- elif node.name in [Tok.DIV, Tok.DIV_EQ]:
3123
- node.gen.py_ast = [self.sync(ast3.Div())]
3124
- elif node.name in [Tok.FLOOR_DIV, Tok.FLOOR_DIV_EQ]:
3125
- node.gen.py_ast = [self.sync(ast3.FloorDiv())]
3126
- elif node.name in [Tok.LSHIFT, Tok.LSHIFT_EQ]:
3127
- node.gen.py_ast = [self.sync(ast3.LShift())]
3128
- elif node.name in [Tok.MOD, Tok.MOD_EQ]:
3129
- node.gen.py_ast = [self.sync(ast3.Mod())]
3130
- elif node.name in [Tok.STAR_MUL, Tok.MUL_EQ]:
3131
- node.gen.py_ast = [self.sync(ast3.Mult())]
3132
- elif node.name in [Tok.DECOR_OP, Tok.MATMUL_EQ]:
3133
- node.gen.py_ast = [self.sync(ast3.MatMult())]
3134
- elif node.name in [Tok.STAR_POW, Tok.STAR_POW_EQ]:
3135
- node.gen.py_ast = [self.sync(ast3.Pow())]
3136
- elif node.name in [Tok.RSHIFT, Tok.RSHIFT_EQ]:
3137
- node.gen.py_ast = [self.sync(ast3.RShift())]
3138
- elif node.name in [Tok.MINUS, Tok.SUB_EQ]:
3139
- node.gen.py_ast = [self.sync(ast3.Sub())]
3140
- elif node.name in [Tok.BW_NOT, Tok.BW_NOT_EQ]:
3141
- node.gen.py_ast = [self.sync(ast3.Invert())]
3142
- elif node.name in [Tok.NOT]:
3143
- node.gen.py_ast = [self.sync(ast3.Not())]
3144
- elif node.name in [Tok.EQ]:
3145
- node.gen.py_ast = [self.sync(ast3.NotEq())]
3146
- elif node.name == Tok.EE:
3147
- node.gen.py_ast = [self.sync(ast3.Eq())]
3148
- elif node.name == Tok.GT:
3149
- node.gen.py_ast = [self.sync(ast3.Gt())]
3150
- elif node.name == Tok.GTE:
3151
- node.gen.py_ast = [self.sync(ast3.GtE())]
3152
- elif node.name == Tok.KW_IN:
3153
- node.gen.py_ast = [self.sync(ast3.In())]
3154
- elif node.name == Tok.KW_IS:
3155
- node.gen.py_ast = [self.sync(ast3.Is())]
3156
- elif node.name == Tok.KW_ISN:
3157
- node.gen.py_ast = [self.sync(ast3.IsNot())]
3158
- elif node.name == Tok.LT:
3159
- node.gen.py_ast = [self.sync(ast3.Lt())]
3160
- elif node.name == Tok.LTE:
3161
- node.gen.py_ast = [self.sync(ast3.LtE())]
3162
- elif node.name == Tok.NE:
3163
- node.gen.py_ast = [self.sync(ast3.NotEq())]
3164
- elif node.name == Tok.KW_NIN:
3165
- 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())]
3166
2831
 
3167
2832
  def exit_name(self, node: uni.Name) -> None:
3168
2833
  node.gen.py_ast = [
@@ -3173,21 +2838,7 @@ class PyastGenPass(UniPass):
3173
2838
  node.gen.py_ast = [self.sync(ast3.Constant(value=float(node.value)))]
3174
2839
 
3175
2840
  def exit_int(self, node: uni.Int) -> None:
3176
- def handle_node_value(value: str) -> int:
3177
- if value.startswith(("0x", "0X")):
3178
- return int(value, 16)
3179
- elif value.startswith(("0b", "0B")):
3180
- return int(value, 2)
3181
- elif value.startswith(("0o", "0O")):
3182
- return int(value, 8)
3183
- else:
3184
- return int(value)
3185
-
3186
- node.gen.py_ast = [
3187
- self.sync(
3188
- ast3.Constant(value=handle_node_value(str(node.value)), kind=None)
3189
- )
3190
- ]
2841
+ node.gen.py_ast = [self.sync(ast3.Constant(value=int(node.value, 0)))]
3191
2842
 
3192
2843
  def exit_string(self, node: uni.String) -> None:
3193
2844
  node.gen.py_ast = [self.sync(ast3.Constant(value=node.lit_value))]