jaclang 0.7.27__py3-none-any.whl → 0.7.30__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 (56) hide show
  1. jaclang/cli/cli.py +3 -0
  2. jaclang/compiler/__init__.py +1 -1
  3. jaclang/compiler/absyntree.py +30 -5
  4. jaclang/compiler/compile.py +1 -1
  5. jaclang/compiler/constant.py +0 -1
  6. jaclang/compiler/jac.lark +1 -5
  7. jaclang/compiler/parser.py +2 -10
  8. jaclang/compiler/passes/main/__init__.py +1 -1
  9. jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +62 -33
  11. jaclang/compiler/passes/main/import_pass.py +285 -64
  12. jaclang/compiler/passes/main/inheritance_pass.py +103 -0
  13. jaclang/compiler/passes/main/py_collect_dep_pass.py +5 -5
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +0 -19
  15. jaclang/compiler/passes/main/pyast_load_pass.py +1 -1
  16. jaclang/compiler/passes/main/schedules.py +2 -0
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +17 -0
  18. jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +130 -0
  19. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -3
  20. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -3
  21. jaclang/compiler/passes/main/tests/test_import_pass.py +16 -17
  22. jaclang/compiler/passes/main/tests/test_type_check_pass.py +24 -0
  23. jaclang/compiler/passes/main/type_check_pass.py +3 -2
  24. jaclang/compiler/passes/tool/jac_formatter_pass.py +0 -1
  25. jaclang/compiler/py_info.py +22 -0
  26. jaclang/compiler/symtable.py +9 -2
  27. jaclang/compiler/tests/test_importer.py +45 -1
  28. jaclang/langserve/tests/test_server.py +2 -2
  29. jaclang/plugin/default.py +86 -62
  30. jaclang/plugin/feature.py +2 -5
  31. jaclang/plugin/spec.py +1 -6
  32. jaclang/runtimelib/architype.py +20 -16
  33. jaclang/runtimelib/importer.py +26 -3
  34. jaclang/runtimelib/machine.py +2 -2
  35. jaclang/runtimelib/test.py +59 -4
  36. jaclang/runtimelib/utils.py +15 -0
  37. jaclang/settings.py +3 -0
  38. jaclang/tests/fixtures/base_class1.jac +11 -0
  39. jaclang/tests/fixtures/base_class2.jac +11 -0
  40. jaclang/tests/fixtures/import_all.jac +7 -0
  41. jaclang/tests/fixtures/import_all_py.py +8 -0
  42. jaclang/tests/fixtures/jactest_imported.jac +6 -0
  43. jaclang/tests/fixtures/jactest_main.jac +22 -0
  44. jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
  45. jaclang/tests/fixtures/test_py.py +12 -0
  46. jaclang/tests/fixtures/visit_sequence.jac +50 -0
  47. jaclang/tests/test_cli.py +83 -1
  48. jaclang/tests/test_language.py +24 -9
  49. jaclang/utils/helpers.py +9 -1
  50. jaclang/utils/test.py +2 -2
  51. jaclang/utils/tests/test_lang_tools.py +4 -2
  52. jaclang/utils/treeprinter.py +6 -3
  53. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/METADATA +3 -3
  54. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/RECORD +56 -45
  55. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/WHEEL +1 -1
  56. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/entry_points.txt +0 -0
@@ -12,8 +12,11 @@ from typing import Optional
12
12
 
13
13
 
14
14
  import jaclang.compiler.absyntree as ast
15
+ from jaclang.compiler.constant import SymbolType
15
16
  from jaclang.compiler.passes import Pass
16
- from jaclang.compiler.passes.main import SubNodeTabPass, SymTabBuildPass
17
+ from jaclang.compiler.passes.main import DefUsePass, SubNodeTabPass, SymTabBuildPass
18
+ from jaclang.compiler.passes.main.sym_tab_build_pass import PyInspectSymTabBuildPass
19
+ from jaclang.compiler.symtable import Symbol, SymbolTable
17
20
  from jaclang.settings import settings
18
21
  from jaclang.utils.log import logging
19
22
 
