jaclang 0.8.1__py3-none-any.whl → 0.8.3__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 (84) 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/constant.py +2 -0
  5. jaclang/compiler/jac.lark +17 -10
  6. jaclang/compiler/larkparse/jac_parser.py +2 -2
  7. jaclang/compiler/parser.py +34 -10
  8. jaclang/compiler/passes/main/__init__.py +2 -14
  9. jaclang/compiler/passes/main/annex_pass.py +2 -8
  10. jaclang/compiler/passes/main/cfg_build_pass.py +38 -12
  11. jaclang/compiler/passes/main/import_pass.py +3 -11
  12. jaclang/compiler/passes/main/pyast_gen_pass.py +246 -592
  13. jaclang/compiler/passes/main/sem_def_match_pass.py +67 -0
  14. jaclang/compiler/passes/main/sym_tab_build_pass.py +8 -0
  15. jaclang/compiler/passes/main/sym_tab_link_pass.py +2 -5
  16. jaclang/compiler/passes/main/tests/fixtures/sem_def_match.impl.jac +12 -0
  17. jaclang/compiler/passes/main/tests/fixtures/sem_def_match.jac +31 -0
  18. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +2 -8
  19. jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
  20. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
  21. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
  22. jaclang/compiler/passes/main/tests/test_sem_def_match_pass.py +38 -0
  23. jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
  24. jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
  25. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +259 -106
  26. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
  27. jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
  28. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
  29. jaclang/compiler/passes/tool/tests/fixtures/has_frmt.jac +13 -0
  30. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
  31. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
  32. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
  33. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +25 -3
  34. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
  35. jaclang/compiler/program.py +23 -60
  36. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +2 -8
  37. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +1 -5
  38. jaclang/compiler/tests/test_importer.py +10 -13
  39. jaclang/compiler/unitree.py +88 -16
  40. jaclang/langserve/__init__.jac +1 -1
  41. jaclang/langserve/engine.jac +113 -108
  42. jaclang/langserve/server.jac +17 -2
  43. jaclang/langserve/tests/server_test/test_lang_serve.py +138 -46
  44. jaclang/langserve/tests/server_test/utils.py +35 -9
  45. jaclang/langserve/tests/test_sem_tokens.py +1 -1
  46. jaclang/langserve/tests/test_server.py +3 -7
  47. jaclang/runtimelib/archetype.py +127 -5
  48. jaclang/runtimelib/importer.py +51 -94
  49. jaclang/runtimelib/machine.py +391 -268
  50. jaclang/runtimelib/meta_importer.py +86 -0
  51. jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
  52. jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
  53. jaclang/runtimelib/tests/test_jaseci.py +3 -1
  54. jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
  55. jaclang/tests/fixtures/async_ability.jac +43 -10
  56. jaclang/tests/fixtures/async_function.jac +18 -0
  57. jaclang/tests/fixtures/async_walker.jac +17 -12
  58. jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
  59. jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
  60. jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
  61. jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
  62. jaclang/tests/fixtures/deep/one_lev.jac +2 -2
  63. jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
  64. jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
  65. jaclang/tests/fixtures/foo.jac +14 -22
  66. jaclang/tests/fixtures/jac_from_py.py +1 -1
  67. jaclang/tests/fixtures/jp_importer.jac +6 -6
  68. jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
  69. jaclang/tests/fixtures/unicode_strings.jac +24 -0
  70. jaclang/tests/fixtures/walker_update.jac +5 -7
  71. jaclang/tests/test_language.py +138 -140
  72. jaclang/tests/test_reference.py +9 -4
  73. jaclang/tests/test_typecheck.py +13 -26
  74. jaclang/utils/lang_tools.py +7 -5
  75. jaclang/utils/module_resolver.py +23 -0
  76. {jaclang-0.8.1.dist-info → jaclang-0.8.3.dist-info}/METADATA +1 -1
  77. {jaclang-0.8.1.dist-info → jaclang-0.8.3.dist-info}/RECORD +79 -72
  78. jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
  79. jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
  80. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
  81. jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
  82. jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
  83. {jaclang-0.8.1.dist-info → jaclang-0.8.3.dist-info}/WHEEL +0 -0
  84. {jaclang-0.8.1.dist-info → jaclang-0.8.3.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
  )
@@ -1001,6 +781,9 @@ class PyastGenPass(UniPass):
1001
781
  def exit_impl_def(self, node: uni.ImplDef) -> None:
