jaclang 0.7.14__py3-none-any.whl → 0.7.17__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 (131) hide show
  1. jaclang/cli/cli.py +147 -77
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +19 -53
  4. jaclang/compiler/absyntree.py +94 -16
  5. jaclang/compiler/constant.py +8 -8
  6. jaclang/compiler/jac.lark +4 -3
  7. jaclang/compiler/parser.py +41 -25
  8. jaclang/compiler/passes/ir_pass.py +4 -13
  9. jaclang/compiler/passes/main/__init__.py +1 -1
  10. jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
  11. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +155 -54
  12. jaclang/compiler/passes/main/import_pass.py +99 -75
  13. jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +328 -565
  15. jaclang/compiler/passes/main/pyast_load_pass.py +33 -6
  16. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
  17. jaclang/compiler/passes/main/registry_pass.py +37 -3
  18. jaclang/compiler/passes/main/schedules.py +9 -2
  19. jaclang/compiler/passes/main/sym_tab_build_pass.py +10 -6
  20. jaclang/compiler/passes/main/tests/__init__.py +1 -1
  21. jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
  22. jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
  23. jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
  24. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
  25. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
  26. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
  27. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
  28. jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
  29. jaclang/compiler/passes/main/type_check_pass.py +22 -5
  30. jaclang/compiler/passes/tool/jac_formatter_pass.py +135 -89
  31. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
  32. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -42
  33. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
  34. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  35. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  36. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  37. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  38. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  39. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  40. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  41. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  42. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  43. jaclang/compiler/passes/transform.py +4 -0
  44. jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
  45. jaclang/compiler/semtable.py +31 -7
  46. jaclang/compiler/symtable.py +16 -11
  47. jaclang/compiler/tests/test_importer.py +25 -10
  48. jaclang/langserve/engine.py +104 -118
  49. jaclang/langserve/sem_manager.py +379 -0
  50. jaclang/langserve/server.py +24 -11
  51. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  52. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  53. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  54. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  55. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  56. jaclang/langserve/tests/fixtures/rename.jac +30 -0
  57. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  58. jaclang/langserve/tests/test_server.py +287 -17
  59. jaclang/langserve/utils.py +184 -98
  60. jaclang/plugin/builtin.py +1 -1
  61. jaclang/plugin/default.py +288 -92
  62. jaclang/plugin/feature.py +65 -27
  63. jaclang/plugin/spec.py +62 -23
  64. jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
  65. jaclang/plugin/tests/test_jaseci.py +414 -42
  66. jaclang/runtimelib/architype.py +650 -0
  67. jaclang/{core → runtimelib}/constructs.py +5 -8
  68. jaclang/{core → runtimelib}/context.py +86 -59
  69. jaclang/runtimelib/importer.py +361 -0
  70. jaclang/runtimelib/machine.py +158 -0
  71. jaclang/runtimelib/memory.py +158 -0
  72. jaclang/{core → runtimelib}/utils.py +30 -15
  73. jaclang/settings.py +5 -4
  74. jaclang/tests/fixtures/abc.jac +3 -3
  75. jaclang/tests/fixtures/access_checker.jac +12 -17
  76. jaclang/tests/fixtures/access_modifier.jac +88 -33
  77. jaclang/tests/fixtures/baddy.jac +3 -0
  78. jaclang/tests/fixtures/baddy.test.jac +3 -0
  79. jaclang/tests/fixtures/bar.jac +34 -0
  80. jaclang/tests/fixtures/byllmissue.jac +1 -5
  81. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  82. jaclang/tests/fixtures/cls_method.jac +41 -0
  83. jaclang/tests/fixtures/dblhello.jac +6 -0
  84. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  85. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  86. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  87. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  88. jaclang/tests/fixtures/edge_ops.jac +1 -1
  89. jaclang/tests/fixtures/edges_walk.jac +1 -1
  90. jaclang/tests/fixtures/err.impl.jac +3 -0
  91. jaclang/tests/fixtures/err.jac +4 -2
  92. jaclang/tests/fixtures/err_runtime.jac +15 -0
  93. jaclang/tests/fixtures/foo.jac +43 -0
  94. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  95. jaclang/tests/fixtures/hello.jac +4 -0
  96. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  97. jaclang/tests/fixtures/impl_grab.jac +4 -1
  98. jaclang/tests/fixtures/import.jac +9 -0
  99. jaclang/tests/fixtures/index_slice.jac +30 -0
  100. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  101. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  102. jaclang/tests/fixtures/needs_import.jac +2 -2
  103. jaclang/tests/fixtures/pyfunc_1.py +1 -1
  104. jaclang/tests/fixtures/pyfunc_2.py +5 -2
  105. jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
  106. jaclang/tests/fixtures/pygame_mock/color.py +3 -0
  107. jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
  108. jaclang/tests/fixtures/pygame_mock/display.py +2 -0
  109. jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
  110. jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
  111. jaclang/tests/fixtures/registry.jac +9 -0
  112. jaclang/tests/fixtures/run_test.jac +4 -4
  113. jaclang/tests/fixtures/semstr.jac +1 -4
  114. jaclang/tests/fixtures/simple_archs.jac +1 -1
  115. jaclang/tests/test_cli.py +109 -3
  116. jaclang/tests/test_language.py +170 -68
  117. jaclang/tests/test_reference.py +2 -3
  118. jaclang/utils/helpers.py +45 -21
  119. jaclang/utils/test.py +9 -0
  120. jaclang/utils/treeprinter.py +30 -7
  121. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/METADATA +3 -2
  122. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/RECORD +126 -90
  123. jaclang/core/architype.py +0 -502
  124. jaclang/core/importer.py +0 -344
  125. jaclang/core/memory.py +0 -99
  126. jaclang/tests/fixtures/aott_raise.jac +0 -25
  127. jaclang/tests/fixtures/package_import.jac +0 -6
  128. /jaclang/{core → runtimelib}/__init__.py +0 -0
  129. /jaclang/{core → runtimelib}/test.py +0 -0
  130. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
  131. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  import ast as ast3