@@ -183,7 +186,7 @@ class JacImportPass(Pass):
183
186
  self.warnings_had += mod_pass.warnings_had
184
187
  mod = mod_pass.ir
185
188
  except Exception as e:
186
- logger.info(e)
189
+ logger.error(e)
187
190
  mod = None
188
191
  if isinstance(mod, ast.Module):
189
192
  self.import_table[target] = mod
@@ -198,19 +201,20 @@ class JacImportPass(Pass):
198
201
  class PyImportPass(JacImportPass):
199
202
  """Jac statically imports Python modules."""
200
203
 
204
+ def __debug_print(self, msg: str) -> None:
205
+ if settings.py_import_pass_debug:
206
+ self.log_info("[PyImportPass] " + msg)
207
+
201
208
  def before_pass(self) -> None:
202
209
  """Only run pass if settings are set to raise python."""
210
+ self.import_from_build_list: list[tuple[ast.Import, ast.Module]] = []
203
211
  super().before_pass()
204
212
  self.__load_builtins()
205
213
 
206
- def __get_current_module(self, node: ast.AstNode) -> str:
207
- parent = node.find_parent_of_type(ast.Module)
208
- mod_list = []
209
- while parent is not None:
210
- mod_list.append(parent)
211
- parent = parent.find_parent_of_type(ast.Module)
212
- mod_list.reverse()
213
- return ".".join(p.name for p in mod_list)
214
+ def after_pass(self) -> None:
215
+ """Build symbol tables for import from nodes."""
216
+ self.__import_from_symbol_table_build()
217
+ return super().after_pass()
214
218
 
215
219
  def process_import(self, i: ast.ModulePath) -> None:
216
220
  """Process an import."""
@@ -218,87 +222,273 @@ class PyImportPass(JacImportPass):
218
222
  # This won't work with py imports as this will fail to import stuff in form of
219
223
  # from a import b
220
224
  # from a import (c, d, e)
221
- # Solution to that is to get the import node and check the from loc then
225
+ # Solution to that is to get the import node and check the from loc `then`
222
226
  # handle it based on if there a from loc or not
223
227
  imp_node = i.parent_of_type(ast.Import)
228
+
224
229
  if imp_node.is_py and not i.sub_module:
225
230
  if imp_node.from_loc:
226
- for j in imp_node.items.items:
227
- assert isinstance(j, ast.ModuleItem)
228
- mod_path = f"{imp_node.from_loc.dot_path_str}.{j.name.sym_name}"
229
- self.import_py_module(
230
- parent_node=j,
231
- mod_path=mod_path,
232
- imported_mod_name=(
233
- j.name.sym_name if not j.alias else j.alias.sym_name
234
- ),
235
- )
231
+ msg = "Processing import from node at href="
232
+ msg += ast.Module.get_href_path(imp_node)
233
+ msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
234
+ self.__debug_print(msg)
235
+ self.__process_import_from(imp_node)
236
+ else:
237
+ msg = "Processing import node at href="
238
+ msg += ast.Module.get_href_path(imp_node)
239
+ msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
240
+ self.__debug_print(msg)
241
+ self.__process_import(imp_node)
242
+
243
+ def __process_import_from(self, imp_node: ast.Import) -> None:
244
+ """Process imports in the form of `from X import I`."""
245
+ assert isinstance(self.ir, ast.Module)
246
+ assert isinstance(imp_node.from_loc, ast.ModulePath)
247
+
248
+ self.__debug_print(f"\tTrying to import {imp_node.from_loc.dot_path_str}")
249
+
250
+ # Attempt to import the Python module X and process it
251
+ imported_mod = self.__import_py_module(
252
+ parent_node_path=ast.Module.get_href_path(imp_node),
253
+ mod_path=imp_node.from_loc.dot_path_str,
254
+ )
255
+
256
+ if imported_mod:
257
+ # Cyclic imports will happen in case of import sys
258
+ # sys stub file imports sys module which means that we need
259
+ # to import sys stub file again in the sys stub file and so on
260
+ # This can be detected by iterating over all the parents and make sure
261
+ # that the parent is in another file than the imported module
262
+ if self.__check_cyclic_imports(imp_node, imported_mod):
263
+ self.__debug_print(
264
+ f"\tCycled imports is found at {imp_node.loc.mod_path} {imp_node.loc}"
265
+ )
266
+ return
267
+
268
+ if imported_mod.name == "builtins":
269
+ self.__debug_print(
270
+ f"\tIgnoring attaching builtins {imp_node.loc.mod_path} {imp_node.loc}"
271
+ )
272
+ return
273
+
274
+ self.__debug_print(
275
+ f"\tAttaching {imported_mod.name} into {ast.Module.get_href_path(imp_node)}"
276
+ )
277
+ msg = f"\tRegistering module:{imported_mod.name} to "
278
+ msg += f"import_from handling with {imp_node.loc.mod_path}:{imp_node.loc}"
279
+ self.__debug_print(msg)
280
+
281
+ self.attach_mod_to_node(imp_node.from_loc, imported_mod)
282
+ self.import_from_build_list.append((imp_node, imported_mod))
283
+ if imported_mod._sym_tab is None:
284
+ self.__debug_print(
285
+ f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
286
+ )
236
287
  else:
237
- for j in imp_node.items.items:
238
- assert isinstance(j, ast.ModulePath)
239
- self.import_py_module(
240
- parent_node=j,
241
- mod_path=j.dot_path_str,
242
- imported_mod_name=(
243
- j.dot_path_str.replace(".", "")
244
- if not j.alias
245
- else j.alias.sym_name
246
- ),
288
+ self.__debug_print(
289
+ f"\tRefreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
290
+ )
291
+ PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
292
+ DefUsePass(input_ir=imported_mod, prior=self)
293
+
294
+ def __import_from_symbol_table_build(self) -> None:
295
+ """Build symbol tables for the imported python modules."""
296
+ is_symbol_tabled_refreshed: list[str] = []
297
+ self.import_from_build_list.reverse()
298
+ for imp_node, imported_mod in self.import_from_build_list:
299
+ # Need to build the symbol tables again to make sure that the
300
+ # complete symbol table is built.
301
+ #
302
+ # Complete symbol tables won't be built in case of another
303
+ # import from statements in the imported modules.
304
+ #
305
+ # A solution was to only build the symbol table here after the
306
+ # full ast is raised but this will cause an issue with symboltable
307
+ # building with normal imports
308
+ #
309
+ # TODO: Change normal imports to call symbolTable here too
310
+
311
+ if imported_mod.loc.mod_path not in is_symbol_tabled_refreshed:
312
+ self.__debug_print(
313
+ f"Refreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
314
+ )
315
+ PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
316
+ DefUsePass(input_ir=imported_mod, prior=self)
317
+ is_symbol_tabled_refreshed.append(imported_mod.loc.mod_path)
318
+
319
+ sym_tab = imported_mod.sym_tab
320
+ parent_sym_tab = imp_node.parent_of_type(ast.Module).sym_tab
321
+
322
+ if imp_node.is_absorb:
323
+ for symbol in sym_tab.tab.values():
324
+ if symbol.sym_type == SymbolType.MODULE:
325
+ continue
326
+ self.__import_from_sym_table_add_symbols(symbol, parent_sym_tab)
327
+ else:
328
+ for i in imp_node.items.items:
329
+ assert isinstance(i, ast.ModuleItem)
330
+ needed_sym = sym_tab.lookup(i.name.sym_name)
331
+
332
+ if needed_sym and needed_sym.defn[0].parent:
333
+ self.__import_from_sym_table_add_symbols(
334
+ needed_sym, parent_sym_tab
335
+ )
336
+ else:
337
+ self.__debug_print(
338
+ f"Can't find a symbol matching {i.name.sym_name} in {sym_tab.name}"
339
+ )
340
+
341
+ def __import_from_sym_table_add_symbols(
342
+ self, sym: Symbol, sym_table: SymbolTable
343
+ ) -> None:
344
+ self.__debug_print(
345
+ f"\tAdding {sym.sym_type}:{sym.sym_name} into {sym_table.name}"
346
+ )
347
+ assert isinstance(sym.defn[0], ast.AstSymbolNode)
348
+ sym_table.def_insert(
349
+ node=sym.defn[0],
350
+ access_spec=sym.access,
351
+ force_overwrite=True,
352
+ )
353
+
354
+ if sym.fetch_sym_tab:
355
+ msg = f"\tAdding SymbolTable:{sym.fetch_sym_tab.name} into "
356
+ msg += f"SymbolTable:{sym_table.name} kids"
357
+ self.__debug_print(msg)
358
+ sym_table.kid.append(sym.fetch_sym_tab)
359
+ elif sym.sym_type not in (SymbolType.VAR, SymbolType.MOD_VAR):
360
+ raise AssertionError(
361
+ f"Unexpected symbol type '{sym.sym_type}' that doesn't have a symbl table"
362
+ )
363
+
364
+ def __process_import(self, imp_node: ast.Import) -> None:
365
+ """Process the imports in form of `import X`."""
366
+ # Expected that each ImportStatement will import one item
367
+ # In case of this assertion fired then we need to revisit this item
368
+ assert len(imp_node.items.items) == 1
369
+ imported_item = imp_node.items.items[0]
370
+ assert isinstance(imported_item, ast.ModulePath)
371
+
372
+ self.__debug_print(f"\tTrying to import {imported_item.dot_path_str}")
373
+ imported_mod = self.__import_py_module(
374
+ parent_node_path=ast.Module.get_href_path(imported_item),
375
+ mod_path=imported_item.dot_path_str,
376
+ imported_mod_name=(
377
+ # TODO: Check this replace
378
+ imported_item.dot_path_str.replace(".", "")
379
+ if not imported_item.alias
380
+ else imported_item.alias.sym_name
381
+ ),
382
+ )
383
+ if imported_mod:
384
+ if self.__check_cyclic_imports(imp_node, imported_mod):
385
+ self.__debug_print(
386
+ f"\tCycled imports is found at {imp_node.loc.mod_path} {imp_node.loc}"
387
+ )
388
+ return
389
+ elif imported_mod.name == "builtins":
390
+ self.__debug_print(
391
+ f"\tIgnoring attaching builtins {imp_node.loc.mod_path} {imp_node.loc}"
392
+ )
393
+ return
394
+
395
+ self.__debug_print(
396
+ f"\tAttaching {imported_mod.name} into {ast.Module.get_href_path(imp_node)}"
397
+ )
398
+ self.attach_mod_to_node(imported_item, imported_mod)
399
+
400
+ if imp_node.is_absorb:
401
+ msg = f"\tRegistering module:{imported_mod.name} to "
402
+ msg += f"import_from (import all) handling with {imp_node.loc.mod_path}:{imp_node.loc}"
403
+ self.__debug_print(msg)
404
+
405
+ self.import_from_build_list.append((imp_node, imported_mod))
406
+ if imported_mod._sym_tab is None:
407
+ self.__debug_print(
408
+ f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
409
+ )
410
+ else:
411
+ self.__debug_print(
412
+ f"\tRefreshing symbol table for module:{ast.Module.get_href_path(imported_mod)}"
247
413
  )
414
+ PyInspectSymTabBuildPass(input_ir=imported_mod, prior=self)
415
+ DefUsePass(input_ir=imported_mod, prior=self)
416
+
417
+ else:
418
+ self.__debug_print(
419
+ f"\tBuilding symbol table for module:{ast.Module.get_href_path(imported_mod)}"
420
+ )
421
+ SymTabBuildPass(input_ir=imported_mod, prior=self)
248
422
 
249
- def import_py_module(
423
+ def __import_py_module(
250
424
  self,
251
- parent_node: ast.ModulePath | ast.ModuleItem,
252
- imported_mod_name: str,
425
+ parent_node_path: str,
253
426
  mod_path: str,
427
+ imported_mod_name: Optional[str] = None,
254
428
  ) -> Optional[ast.Module]:
255
- """Import a module."""
429
+ """Import a python module."""
256
430
  from jaclang.compiler.passes.main import PyastBuildPass
257
431
 
258
432
  assert isinstance(self.ir, ast.Module)
259
433
 
260
- python_raise_map = self.ir.py_raise_map
261
- file_to_raise = None
434
+ python_raise_map = self.ir.py_info.py_raise_map
435
+ file_to_raise: Optional[str] = None
262
436
 
263
437
  if mod_path in python_raise_map:
264
438
  file_to_raise = python_raise_map[mod_path]
265
439
  else:
266
- resolved_mod_path = (
267
- f"{self.__get_current_module(parent_node)}.{imported_mod_name}"
268
- )
269
- assert isinstance(self.ir, ast.Module)
440
+ # TODO: Is it fine to use imported_mod_name or get it from mod_path?
441
+ resolved_mod_path = f"{parent_node_path}.{mod_path}"
442
+ resolved_mod_path = resolved_mod_path.replace("..", ".")
270
443
  resolved_mod_path = resolved_mod_path.replace(f"{self.ir.name}.", "")
271
444
  file_to_raise = python_raise_map.get(resolved_mod_path)
272
445
 
273
446
  if file_to_raise is None:
447
+ self.__debug_print("\tNo file is found to do the import")
274
448
  return None
275
449
 
450
+ self.__debug_print(f"\tFile used to do the import is {file_to_raise}")
451
+
276
452
  try:
277
- if file_to_raise not in {None, "built-in", "frozen"}:
278
- if file_to_raise in self.import_table:
279
- return self.import_table[file_to_raise]
280
-
281
- with open(file_to_raise, "r", encoding="utf-8") as f:
282
- file_source = f.read()
283
- mod = PyastBuildPass(
284
- input_ir=ast.PythonModuleAst(
285
- py_ast.parse(file_source),
286
- orig_src=ast.JacSource(file_source, file_to_raise),
287
- ),
288
- ).ir
289
- SubNodeTabPass(input_ir=mod, prior=self)
290
- if mod:
291
- mod.name = imported_mod_name
292
- self.import_table[file_to_raise] = mod
293
- self.attach_mod_to_node(parent_node, mod)
294
- SymTabBuildPass(input_ir=mod, prior=self)
295
- return mod
296
- else:
297
- raise self.ice(f"Failed to import python module {mod_path}")
453
+ if file_to_raise in {None, "built-in", "frozen"}:
454
+ return None
455
+
456
+ if file_to_raise in self.import_table:
457
+ self.__debug_print(
458
+ f"\t{file_to_raise} was raised before, getting it from cache"
459
+ )
460
+ return self.import_table[file_to_raise]
461
+
462
+ with open(file_to_raise, "r", encoding="utf-8") as f:
463
+ file_source = f.read()
464
+ mod = PyastBuildPass(
465
+ input_ir=ast.PythonModuleAst(
466
+ py_ast.parse(file_source),
467
+ orig_src=ast.JacSource(file_source, file_to_raise),
468
+ ),
469
+ ).ir
470
+ SubNodeTabPass(input_ir=mod, prior=self)
471
+
472
+ if mod:
473
+ mod.name = imported_mod_name if imported_mod_name else mod.name
474
+ if mod.name == "__init__":
475
+ mod_name = mod.loc.mod_path.split(os.sep)[-2]
476
+ self.__debug_print(
477
+ f"\tRaised the __init__ file and rename the mod to be {mod_name}"
478
+ )
479
+ mod.name = mod_name
480
+ self.import_table[file_to_raise] = mod
481
+ mod.py_info.is_raised_from_py = True
482
+ self.__debug_print(
483
+ f"\t{file_to_raise} is raised, adding it to the cache"
484
+ )
485
+ return mod
486
+ else:
487
+ raise self.ice(f"\tFailed to import python module {mod_path}")
488
+
298
489
  except Exception as e:
299
- self.error(f"Failed to import python module {mod_path}")
490
+ self.error(f"\tFailed to import python module {mod_path}")
300
491
  raise e
301
- return None
302
492
 
303
493
  def __load_builtins(self) -> None:
304
494
  """Pyraise builtins to help with builtins auto complete."""
@@ -330,3 +520,34 @@ class PyImportPass(JacImportPass):
330
520
  def annex_impl(self, node: ast.Module) -> None:
331
521
  """Annex impl and test modules."""
332
522
  return None
523
+
524
+ def __handle_different_site_packages(self, mod_path: str) -> str:
525
+ if "site-packages" in mod_path:
526
+ mod_path = mod_path[mod_path.index("site-packages") :]
527
+ return mod_path
528
+
529
+ def __check_cyclic_imports(
530
+ self, imp_node: ast.AstNode, imported_module: ast.Module
531
+ ) -> bool:
532
+ """Check cyclic imports that might happen."""
533
+ # Example of cyclic imports is import os
534
+ # In the os stub file it imports the real os module which will cause os
535
+ # stub to be raised again and so on
536
+ # Another example is numpy. numpy init file imports multidim array file
537
+ # which imports again more items from numpy and so on.
538
+ imp_node_file = self.__handle_different_site_packages(imp_node.loc.mod_path)
539
+ imported_module_file = self.__handle_different_site_packages(
540
+ imported_module.loc.mod_path
541
+ )
542
+ if imp_node_file == imported_module_file:
543
+ return True
544
+
545
+ parent: Optional[ast.AstNode] = imp_node.parent
546
+ while parent is not None:
547
+ parent_file = self.__handle_different_site_packages(parent.loc.mod_path)
548
+ if parent_file == imported_module_file:
549
+ return True
550
+ else:
551
+ parent = parent.find_parent_of_type(ast.Module)
552
+
553
+ return False
@@ -0,0 +1,103 @@
1
+ """Pass used to add the inherited symbols for architypes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import jaclang.compiler.absyntree as ast
8
+ from jaclang.compiler.passes import Pass
9
+ from jaclang.compiler.symtable import Symbol, SymbolTable
10
+ from jaclang.settings import settings
11
+
12
+
13
+ class InheritancePass(Pass):
14
+ """Add inherited abilities in the target symbol tables."""
15
+
16
+ def __debug_print(self, msg: str) -> None:
17
+ if settings.inherit_pass_debug:
18
+ self.log_info("[PyImportPass] " + msg)
19
+
20
+ def __lookup(self, name: str, sym_table: SymbolTable) -> Optional[Symbol]:
21
+ symbol = sym_table.lookup(name)
22
+ if symbol is None:
23
+ # Check if the needed symbol in builtins
24
+ builtins_symtable = self.ir.sym_tab.find_scope("builtins")
25
+ assert builtins_symtable is not None
26
+ symbol = builtins_symtable.lookup(name)
27
+ return symbol
28
+
29
+ def enter_architype(self, node: ast.Architype) -> None:
30
+ """Fill architype symbol tables with abilities from parent architypes."""
31
+ if node.base_classes is None:
32
+ return
33
+
34
+ for item in node.base_classes.items:
35
+ # The assumption is that the base class can only be a name node
36
+ # or an atom trailer only.
37
+ assert isinstance(item, (ast.Name, ast.AtomTrailer))
38
+
39
+ # In case of name node, then get the symbol table that contains
40
+ # the current class and lookup for that name after that use the
41
+ # symbol to get the symbol table of the base class
42
+ if isinstance(item, ast.Name):
43
+ assert node.sym_tab.parent is not None
44
+ base_class_symbol = self.__lookup(item.sym_name, node.sym_tab.parent)
45
+ if base_class_symbol is None:
46
+ msg = "Missing symbol for base class "
47
+ msg += f"{ast.Module.get_href_path(item)}.{item.sym_name}"
48
+ msg += f" needed for {ast.Module.get_href_path(node)}"
49
+ self.__debug_print(msg)
50
+ continue
51
+ base_class_symbol_table = base_class_symbol.fetch_sym_tab
52
+ if (
53
+ base_class_symbol_table is None
54
+ and base_class_symbol.defn[0]
55
+ .parent_of_type(ast.Module)
56
+ .py_info.is_raised_from_py
57
+ ):
58
+ msg = "Missing symbol table for python base class "
59
+ msg += f"{ast.Module.get_href_path(item)}.{item.sym_name}"
60
+ msg += f" needed for {ast.Module.get_href_path(node)}"
61
+ self.__debug_print(msg)
62
+ continue
63
+ assert base_class_symbol_table is not None
64
+ node.sym_tab.inherit_sym_tab(base_class_symbol_table)
65
+
66
+ # In case of atom trailer, unwind it and use each name node to
67
+ # as the code above to lookup for the base class
68
+ elif isinstance(item, ast.AtomTrailer):
69
+ current_sym_table = node.sym_tab.parent
70
+ not_found: bool = False
71
+ assert current_sym_table is not None
72
+ for name in item.as_attr_list:
73
+ sym = self.__lookup(name.sym_name, current_sym_table)
74
+ if sym is None:
75
+ msg = "Missing symbol for base class "
76
+ msg += f"{ast.Module.get_href_path(name)}.{name.sym_name}"
77
+ msg += f" needed for {ast.Module.get_href_path(node)}"
78
+ self.__debug_print(msg)
79
+ not_found = True
80
+ break
81
+ current_sym_table = sym.fetch_sym_tab
82
+
83
+ # In case of python nodes, the base class may not be
84
+ # raised so ignore these classes for now
85
+ # TODO Do we need to import these classes?
86
+ if (
87
+ sym.defn[0].parent_of_type(ast.Module).py_info.is_raised_from_py
88
+ and current_sym_table is None
89
+ ):
90
+ msg = "Missing symbol table for python base class "
91
+ msg += f"{ast.Module.get_href_path(name)}.{name.sym_name}"
92
+ msg += f" needed for {ast.Module.get_href_path(node)}"
93
+ self.__debug_print(msg)
94
+ not_found = True
95
+ break
96
+
97
+ assert current_sym_table is not None
98
+
99
+ if not_found:
100
+ continue
101
+
102
+ assert current_sym_table is not None
103
+ node.sym_tab.inherit_sym_tab(current_sym_table)
@@ -35,7 +35,7 @@ class PyCollectDepsPass(Pass):
35
35
  if isinstance(node, ast.ModulePath):
36
36
  if node.path:
37
37
  path = ".".join([i.value for i in node.path])
38
- node.abs_path = self.ir.py_mod_dep_map.get(path)
38
+ node.abs_path = self.ir.py_info.py_mod_dep_map.get(path)
39
39
  if node.abs_path and os.path.isfile(node.abs_path.replace(".pyi", ".py")):
40
40
  node.abs_path = node.abs_path.replace(".pyi", ".py")
41
41
 
@@ -45,7 +45,7 @@ class PyCollectDepsPass(Pass):
45
45
  if mod_path_node.path:
46
46
  path = ".".join([i.value for i in mod_path_node.path])
47
47
  path += f".{node.name.value}"
48
- node.abs_path = self.ir.py_mod_dep_map.get(path)
48
+ node.abs_path = self.ir.py_info.py_mod_dep_map.get(path)
49
49
  if node.abs_path and os.path.isfile(node.abs_path.replace(".pyi", ".py")):
50
50
  node.abs_path = node.abs_path.replace(".pyi", ".py")
51
51
 
@@ -61,17 +61,17 @@ class PyCollectDepsPass(Pass):
61
61
  else:
62
62
  mod_name = node_full_name
63
63
 
64
- if mod_name not in self.ir.py_mod_dep_map:
64
+ if mod_name not in self.ir.py_info.py_mod_dep_map:
65
65
  self.__debug_print(
66
66
  f"Can't find a python file associated with {type(node)}::{node.loc}"
67
67
  )
68
68
  return
69
69
 
70
- mode_path = self.ir.py_mod_dep_map[mod_name]
70
+ mode_path = self.ir.py_info.py_mod_dep_map[mod_name]
71
71
  if mode_path.endswith(".jac"):
72
72
  return
73
73
 
74
- self.ir.py_raise_map[mod_name] = mode_path
74
+ self.ir.py_info.py_raise_map[mod_name] = mode_path
75
75
  else:
76
76
  self.__debug_print(
77
77
  f"Collect python dependencies is not supported in {type(node)}::{node.loc}"
@@ -2482,25 +2482,6 @@ class PyastGenPass(Pass):
2482
2482
  return func_node.gen.py_ast
2483
2483
  elif node.op.name == Tok.PIPE_FWD and isinstance(node.right, ast.TupleVal):
2484
2484
  self.error("Invalid pipe target.")
2485
- elif node.op.name == Tok.ELVIS_OP:
2486
- self.needs_jac_feature()
2487
- return [
2488
- self.sync(
2489
- ast3.Call(
2490
- func=self.sync(
2491
- ast3.Attribute(
2492
- value=self.sync(
2493
- ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
2494
- ),
2495
- attr="elvis",
2496
- ctx=ast3.Load(),
2497
- )
2498
- ),
2499
- args=[node.left.gen.py_ast[0], node.right.gen.py_ast[0]],
2500
- keywords=[],
2501
- )
2502
- )
2503
- ]
2504
2485
  else:
2505
2486
  self.error(
2506
2487
  f"Binary operator {node.op.value} not supported in bootstrap Jac"
@@ -129,7 +129,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
129
129
  is_imported=False,
130
130
  kid=valid,
131
131
  )
132
- ret.is_raised_from_py = True
132
+ ret.py_info.is_raised_from_py = True
133
133
  return self.nu(ret)
134
134
 
135
135
  def proc_function_def(
@@ -20,6 +20,7 @@ from .fuse_typeinfo_pass import FuseTypeInfoPass # noqa: I100
20
20
  from .registry_pass import RegistryPass # noqa: I100
21
21
  from .access_modifier_pass import AccessCheckPass # noqa: I100
22
22
  from .py_collect_dep_pass import PyCollectDepsPass # noqa: I100
23
+ from .inheritance_pass import InheritancePass # noqa: I100
23
24
 
24
25
  py_code_gen = [
25
26
  SubNodeTabPass,
@@ -38,6 +39,7 @@ type_checker_sched = [
38
39
  PyCollectDepsPass,
39
40
  PyImportPass,
40
41
  DefUsePass,
42
+ InheritancePass,
41
43
  FuseTypeInfoPass,
42
44
  AccessCheckPass,
43
45
  ]
@@ -4,6 +4,8 @@ This pass builds the symbol table tree for the Jaseci Ast. It also adds symbols
4
4
  for globals, imports, architypes, and abilities declarations and definitions.
5
5
  """
6
6
 
7
+ from typing import TypeVar
8
+
7
9
  import jaclang.compiler.absyntree as ast
8
10
  from jaclang.compiler.passes import Pass
9
11
  from jaclang.compiler.symtable import SymbolTable
@@ -19,6 +21,7 @@ class SymTabBuildPass(Pass):
19
21
  def push_scope(self, name: str, key_node: ast.AstNode) -> None:
20
22
  """Push scope."""
21
23
  inherit = key_node.parent
24
+
22
25
  if not len(self.cur_sym_tab) and not inherit:
23
26
  self.cur_sym_tab.append(SymbolTable(name, key_node))
24
27
  elif not len(self.cur_sym_tab) and inherit:
@@ -1284,3 +1287,17 @@ class SymTabBuildPass(Pass):
1284
1287
  def enter_comment_token(self, node: ast.CommentToken) -> None:
1285
1288
  """Sub objects."""
1286
1289
  self.sync_node_to_scope(node)
1290
+
1291
+
1292
+ T = TypeVar("T", bound=ast.AstNode)
1293
+
1294
+
1295
+ class PyInspectSymTabBuildPass(SymTabBuildPass):
1296
+ """Jac Symbol table build pass."""
1297
+
1298
+ def push_scope(self, name: str, key_node: ast.AstNode) -> None:
1299
+ """Push scope."""
1300
+ if not len(self.cur_sym_tab):
1301
+ self.cur_sym_tab.append(SymbolTable(name, key_node))
1302
+ else:
1303
+ self.cur_sym_tab.append(self.cur_scope.push_kid_scope(name, key_node))