1002
782
  pass
1003
783
 
784
+ def exit_sem_def(self, node: uni.SemDef) -> None:
785
+ pass
786
+
1004
787
  def exit_func_signature(self, node: uni.FuncSignature) -> None:
1005
788
  params = (
1006
789
  [self.sync(ast3.arg(arg="self", annotation=None))]
@@ -1829,25 +1612,9 @@ class PyastGenPass(UniPass):
1829
1612
  ]
1830
1613
 
1831
1614
  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
- ]
1615
+ node.gen.py_ast = [
1616
+ self.sync(ast3.Await(value=cast(ast3.expr, node.target.gen.py_ast[0])))
1617
+ ]
1851
1618
 
1852
1619
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
1853
1620
  py_nodes = []
@@ -2180,42 +1947,17 @@ class PyastGenPass(UniPass):
2180
1947
  ]
2181
1948
 
2182
1949
  def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
2183
- if node.op.name == Tok.NOT:
1950
+ op_cls = UNARY_OP_MAP.get(node.op.name)
1951
+ if op_cls:
2184
1952
  node.gen.py_ast = [
2185
1953
  self.sync(
2186
1954
  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()),
1955
+ op=self.sync(op_cls()),
2215
1956
  operand=cast(ast3.expr, node.operand.gen.py_ast[0]),
2216
1957
  )
2217
1958
  )
2218
1959
  ]
1960
+ return
2219
1961
  elif node.op.name in [Tok.PIPE_FWD, Tok.KW_SPAWN, Tok.A_PIPE_FWD]:
2220
1962
  node.gen.py_ast = [
2221
1963
  self.sync(
@@ -2573,8 +2315,10 @@ class PyastGenPass(UniPass):
2573
2315
 
2574
2316
  return JacMachineInterface.get_by_llm_call_args(self, node)
2575
2317
 
2576
- def exit_func_call(self, node: uni.FuncCall) -> None:
2577
- func = node.target.gen.py_ast[0]
2318
+ def gen_call_args(
2319
+ self, node: uni.FuncCall
2320
+ ) -> tuple[list[ast3.expr], list[ast3.keyword]]:
2321
+ """Generate the arguments for a function call."""
2578
2322
  args = []
2579
2323
  keywords = []
2580
2324
  if node.params:
@@ -2596,10 +2340,21 @@ class PyastGenPass(UniPass):
2596
2340
  keywords.append(x.gen.py_ast[0])
2597
2341
  else:
2598
2342
  self.ice("Invalid Parameter")
2599
- if node.genai_call:
2343
+ return args, keywords
2344
+
2345
+ def exit_func_call(self, node: uni.FuncCall) -> None:
2346
+ if node.body_genai_call:
2347
+ node.gen.py_ast = self.gen_llm_call_override(node)
2348
+
2349
+ # TODO: This needs to be changed to only generate parameters no the body.
2350
+ elif node.genai_call:
2600
2351
  by_llm_call_args = self.get_by_llm_call_args(node)
2601
2352
  node.gen.py_ast = [self.sync(self.by_llm_call(**by_llm_call_args))]
2353
+
2602
2354
  else:
2355
+ func = node.target.gen.py_ast[0]
2356
+ args, keywords = self.gen_call_args(node)
2357
+
2603
2358
  node.gen.py_ast = [
2604
2359
  self.sync(
2605
2360
  ast3.Call(
@@ -2699,149 +2454,115 @@ class PyastGenPass(UniPass):
2699
2454
  ]
2700
2455
 
2701
2456
  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
2457
+ origin = None
2458
+ cur = node.chain[0]
2459
+ chomp = [*node.chain[1:]]
2705
2460
  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
2461
 
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)))]
2462
+ if not isinstance(cur, uni.EdgeOpRef):
2463
+ origin = cur.gen.py_ast[0]
2464
+ cur = cast(uni.EdgeOpRef, chomp.pop(0))
2783
2465
 