4
4
  import importlib
5
- import inspect
6
5
  import marshal
7
6
  import os
8
7
  import pickle
@@ -19,10 +18,12 @@ from jaclang.compiler.constant import Constants
19
18
  from jaclang.compiler.passes.main.pyast_load_pass import PyastBuildPass
20
19
  from jaclang.compiler.passes.main.schedules import py_code_gen_typed
21
20
  from jaclang.compiler.passes.tool.schedules import format_pass
22
- from jaclang.core.constructs import Architype
23
21
  from jaclang.plugin.builtin import dotgen
24
22
  from jaclang.plugin.feature import JacCmd as Cmd
25
23
  from jaclang.plugin.feature import JacFeature as Jac
24
+ from jaclang.runtimelib.constructs import WalkerArchitype
25
+ from jaclang.runtimelib.context import ExecutionContext
26
+ from jaclang.runtimelib.machine import JacMachine, JacProgram
26
27
  from jaclang.utils.helpers import debugger as db
27
28
  from jaclang.utils.lang_tools import AstTool
28
29
 
@@ -67,12 +68,7 @@ def format(path: str, outfile: str = "", debug: bool = False) -> None:
67
68
 
68
69
  @cmd_registry.register
69
70
  def run(
70
- filename: str,
71
- session: str = "",
72
- main: bool = True,
73
- cache: bool = True,
74
- walker: str = "",
75
- node: str = "",
71
+ filename: str, session: str = "", main: bool = True, cache: bool = True
76
72
  ) -> None:
77
73
  """Run the specified .jac file."""
78
74
  # if no session specified, check if it was defined when starting the command shell
@@ -86,81 +82,93 @@ def run(
86
82
  else ""
87
83
  )
88
84
 
89
- Jac.context().init_memory(session)
90
-
91
85
  base, mod = os.path.split(filename)
92
86
  base = base if base else "./"
93
87
  mod = mod[:-4]
88
+
89
+ jctx = ExecutionContext.create(session=session)
90
+
94
91
  if filename.endswith(".jac"):
95
- ret_module = jac_import(
92
+ jac_import(
96
93
  target=mod,
97
94
  base_path=base,
98
95
  cachable=cache,
99
96
  override_name="__main__" if main else None,
100
97
  )
101
- if ret_module is None:
102
- loaded_mod = None
103
- else:
104
- (loaded_mod,) = ret_module
105
98
  elif filename.endswith(".jir"):
106
99
  with open(filename, "rb") as f:
107
- ir = pickle.load(f)
108
- ret_module = jac_import(
100
+ JacMachine(base).attach_program(
101
+ JacProgram(mod_bundle=pickle.load(f), bytecode=None)
102
+ )
103
+ jac_import(
109
104
  target=mod,
110
105
  base_path=base,
111
106
  cachable=cache,
112
107
  override_name="__main__" if main else None,
113
- mod_bundle=ir,
114
108
  )
115
- if ret_module is None:
116
- loaded_mod = None
117
- else:
118
- (loaded_mod,) = ret_module
119
109
  else:
120
- print("Not a .jac file.")
121
- return
122
-
123
- if not node or node == "root":
124
- entrypoint: Architype = Jac.get_root()
125
- else:
126
- obj = Jac.context().get_obj(UUID(node))
127
- if obj is None:
128
- print(f"Entrypoint {node} not found.")
129
- return
130
- entrypoint = obj
131
-
132
- # TODO: handle no override name
133
- if walker:
134
- walker_module = dict(inspect.getmembers(loaded_mod)).get(walker)
135
- if walker_module:
136
- Jac.spawn_call(entrypoint, walker_module())
137
- else:
138
- print(f"Walker {walker} not found.")
110
+ jctx.close()
111
+ JacMachine.detach()
112
+ raise ValueError("Not a valid file!\nOnly supports `.jac` and `.jir`")
139
113
 
140
- Jac.reset_context()
114
+ jctx.close()
115
+ JacMachine.detach()
141
116
 
142
117
 
143
118
  @cmd_registry.register
144
- def get_object(id: str, session: str = "") -> dict:
119
+ def get_object(
120
+ filename: str, id: str, session: str = "", main: bool = True, cache: bool = True
121
+ ) -> dict:
145
122
  """Get the object with the specified id."""
146
123
  if session == "":
147
- session = cmd_registry.args.session if "session" in cmd_registry.args else ""
124
+ session = (
125
+ cmd_registry.args.session
126
+ if hasattr(cmd_registry, "args")
127
+ and hasattr(cmd_registry.args, "session")
128
+ and cmd_registry.args.session
129
+ else ""
130
+ )
148
131
 
149
- Jac.context().init_memory(session)
132
+ base, mod = os.path.split(filename)
133
+ base = base if base else "./"
134
+ mod = mod[:-4]
150
135
 
151
- if id == "root":
152
- id_uuid = UUID(int=0)
136
+ jctx = ExecutionContext.create(session=session)
137
+
138
+ if filename.endswith(".jac"):
139
+ jac_import(
140
+ target=mod,
141
+ base_path=base,
142
+ cachable=cache,
143
+ override_name="__main__" if main else None,
144
+ )
145
+ elif filename.endswith(".jir"):
146
+ with open(filename, "rb") as f:
147
+ JacMachine(base).attach_program(
148
+ JacProgram(mod_bundle=pickle.load(f), bytecode=None)
149
+ )
150
+ jac_import(
151
+ target=mod,
152
+ base_path=base,
153
+ cachable=cache,
154
+ override_name="__main__" if main else None,
155
+ )
153
156
  else:
154
- id_uuid = UUID(id)
157
+ jctx.close()
158
+ JacMachine.detach()
159
+ raise ValueError("Not a valid file!\nOnly supports `.jac` and `.jir`")
155
160
 
156
- obj = Jac.context().get_obj(id_uuid)
157
- if obj is None:
158
- print(f"Object with id {id} not found.")
159
- Jac.reset_context()
160
- return {}
161
+ data = {}
162
+ if id == "root":
163
+ data = jctx.root.__getstate__()
164
+ elif obj := jctx.mem.find_by_id(UUID(id)):
165
+ data = obj.__getstate__()
161
166
  else:
162
- Jac.reset_context()
163
- return obj.__getstate__()
167
+ print(f"Object with id {id} not found.")
168
+
169
+ jctx.close()
170
+ JacMachine.detach()
171
+ return data
164
172
 
165
173
 
166
174
  @cmd_registry.register
@@ -210,25 +218,78 @@ def lsp() -> None:
210
218
 
211
219
 
212
220
  @cmd_registry.register
213
- def enter(filename: str, entrypoint: str, args: list) -> None:
214
- """Run the specified entrypoint function in the given .jac file.
221
+ def enter(
222
+ filename: str,
223
+ entrypoint: str,
224
+ args: list,
225
+ session: str = "",
226
+ main: bool = True,
227
+ cache: bool = True,
228
+ root: str = "",
229
+ node: str = "",
230
+ ) -> None:
231
+ """
232
+ Run the specified entrypoint function in the given .jac file.
215
233
 
216
234
  :param filename: The path to the .jac file.
217
235
  :param entrypoint: The name of the entrypoint function.
218
236
  :param args: Arguments to pass to the entrypoint function.
237
+ :param session: shelve.Shelf file path.
238
+ :param root: root executor.
239
+ :param node: starting node.
219
240
  """
241
+ if session == "":
242
+ session = (
243
+ cmd_registry.args.session
244
+ if hasattr(cmd_registry, "args")
245
+ and hasattr(cmd_registry.args, "session")
246
+ and cmd_registry.args.session
247
+ else ""
248
+ )
249
+
250
+ base, mod = os.path.split(filename)
251
+ base = base if base else "./"
252
+ mod = mod[:-4]
253
+
254
+ jctx = ExecutionContext.create(session=session, root=root)
255
+
220
256
  if filename.endswith(".jac"):
221
- base, mod_name = os.path.split(filename)
222
- base = base if base else "./"
223
- mod_name = mod_name[:-4]
224
- mod = jac_import(target=mod_name, base_path=base)
225
- if not mod:
257
+ ret_module = jac_import(
258
+ target=mod,
259
+ base_path=base,
260
+ cachable=cache,
261
+ override_name="__main__" if main else None,
262
+ )
263
+ elif filename.endswith(".jir"):
264
+ with open(filename, "rb") as f:
265
+ JacMachine(base).attach_program(
266
+ JacProgram(mod_bundle=pickle.load(f), bytecode=None)
267
+ )
268
+ ret_module = jac_import(
269
+ target=mod,
270
+ base_path=base,
271
+ cachable=cache,
272
+ override_name="__main__" if main else None,
273
+ )
274
+ else:
275
+ jctx.close()
276
+ JacMachine.detach()
277
+ raise ValueError("Not a valid file!\nOnly supports `.jac` and `.jir`")
278
+
279
+ if ret_module:
280
+ (loaded_mod,) = ret_module
281
+ if not loaded_mod:
226
282
  print("Errors occurred while importing the module.")
227
- return
228
283
  else:
229
- getattr(mod, entrypoint)(*args)
230
- else:
231
- print("Not a .jac file.")
284
+ architype = getattr(loaded_mod, entrypoint)(*args)
285
+
286
+ jctx.set_entry_node(node)
287
+
288
+ if isinstance(architype, WalkerArchitype) and jctx.validate_access():
289
+ Jac.spawn_call(jctx.entry_node.architype, architype)
290
+
291
+ jctx.close()
292
+ JacMachine.detach()
232
293
 
233
294
 
234
295
  @cmd_registry.register
@@ -251,6 +312,8 @@ def test(
251
312
 
252
313
  jac test => jac test -d .
253
314
  """
315
+ jctx = ExecutionContext.create()
316
+
254
317
  failcount = Jac.run_test(
255
318
  filepath=filepath,
256
319
  filter=filter,
@@ -259,6 +322,9 @@ def test(
259
322
  directory=directory,
260
323
  verbose=verbose,
261
324
  )
325
+
326
+ jctx.close()
327
+
262
328
  if failcount:
263
329
  raise SystemExit(f"Tests failed: {failcount}")
264
330
 
@@ -357,17 +423,16 @@ def dot(
357
423
  else ""
358
424
  )
359
425
 
360
- Jac.context().init_memory(session)
361
-
362
426
  base, mod = os.path.split(filename)
363
427
  base = base if base else "./"
364
428
  mod = mod[:-4]
429
+
430
+ jctx = ExecutionContext.create(session=session)
431
+
365
432
  if filename.endswith(".jac"):
366
- jac_import(
367
- target=mod,
368
- base_path=base,
369
- )
370
- module = importlib.import_module(mod)
433
+ jac_machine = JacMachine(base)
434
+ jac_import(target=mod, base_path=base)
435
+ module = jac_machine.loaded_modules.get(mod)
371
436
  globals().update(vars(module))
372
437
  try:
373
438
  node = globals().get(initial, eval(initial)) if initial else None
@@ -385,7 +450,7 @@ def dot(
385
450
  import traceback
386
451
 
387
452
  traceback.print_exc()
388
- Jac.reset_context()
453
+ jctx.close()
389
454
  return
390
455
  file_name = saveto if saveto else f"{mod}.dot"
391
456
  with open(file_name, "w") as file:
@@ -394,7 +459,7 @@ def dot(
394
459
  else:
395
460
  print("Not a .jac file.")
396
461
 
397
- Jac.reset_context()
462
+ jctx.close()
398
463
 
399
464
 
400
465
  @cmd_registry.register
@@ -437,12 +502,17 @@ def start_cli() -> None:
437
502
  parser = cmd_registry.parser
438
503
  args = parser.parse_args()
439
504
  cmd_registry.args = args
505
+
506
+ if args.version:
507
+ version = importlib.metadata.version("jaclang")
508
+ print(f"Jac version {version}")
509
+ return
510
+
440
511
  command = cmd_registry.get(args.command)
441
512
  if command:
442
513
  args_dict = vars(args)
443
514
  args_dict.pop("command")
444
- if command not in ["run"]:
445
- args_dict.pop("session")
515
+ args_dict.pop("version", None)
446
516
  ret = command.call(**args_dict)
447
517
  if ret:
448
518
  print(ret)
jaclang/cli/cmdreg.py CHANGED
@@ -38,7 +38,7 @@ class CommandRegistry:
38
38
  self.registry = {}
39
39
  self.parser = argparse.ArgumentParser(prog="jac")
40
40
  self.parser.add_argument(
41
- "--session", help="Session file path", nargs="?", default=""
41
+ "-V", "--version", action="store_true", help="Show the Jac version"
42
42
  )
43
43
  self.sub_parsers = self.parser.add_subparsers(title="commands", dest="command")
44
44
  self.args = argparse.Namespace()
@@ -172,17 +172,14 @@ class CommandShell(cmd.Cmd):
172
172
 
173
173
  def default(self, line: str) -> None:
174
174
  """Process the command line input."""
175
- try:
176
- args = vars(self.cmd_reg.parser.parse_args(line.split()))
177
- command = self.cmd_reg.get(args["command"])
178
- if command:
179
- args.pop("command")
180
- ret = command.call(**args)
181
- if ret:
182
- ret_str = pprint.pformat(ret, indent=2)
183
- self.stdout.write(f"{ret_str}\n")
184
- except Exception as e:
185
- print(e)
175
+ args = vars(self.cmd_reg.parser.parse_args(line.split()))
176
+ command = self.cmd_reg.get(args["command"])
177
+ if command:
178
+ args.pop("command")
179
+ ret = command.call(**args)
180
+ if ret:
181
+ ret_str = pprint.pformat(ret, indent=2)
182
+ self.stdout.write(f"{ret_str}\n")
186
183
 
187
184
  def do_help(self, arg: str) -> None:
188
185
  """Jac CLI 'help' implementaion."""
@@ -35,7 +35,6 @@ def generate_static_parser(force: bool = False) -> None:
35
35
  logging.error(f"Error generating reference files: {e}")
36
36
 
37
37
 
38
- generate_static_parser()
39
38
  try:
40
39
  from jaclang.compiler.generated import jac_parser as jac_lark
41
40
  except ModuleNotFoundError:
@@ -50,62 +49,29 @@ TOKEN_MAP = {
50
49
  for x in jac_lark.Lark_StandAlone().parser.lexer_conf.terminals
51
50
  }
52
51
 
52
+ # fmt: off
53
53
  TOKEN_MAP.update(
54
54
  {
55
- "CARROW_L": "<++",
56
- "CARROW_R": "++>",
57
- "GLOBAL_OP": ":global:",
58
- "NONLOCAL_OP": ":nonlocal:",
59
- "WALKER_OP": ":walker:",
60
- "NODE_OP": ":node:",
61
- "EDGE_OP": ":edge:",
62
- "CLASS_OP": ":class:",
63
- "OBJECT_OP": ":obj:",
64
- "TYPE_OP": "`",
65
- "ABILITY_OP": ":can:",
66
- "ELVIS_OP": "?:",
67
- "NULL_OK": "?",
68
- "KW_OR": "|",
69
- "ARROW_BI": "<-->",
70
- "ARROW_L": "<--",
71
- "ARROW_R": "-->",
72
- "ARROW_L_P1": "<-:",
73
- "ARROW_R_P2": ":->",
74
- "ARROW_L_P2": ":-",
75
- "ARROW_R_P1": "-:",
76
- "CARROW_BI": "<++>",
77
- "CARROW_L": "<++",
78
- "CARROW_R": "++>",
79
- "CARROW_L_P1": "<+:",
80
- "CARROW_R_P2": ":+>",
81
- "CARROW_L_P2": ":+",
82
- "CARROW_R_P1": "+:",
83
- "PIPE_FWD": "|>",
84
- "PIPE_BKWD": "<|",
85
- "A_PIPE_FWD": ":>",
86
- "A_PIPE_BKWD": "<:",
87
- "DOT_FWD": ".>",
88
- "STAR_POW": "**",
89
- "STAR_MUL": "*",
90
- "FLOOR_DIV": "//",
91
- "DIV": "/",
92
- "PYNLINE": "::py::",
93
- "ADD_EQ": "+=",
94
- "SUB_EQ": "-=",
95
- "STAR_POW_EQ": "**=",
96
- "MUL_EQ": "*=",
97
- "FLOOR_DIV_EQ": "//=",
98
- "DIV_EQ": "/=",
99
- "MOD_EQ": "%=",
100
- "BW_AND_EQ": "&=",
101
- "BW_OR_EQ": "|=",
102
- "BW_XOR_EQ": "^=",
103
- "BW_NOT_EQ": "~=",
104
- "LSHIFT_EQ": "<<=",
105
- "RSHIFT_EQ": ">>=",
106
- "ELLIPSIS": "...",
55
+ "CARROW_L": "<++", "CARROW_R": "++>", "GLOBAL_OP": ":global:",
56
+ "NONLOCAL_OP": ":nonlocal:", "WALKER_OP": ":walker:", "NODE_OP": ":node:",
57
+ "EDGE_OP": ":edge:", "CLASS_OP": ":class:", "OBJECT_OP": ":obj:",
58
+ "TYPE_OP": "`", "ABILITY_OP": ":can:", "ELVIS_OP": "?:", "NULL_OK": "?",
59
+ "KW_OR": "|", "ARROW_BI": "<-->", "ARROW_L": "<--",
60
+ "ARROW_R": "-->", "ARROW_L_P1": "<-:", "ARROW_R_P2": ":->",
61
+ "ARROW_L_P2": ":-", "ARROW_R_P1": "-:", "CARROW_BI": "<++>",
62
+ "CARROW_L": "<++", "CARROW_R": "++>", "CARROW_L_P1": "<+:",
63
+ "CARROW_R_P2": ":+>", "CARROW_L_P2": ":+", "CARROW_R_P1": "+:",
64
+ "PIPE_FWD": "|>", "PIPE_BKWD": "<|", "A_PIPE_FWD": ":>",
65
+ "A_PIPE_BKWD": "<:", "DOT_FWD": ".>", "STAR_POW": "**",
66
+ "STAR_MUL": "*", "FLOOR_DIV": "//", "DIV": "/",
67
+ "PYNLINE": "::py::", "ADD_EQ": "+=", "SUB_EQ": "-=",
68
+ "STAR_POW_EQ": "**=", "MUL_EQ": "*=", "FLOOR_DIV_EQ": "//=",
69
+ "DIV_EQ": "/=", "MOD_EQ": "%=", "BW_AND_EQ": "&=",
70
+ "BW_OR_EQ": "|=", "BW_XOR_EQ": "^=", "BW_NOT_EQ": "~=",
71
+ "LSHIFT_EQ": "<<=", "RSHIFT_EQ": ">>=", "ELLIPSIS": "...",
107
72
  }
108
73
  )
74
+ # fmt: on
109
75
 
110
76
 
111
77
  __all__ = ["jac_lark", "TOKEN_MAP"]
@@ -55,15 +55,10 @@ class AstNode:
55
55
  """Get symbol table."""
56
56
  # sym_tab should never be accessed without being set in codebase
57
57
  if not self._sym_tab:
58
- import traceback
59
-
60
- if self.parent:
61
- print(f"Parent: {self.parent.pp()}")
62
- print("Node: ", self.pp())
63
- stack_trace = traceback.format_stack()
64
- print("".join(stack_trace))
65
58
  raise ValueError(
66
- f"Symbol table not set for {type(self).__name__}. Impossible."
59
+ f"Symbol table not set for {type(self).__name__}. Impossible.\n"
60
+ f"Node: {self.pp()}\n"
61
+ f"Parent: {self.parent.pp() if self.parent else None}\n"
67
62
  )
68
63
  return self._sym_tab
69
64
 
@@ -163,7 +158,7 @@ class AstNode:
163
158
  """Get parent of type."""
164
159
  from jaclang.compiler.passes import Pass
165
160
 
166
- return Pass.has_parent_of_type(node=self, typ=typ)
161
+ return Pass.find_parent_of_type(node=self, typ=typ)
167
162
 
168
163
  def parent_of_type(self, typ: Type[T]) -> T:
169
164
  """Get parent of type."""
@@ -635,8 +630,11 @@ class Module(AstDocNode):
635
630
  self.impl_mod: list[Module] = []
636
631
  self.test_mod: list[Module] = []
637
632
  self.mod_deps: dict[str, Module] = {}
633
+ self.py_mod_dep_map: dict[str, str] = {}
634
+ self.py_raise_map: dict[str, str] = {}
638
635
  self.registry = registry
639
636
  self.terminals: list[Token] = terminals
637
+ self.is_raised_from_py: bool = False
640
638
  AstNode.__init__(self, kid=kid)
641
639
  AstDocNode.__init__(self, doc=doc)
642
640
 
@@ -863,7 +861,7 @@ class Import(ElementStmt, CodeBlockStmt):
863
861
 
864
862
  def __init__(
865
863
  self,
866
- hint: SubTag[Name],
864
+ hint: Optional[SubTag[Name]],
867
865
  from_loc: Optional[ModulePath],
868
866
  items: SubNodeList[ModuleItem] | SubNodeList[ModulePath],
869
867
  is_absorb: bool, # For includes
@@ -878,11 +876,52 @@ class Import(ElementStmt, CodeBlockStmt):
878
876
  AstNode.__init__(self, kid=kid)
879
877
  AstDocNode.__init__(self, doc=doc)
880
878
 
879
+ @property
880
+ def is_py(self) -> bool:
881
+ """Check if import is python."""
882
+ if self.hint and self.hint.tag.value == "py":
883
+ return True
884
+ if not self.hint:
885
+ return not self.__jac_detected
886
+ return False
887
+
888
+ @property
889
+ def is_jac(self) -> bool:
890
+ """Check if import is jac."""
891
+ if self.hint and self.hint.tag.value == "jac":
892
+ return True
893
+ if not self.hint:
894
+ return self.__jac_detected
895
+ return False
896
+
897
+ @property
898
+ def __jac_detected(self) -> bool:
899
+ """Check if import is jac."""
900
+ if self.from_loc:
901
+ if self.from_loc.resolve_relative_path().endswith(".jac"):
902
+ return True
903
+ if os.path.isdir(self.from_loc.resolve_relative_path()):
904
+ if os.path.exists(
905
+ os.path.join(self.from_loc.resolve_relative_path(), "__init__.jac")
906
+ ):
907
+ return True
908
+ for i in self.items.items:
909
+ if isinstance(
910
+ i, ModuleItem
911
+ ) and self.from_loc.resolve_relative_path(i.name.value).endswith(
912
+ ".jac"
913
+ ):
914
+ return True
915
+ return any(
916
+ isinstance(i, ModulePath) and i.resolve_relative_path().endswith(".jac")
917
+ for i in self.items.items
918
+ )
919
+
881
920
  def normalize(self, deep: bool = False) -> bool:
882
921
  """Normalize import node."""
883
922
  res = True
884
923
  if deep:
885
- res = self.hint.normalize(deep)
924
+ res = self.hint.normalize(deep) if self.hint else res
886
925
  res = res and self.from_loc.normalize(deep) if self.from_loc else res
887
926
  res = res and self.items.normalize(deep)
888
927
  res = res and self.doc.normalize(deep) if self.doc else res
@@ -893,7 +932,8 @@ class Import(ElementStmt, CodeBlockStmt):
893
932
  new_kid.append(self.gen_token(Tok.KW_INCLUDE))
894
933
  else:
895
934
  new_kid.append(self.gen_token(Tok.KW_IMPORT))
896
- new_kid.append(self.hint)
935
+ if self.hint:
936
+ new_kid.append(self.hint)
897
937
  if self.from_loc:
898
938
  new_kid.append(self.gen_token(Tok.KW_FROM))
899
939
  new_kid.append(self.from_loc)
@@ -919,6 +959,7 @@ class ModulePath(AstSymbolNode):
919
959
  self.level = level
920
960
  self.alias = alias
921
961
  self.sub_module: Optional[Module] = None
962
+ self.abs_path: Optional[str] = None
922
963
 
923
964
  name_spec = alias if alias else path[0] if path else None
924
965
 
@@ -940,12 +981,32 @@ class ModulePath(AstSymbolNode):
940
981
  )
941
982
 
942
983
  @property
943
- def path_str(self) -> str:
984
+ def dot_path_str(self) -> str:
944
985
  """Get path string."""
945
986
  return ("." * self.level) + ".".join(
946
987
  [p.value for p in self.path] if self.path else [self.name_spec.sym_name]
947
988
  )
948
989
 
990
+ def resolve_relative_path(self, target_item: Optional[str] = None) -> str:
991
+ """Convert an import target string into a relative file path."""
992
+ target = self.dot_path_str
993
+ if target_item:
994
+ target += f".{target_item}"
995
+ base_path = os.path.dirname(self.loc.mod_path)
996
+ base_path = base_path if base_path else os.getcwd()
997
+ parts = target.split(".")
998
+ traversal_levels = self.level - 1 if self.level > 0 else 0
999
+ actual_parts = parts[traversal_levels:]
1000
+ for _ in range(traversal_levels):
1001
+ base_path = os.path.dirname(base_path)
1002
+ relative_path = os.path.join(base_path, *actual_parts)
1003
+ relative_path = (
1004
+ relative_path + ".jac"
1005
+ if os.path.exists(relative_path + ".jac")
1006
+ else relative_path
1007
+ )
1008
+ return relative_path
1009
+
949
1010
  def normalize(self, deep: bool = False) -> bool:
950
1011
  """Normalize module path node."""
951
1012
  res = True
@@ -991,6 +1052,7 @@ class ModuleItem(AstSymbolNode):
991
1052
  name_spec=alias if alias else name,
992
1053
  sym_category=SymbolType.MOD_VAR,
993
1054
  )
1055
+ self.abs_path: Optional[str] = None
994
1056
 
995
1057
  @property
996
1058
  def from_parent(self) -> Import:
@@ -1132,7 +1194,6 @@ class ArchDef(AstImplOnlyNode):
1132
1194
  body: SubNodeList[ArchBlockStmt],
1133
1195
  kid: Sequence[AstNode],
1134
1196
  doc: Optional[String] = None,
1135
- decorators: Optional[SubNodeList[Expr]] = None,
1136
1197
  decl_link: Optional[Architype] = None,
1137
1198
  ) -> None:
1138
1199
  """Initialize arch def node."""
@@ -1473,6 +1534,23 @@ class FuncSignature(AstSemStrNode):
1473
1534
  and self.parent.decl_link.is_static
1474
1535
  )
1475
1536
 
1537
+ @property
1538
+ def is_in_py_class(self) -> bool:
1539
+ """Check if the ability belongs to a class."""
1540
+ is_archi = self.find_parent_of_type(Architype)
1541
+ is_class = is_archi is not None and is_archi.arch_type.name == Tok.KW_CLASS
1542
+
1543
+ return (
1544
+ isinstance(self.parent, Ability)
1545
+ and self.parent.is_method is not None
1546
+ and is_class
1547
+ ) or (
1548
+ isinstance(self.parent, AbilityDef)
1549
+ and isinstance(self.parent.decl_link, Ability)
1550
+ and self.parent.decl_link.is_method
1551
+ and is_class
1552
+ )
1553
+
1476
1554
 
1477
1555
  class EventSignature(AstSemStrNode):
1478
1556
  """EventSignature node type for Jac Ast."""
@@ -4240,11 +4318,11 @@ class Ellipsis(Literal):
4240
4318
  class EmptyToken(Token):
4241
4319
  """EmptyToken node type for Jac Ast."""
4242
4320
 
4243
- def __init__(self) -> None:
4321
+ def __init__(self, file_path: str = "") -> None:
4244
4322
  """Initialize empty token."""
4245
4323
  super().__init__(
4246
4324
  name="EmptyToken",
4247
- file_path="",
4325
+ file_path=file_path,
4248
4326
  value="",
4249
4327
  line=0,
4250
4328
  end_line=0,