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.
- jaclang/cli/cli.py +3 -0
- jaclang/compiler/__init__.py +1 -1
- jaclang/compiler/absyntree.py +30 -5
- jaclang/compiler/compile.py +1 -1
- jaclang/compiler/constant.py +0 -1
- jaclang/compiler/jac.lark +1 -5
- jaclang/compiler/parser.py +2 -10
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +62 -33
- jaclang/compiler/passes/main/import_pass.py +285 -64
- jaclang/compiler/passes/main/inheritance_pass.py +103 -0
- jaclang/compiler/passes/main/py_collect_dep_pass.py +5 -5
- jaclang/compiler/passes/main/pyast_gen_pass.py +0 -19
- jaclang/compiler/passes/main/pyast_load_pass.py +1 -1
- jaclang/compiler/passes/main/schedules.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +17 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +130 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -3
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -3
- jaclang/compiler/passes/main/tests/test_import_pass.py +16 -17
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +24 -0
- jaclang/compiler/passes/main/type_check_pass.py +3 -2
- jaclang/compiler/passes/tool/jac_formatter_pass.py +0 -1
- jaclang/compiler/py_info.py +22 -0
- jaclang/compiler/symtable.py +9 -2
- jaclang/compiler/tests/test_importer.py +45 -1
- jaclang/langserve/tests/test_server.py +2 -2
- jaclang/plugin/default.py +86 -62
- jaclang/plugin/feature.py +2 -5
- jaclang/plugin/spec.py +1 -6
- jaclang/runtimelib/architype.py +20 -16
- jaclang/runtimelib/importer.py +26 -3
- jaclang/runtimelib/machine.py +2 -2
- jaclang/runtimelib/test.py +59 -4
- jaclang/runtimelib/utils.py +15 -0
- jaclang/settings.py +3 -0
- jaclang/tests/fixtures/base_class1.jac +11 -0
- jaclang/tests/fixtures/base_class2.jac +11 -0
- jaclang/tests/fixtures/import_all.jac +7 -0
- jaclang/tests/fixtures/import_all_py.py +8 -0
- jaclang/tests/fixtures/jactest_imported.jac +6 -0
- jaclang/tests/fixtures/jactest_main.jac +22 -0
- jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
- jaclang/tests/fixtures/test_py.py +12 -0
- jaclang/tests/fixtures/visit_sequence.jac +50 -0
- jaclang/tests/test_cli.py +83 -1
- jaclang/tests/test_language.py +24 -9
- jaclang/utils/helpers.py +9 -1
- jaclang/utils/test.py +2 -2
- jaclang/utils/tests/test_lang_tools.py +4 -2
- jaclang/utils/treeprinter.py +6 -3
- {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/METADATA +3 -3
- {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/RECORD +56 -45
- {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/WHEEL +1 -1
- {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.
|
|
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
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
|
423
|
+
def __import_py_module(
|
|
250
424
|
self,
|
|
251
|
-
|
|
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
|
-
|
|
267
|
-
|
|
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
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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"
|
|
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"
|
|
@@ -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))
|