2784
- if targ:
2785
- keywords.append(
2786
- self.sync(ast3.keyword(arg="targets", value=cast(ast3.expr, targ)))
2466
+ pynode = self.sync(
2467
+ ast3.Call(
2468
+ func=self.jaclib_obj("Path"),
2469
+ args=[cast(ast3.expr, origin or cur.gen.py_ast[0])],
2470
+ keywords=[],
2787
2471
  )
2472
+ )
2788
2473
 
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
- ),
2474
+ while True:
2475
+ keywords = []
2476
+ if cur.filter_cond:
2477
+ keywords.append(
2478
+ self.sync(
2479
+ ast3.keyword(
2480
+ arg="edge",
2481
+ value=cast(
2482
+ ast3.expr, self.sync(cur.filter_cond.gen.py_ast[0])
2483
+ ),
2484
+ )
2801
2485
  )
2802
2486
  )
2803
- )
2804
2487
 
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
- ),
2488
+ if chomp and not isinstance(chomp[0], uni.EdgeOpRef):
2489
+ filt = chomp.pop(0)
2490
+ keywords.append(
2491
+ self.sync(
2492
+ ast3.keyword(
2493
+ arg="node",
2494
+ value=cast(ast3.expr, self.sync(filt.gen.py_ast[0])),
2495
+ )
2813
2496
  )
2814
2497
  )
2498
+
2499
+ pynode = self.sync(
2500
+ ast3.Call(
2501
+ func=self.sync(
2502
+ ast3.Attribute(
2503
+ value=pynode,
2504
+ attr=f"_{cur.edge_dir.name.lower()}",
2505
+ ctx=ast3.Load(),
2506
+ )
2507
+ ),
2508
+ args=[],
2509
+ keywords=keywords,
2510
+ )
2815
2511
  )
2816
2512
 
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
- )
2513
+ if chomp:
2514
+ cur = cast(uni.EdgeOpRef, chomp.pop(0))
2515
+ else:
2516
+ break
2517
+
2518
+ if node.edges_only:
2519
+ pynode = self.sync(
2520
+ ast3.Call(
2521
+ func=self.sync(
2522
+ ast3.Attribute(
2523
+ value=pynode,
2524
+ attr="edge",
2525
+ ctx=ast3.Load(),
2526
+ )
2527
+ ),
2528
+ args=[],
2529
+ keywords=[],
2824
2530
  )
2825
2531
  )
2826
2532
 
2827
2533
  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
- )
2534
+ pynode = self.sync(
2535
+ ast3.Call(
2536
+ func=self.sync(
2537
+ ast3.Attribute(
2538
+ value=pynode,
2539
+ attr="visit",
2540
+ ctx=ast3.Load(),
2541
+ )
2542
+ ),
2543
+ args=[],
2544
+ keywords=[],
2834
2545
  )
2835
2546
  )
2836
2547
 
2837
- return self.sync(
2548
+ pynode = self.sync(
2838
2549
  ast3.Call(
2839
2550
  func=self.jaclib_obj("refs"),
2840
- args=[],
2841
- keywords=keywords,
2551
+ args=[pynode],
2552
+ keywords=[],
2842
2553
  )
2843
2554
  )
2844
2555
 
2556
+ node.gen.py_ast = [pynode]
2557
+
2558
+ def exit_edge_op_ref(self, node: uni.EdgeOpRef) -> None:
2559
+ loc = self.sync(
2560
+ ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
2561
+ if node.from_walker
2562
+ else ast3.Name(id="self", ctx=ast3.Load())
2563
+ )
2564
+ node.gen.py_ast = [loc]
2565
+
2845
2566
  def exit_disconnect_op(self, node: uni.DisconnectOp) -> None:
2846
2567
  node.gen.py_ast = node.edge_spec.gen.py_ast
2847
2568
 
@@ -3107,62 +2828,9 @@ class PyastGenPass(UniPass):
3107
2828
  ]
3108
2829
 
3109
2830
  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())]
2831
+ op_cls = TOKEN_AST_MAP.get(node.name)
2832
+ if op_cls:
2833
+ node.gen.py_ast = [self.sync(op_cls())]
3166
2834
 
3167
2835
  def exit_name(self, node: uni.Name) -> None:
3168
2836
  node.gen.py_ast = [
@@ -3173,21 +2841,7 @@ class PyastGenPass(UniPass):
3173
2841
  node.gen.py_ast = [self.sync(ast3.Constant(value=float(node.value)))]
3174
2842
 
3175
2843
  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
- ]
2844
+ node.gen.py_ast = [self.sync(ast3.Constant(value=int(node.value, 0)))]
3191
2845
 
3192
2846
  def exit_string(self, node: uni.String) -> None:
3193
2847
  node.gen.py_ast = [self.sync(ast3.Constant(value=node.lit_value))]