jaclang 0.8.8__py3-none-any.whl → 0.8.10__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 (114) hide show
  1. jaclang/cli/cli.py +194 -10
  2. jaclang/cli/cmdreg.py +144 -8
  3. jaclang/compiler/__init__.py +6 -1
  4. jaclang/compiler/codeinfo.py +16 -1
  5. jaclang/compiler/constant.py +33 -8
  6. jaclang/compiler/jac.lark +154 -62
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +656 -149
  9. jaclang/compiler/passes/__init__.py +2 -1
  10. jaclang/compiler/passes/ast_gen/__init__.py +5 -0
  11. jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
  12. jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
  13. jaclang/compiler/passes/ecmascript/__init__.py +25 -0
  14. jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
  15. jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
  16. jaclang/compiler/passes/ecmascript/estree.py +972 -0
  17. jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
  18. jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
  19. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
  20. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
  21. jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
  22. jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
  23. jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
  24. jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
  25. jaclang/compiler/passes/main/__init__.py +0 -3
  26. jaclang/compiler/passes/main/annex_pass.py +23 -1
  27. jaclang/compiler/passes/main/def_use_pass.py +1 -0
  28. jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
  29. jaclang/compiler/passes/main/pyast_load_pass.py +48 -11
  30. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  31. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  32. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  34. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  35. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  36. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  37. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -3
  38. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  39. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  40. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
  41. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  42. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  43. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +219 -20
  44. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  45. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  46. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  47. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
  48. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  49. jaclang/compiler/passes/transform.py +9 -1
  50. jaclang/compiler/passes/uni_pass.py +5 -7
  51. jaclang/compiler/program.py +27 -26
  52. jaclang/compiler/tests/test_client_codegen.py +113 -0
  53. jaclang/compiler/tests/test_importer.py +12 -10
  54. jaclang/compiler/tests/test_parser.py +249 -3
  55. jaclang/compiler/type_system/type_evaluator.jac +1078 -0
  56. jaclang/compiler/type_system/type_utils.py +1 -1
  57. jaclang/compiler/type_system/types.py +6 -0
  58. jaclang/compiler/unitree.py +438 -82
  59. jaclang/langserve/engine.jac +224 -288
  60. jaclang/langserve/sem_manager.jac +12 -8
  61. jaclang/langserve/server.jac +48 -48
  62. jaclang/langserve/tests/fixtures/greet.py +17 -0
  63. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  64. jaclang/langserve/tests/fixtures/user.jac +15 -0
  65. jaclang/langserve/tests/test_server.py +66 -371
  66. jaclang/lib.py +17 -0
  67. jaclang/runtimelib/archetype.py +25 -25
  68. jaclang/runtimelib/client_bundle.py +169 -0
  69. jaclang/runtimelib/client_runtime.jac +586 -0
  70. jaclang/runtimelib/constructs.py +4 -2
  71. jaclang/runtimelib/machine.py +308 -139
  72. jaclang/runtimelib/meta_importer.py +111 -22
  73. jaclang/runtimelib/mtp.py +15 -0
  74. jaclang/runtimelib/server.py +1089 -0
  75. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  76. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  77. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  78. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  79. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  80. jaclang/runtimelib/tests/test_client_render.py +63 -0
  81. jaclang/runtimelib/tests/test_serve.py +1069 -0
  82. jaclang/settings.py +0 -3
  83. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  84. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  85. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  86. jaclang/tests/fixtures/iife_functions.jac +142 -0
  87. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  88. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  89. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  90. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  91. jaclang/tests/fixtures/py2jac_empty.py +0 -0
  92. jaclang/tests/fixtures/py_run.py +7 -5
  93. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  94. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  95. jaclang/tests/test_cli.py +134 -18
  96. jaclang/tests/test_language.py +120 -32
  97. jaclang/tests/test_reference.py +20 -3
  98. jaclang/utils/NonGPT.py +375 -0
  99. jaclang/utils/helpers.py +64 -20
  100. jaclang/utils/lang_tools.py +31 -4
  101. jaclang/utils/tests/test_lang_tools.py +5 -16
  102. jaclang/utils/treeprinter.py +8 -3
  103. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  104. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
  105. jaclang/compiler/passes/main/binder_pass.py +0 -594
  106. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  107. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  108. jaclang/compiler/type_system/type_evaluator.py +0 -844
  109. jaclang/langserve/tests/session.jac +0 -294
  110. jaclang/langserve/tests/test_dev_server.py +0 -80
  111. jaclang/runtimelib/importer.py +0 -351
  112. jaclang/tests/test_typecheck.py +0 -542
  113. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  114. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
