jaclang 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (77) hide show
  1. jaclang/__init__.py +6 -0
  2. jaclang/cli/cli.py +21 -50
  3. jaclang/compiler/codeinfo.py +0 -1
  4. jaclang/compiler/jac.lark +12 -10
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +18 -10
  7. jaclang/compiler/passes/main/__init__.py +0 -14
  8. jaclang/compiler/passes/main/annex_pass.py +2 -8
  9. jaclang/compiler/passes/main/cfg_build_pass.py +38 -12
  10. jaclang/compiler/passes/main/import_pass.py +3 -11
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +243 -592
  12. jaclang/compiler/passes/main/sym_tab_link_pass.py +2 -5
  13. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +2 -8
  14. jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
  15. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
  16. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
  17. jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
  18. jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
  19. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +237 -105
  20. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
  21. jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
  23. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
  25. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
  26. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +18 -3
  27. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
  28. jaclang/compiler/program.py +21 -60
  29. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +2 -8
  30. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +1 -5
  31. jaclang/compiler/tests/test_importer.py +10 -13
  32. jaclang/compiler/unitree.py +32 -16
  33. jaclang/langserve/__init__.jac +1 -1
  34. jaclang/langserve/engine.jac +113 -108
  35. jaclang/langserve/server.jac +17 -2
  36. jaclang/langserve/tests/server_test/test_lang_serve.py +138 -46
  37. jaclang/langserve/tests/server_test/utils.py +35 -9
  38. jaclang/langserve/tests/test_sem_tokens.py +1 -1
  39. jaclang/langserve/tests/test_server.py +3 -7
  40. jaclang/runtimelib/archetype.py +127 -5
  41. jaclang/runtimelib/importer.py +51 -94
  42. jaclang/runtimelib/machine.py +391 -268
  43. jaclang/runtimelib/meta_importer.py +86 -0
  44. jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
  45. jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
  46. jaclang/runtimelib/tests/test_jaseci.py +3 -1
  47. jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
  48. jaclang/tests/fixtures/async_ability.jac +43 -10
  49. jaclang/tests/fixtures/async_function.jac +18 -0
  50. jaclang/tests/fixtures/async_walker.jac +17 -12
  51. jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
  52. jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
  53. jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
  54. jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
  55. jaclang/tests/fixtures/deep/one_lev.jac +2 -2
  56. jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
  57. jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
  58. jaclang/tests/fixtures/foo.jac +14 -22
  59. jaclang/tests/fixtures/jac_from_py.py +1 -1
  60. jaclang/tests/fixtures/jp_importer.jac +6 -6
  61. jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
  62. jaclang/tests/fixtures/unicode_strings.jac +24 -0
  63. jaclang/tests/fixtures/walker_update.jac +5 -7
  64. jaclang/tests/test_language.py +138 -140
  65. jaclang/tests/test_reference.py +9 -4
  66. jaclang/tests/test_typecheck.py +13 -26
  67. jaclang/utils/lang_tools.py +7 -5
  68. jaclang/utils/module_resolver.py +23 -0
  69. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/METADATA +1 -1
  70. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/RECORD +72 -70
  71. jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
  72. jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
  73. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
  74. jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
  75. jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
  76. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/WHEEL +0 -0
  77. {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/entry_points.txt +0 -0
@@ -5,19 +5,16 @@ from __future__ import annotations
5
5
  import importlib
6
6
  import importlib.util
7
7
  import os
8
- import site
9
8
  import sys
10
9
  import types
11
10
  from os import getcwd, path
12
- from typing import Optional, TYPE_CHECKING, Union
11
+ from typing import Optional, Union
13
12
 
14
13
  from jaclang.runtimelib.machine import JacMachineInterface
15
14
  from jaclang.runtimelib.utils import sys_path_context
16
15
  from jaclang.utils.helpers import dump_traceback
17
16
  from jaclang.utils.log import logging
18
-
19
- if TYPE_CHECKING:
20
- from jaclang.runtimelib.machine import JacMachine
17
+ from jaclang.utils.module_resolver import get_jac_search_paths
21
18
 
22
19
  logger = logging.getLogger(__name__)
23
20
 
@@ -98,49 +95,8 @@ class ImportReturn:
98
95
  setattr(module, alias, item)
99
96
 
100
97
  for name, alias in items.items():
101
- try:
102
- item = getattr(module, name)
103
- handle_item_loading(item, alias)
104
- except AttributeError:
105
- found = False
106
- if lang == "jac":
107
- jac_file_path = (
108
- os.path.join(module.__path__[0], f"{name}.jac")
109
- if hasattr(module, "__path__")
110
- else module.__file__
111
- )
112
- if hasattr(module, "__path__"):
113
- init_jac = os.path.join(module.__path__[0], "__init__.jac")
114
- if os.path.isfile(init_jac):
115
- package_name = module.__name__
116
- init_mod = self.importer.jac_machine.loaded_modules.get(
117
- package_name,
118
- self.load_jac_mod_as_item(
119
- module=module,
120
- name="__init__",
121
- jac_file_path=init_jac,
122
- ),
123
- )
124
- # Attach all public names from __init__.jac to the package module
125
- for k, v in init_mod.__dict__.items():
126
- if not k.startswith("_"):
127
- setattr(module, k, v)
128
- if hasattr(module, name):
129
- item = getattr(module, name)
130
- handle_item_loading(item, alias)
131
- found = True
132
- if not found and jac_file_path and os.path.isfile(jac_file_path):
133
- item = self.load_jac_mod_as_item(
134
- module=module,
135
- name=name,
136
- jac_file_path=jac_file_path,
137
- )
138
- handle_item_loading(item, alias)
139
- else:
140
- if hasattr(module, "__path__"):
141
- full_module_name = f"{module.__name__}.{name}"
142
- item = importlib.import_module(full_module_name)
143
- handle_item_loading(item, alias)
98
+ item = getattr(module, name)
99
+ handle_item_loading(item, alias)
144
100
 
145
101
  def load_jac_mod_as_item(
146
102
  self,
@@ -149,6 +105,8 @@ class ImportReturn:
149
105
  jac_file_path: str,
150
106
  ) -> Optional[types.ModuleType]:
151
107
  """Load a single .jac file into the specified module component."""
108
+ from jaclang.runtimelib.machine import JacMachine
109
+
152
110
  try:
153
111
  package_name = (
154
112
  f"{module.__name__}.{name}"
@@ -156,7 +114,7 @@ class ImportReturn:
156
114
  else module.__name__
157
115
  )
158
116
  if isinstance(self.importer, JacImporter):
159
- new_module = self.importer.jac_machine.loaded_modules.get(
117
+ new_module = JacMachine.loaded_modules.get(
160
118
  package_name,
161
119
  self.importer.create_jac_py_module(
162
120
  self.importer.get_sys_mod_name(jac_file_path),
@@ -164,9 +122,7 @@ class ImportReturn:
164
122
  jac_file_path,
165
123
  ),
166
124
  )
167
- codeobj = self.importer.jac_machine.program.get_bytecode(
168
- full_target=jac_file_path
169
- )
125
+ codeobj = JacMachine.program.get_bytecode(full_target=jac_file_path)
170
126
  if not codeobj:
171
127
  raise ImportError(f"No bytecode found for {jac_file_path}")
172
128
 
@@ -180,28 +136,18 @@ class ImportReturn:
180
136
  class Importer:
181
137
  """Abstract base class for all importers."""
182
138
 
183
- def __init__(self, jac_machine: JacMachine) -> None:
139
+ def __init__(self) -> None:
184
140
  """Initialize the Importer object."""
185
- self.jac_machine = jac_machine
186
141
  self.result: Optional[ImportReturn] = None
187
142
 
188
143
  def run_import(self, spec: ImportPathSpec) -> ImportReturn:
189
144
  """Run the import process."""
190
145
  raise NotImplementedError
191
146
 
192
- def update_sys(self, module: types.ModuleType, spec: ImportPathSpec) -> None:
193
- """Update sys.modules with the newly imported module."""
194
- if spec.module_name not in self.jac_machine.loaded_modules:
195
- JacMachineInterface.load_module(self.jac_machine, spec.module_name, module)
196
-
197
147
 
198
148
  class PythonImporter(Importer):
199
149
  """Importer for Python modules."""
200
150
 
201
- def __init__(self, jac_machine: JacMachine) -> None:
202
- """Initialize the PythonImporter object."""
203
- self.jac_machine = jac_machine
204
-
205
151
  def run_import(self, spec: ImportPathSpec) -> ImportReturn:
206
152
  """Run the import process for Python modules."""
207
153
  try:
@@ -273,12 +219,10 @@ class PythonImporter(Importer):
273
219
  class JacImporter(Importer):
274
220
  """Importer for Jac modules."""
275
221
 
276
- def __init__(self, jac_machine: JacMachine) -> None:
277
- """Initialize the JacImporter object."""
278
- self.jac_machine = jac_machine
279
-
280
222
  def get_sys_mod_name(self, full_target: str) -> str:
281
223
  """Generate proper module names from file paths."""
224
+ from jaclang.runtimelib.machine import JacMachine
225
+
282
226
  full_target = os.path.abspath(full_target)
283
227
 
284
228
  # If the file is located within a site-packages directory, strip that prefix.
@@ -288,7 +232,7 @@ class JacImporter(Importer):
288
232
  rel = full_target[sp_index + len("site-packages") :]
289
233
  rel = rel.lstrip(os.sep)
290
234
  else:
291
- rel = path.relpath(full_target, start=self.jac_machine.base_path_dir)
235
+ rel = path.relpath(full_target, start=JacMachine.base_path_dir)
292
236
  rel = os.path.splitext(rel)[0]
293
237
  if os.path.basename(rel) == "__init__":
294
238
  rel = os.path.dirname(rel)
@@ -304,10 +248,36 @@ class JacImporter(Importer):
304
248
  module.__name__ = module_name
305
249
  module.__path__ = [full_mod_path]
306
250
  module.__file__ = None
307
- module.__dict__["__jac_mach__"] = self.jac_machine
308
251
 
309
- if module_name not in self.jac_machine.loaded_modules:
310
- JacMachineInterface.load_module(self.jac_machine, module_name, module)
252
+ JacMachineInterface.load_module(module_name, module)
253
+
254
+ # If the directory contains an __init__.jac, execute it so that the
255
+ # package namespace is populated immediately (mirrors Python's own
256
+ # package behaviour with __init__.py).
257
+ init_jac = os.path.join(full_mod_path, "__init__.jac")
258
+ if os.path.isfile(init_jac):
259
+ from jaclang.runtimelib.machine import JacMachine
260
+
261
+ # Point the package's __file__ to the init file for introspection
262
+ module.__file__ = init_jac
263
+
264
+ codeobj = JacMachine.program.get_bytecode(full_target=init_jac)
265
+ if not codeobj:
266
+ # Compilation should have provided bytecode for __init__.jac.
267
+ # Raising ImportError here surfaces compile-time issues clearly.
268
+ raise ImportError(f"No bytecode found for {init_jac}")
269
+
270
+ try:
271
+ # Ensure the directory is on sys.path while executing so that
272
+ # relative imports inside __init__.jac resolve correctly.
273
+ with sys_path_context(full_mod_path):
274
+ exec(codeobj, module.__dict__)
275
+ except Exception as e:
276
+ # Log detailed traceback for easier debugging and re-raise.
277
+ logger.error(e)
278
+ logger.error(dump_traceback(e))
279
+ raise e
280
+
311
281
  return module
312
282
 
313
283
  def create_jac_py_module(
@@ -317,16 +287,17 @@ class JacImporter(Importer):
317
287
  full_target: str,
318
288
  ) -> types.ModuleType:
319
289
  """Create a module."""
290
+ from jaclang.runtimelib.machine import JacMachine
291
+
320
292
  module = types.ModuleType(module_name)
321
293
  module.__file__ = full_target
322
294
  module.__name__ = module_name
323
- module.__dict__["__jac_mach__"] = self.jac_machine
324
295
  if package_path:
325
296
  base_path = full_target.split(package_path.replace(".", path.sep))[0]
326
297
  parts = package_path.split(".")
327
298
  for i in range(len(parts)):
328
299
  package_name = ".".join(parts[: i + 1])
329
- if package_name not in self.jac_machine.loaded_modules:
300
+ if package_name not in JacMachine.loaded_modules:
330
301
  full_mod_path = path.join(
331
302
  base_path, package_name.replace(".", path.sep)
332
303
  )
@@ -334,36 +305,24 @@ class JacImporter(Importer):
334
305
  module_name=package_name,
335
306
  full_mod_path=full_mod_path,
336
307
  )
337
- JacMachineInterface.load_module(self.jac_machine, module_name, module)
308
+ JacMachineInterface.load_module(module_name, module)
338
309
  return module
339
310
 
340
311
  def run_import(
341
312
  self, spec: ImportPathSpec, reload: Optional[bool] = False
342
313
  ) -> ImportReturn:
343
314
  """Run the import process for Jac modules."""
315
+ from jaclang.runtimelib.machine import JacMachine
316
+
344
317
  unique_loaded_items: list[types.ModuleType] = []
345
318
  module = None
346
319
  # Gather all possible search paths
347
- jacpaths = os.environ.get("JACPATH", "")
348
- search_paths = [spec.caller_dir]
349
- for site_dir in site.getsitepackages():
350
- if site_dir and site_dir not in search_paths:
351
- search_paths.append(site_dir)
352
- user_site = getattr(site, "getusersitepackages", None)
353
- if user_site:
354
- user_dir = site.getusersitepackages()
355
- if user_dir and user_dir not in search_paths:
356
- search_paths.append(user_dir)
357
- if jacpaths:
358
- for p in jacpaths.split(":"):
359
- p = p.strip()
360
- if p and p not in search_paths:
361
- search_paths.append(p)
320
+ search_paths = get_jac_search_paths(spec.caller_dir)
362
321
 
363
322
  found_path = None
364
323
  target_path_components = spec.target.split(".")
365
324
  for search_path in search_paths:
366
- candidate = os.path.join(search_path, "/".join(target_path_components))
325
+ candidate = os.path.join(search_path, *target_path_components)
367
326
  # Check if the candidate is a directory or a .jac file
368
327
  if (os.path.isdir(candidate)) or (os.path.isfile(candidate + ".jac")):
369
328
  found_path = candidate
@@ -386,7 +345,7 @@ class JacImporter(Importer):
386
345
  else:
387
346
  module_name = self.get_sys_mod_name(spec.full_target)
388
347
 
389
- module = self.jac_machine.loaded_modules.get(module_name)
348
+ module = JacMachine.loaded_modules.get(module_name)
390
349
 
391
350
  if not module or module.__name__ == "__main__" or reload:
392
351
  if os.path.isdir(spec.full_target):
@@ -398,9 +357,7 @@ class JacImporter(Importer):
398
357
  spec.package_path,
399
358
  spec.full_target,
400
359
  )
401
- codeobj = self.jac_machine.program.get_bytecode(
402
- full_target=spec.full_target
403
- )
360
+ codeobj = JacMachine.program.get_bytecode(full_target=spec.full_target)
404
361
 
405
362
  # Since this is a compile time error, we can safely raise an exception here.
406
363
  if not codeobj: