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
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import ast as ast3
6
- import asyncio
7
6
  import fnmatch
8
7
  import html
9
8
  import inspect
@@ -14,12 +13,13 @@ import types
14
13
  from collections import OrderedDict
15
14
  from concurrent.futures import Future, ThreadPoolExecutor
16
15
  from dataclasses import MISSING, dataclass, field
17
- from functools import partial, wraps
16
+ from functools import wraps
18
17
  from inspect import getfile
19
18
  from logging import getLogger
20
19
  from typing import (
21
20
  Any,
22
21
  Callable,
22
+ Coroutine,
23
23
  Mapping,
24
24
  Optional,
25
25
  ParamSpec,
@@ -39,7 +39,9 @@ from jaclang.compiler.constant import Constants as Con, EdgeDir, colors
39
39
  from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
40
40
  from jaclang.compiler.program import JacProgram
41
41
  from jaclang.runtimelib.archetype import (
42
+ DataSpatialDestination,
42
43
  DataSpatialFunction,
44
+ DataSpatialPath,
43
45
  GenericEdge as _GenericEdge,
44
46
  Root as _Root,
45
47
  )
@@ -77,6 +79,64 @@ T = TypeVar("T")
77
79
  P = ParamSpec("P")
78
80
 
79
81
 
82
+ class ExecutionContext:
83
+ """Execution Context."""
84
+
85
+ def __init__(
86
+ self,
87
+ session: Optional[str] = None,
88
+ root: Optional[str] = None,
89
+ ) -> None:
90
+ """Initialize JacMachine."""
91
+ self.mem: Memory = ShelfStorage(session)
92
+ self.reports: list[Any] = []
93
+ sr_arch = Root()
94
+ sr_anch = sr_arch.__jac__
95
+ sr_anch.id = UUID(Con.SUPER_ROOT_UUID)
96
+ sr_anch.persistent = False
97
+ self.system_root = sr_anch
98
+ self.custom: Any = MISSING
99
+ if not isinstance(
100
+ system_root := self.mem.find_by_id(UUID(Con.SUPER_ROOT_UUID)), NodeAnchor
101
+ ):
102
+ system_root = cast(NodeAnchor, Root().__jac__) # type: ignore[attr-defined]
103
+ system_root.id = UUID(Con.SUPER_ROOT_UUID)
104
+ self.mem.set(system_root.id, system_root)
105
+
106
+ self.system_root = system_root
107
+
108
+ self.entry_node = self.root_state = self.init_anchor(root, self.system_root)
109
+
110
+ def init_anchor(
111
+ self,
112
+ anchor_id: str | None,
113
+ default: NodeAnchor,
114
+ ) -> NodeAnchor:
115
+ """Load initial anchors."""
116
+ if anchor_id:
117
+ if isinstance(anchor := self.mem.find_by_id(UUID(anchor_id)), NodeAnchor):
118
+ return anchor
119
+ raise ValueError(f"Invalid anchor id {anchor_id} !")
120
+ return default
121
+
122
+ def set_entry_node(self, entry_node: str | None) -> None:
123
+ """Override entry."""
124
+ self.entry_node = self.init_anchor(entry_node, self.root_state)
125
+
126
+ def close(self) -> None:
127
+ """Close current ExecutionContext."""
128
+ self.mem.close()
129
+ JacMachine.reset_machine()
130
+
131
+ def get_root(self) -> Root:
132
+ """Get current root."""
133
+ return cast(Root, self.root_state.archetype)
134
+
135
+ def global_system_root(self) -> NodeAnchor:
136
+ """Get global system root."""
137
+ return self.system_root
138
+
139
+
80
140
  class JacAccessValidation:
81
141
  """Jac Access Validation Specs."""
82
142
 
@@ -233,69 +293,105 @@ class JacNode:
233
293
 
234
294
  @staticmethod
235
295
  def get_edges(
236
- node: NodeAnchor,
237
- dir: EdgeDir,
238
- filter: Callable[[EdgeArchetype], bool] | None,
239
- target_obj: list[NodeArchetype] | None,
296
+ origin: list[NodeArchetype], destination: DataSpatialDestination
240
297
  ) -> list[EdgeArchetype]:
241
298
  """Get edges connected to this node."""
242
- ret_edges: list[EdgeArchetype] = []
243
- for anchor in node.edges:
244
- if (
245
- (source := anchor.source)
246
- and (target := anchor.target)
247
- and (not filter or filter(anchor.archetype))
248
- and source.archetype
249
- and target.archetype
250
- ):
299
+ edges: OrderedDict[EdgeAnchor, EdgeArchetype] = OrderedDict()
300
+ for node in origin:
301
+ nanch = node.__jac__
302
+ for anchor in nanch.edges:
251
303
  if (
252
- dir in [EdgeDir.OUT, EdgeDir.ANY]
253
- and node == source
254
- and (not target_obj or target.archetype in target_obj)
255
- and JacMachineInterface.check_read_access(target)
304
+ (source := anchor.source)
305
+ and (target := anchor.target)
306
+ and destination.edge_filter(anchor.archetype)
307
+ and source.archetype
308
+ and target.archetype
256
309
  ):
257
- ret_edges.append(anchor.archetype)
310
+ if (
311
+ destination.direction in [EdgeDir.OUT, EdgeDir.ANY]
312
+ and nanch == source
313
+ and destination.node_filter(target.archetype)
314
+ and JacMachineInterface.check_read_access(target)
315
+ ):
316
+ edges[anchor] = anchor.archetype
317
+ if (
318
+ destination.direction in [EdgeDir.IN, EdgeDir.ANY]
319
+ and nanch == target
320
+ and destination.node_filter(source.archetype)
321
+ and JacMachineInterface.check_read_access(source)
322
+ ):
323
+ edges[anchor] = anchor.archetype
324
+ return list(edges.values())
325
+
326
+ @staticmethod
327
+ def get_edges_with_node(
328
+ origin: list[NodeArchetype],
329
+ destination: DataSpatialDestination,
330
+ from_visit: bool = False,
331
+ ) -> list[EdgeArchetype | NodeArchetype]:
332
+ """Get edges connected to this node and the node."""
333
+ loc: OrderedDict[
334
+ Union[NodeAnchor, EdgeAnchor], Union[NodeArchetype, EdgeArchetype]
335
+ ] = OrderedDict()
336
+ for node in origin:
337
+ nanch = node.__jac__
338
+ for anchor in nanch.edges:
258
339
  if (
259
- dir in [EdgeDir.IN, EdgeDir.ANY]
260
- and node == target
261
- and (not target_obj or source.archetype in target_obj)
262
- and JacMachineInterface.check_read_access(source)
340
+ (source := anchor.source)
341
+ and (target := anchor.target)
342
+ and destination.edge_filter(anchor.archetype)
343
+ and source.archetype
344
+ and target.archetype
263
345
  ):
264
- ret_edges.append(anchor.archetype)
265
- return ret_edges
346
+ if (
347
+ destination.direction in [EdgeDir.OUT, EdgeDir.ANY]
348
+ and nanch == source
349
+ and destination.node_filter(target.archetype)
350
+ and JacMachineInterface.check_read_access(target)
351
+ ):
352
+ loc[anchor] = anchor.archetype
353
+ loc[target] = target.archetype
354
+ if (
355
+ destination.direction in [EdgeDir.IN, EdgeDir.ANY]
356
+ and nanch == target
357
+ and destination.node_filter(source.archetype)
358
+ and JacMachineInterface.check_read_access(source)
359
+ ):
360
+ loc[anchor] = anchor.archetype
361
+ loc[source] = source.archetype
362
+ return list(loc.values())
266
363
 
267
364
  @staticmethod
268
365
  def edges_to_nodes(
269
- node: NodeAnchor,
270
- dir: EdgeDir,
271
- filter: Callable[[EdgeArchetype], bool] | None,
272
- target_obj: list[NodeArchetype] | None,
366
+ origin: list[NodeArchetype], destination: DataSpatialDestination
273
367
  ) -> list[NodeArchetype]:
274
368
  """Get set of nodes connected to this node."""
275
- ret_edges: list[NodeArchetype] = []
276
- for anchor in node.edges:
277
- if (
278
- (source := anchor.source)
279
- and (target := anchor.target)
280
- and (not filter or filter(anchor.archetype))
281
- and source.archetype
282
- and target.archetype
283
- ):
369
+ nodes: OrderedDict[NodeAnchor, NodeArchetype] = OrderedDict()
370
+ for node in origin:
371
+ nanch = node.__jac__
372
+ for anchor in nanch.edges:
284
373
  if (
285
- dir in [EdgeDir.OUT, EdgeDir.ANY]
286
- and node == source
287
- and (not target_obj or target.archetype in target_obj)
288
- and JacMachineInterface.check_read_access(target)
289
- ):
290
- ret_edges.append(target.archetype)
291
- if (
292
- dir in [EdgeDir.IN, EdgeDir.ANY]
293
- and node == target
294
- and (not target_obj or source.archetype in target_obj)
295
- and JacMachineInterface.check_read_access(source)
374
+ (source := anchor.source)
375
+ and (target := anchor.target)
376
+ and destination.edge_filter(anchor.archetype)
377
+ and source.archetype
378
+ and target.archetype
296
379
  ):
297
- ret_edges.append(source.archetype)
298
- return ret_edges
380
+ if (
381
+ destination.direction in [EdgeDir.OUT, EdgeDir.ANY]
382
+ and nanch == source
383
+ and destination.node_filter(target.archetype)
384
+ and JacMachineInterface.check_read_access(target)
385
+ ):
386
+ nodes[target] = target.archetype
387
+ if (
388
+ destination.direction in [EdgeDir.IN, EdgeDir.ANY]
389
+ and nanch == target
390
+ and destination.node_filter(source.archetype)
391
+ and JacMachineInterface.check_read_access(source)
392
+ ):
393
+ nodes[source] = source.archetype
394
+ return list(nodes.values())
299
395
 
300
396
  @staticmethod
301
397
  def remove_edge(node: NodeAnchor, edge: EdgeAnchor) -> None:
@@ -477,7 +573,115 @@ class JacWalker:
477
573
  return warch
478
574
 
479
575
  @staticmethod
480
- def spawn(op1: Archetype, op2: Archetype) -> WalkerArchetype | asyncio.Future:
576
+ async def async_spawn_call(
577
+ walker: WalkerAnchor,
578
+ node: NodeAnchor | EdgeAnchor,
579
+ ) -> WalkerArchetype:
580
+ """Jac's spawn operator feature."""
581
+ warch = walker.archetype
582
+ walker.path = []
583
+ current_loc = node.archetype
584
+
585
+ # walker ability on any entry
586
+ for i in warch._jac_entry_funcs_:
587
+ if not i.trigger:
588
+ result = i.func(warch, current_loc)
589
+ if isinstance(result, Coroutine):
590
+ await result
591
+ if walker.disengaged:
592
+ return warch
593
+
594
+ while len(walker.next):
595
+ if current_loc := walker.next.pop(0).archetype:
596
+ # walker ability with loc entry
597
+ for i in warch._jac_entry_funcs_:
598
+ if (
599
+ i.trigger
600
+ and (
601
+ all_issubclass(i.trigger, NodeArchetype)
602
+ or all_issubclass(i.trigger, EdgeArchetype)
603
+ )
604
+ and isinstance(current_loc, i.trigger)
605
+ ):
606
+ result = i.func(warch, current_loc)
607
+ if isinstance(result, Coroutine):
608
+ await result
609
+ if walker.disengaged:
610
+ return warch
611
+
612
+ # loc ability with any entry
613
+ for i in current_loc._jac_entry_funcs_:
614
+ if not i.trigger:
615
+ result = i.func(current_loc, warch)
616
+ if isinstance(result, Coroutine):
617
+ await result
618
+ if walker.disengaged:
619
+ return warch
620
+
621
+ # loc ability with walker entry
622
+ for i in current_loc._jac_entry_funcs_:
623
+ if (
624
+ i.trigger
625
+ and all_issubclass(i.trigger, WalkerArchetype)
626
+ and isinstance(warch, i.trigger)
627
+ ):
628
+ result = i.func(current_loc, warch)
629
+ if isinstance(result, Coroutine):
630
+ await result
631
+ if walker.disengaged:
632
+ return warch
633
+
634
+ # loc ability with walker exit
635
+ for i in current_loc._jac_exit_funcs_:
636
+ if (
637
+ i.trigger
638
+ and all_issubclass(i.trigger, WalkerArchetype)
639
+ and isinstance(warch, i.trigger)
640
+ ):
641
+ result = i.func(current_loc, warch)
642
+ if isinstance(result, Coroutine):
643
+ await result
644
+ if walker.disengaged:
645
+ return warch
646
+
647
+ # loc ability with any exit
648
+ for i in current_loc._jac_exit_funcs_:
649
+ if not i.trigger:
650
+ result = i.func(current_loc, warch)
651
+ if isinstance(result, Coroutine):
652
+ await result
653
+ if walker.disengaged:
654
+ return warch
655
+
656
+ # walker ability with loc exit
657
+ for i in warch._jac_exit_funcs_:
658
+ if (
659
+ i.trigger
660
+ and (
661
+ all_issubclass(i.trigger, NodeArchetype)
662
+ or all_issubclass(i.trigger, EdgeArchetype)
663
+ )
664
+ and isinstance(current_loc, i.trigger)
665
+ ):
666
+ result = i.func(warch, current_loc)
667
+ if isinstance(result, Coroutine):
668
+ await result
669
+ if walker.disengaged:
670
+ return warch
671
+ # walker ability with any exit
672
+ for i in warch._jac_exit_funcs_:
673
+ if not i.trigger:
674
+ result = i.func(warch, current_loc)
675
+ if isinstance(result, Coroutine):
676
+ await result
677
+ if walker.disengaged:
678
+ return warch
679
+
680
+ walker.ignores = []
681
+ return warch
682
+
683
+ @staticmethod
684
+ def spawn(op1: Archetype, op2: Archetype) -> WalkerArchetype | Coroutine:
481
685
  """Jac's spawn operator feature."""
482
686
  edge: EdgeAnchor | None = None
483
687
  if isinstance(op1, WalkerArchetype):
@@ -511,12 +715,7 @@ class JacWalker:
511
715
  walker.next = [node]
512
716
 
513
717
  if warch.__jac_async__:
514
- machine = JacMachineInterface.py_get_jac_machine()
515
- _event_loop = machine._event_loop
516
- func = partial(JacMachineInterface.spawn_call, *(walker, loc))
517
- return asyncio.ensure_future(
518
- _event_loop.run_in_executor(None, func), loop=_event_loop
519
- )
718
+ return JacMachineInterface.async_spawn_call(walker=walker, node=loc)
520
719
  else:
521
720
  return JacMachineInterface.spawn_call(walker=walker, node=loc)
522
721
 
@@ -542,6 +741,8 @@ class JacClassReferences:
542
741
  Root: TypeAlias = _Root
543
742
  GenericEdge: TypeAlias = _GenericEdge
544
743
 
744
+ Path: TypeAlias = DataSpatialPath
745
+
545
746
 
546
747
  class JacBuiltin:
547
748
  """Jac Builtins."""
@@ -656,9 +857,9 @@ class JacBasics:
656
857
  """Set Class References."""
657
858
 
658
859
  @staticmethod
659
- def get_context() -> JacMachine:
860
+ def get_context() -> ExecutionContext:
660
861
  """Get current execution context."""
661
- return JacMachineInterface.py_get_jac_machine()
862
+ return JacMachine.exec_ctx
662
863
 
663
864
  @staticmethod
664
865
  def reset_graph(root: Optional[Root] = None) -> int:
@@ -759,54 +960,8 @@ class JacBasics:
759
960
 
760
961
  return decorator
761
962
 
762
- @staticmethod
763
- def py_get_jac_machine() -> JacMachine:
764
- """Get jac machine from python context."""
765
- machine = JacBasics.py_find_jac_machine()
766
- if not machine:
767
- raise RuntimeError("Jac machine not found in python context. ")
768
- return machine
769
-
770
- @staticmethod
771
- def py_find_jac_machine() -> Optional[JacMachine]:
772
- """Get jac machine from python context."""
773
- machine = None
774
- for i in inspect.stack():
775
- machine = i.frame.f_globals.get("__jac_mach__") or i.frame.f_locals.get(
776
- "__jac_mach__"
777
- )
778
- if machine:
779
- break
780
- return machine
781
-
782
- @staticmethod
783
- def py_jac_import(
784
- target: str,
785
- base_path: str,
786
- absorb: bool = False,
787
- mdl_alias: Optional[str] = None,
788
- override_name: Optional[str] = None,
789
- items: Optional[dict[str, Union[str, Optional[str]]]] = None,
790
- reload_module: Optional[bool] = False,
791
- ) -> tuple[types.ModuleType, ...]:
792
- """Core Import Process."""
793
- machine = JacBasics.py_find_jac_machine()
794
- if not machine:
795
- machine = JacMachine(base_path=base_path)
796
- return JacMachineInterface.jac_import(
797
- mach=machine,
798
- target=target,
799
- base_path=base_path,
800
- absorb=absorb,
801
- mdl_alias=mdl_alias,
802
- override_name=override_name,
803
- items=items,
804
- reload_module=reload_module,
805
- )
806
-
807
963
  @staticmethod
808
964
  def jac_import(
809
- mach: JacMachine,
810
965
  target: str,
811
966
  base_path: str,
812
967
  absorb: bool = False,
@@ -834,13 +989,13 @@ class JacBasics:
834
989
  items,
835
990
  )
836
991
 
837
- if not mach.program:
838
- JacMachineInterface.attach_program(mach, JacProgram())
992
+ if not JacMachine.program:
993
+ JacMachineInterface.attach_program(JacProgram())
839
994
 
840
995
  if lng == "py":
841
- import_result = PythonImporter(mach).run_import(spec)
996
+ import_result = PythonImporter().run_import(spec)
842
997
  else:
843
- import_result = JacImporter(mach).run_import(spec, reload_module)
998
+ import_result = JacImporter().run_import(spec, reload_module)
844
999
 
845
1000
  return (
846
1001
  (import_result.ret_mod,)
@@ -864,7 +1019,6 @@ class JacBasics:
864
1019
 
865
1020
  @staticmethod
866
1021
  def run_test(
867
- mach: JacMachine,
868
1022
  filepath: str,
869
1023
  func_name: Optional[str] = None,
870
1024
  filter: Optional[str] = None,
@@ -884,9 +1038,7 @@ class JacBasics:
884
1038
  if mod_name.endswith(".test"):
885
1039
  mod_name = mod_name[:-5]
886
1040
  JacTestCheck.reset()
887
- JacMachineInterface.jac_import(
888
- mach=mach, target=mod_name, base_path=base
889
- )
1041
+ JacMachineInterface.jac_import(target=mod_name, base_path=base)
890
1042
  JacTestCheck.run_test(
891
1043
  xit, maxfail, verbose, os.path.abspath(filepath), func_name
892
1044
  )
@@ -915,7 +1067,7 @@ class JacBasics:
915
1067
  print(f"\n\n\t\t* Inside {root_dir}" + "/" + f"{file} *")
916
1068
  JacTestCheck.reset()
917
1069
  JacMachineInterface.jac_import(
918
- mach=mach, target=file[:-4], base_path=root_dir
1070
+ target=file[:-4], base_path=root_dir
919
1071
  )
920
1072
  JacTestCheck.run_test(
921
1073
  xit, maxfail, verbose, os.path.abspath(file), func_name
@@ -950,52 +1102,31 @@ class JacBasics:
950
1102
 
951
1103
  @staticmethod
952
1104
  def refs(
953
- sources: NodeArchetype | list[NodeArchetype],
954
- targets: NodeArchetype | list[NodeArchetype] | None = None,
955
- dir: EdgeDir = EdgeDir.OUT,
956
- filter: Callable[[EdgeArchetype], bool] | None = None,
957
- edges_only: bool = False,
958
- from_visit: bool = False,
1105
+ path: DataSpatialPath | NodeArchetype | list[NodeArchetype],
959
1106
  ) -> (
960
1107
  list[NodeArchetype] | list[EdgeArchetype] | list[NodeArchetype | EdgeArchetype]
961
1108
  ):
962
1109
  """Jac's apply_dir stmt feature."""
963
- if isinstance(sources, NodeArchetype):
964
- sources = [sources]
965
- targ_obj_set: Optional[list[NodeArchetype]] = (
966
- [targets]
967
- if isinstance(targets, NodeArchetype)
968
- else targets if targets else None
969
- )
970
- if edges_only:
971
- connected_edges: list[EdgeArchetype | NodeArchetype] = []
972
- for node in sources:
973
- edges = JacMachineInterface.get_edges(
974
- node.__jac__, dir, filter, target_obj=targ_obj_set
975
- )
976
- for edge in edges:
977
- assert isinstance(edge, EdgeArchetype)
978
- if edge in connected_edges:
979
- continue
980
- if from_visit:
981
- connected_edges.append(edge)
982
- if dir == EdgeDir.IN:
983
- connected_edges.append(edge.__jac__.source.archetype)
984
- else:
985
- connected_edges.append(edge.__jac__.target.archetype)
986
- else:
987
- connected_edges.append(edge)
988
- return connected_edges
1110
+ if not isinstance(path, DataSpatialPath):
1111
+ path = DataSpatialPath(path, [DataSpatialDestination(EdgeDir.OUT)])
1112
+
1113
+ origin = path.origin
1114
+
1115
+ if path.edge_only:
1116
+ destinations = path.destinations[:-1]
989
1117
  else:
990
- connected_nodes: list[NodeArchetype] = []
991
- for node in sources:
992
- nodes = JacMachineInterface.edges_to_nodes(
993
- node.__jac__, dir, filter, target_obj=targ_obj_set
1118
+ destinations = path.destinations
1119
+ while destinations:
1120
+ dest = path.destinations.pop(0)
1121
+ origin = JacMachineInterface.edges_to_nodes(origin, dest)
1122
+
1123
+ if path.edge_only:
1124
+ if path.from_visit:
1125
+ return JacMachineInterface.get_edges_with_node(
1126
+ origin, path.destinations[-1]
994
1127
  )
995
- connected_nodes.extend(
996
- node for node in nodes if node not in connected_nodes
997
- )
998
- return connected_nodes
1128
+ return JacMachineInterface.get_edges(origin, path.destinations[-1])
1129
+ return origin
999
1130
 
1000
1131
  @staticmethod
1001
1132
  def filter(
@@ -1095,7 +1226,7 @@ class JacBasics:
1095
1226
  @staticmethod
1096
1227
  def root() -> Root:
1097
1228
  """Jac's root getter."""
1098
- return JacMachineInterface.py_get_jac_machine().get_root()
1229
+ return JacMachine.get_context().get_root()
1099
1230
 
1100
1231
  @staticmethod
1101
1232
  def build_edge(
@@ -1209,6 +1340,40 @@ class JacBasics:
1209
1340
  "mtllm is not installed. Please install it with `pip install mtllm` and run `jac clean`."
1210
1341
  )
1211
1342
 
1343
+ @staticmethod
1344
+ def gen_llm_call_override(
1345
+ _pass: PyastGenPass, node: ast.FuncCall
1346
+ ) -> list[ast3.AST]:
1347
+ """Generate python ast nodes for llm function body override syntax.
1348
+
1349
+ example:
1350
+ foo() by llm();
1351
+ """
1352
+ _pass.log_warning(
1353
+ "MT-LLM is not installed. Please install it with `pip install mtllm`."
1354
+ )
1355
+ return [
1356
+ _pass.sync(
1357
+ ast3.Raise(
1358
+ _pass.sync(
1359
+ ast3.Call(
1360
+ func=_pass.sync(
1361
+ ast3.Name(id="ImportError", ctx=ast3.Load())
1362
+ ),
1363
+ args=[
1364
+ _pass.sync(
1365
+ ast3.Constant(
1366
+ value="mtllm is not installed. Please install it with `pip install mtllm` and run `jac clean`." # noqa: E501
1367
+ )
1368
+ )
1369
+ ],
1370
+ keywords=[],
1371
+ )
1372
+ )
1373
+ )
1374
+ )
1375
+ ]
1376
+
1212
1377
  @staticmethod
1213
1378
  def gen_llm_body(_pass: PyastGenPass, node: ast.Ability) -> list[ast3.AST]:
1214
1379
  """Generate the by LLM body."""
@@ -1346,32 +1511,49 @@ class JacBasics:
1346
1511
  "exclude_info": [],
1347
1512
  }
1348
1513
 
1514
+ @staticmethod
1515
+ def get_semstr_type(
1516
+ file_loc: str, scope: str, attr: str, return_semstr: bool
1517
+ ) -> Optional[str]:
1518
+ """Jac's get_semstr_type feature."""
1519
+
1520
+ @staticmethod
1521
+ def obj_scope(file_loc: str, attr: str) -> str:
1522
+ """Jac's gather_scope feature."""
1523
+ return ""
1524
+
1525
+ @staticmethod
1526
+ def get_sem_type(file_loc: str, attr: str) -> tuple[str | None, str | None]:
1527
+ """Jac's get_semstr_type implementation."""
1528
+ return None, None
1529
+
1349
1530
 
1350
1531
  class JacUtils:
1351
1532
  """Jac Machine Utilities."""
1352
1533
 
1353
1534
  @staticmethod
1354
- def attach_program(mach: JacMachine, jac_program: JacProgram) -> None:
1535
+ def attach_program(jac_program: JacProgram) -> None:
1355
1536
  """Attach a JacProgram to the machine."""
1356
- mach.program = jac_program
1537
+ JacMachine.program = jac_program
1357
1538
 
1358
1539
  @staticmethod
1359
1540
  def load_module(
1360
- mach: JacMachine, module_name: str, module: types.ModuleType
1541
+ module_name: str, module: types.ModuleType, force: bool = False
1361
1542
  ) -> None:
1362
1543
  """Load a module into the machine."""
1363
- mach.loaded_modules[module_name] = module
1364
- sys.modules[module_name] = module # TODO: May want to nuke this one day
1544
+ if module_name not in JacMachine.loaded_modules or force:
1545
+ JacMachine.loaded_modules[module_name] = module
1546
+ sys.modules[module_name] = module # TODO: May want to nuke this one day
1365
1547
 
1366
1548
  @staticmethod
1367
- def list_modules(mach: JacMachine) -> list[str]:
1549
+ def list_modules() -> list[str]:
1368
1550
  """List all loaded modules."""
1369
- return list(mach.loaded_modules.keys())
1551
+ return list(JacMachine.loaded_modules.keys())
1370
1552
 
1371
1553
  @staticmethod
1372
- def list_walkers(mach: JacMachine, module_name: str) -> list[str]:
1554
+ def list_walkers(module_name: str) -> list[str]:
1373
1555
  """List all walkers in a specific module."""
1374
- module = mach.loaded_modules.get(module_name)
1556
+ module = JacMachine.loaded_modules.get(module_name)
1375
1557
  if module:
1376
1558
  walkers = []
1377
1559
  for name, obj in inspect.getmembers(module):
@@ -1381,9 +1563,9 @@ class JacUtils:
1381
1563
  return []
1382
1564
 
1383
1565
  @staticmethod
1384
- def list_nodes(mach: JacMachine, module_name: str) -> list[str]:
1566
+ def list_nodes(module_name: str) -> list[str]:
1385
1567
  """List all nodes in a specific module."""
1386
- module = mach.loaded_modules.get(module_name)
1568
+ module = JacMachine.loaded_modules.get(module_name)
1387
1569
  if module:
1388
1570
  nodes = []
1389
1571
  for name, obj in inspect.getmembers(module):
@@ -1393,9 +1575,9 @@ class JacUtils:
1393
1575
  return []
1394
1576
 
1395
1577
  @staticmethod
1396
- def list_edges(mach: JacMachine, module_name: str) -> list[str]:
1578
+ def list_edges(module_name: str) -> list[str]:
1397
1579
  """List all edges in a specific module."""
1398
- module = mach.loaded_modules.get(module_name)
1580
+ module = JacMachine.loaded_modules.get(module_name)
1399
1581
  if module:
1400
1582
  nodes = []
1401
1583
  for name, obj in inspect.getmembers(module):
@@ -1406,7 +1588,6 @@ class JacUtils:
1406
1588
 
1407
1589
  @staticmethod
1408
1590
  def create_archetype_from_source(
1409
- mach: JacMachine,
1410
1591
  source_code: str,
1411
1592
  module_name: Optional[str] = None,
1412
1593
  base_path: Optional[str] = None,
@@ -1417,12 +1598,12 @@ class JacUtils:
1417
1598
  from jaclang.runtimelib.importer import JacImporter, ImportPathSpec
1418
1599
 
1419
1600
  if not base_path:
1420
- base_path = mach.base_path or os.getcwd()
1601
+ base_path = JacMachine.base_path_dir or os.getcwd()
1421
1602
 
1422
1603
  if base_path and not os.path.exists(base_path):
1423
1604
  os.makedirs(base_path)
1424
1605
  if not module_name:
1425
- module_name = f"_dynamic_module_{len(mach.loaded_modules)}"
1606
+ module_name = f"_dynamic_module_{len(JacMachine.loaded_modules)}"
1426
1607
  with tempfile.NamedTemporaryFile(
1427
1608
  mode="w",
1428
1609
  suffix=".jac",
@@ -1434,7 +1615,7 @@ class JacUtils:
1434
1615
  tmp_file.write(source_code)
1435
1616
 
1436
1617
  try:
1437
- importer = JacImporter(mach)
1618
+ importer = JacImporter()
1438
1619
  tmp_file_basename = os.path.basename(tmp_file_path)
1439
1620
  tmp_module_name, _ = os.path.splitext(tmp_file_basename)
1440
1621
 
@@ -1451,7 +1632,7 @@ class JacUtils:
1451
1632
  import_result = importer.run_import(spec, reload=False)
1452
1633
  module = import_result.ret_mod
1453
1634
 
1454
- mach.loaded_modules[module_name] = module
1635
+ JacMachine.loaded_modules[module_name] = module
1455
1636
  return module
1456
1637
  except Exception as e:
1457
1638
  logger.error(f"Error importing dynamic module '{module_name}': {e}")
@@ -1462,20 +1643,19 @@ class JacUtils:
1462
1643
 
1463
1644
  @staticmethod
1464
1645
  def update_walker(
1465
- mach: JacMachine,
1466
1646
  module_name: str,
1467
1647
  items: Optional[dict[str, Union[str, Optional[str]]]],
1468
1648
  ) -> tuple[types.ModuleType, ...]:
1469
1649
  """Reimport the module."""
1470
1650
  from .importer import JacImporter, ImportPathSpec
1471
1651
 
1472
- if module_name in mach.loaded_modules:
1652
+ if module_name in JacMachine.loaded_modules:
1473
1653
  try:
1474
- old_module = mach.loaded_modules[module_name]
1475
- importer = JacImporter(mach)
1654
+ old_module = JacMachine.loaded_modules[module_name]
1655
+ importer = JacImporter()
1476
1656
  spec = ImportPathSpec(
1477
1657
  target=module_name,
1478
- base_path=mach.base_path,
1658
+ base_path=JacMachine.base_path_dir,
1479
1659
  absorb=False,
1480
1660
  mdl_alias=None,
1481
1661
  override_name=None,
@@ -1504,13 +1684,12 @@ class JacUtils:
1504
1684
 
1505
1685
  @staticmethod
1506
1686
  def spawn_node(
1507
- mach: JacMachine,
1508
1687
  node_name: str,
1509
1688
  attributes: Optional[dict] = None,
1510
1689
  module_name: str = "__main__",
1511
1690
  ) -> NodeArchetype:
1512
1691
  """Spawn a node instance of the given node_name with attributes."""
1513
- node_class = JacMachineInterface.get_archetype(mach, module_name, node_name)
1692
+ node_class = JacMachineInterface.get_archetype(module_name, node_name)
1514
1693
  if isinstance(node_class, type) and issubclass(node_class, NodeArchetype):
1515
1694
  if attributes is None:
1516
1695
  attributes = {}
@@ -1521,13 +1700,12 @@ class JacUtils:
1521
1700
 
1522
1701
  @staticmethod
1523
1702
  def spawn_walker(
1524
- mach: JacMachine,
1525
1703
  walker_name: str,
1526
1704
  attributes: Optional[dict] = None,
1527
1705
  module_name: str = "__main__",
1528
1706
  ) -> WalkerArchetype:
1529
1707
  """Spawn a walker instance of the given walker_name."""
1530
- walker_class = JacMachineInterface.get_archetype(mach, module_name, walker_name)
1708
+ walker_class = JacMachineInterface.get_archetype(module_name, walker_name)
1531
1709
  if isinstance(walker_class, type) and issubclass(walker_class, WalkerArchetype):
1532
1710
  if attributes is None:
1533
1711
  attributes = {}
@@ -1537,27 +1715,17 @@ class JacUtils:
1537
1715
  raise ValueError(f"Walker {walker_name} not found.")
1538
1716
 
1539
1717
  @staticmethod
1540
- def get_archetype(
1541
- mach: JacMachine, module_name: str, archetype_name: str
1542
- ) -> Optional[Archetype]:
1718
+ def get_archetype(module_name: str, archetype_name: str) -> Optional[Archetype]:
1543
1719
  """Retrieve an archetype class from a module."""
1544
- module = mach.loaded_modules.get(module_name)
1720
+ module = JacMachine.loaded_modules.get(module_name)
1545
1721
  if module:
1546
1722
  return getattr(module, archetype_name, None)
1547
1723
  return None
1548
1724
 
1549
- @staticmethod
1550
- def await_obj(obj: Any) -> Any: # noqa: ANN401
1551
- """Await an object if it is a coroutine or async or future function."""
1552
- machine = JacMachineInterface.py_get_jac_machine()
1553
- _event_loop = machine._event_loop
1554
- return _event_loop.run_until_complete(obj)
1555
-
1556
1725
  @staticmethod
1557
1726
  def thread_run(func: Callable, *args: object) -> Future: # noqa: ANN401
1558
1727
  """Run a function in a thread."""
1559
- machine = JacMachine.py_get_jac_machine()
1560
- _executor = machine.pool
1728
+ _executor = JacMachine.pool
1561
1729
  return _executor.submit(func, *args)
1562
1730
 
1563
1731
  @staticmethod
@@ -1583,80 +1751,35 @@ class JacMachineInterface(
1583
1751
  class JacMachine(JacMachineInterface):
1584
1752
  """Jac Machine State."""
1585
1753
 
1586
- def __init__(
1587
- self,
1588
- base_path: str = "",
1589
- session: Optional[str] = None,
1590
- root: Optional[str] = None,
1591
- ) -> None:
1592
- """Initialize JacMachine."""
1593
- self.loaded_modules: dict[str, types.ModuleType] = {}
1594
- if not base_path:
1595
- base_path = os.getcwd()
1596
- # Ensure the base_path is a list rather than a string
1597
- self.base_path = base_path
1598
- self.base_path_dir = (
1599
- os.path.dirname(base_path)
1600
- if not os.path.isdir(base_path)
1601
- else os.path.abspath(base_path)
1602
- )
1603
- self.program: JacProgram = JacProgram()
1604
- self.pool = ThreadPoolExecutor()
1605
- self._event_loop = asyncio.new_event_loop()
1606
- self.mem: Memory = ShelfStorage(session)
1607
- self.reports: list[Any] = []
1608
- sr_arch = Root()
1609
- sr_anch = sr_arch.__jac__
1610
- sr_anch.id = UUID(Con.SUPER_ROOT_UUID)
1611
- sr_anch.persistent = False
1612
- self.system_root = sr_anch
1613
- self.custom: Any = MISSING
1614
- if not isinstance(
1615
- system_root := self.mem.find_by_id(UUID(Con.SUPER_ROOT_UUID)), NodeAnchor
1616
- ):
1617
- system_root = cast(NodeAnchor, Root().__jac__) # type: ignore[attr-defined]
1618
- system_root.id = UUID(Con.SUPER_ROOT_UUID)
1619
- self.mem.set(system_root.id, system_root)
1620
-
1621
- self.system_root = system_root
1622
-
1623
- self.entry_node = self.root_state = self.init_anchor(root, self.system_root)
1624
-
1625
- def init_anchor(
1626
- self,
1627
- anchor_id: str | None,
1628
- default: NodeAnchor,
1629
- ) -> NodeAnchor:
1630
- """Load initial anchors."""
1631
- if anchor_id:
1632
- if isinstance(anchor := self.mem.find_by_id(UUID(anchor_id)), NodeAnchor):
1633
- return anchor
1634
- raise ValueError(f"Invalid anchor id {anchor_id} !")
1635
- return default
1636
-
1637
- def set_entry_node(self, entry_node: str | None) -> None:
1638
- """Override entry."""
1639
- self.entry_node = self.init_anchor(entry_node, self.root_state)
1640
-
1641
- def close(self) -> None:
1642
- """Close current ExecutionContext."""
1643
- call_jac_func_with_machine(mach=self, func=self.mem.close)
1644
-
1645
- def get_root(self) -> Root:
1646
- """Get current root."""
1647
- return cast(Root, self.root_state.archetype)
1754
+ loaded_modules: dict[str, types.ModuleType] = {}
1755
+ base_path_dir: str = os.getcwd()
1756
+ program: JacProgram = JacProgram()
1757
+ pool: ThreadPoolExecutor = ThreadPoolExecutor()
1758
+ exec_ctx: ExecutionContext = ExecutionContext()
1648
1759
 
1649
- def global_system_root(self) -> NodeAnchor:
1650
- """Get global system root."""
1651
- return self.system_root
1760
+ @staticmethod
1761
+ def set_base_path(base_path: str) -> None:
1762
+ """Set the base path for the machine."""
1763
+ JacMachine.reset_machine()
1764
+ JacMachine.base_path_dir = (
1765
+ base_path if os.path.isdir(base_path) else os.path.dirname(base_path)
1766
+ )
1652
1767
 
1768
+ @staticmethod
1769
+ def set_context(context: ExecutionContext) -> None:
1770
+ """Set the context for the machine."""
1771
+ JacMachine.exec_ctx = context
1653
1772
 
1654
- def call_jac_func_with_machine( # TODO: remove this
1655
- mach: JacMachine, func: Callable, *args: Any # noqa: ANN401
1656
- ) -> Any: # noqa: ANN401
1657
- """Call Jac function with machine context in local."""
1658
- __jac_mach__ = mach # noqa: F841
1659
- return func(*args)
1773
+ @staticmethod
1774
+ def reset_machine() -> None:
1775
+ """Reset the machine."""
1776
+ # for i in JacMachine.loaded_modules.values():
1777
+ # sys.modules.pop(i.__name__, None)
1778
+ JacMachine.loaded_modules.clear()
1779
+ JacMachine.base_path_dir = os.getcwd()
1780
+ JacMachine.program = JacProgram()
1781
+ JacMachine.pool = ThreadPoolExecutor()
1782
+ JacMachine.exec_ctx = ExecutionContext()
1660
1783
 
1661
1784
 
1662
1785
  def generate_plugin_helpers(