@@ -10,6 +10,7 @@ import sys
10
10
  import tempfile
11
11
  import types
12
12
  from collections import OrderedDict
13
+ from collections.abc import Mapping, Sequence
13
14
  from concurrent.futures import Future, ThreadPoolExecutor
14
15
  from dataclasses import MISSING, dataclass, field
15
16
  from functools import wraps
@@ -27,6 +28,7 @@ from typing import (
27
28
  TypeVar,
28
29
  Union,
29
30
  cast,
31
+ get_type_hints,
30
32
  )
31
33
  from uuid import UUID
32
34
 
@@ -34,10 +36,10 @@ from uuid import UUID
34
36
  from jaclang.compiler.constant import Constants as Con, EdgeDir, colors
35
37
  from jaclang.compiler.program import JacProgram
36
38
  from jaclang.runtimelib.archetype import (
37
- DataSpatialDestination,
38
- DataSpatialFunction,
39
- DataSpatialPath,
40
39
  GenericEdge as _GenericEdge,
40
+ ObjectSpatialDestination,
41
+ ObjectSpatialFunction,
42
+ ObjectSpatialPath,
41
43
  Root as _Root,
42
44
  )
43
45
  from jaclang.runtimelib.constructs import (
@@ -55,6 +57,7 @@ from jaclang.runtimelib.constructs import (
55
57
  WalkerArchetype,
56
58
  )
57
59
  from jaclang.runtimelib.memory import Memory, Shelf, ShelfStorage
60
+ from jaclang.runtimelib.mtp import MTIR
58
61
  from jaclang.runtimelib.utils import (
59
62
  all_issubclass,
60
63
  traverse_graph,
@@ -85,37 +88,31 @@ class ExecutionContext:
85
88
  self.mem: Memory = ShelfStorage(session)
86
89
  self.reports: list[Any] = []
87
90
  self.custom: Any = MISSING
88
- if not isinstance(
89
- system_root := self.mem.find_by_id(UUID(Con.SUPER_ROOT_UUID)), NodeAnchor
90
- ):
91
- system_root = cast(NodeAnchor, Root().__jac__) # type: ignore[attr-defined]
92
- system_root.id = UUID(Con.SUPER_ROOT_UUID)
93
- self.mem.set(system_root.id, system_root)
94
-
95
- self.system_root = system_root
96
-
97
- self.entry_node = self.root_state = self.init_anchor(root, self.system_root)
91
+ self.system_root = self.mem.find_by_id(UUID(Con.SUPER_ROOT_UUID))
92
+ if not isinstance(self.system_root, NodeAnchor):
93
+ self.system_root = cast(NodeAnchor, Root().__jac__)
94
+ self.system_root.id = UUID(Con.SUPER_ROOT_UUID)
95
+ self.mem.set(self.system_root.id, self.system_root)
96
+ self.entry_node = self.root_state = (
97
+ self._get_anchor(root) if root else self.system_root
98
+ )
98
99
 
99
- def init_anchor(
100
- self,
101
- anchor_id: str | None,
102
- default: NodeAnchor,
103
- ) -> NodeAnchor:
104
- """Load initial anchors."""
105
- if anchor_id:
106
- if isinstance(anchor := self.mem.find_by_id(UUID(anchor_id)), NodeAnchor):
107
- return anchor
100
+ def _get_anchor(self, anchor_id: str) -> NodeAnchor:
101
+ """Get anchor by ID or raise error."""
102
+ anchor = self.mem.find_by_id(UUID(anchor_id))
103
+ if not isinstance(anchor, NodeAnchor):
108
104
  raise ValueError(f"Invalid anchor id {anchor_id} !")
109
- return default
105
+ return anchor
110
106
 
111
107
  def set_entry_node(self, entry_node: str | None) -> None:
112
- """Override entry."""
113
- self.entry_node = self.init_anchor(entry_node, self.root_state)
108
+ """Override entry node."""
109
+ self.entry_node = (
110
+ self._get_anchor(entry_node) if entry_node else self.root_state
111
+ )
114
112
 
115
113
  def close(self) -> None:
116
114
  """Close current ExecutionContext."""
117
115
  self.mem.close()
118
- JacMachine.reset_machine()
119
116
 
120
117
  def get_root(self) -> Root:
121
118
  """Get current root."""
@@ -267,7 +264,7 @@ class JacNode:
267
264
 
268
265
  @staticmethod
269
266
  def get_edges(
270
- origin: list[NodeArchetype], destination: DataSpatialDestination
267
+ origin: list[NodeArchetype], destination: ObjectSpatialDestination
271
268
  ) -> list[EdgeArchetype]:
272
269
  """Get edges connected to this node."""
273
270
  edges: OrderedDict[EdgeAnchor, EdgeArchetype] = OrderedDict()
@@ -300,7 +297,7 @@ class JacNode:
300
297
  @staticmethod
301
298
  def get_edges_with_node(
302
299
  origin: list[NodeArchetype],
303
- destination: DataSpatialDestination,
300
+ destination: ObjectSpatialDestination,
304
301
  from_visit: bool = False,
305
302
  ) -> list[EdgeArchetype | NodeArchetype]:
306
303
  """Get edges connected to this node and the node."""
@@ -337,7 +334,7 @@ class JacNode:
337
334
 
338
335
  @staticmethod
339
336
  def edges_to_nodes(
340
- origin: list[NodeArchetype], destination: DataSpatialDestination
337
+ origin: list[NodeArchetype], destination: ObjectSpatialDestination
341
338
  ) -> list[NodeArchetype]:
342
339
  """Get set of nodes connected to this node."""
343
340
  nodes: OrderedDict[NodeAnchor, NodeArchetype] = OrderedDict()
@@ -424,36 +421,6 @@ class JacWalker:
424
421
  else:
425
422
  raise TypeError("Invalid walker object")
426
423
 
427
- @staticmethod
428
- def ignore(
429
- walker: WalkerArchetype,
430
- expr: (
431
- list[NodeArchetype | EdgeArchetype]
432
- | list[NodeArchetype]
433
- | list[EdgeArchetype]
434
- | NodeArchetype
435
- | EdgeArchetype
436
- ),
437
- ) -> bool: # noqa: ANN401
438
- """Jac's ignore stmt feature."""
439
- if isinstance(walker, WalkerArchetype):
440
- wanch = walker.__jac__
441
- before_len = len(wanch.ignores)
442
- for anchor in (
443
- (i.__jac__ for i in expr) if isinstance(expr, list) else [expr.__jac__]
444
- ):
445
- if anchor not in wanch.ignores:
446
- if isinstance(anchor, NodeAnchor):
447
- wanch.ignores.append(anchor)
448
- elif isinstance(anchor, EdgeAnchor):
449
- if target := anchor.target:
450
- wanch.ignores.append(target)
451
- else:
452
- raise ValueError("Edge has no target.")
453
- return len(wanch.ignores) > before_len
454
- else:
455
- raise TypeError("Invalid walker object")
456
-
457
424
  @staticmethod
458
425
  def spawn_call(
459
426
  walker: WalkerAnchor,
@@ -718,7 +685,7 @@ class JacClassReferences:
718
685
 
719
686
  TYPE_CHECKING: bool = TYPE_CHECKING
720
687
  EdgeDir: TypeAlias = EdgeDir
721
- DSFunc: TypeAlias = DataSpatialFunction
688
+ DSFunc: TypeAlias = ObjectSpatialFunction
722
689
 
723
690
  Obj: TypeAlias = Archetype
724
691
  Node: TypeAlias = NodeArchetype
@@ -728,7 +695,7 @@ class JacClassReferences:
728
695
  Root: TypeAlias = _Root
729
696
  GenericEdge: TypeAlias = _GenericEdge
730
697
 
731
- Path: TypeAlias = DataSpatialPath
698
+ OPath: TypeAlias = ObjectSpatialPath
732
699
 
733
700
 
734
701
  class JacBuiltin:
@@ -967,45 +934,163 @@ class JacBasics:
967
934
  target: str,
968
935
  base_path: str,
969
936
  absorb: bool = False,
970
- mdl_alias: Optional[str] = None,
971
937
  override_name: Optional[str] = None,
972
938
  items: Optional[dict[str, Union[str, Optional[str]]]] = None,
973
939
  reload_module: Optional[bool] = False,
974
940
  lng: Optional[str] = None,
975
941
  ) -> tuple[types.ModuleType, ...]:
976
- """Core Import Process."""
977
- from jaclang.runtimelib.importer import (
978
- ImportPathSpec,
979
- JacImporter,
980
- PythonImporter,
981
- )
942
+ """Import a Jac or Python module using Python's standard import machinery.
943
+
944
+ This function bridges Jac's import semantics with Python's import system,
945
+ leveraging importlib.import_module() which automatically invokes our
946
+ JacMetaImporter (registered in sys.meta_path).
947
+
948
+ Args:
949
+ target: Module name to import (e.g., "foo.bar" or ".relative")
950
+ base_path: Base directory for resolving the module
951
+ absorb: If True with items, return module instead of items
952
+ override_name: Special handling for "__main__" execution context
953
+ items: Specific items to import from module (like "from X import Y")
954
+ reload_module: Force reload even if already in sys.modules
955
+ lng: Language hint ("jac", "py", etc.) - auto-detected if None
956
+
957
+ Returns:
958
+ Tuple of imported module(s) or item(s)
959
+
960
+ Examples:
961
+ # Import entire module
962
+ (mod,) = jac_import("mymod", "/path/to/base")
963
+
964
+ # Import specific items
965
+ (func, cls) = jac_import("mymod", "/path", items={"myfunc": None, "MyClass": None})
966
+
967
+ # Run as __main__
968
+ jac_import("mymod", "/path", override_name="__main__")
969
+ """
970
+ import importlib
971
+ import importlib.util
982
972
 
983
973
  if lng is None:
984
974
  lng = infer_language(target, base_path)
985
975
 
986
- spec = ImportPathSpec(
987
- target,
988
- base_path,
989
- absorb,
990
- mdl_alias,
991
- override_name,
992
- lng,
993
- items,
994
- )
995
-
996
976
  if not JacMachine.program:
997
977
  JacMachineInterface.attach_program(JacProgram())
998
978
 
999
- if lng == "py":
1000
- import_result = PythonImporter().run_import(spec)
979
+ # Compute the module name
980
+ # Convert relative imports (e.g., ".foo") to absolute
981
+ if target.startswith("."):
982
+ # Relative import - need to resolve against base_path
983
+ caller_dir = (
984
+ base_path if os.path.isdir(base_path) else os.path.dirname(base_path)
985
+ )
986
+ chomp_target = target
987
+ while chomp_target.startswith("."):
988
+ if len(chomp_target) > 1 and chomp_target[1] == ".":
989
+ caller_dir = os.path.dirname(caller_dir)
990
+ chomp_target = chomp_target[1:]
991
+ else:
992
+ chomp_target = chomp_target[1:]
993
+ break
994
+ module_name = chomp_target
1001
995
  else:
1002
- import_result = JacImporter().run_import(spec, reload_module)
996
+ module_name = target
1003
997
 
1004
- return (
1005
- (import_result.ret_mod,)
1006
- if absorb or not items
1007
- else tuple(import_result.ret_items)
998
+ # Add base_path to sys.path for import resolution
999
+ # The meta importer uses this for finding modules
1000
+ caller_dir = (
1001
+ base_path if os.path.isdir(base_path) else os.path.dirname(base_path)
1008
1002
  )
1003
+ original_path = None
1004
+
1005
+ # Only modify sys.path if the directory isn't already there
1006
+ if caller_dir and caller_dir not in sys.path:
1007
+ original_path = sys.path.copy()
1008
+ sys.path.insert(0, caller_dir)
1009
+
1010
+ try:
1011
+ # Handle special case: override_name="__main__" means run as script
1012
+ if override_name == "__main__":
1013
+ # For __main__ execution, we use spec_from_file_location
1014
+ from jaclang.runtimelib.meta_importer import JacMetaImporter
1015
+
1016
+ finder = JacMetaImporter()
1017
+ # Pass None as path for top-level imports (e.g., "micro.simple_walk")
1018
+ # This ensures the meta importer searches sys.path correctly
1019
+ spec = finder.find_spec(module_name, None)
1020
+ if (
1021
+ spec
1022
+ and spec.origin
1023
+ and spec.origin.endswith(".jac")
1024
+ and lng == "py"
1025
+ ):
1026
+ spec = None
1027
+
1028
+ if (not spec or not spec.origin) and lng == "py":
1029
+ file_path = os.path.join(caller_dir, f"{module_name}.py")
1030
+ if os.path.isfile(file_path):
1031
+ spec_name = (
1032
+ "__main__" if override_name == "__main__" else module_name
1033
+ )
1034
+ spec = importlib.util.spec_from_file_location(
1035
+ spec_name, file_path
1036
+ )
1037
+
1038
+ if not spec or not spec.origin:
1039
+ raise ImportError(f"Cannot find module {module_name}")
1040
+
1041
+ # Create or get __main__ module
1042
+ if "__main__" in sys.modules and not reload_module:
1043
+ module = sys.modules["__main__"]
1044
+ # Clear the module's dict except for special attributes
1045
+ to_keep = {
1046
+ k: v for k, v in module.__dict__.items() if k.startswith("__")
1047
+ }
1048
+ # module.__dict__.clear()
1049
+ module.__dict__.update(to_keep)
1050
+ else:
1051
+ module = types.ModuleType("__main__")
1052
+ sys.modules["__main__"] = module
1053
+
1054
+ # Set module attributes
1055
+ module.__file__ = spec.origin
1056
+ module.__name__ = "__main__"
1057
+ module.__spec__ = spec
1058
+ if spec.submodule_search_locations:
1059
+ module.__path__ = spec.submodule_search_locations
1060
+
1061
+ # Register in JacMachine
1062
+ JacMachineInterface.load_module("__main__", module)
1063
+
1064
+ # Execute the module
1065
+ if spec.loader:
1066
+ spec.loader.exec_module(module)
1067
+ elif reload_module and module_name in sys.modules:
1068
+ # Handle reload case
1069
+ module = importlib.reload(sys.modules[module_name])
1070
+ else:
1071
+ # Use Python's standard import machinery
1072
+ # This will invoke JacMetaImporter.find_spec() and exec_module()
1073
+ module = importlib.import_module(module_name)
1074
+
1075
+ # Handle selective item imports
1076
+ if items:
1077
+ imported_items = []
1078
+ for item_name, _ in items.items():
1079
+ if hasattr(module, item_name):
1080
+ item = getattr(module, item_name)
1081
+ imported_items.append(item)
1082
+ else:
1083
+ raise ImportError(
1084
+ f"Cannot import name '{item_name}' from '{module_name}'"
1085
+ )
1086
+ return tuple(imported_items) if not absorb else (module,)
1087
+
1088
+ return (module,)
1089
+
1090
+ finally:
1091
+ # Restore original sys.path if we modified it
1092
+ if original_path is not None:
1093
+ sys.path[:] = original_path
1009
1094
 
1010
1095
  @staticmethod
1011
1096
  def jac_test(test_fun: Callable) -> Callable:
@@ -1021,6 +1106,30 @@ class JacBasics:
1021
1106
 
1022
1107
  return test_deco
1023
1108
 
1109
+ @staticmethod
1110
+ def jsx(
1111
+ tag: object,
1112
+ attributes: Mapping[str, object] | None = None,
1113
+ children: Sequence[object] | None = None,
1114
+ ) -> dict[str, object]:
1115
+ """JSX interface for creating elements.
1116
+
1117
+ Args:
1118
+ tag: Element tag (string for HTML elements, callable for components)
1119
+ attributes: Element attributes/props
1120
+ children: Child elements
1121
+
1122
+ Returns:
1123
+ JSX element representation (implementation-defined)
1124
+ """
1125
+ props: dict[str, object] = dict(attributes) if attributes else {}
1126
+ child_list = list(children) if children else []
1127
+ return {
1128
+ "tag": tag,
1129
+ "props": props,
1130
+ "children": child_list,
1131
+ }
1132
+
1024
1133
  @staticmethod
1025
1134
  def run_test(
1026
1135
  filepath: str,
@@ -1096,23 +1205,24 @@ class JacBasics:
1096
1205
  return field(init=init)
1097
1206
 
1098
1207
  @staticmethod
1099
- def report(expr: Any, custom: bool = False) -> None: # noqa: ANN401
1208
+ def log_report(expr: Any, custom: bool = False) -> None: # noqa: ANN401
1100
1209
  """Jac's report stmt feature."""
1101
1210
  ctx = JacMachineInterface.get_context()
1102
1211
  if custom:
1103
1212
  ctx.custom = expr
1104
1213
  else:
1214
+ print(expr)
1105
1215
  ctx.reports.append(expr)
1106
1216
 
1107
1217
  @staticmethod
1108
1218
  def refs(
1109
- path: DataSpatialPath | NodeArchetype | list[NodeArchetype],
1219
+ path: ObjectSpatialPath | NodeArchetype | list[NodeArchetype],
1110
1220
  ) -> (
1111
1221
  list[NodeArchetype] | list[EdgeArchetype] | list[NodeArchetype | EdgeArchetype]
1112
1222
  ):
1113
1223
  """Jac's apply_dir stmt feature."""
1114
- if not isinstance(path, DataSpatialPath):
1115
- path = DataSpatialPath(path, [DataSpatialDestination(EdgeDir.OUT)])
1224
+ if not isinstance(path, ObjectSpatialPath):
1225
+ path = ObjectSpatialPath(path, [ObjectSpatialDestination(EdgeDir.OUT)])
1116
1226
 
1117
1227
  origin = path.origin
1118
1228
 
@@ -1134,13 +1244,13 @@ class JacBasics:
1134
1244
 
1135
1245
  @staticmethod
1136
1246
  async def arefs(
1137
- path: DataSpatialPath | NodeArchetype | list[NodeArchetype],
1247
+ path: ObjectSpatialPath | NodeArchetype | list[NodeArchetype],
1138
1248
  ) -> None:
1139
1249
  """Jac's apply_dir stmt feature."""
1140
1250
  pass
1141
1251
 
1142
1252
  @staticmethod
1143
- def filter(
1253
+ def filter_on(
1144
1254
  items: list[Archetype],
1145
1255
  func: Callable[[Archetype], bool],
1146
1256
  ) -> list[Archetype]:
@@ -1226,7 +1336,7 @@ class JacBasics:
1226
1336
  return disconnect_occurred
1227
1337
 
1228
1338
  @staticmethod
1229
- def assign(target: list[T], attr_val: tuple[tuple[str], tuple[Any]]) -> list[T]:
1339
+ def assign_all(target: list[T], attr_val: tuple[tuple[str], tuple[Any]]) -> list[T]:
1230
1340
  """Jac's assign comprehension feature."""
1231
1341
  for obj in target:
1232
1342
  attrs, values = attr_val
@@ -1328,17 +1438,28 @@ class JacBasics:
1328
1438
  JacMachineInterface.get_context().mem.remove(anchor.id)
1329
1439
 
1330
1440
  @staticmethod
1331
- def entry(func: Callable) -> Callable:
1441
+ def on_entry(func: Callable) -> Callable:
1332
1442
  """Mark a method as jac entry with this decorator."""
1333
1443
  setattr(func, "__jac_entry", None) # noqa:B010
1334
1444
  return func
1335
1445
 
1336
1446
  @staticmethod
1337
- def exit(func: Callable) -> Callable:
1447
+ def on_exit(func: Callable) -> Callable:
1338
1448
  """Mark a method as jac exit with this decorator."""
1339
1449
  setattr(func, "__jac_exit", None) # noqa:B010
1340
1450
  return func
1341
1451
 
1452
+
1453
+ class JacByLLM:
1454
+ """Jac byLLM integration."""
1455
+
1456
+ @staticmethod
1457
+ def get_mtir(
1458
+ caller: Callable, args: dict[int | str, object], call_params: dict[str, object]
1459
+ ) -> MTIR:
1460
+ """Get byLLM library."""
1461
+ return MTIR(caller=caller, args=args, call_params=call_params)
1462
+
1342
1463
  @staticmethod
1343
1464
  def sem(semstr: str, inner_semstr: dict[str, str]) -> Callable:
1344
1465
  """Attach the semstring to the given object."""
@@ -1351,11 +1472,47 @@ class JacBasics:
1351
1472
  return decorator
1352
1473
 
1353
1474
  @staticmethod
1354
- def call_llm(model: object, mtir: object) -> Any: # noqa: ANN401
1475
+ def call_llm(model: object, mtir: MTIR) -> Any: # noqa: ANN401
1355
1476
  """Call the LLM model."""
1356
- raise ImportError(
1357
- "byLLM is not installed. Please install it with `pip install byllm` and run `jac clean`."
1358
- )
1477
+ from jaclang.utils.NonGPT import random_value_for_type
1478
+
1479
+ try:
1480
+ type_hints = get_type_hints(
1481
+ mtir.caller,
1482
+ globalns=getattr(mtir.caller, "__globals__", {}),
1483
+ localns=None,
1484
+ include_extras=True,
1485
+ )
1486
+ except Exception:
1487
+ type_hints = getattr(mtir.caller, "__annotations__", {})
1488
+ return_type = type_hints.get("return", Any)
1489
+
1490
+ # Generate and return a random value matching the return type
1491
+ return random_value_for_type(return_type)
1492
+
1493
+ @staticmethod
1494
+ def by(model: object) -> Callable:
1495
+ """Python library mode decorator for Jac's by llm() syntax."""
1496
+
1497
+ def _decorator(caller: Callable) -> Callable:
1498
+ def _wrapped_caller(*args: object, **kwargs: object) -> object:
1499
+ invoke_args: dict[int | str, object] = {}
1500
+ for i, arg in enumerate(args):
1501
+ invoke_args[i] = arg
1502
+ for key, value in kwargs.items():
1503
+ invoke_args[key] = value
1504
+ mtir = JacMachine.get_mtir(
1505
+ caller=caller,
1506
+ args=invoke_args,
1507
+ call_params=(
1508
+ model.call_params if hasattr(model, "call_params") else {}
1509
+ ),
1510
+ )
1511
+ return JacMachine.call_llm(model, mtir)
1512
+
1513
+ return _wrapped_caller
1514
+
1515
+ return _decorator
1359
1516
 
1360
1517
 
1361
1518
  class JacUtils:
@@ -1387,7 +1544,11 @@ class JacUtils:
1387
1544
  if module:
1388
1545
  walkers = []
1389
1546
  for name, obj in inspect.getmembers(module):
1390
- if isinstance(obj, type) and issubclass(obj, WalkerArchetype):
1547
+ if (
1548
+ isinstance(obj, type)
1549
+ and issubclass(obj, WalkerArchetype)
1550
+ and obj.__module__ == module_name
1551
+ ):
1391
1552
  walkers.append(name)
1392
1553
  return walkers
1393
1554
  return []
@@ -1399,7 +1560,11 @@ class JacUtils:
1399
1560
  if module:
1400
1561
  nodes = []
1401
1562
  for name, obj in inspect.getmembers(module):
1402
- if isinstance(obj, type) and issubclass(obj, NodeArchetype):
1563
+ if (
1564
+ isinstance(obj, type)
1565
+ and issubclass(obj, NodeArchetype)
1566
+ and obj.__module__ == module_name
1567
+ ):
1403
1568
  nodes.append(name)
1404
1569
  return nodes
1405
1570
  return []
@@ -1409,11 +1574,15 @@ class JacUtils:
1409
1574
  """List all edges in a specific module."""
1410
1575
  module = JacMachine.loaded_modules.get(module_name)
1411
1576
  if module:
1412
- nodes = []
1577
+ edges = []
1413
1578
  for name, obj in inspect.getmembers(module):
1414
- if isinstance(obj, type) and issubclass(obj, EdgeArchetype):
1415
- nodes.append(name)
1416
- return nodes
1579
+ if (
1580
+ isinstance(obj, type)
1581
+ and issubclass(obj, EdgeArchetype)
1582
+ and obj.__module__ == module_name
1583
+ ):
1584
+ edges.append(name)
1585
+ return edges
1417
1586
  return []
1418
1587
 
1419
1588
  @staticmethod
@@ -1424,9 +1593,11 @@ class JacUtils:
1424
1593
  cachable: bool = False,
1425
1594
  keep_temporary_files: bool = False,
1426
1595
  ) -> Optional[types.ModuleType]:
1427
- """Dynamically creates archetypes (nodes, walkers, etc.) from Jac source code."""
1428
- from jaclang.runtimelib.importer import JacImporter, ImportPathSpec
1596
+ """Dynamically creates archetypes (nodes, walkers, etc.) from Jac source code.
1429
1597
 
1598
+ This leverages Python's standard import machinery via jac_import(),
1599
+ which will automatically invoke JacMetaImporter.
1600
+ """
1430
1601
  if not base_path:
1431
1602
  base_path = JacMachine.base_path_dir or os.getcwd()
1432
1603
 
@@ -1434,6 +1605,7 @@ class JacUtils:
1434
1605
  os.makedirs(base_path)
1435
1606
  if not module_name:
1436
1607
  module_name = f"_dynamic_module_{len(JacMachine.loaded_modules)}"
1608
+
1437
1609
  with tempfile.NamedTemporaryFile(
1438
1610
  mode="w",
1439
1611
  suffix=".jac",
@@ -1445,24 +1617,20 @@ class JacUtils:
1445
1617
  tmp_file.write(source_code)
1446
1618
 
1447
1619
  try:
1448
- importer = JacImporter()
1449
1620
  tmp_file_basename = os.path.basename(tmp_file_path)
1450
1621
  tmp_module_name, _ = os.path.splitext(tmp_file_basename)
1451
1622
 
1452
- spec = ImportPathSpec(
1623
+ # Use simplified jac_import which delegates to importlib
1624
+ result = JacMachineInterface.jac_import(
1453
1625
  target=tmp_module_name,
1454
1626
  base_path=base_path,
1455
- absorb=False,
1456
- mdl_alias=None,
1457
1627
  override_name=module_name,
1458
1628
  lng="jac",
1459
- items=None,
1460
1629
  )
1630
+ module = result[0] if result else None
1461
1631
 
1462
- import_result = importer.run_import(spec, reload=False)
1463
- module = import_result.ret_mod
1464
-
1465
- JacMachine.loaded_modules[module_name] = module
1632
+ if module:
1633
+ JacMachine.loaded_modules[module_name] = module
1466
1634
  return module
1467
1635
  except Exception as e:
1468
1636
  logger.error(f"Error importing dynamic module '{module_name}': {e}")
@@ -1476,36 +1644,31 @@ class JacUtils:
1476
1644
  module_name: str,
1477
1645
  items: Optional[dict[str, Union[str, Optional[str]]]],
1478
1646
  ) -> tuple[types.ModuleType, ...]:
1479
- """Reimport the module."""
1480
- from .importer import JacImporter, ImportPathSpec
1481
-
1647
+ """Reimport the module using Python's reload mechanism."""
1482
1648
  if module_name in JacMachine.loaded_modules:
1483
1649
  try:
1484
1650
  old_module = JacMachine.loaded_modules[module_name]
1485
- importer = JacImporter()
1486
- spec = ImportPathSpec(
1651
+
1652
+ # Use jac_import with reload flag
1653
+ result = JacMachineInterface.jac_import(
1487
1654
  target=module_name,
1488
1655
  base_path=JacMachine.base_path_dir,
1489
- absorb=False,
1490
- mdl_alias=None,
1491
- override_name=None,
1492
- lng="jac",
1493
1656
  items=items,
1657
+ reload_module=True,
1658
+ lng="jac",
1494
1659
  )
1495
- import_result = importer.run_import(spec, reload=True)
1496
- ret_items = []
1660
+
1661
+ # Update the old module's attributes if specific items were requested
1497
1662
  if items:
1498
- for item_name in items:
1499
- if hasattr(old_module, item_name):
1500
- new_attr = getattr(import_result.ret_mod, item_name, None)
1501
- if new_attr:
1502
- ret_items.append(new_attr)
1503
- setattr(
1504
- old_module,
1505
- item_name,
1506
- new_attr,
1507
- )
1508
- return (old_module,) if not items else tuple(ret_items)
1663
+ ret_items = []
1664
+ for idx, item_name in enumerate(items.keys()):
1665
+ if hasattr(old_module, item_name) and idx < len(result):
1666
+ new_attr = result[idx]
1667
+ ret_items.append(new_attr)
1668
+ setattr(old_module, item_name, new_attr)
1669
+ return tuple(ret_items)
1670
+
1671
+ return (old_module,)
1509
1672
  except Exception as e:
1510
1673
  logger.error(f"Failed to update module {module_name}: {e}")
1511
1674
  else:
@@ -1573,6 +1736,7 @@ class JacMachineInterface(
1573
1736
  JacBuiltin,
1574
1737
  JacCmd,
1575
1738
  JacBasics,
1739
+ JacByLLM,
1576
1740
  JacUtils,
1577
1741
  ):
1578
1742
  """Jac Feature."""
@@ -1710,10 +1874,15 @@ class JacMachine(JacMachineInterface):
1710
1874
  @staticmethod
1711
1875
  def reset_machine() -> None:
1712
1876
  """Reset the machine."""
1713
- # for i in JacMachine.loaded_modules.values():
1714
- # sys.modules.pop(i.__name__, None)
1877
+ # Remove Jac modules from sys.modules, but skip special module names
1878
+ # that Python relies on (like __main__, __mp_main__, etc.)
1879
+ special_modules = {"__main__", "__mp_main__", "builtins"}
1880
+ for i in JacMachine.loaded_modules.values():
1881
+ if i.__name__ not in special_modules:
1882
+ sys.modules.pop(i.__name__, None)
1715
1883
  JacMachine.loaded_modules.clear()
1716
1884
  JacMachine.base_path_dir = os.getcwd()
1717
1885
  JacMachine.program = JacProgram()
1718
1886
  JacMachine.pool = ThreadPoolExecutor()
1887
+ JacMachine.exec_ctx.mem.close()
1719
1888
  JacMachine.exec_ctx = ExecutionContext()