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
@@ -6,8 +6,7 @@ mypy apis into Jac and use jac py ast in it.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import traceback
10
- from typing import Callable, TypeVar
9
+ from typing import Callable, Optional, TypeVar
11
10
 
12
11
  import jaclang.compiler.absyntree as ast
13
12
  from jaclang.compiler.passes import Pass
@@ -30,12 +29,12 @@ class FuseTypeInfoPass(Pass):
30
29
 
31
30
  node_type_hash: dict[MypyNodes.Node | VNode, MyType] = {}
32
31
 
33
- def __debug_print(self, *argv: object) -> None:
32
+ def __debug_print(self, msg: str) -> None:
34
33
  if settings.fuse_type_info_debug:
35
- print("FuseTypeInfo::", *argv)
34
+ self.log_info("FuseTypeInfo::" + msg)
36
35
 
37
36
  def __call_type_handler(
38
- self, node: ast.AstSymbolNode, mypy_type: MypyTypes.ProperType
37
+ self, node: ast.AstSymbolNode, mypy_type: MypyTypes.Type
39
38
  ) -> None:
40
39
  mypy_type_name = pascal_to_snake(mypy_type.__class__.__name__)
41
40
  type_handler_name = f"get_type_from_{mypy_type_name}"
@@ -53,6 +52,9 @@ class FuseTypeInfoPass(Pass):
53
52
  if typ[0] == "builtins":
54
53
  return
55
54
 
55
+ if node.sym_type == "types.ModuleType" and node.sym:
56
+ node.name_spec.type_sym_tab = node.sym.parent_tab.find_scope(node.sym_name)
57
+
56
58
  assert isinstance(self.ir, ast.Module)
57
59
 
58
60
  if typ_sym_table:
@@ -66,13 +68,44 @@ class FuseTypeInfoPass(Pass):
66
68
  if typ_sym_table != self.ir.sym_tab:
67
69
  node.name_spec.type_sym_tab = typ_sym_table
68
70
 
71
+ def __collect_python_dependencies(self, node: ast.AstNode) -> None:
72
+ assert isinstance(node, ast.AstSymbolNode)
73
+ assert isinstance(self.ir, ast.Module)
74
+
75
+ mypy_node = node.gen.mypy_ast[0]
76
+
77
+ if isinstance(mypy_node, MypyNodes.RefExpr) and mypy_node.node:
78
+ node_full_name = mypy_node.node.fullname
79
+ if "." in node_full_name:
80
+ mod_name = node_full_name[: node_full_name.rindex(".")]
81
+ else:
82
+ mod_name = node_full_name
83
+
84
+ if mod_name not in self.ir.py_mod_dep_map:
85
+ self.__debug_print(
86
+ f"Can't find a python file associated with {type(node)}::{node.loc}"
87
+ )
88
+ return
89
+
90
+ mode_path = self.ir.py_mod_dep_map[mod_name]
91
+ if mode_path.endswith(".jac"):
92
+ return
93
+
94
+ self.ir.py_raise_map[mod_name] = mode_path
95
+ else:
96
+ self.__debug_print(
97
+ f"Collect python dependencies is not supported in {type(node)}::{node.loc}"
98
+ )
99
+
69
100
  @staticmethod
70
101
  def __handle_node(
71
102
  func: Callable[[FuseTypeInfoPass, T], None]
72
103
  ) -> Callable[[FuseTypeInfoPass, T], None]:
73
104
  def node_handler(self: FuseTypeInfoPass, node: T) -> None:
74
105
  if not isinstance(node, ast.AstSymbolNode):
75
- print(f"Warning {node.__class__.__name__} is not an AstSymbolNode")
106
+ self.__debug_print(
107
+ f"Warning {node.__class__.__name__} is not an AstSymbolNode"
108
+ )
76
109
 
77
110
  try:
78
111
  jac_node_str = f'jac node "{node.loc}::{node.__class__.__name__}'
@@ -85,6 +118,7 @@ class FuseTypeInfoPass(Pass):
85
118
  if len(node.gen.mypy_ast) == 1:
86
119
  func(self, node)
87
120
  self.__set_sym_table_link(node)
121
+ self.__collect_python_dependencies(node)
88
122
 
89
123
  # Jac node has multiple mypy nodes linked to it
90
124
  elif len(node.gen.mypy_ast) > 1:
@@ -100,27 +134,28 @@ class FuseTypeInfoPass(Pass):
100
134
  # Check the number of unique mypy nodes linked
101
135
  if len(temp) > 1:
102
136
  self.__debug_print(
103
- jac_node_str, "has multiple mypy nodes associated to it"
137
+ f"{jac_node_str} has multiple mypy nodes associated to it"
104
138
  )
105
139
  else:
106
140
  self.__debug_print(
107
- jac_node_str, "has duplicate mypy nodes associated to it"
141
+ f"{jac_node_str} has duplicate mypy nodes associated to it"
108
142
  )
109
143
  func(self, node)
110
144
  self.__set_sym_table_link(node)
145
+ self.__collect_python_dependencies(node)
111
146
 
112
147
  # Jac node doesn't have mypy nodes linked to it
113
148
  else:
114
149
  self.__debug_print(
115
- jac_node_str, "doesn't have mypy node associated to it"
150
+ f"{jac_node_str} doesn't have mypy node associated to it"
116
151
  )
117
152
 
118
153
  except AttributeError as e:
154
+ if settings.fuse_type_info_debug:
155
+ raise e
119
156
  self.__debug_print(
120
- f'Internal error happened while parsing "{e.obj.__class__.__name__}"'
157
+ f'{node.loc}: Internal error happened while parsing "{e.obj.__class__.__name__}"'
121
158
  )
122
- traceback.print_exc()
123
- print(e)
124
159
 
125
160
  return node_handler
126
161
 
@@ -129,12 +164,7 @@ class FuseTypeInfoPass(Pass):
129
164
 
130
165
  if isinstance(mypy_node, MypyNodes.MemberExpr):
131
166
  if mypy_node in self.node_type_hash:
132
- t = str(self.node_type_hash[mypy_node])
133
- if "def" in t and "->" in t:
134
- t = t.split("->")[1].strip()
135
- elif "def" in t:
136
- t = "None"
137
- node.name_spec.sym_type = t
167
+ self.__call_type_handler(node, self.node_type_hash[mypy_node])
138
168
  else:
139
169
  self.__debug_print(f"{node.loc} MemberExpr type is not found")
140
170
 
@@ -159,8 +189,8 @@ class FuseTypeInfoPass(Pass):
159
189
 
160
190
  else:
161
191
  self.__debug_print(
162
- f'"{node.loc}::{node.__class__.__name__}" mypy (with node attr) node isn\'t supported',
163
- type(mypy_node),
192
+ f'"{node.loc}::{node.__class__.__name__}" mypy (with node attr) node isn\'t supported'
193
+ f"{type(mypy_node)}"
164
194
  )
165
195
 
166
196
  else:
@@ -175,8 +205,8 @@ class FuseTypeInfoPass(Pass):
175
205
  self.__call_type_handler(node, mypy_node.func.type.ret_type)
176
206
  else:
177
207
  self.__debug_print(
178
- f'"{node.loc}::{node.__class__.__name__}" mypy node isn\'t supported',
179
- type(mypy_node),
208
+ f'"{node.loc}::{node.__class__.__name__}" mypy node isn\'t supported'
209
+ f"{type(mypy_node)}"
180
210
  )
181
211
 
182
212
  @__handle_node
@@ -187,12 +217,12 @@ class FuseTypeInfoPass(Pass):
187
217
  @__handle_node
188
218
  def enter_module_path(self, node: ast.ModulePath) -> None:
189
219
  """Pass handler for ModulePath nodes."""
190
- self.__debug_print("Getting type not supported in", type(node))
220
+ self.__debug_print(f"Getting type not supported in {type(node)}")
191
221
 
192
222
  @__handle_node
193
223
  def enter_module_item(self, node: ast.ModuleItem) -> None:
194
224
  """Pass handler for ModuleItem nodes."""
195
- self.__debug_print("Getting type not supported in", type(node))
225
+ self.__debug_print(f"Getting type not supported in {type(node)}")
196
226
 
197
227
  @__handle_node
198
228
  def enter_architype(self, node: ast.Architype) -> None:
@@ -202,7 +232,7 @@ class FuseTypeInfoPass(Pass):
202
232
  @__handle_node
203
233
  def enter_arch_def(self, node: ast.ArchDef) -> None:
204
234
  """Pass handler for ArchDef nodes."""
205
- self.__debug_print("Getting type not supported in", type(node))
235
+ self.__debug_print(f"Getting type not supported in {type(node)}")
206
236
 
207
237
  @__handle_node
208
238
  def enter_enum(self, node: ast.Enum) -> None:
@@ -212,7 +242,7 @@ class FuseTypeInfoPass(Pass):
212
242
  @__handle_node
213
243
  def enter_enum_def(self, node: ast.EnumDef) -> None:
214
244
  """Pass handler for EnumDef nodes."""
215
- self.__debug_print("Getting type not supported in", type(node))
245
+ self.__debug_print(f"Getting type not supported in {type(node)}")
216
246
 
217
247
  @__handle_node
218
248
  def enter_ability(self, node: ast.Ability) -> None:
@@ -221,8 +251,8 @@ class FuseTypeInfoPass(Pass):
221
251
  self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
222
252
  else:
223
253
  self.__debug_print(
224
- f"{node.loc}: Can't get type of an ability from mypy node other than Ability.",
225
- type(node.gen.mypy_ast[0]),
254
+ f"{node.loc}: Can't get type of an ability from mypy node other than Ability. "
255
+ f"{type(node.gen.mypy_ast[0])}"
226
256
  )
227
257
 
228
258
  @__handle_node
@@ -232,8 +262,8 @@ class FuseTypeInfoPass(Pass):
232
262
  self.__call_type_handler(node, node.gen.mypy_ast[0].type.ret_type)
233
263
  else:
234
264
  self.__debug_print(
235
- f"{node.loc}: Can't get type of an AbilityDef from mypy node other than FuncDef.",
236
- type(node.gen.mypy_ast[0]),
265
+ f"{node.loc}: Can't get type of an AbilityDef from mypy node other than FuncDef. "
266
+ f"{type(node.gen.mypy_ast[0])}"
237
267
  )
238
268
 
239
269
  @__handle_node
@@ -279,7 +309,7 @@ class FuseTypeInfoPass(Pass):
279
309
  @__handle_node
280
310
  def enter_f_string(self, node: ast.FString) -> None:
281
311
  """Pass handler for FString nodes."""
282
- self.__debug_print("Getting type not supported in", type(node))
312
+ self.__debug_print(f"Getting type not supported in {type(node)}")
283
313
 
284
314
  @__handle_node
285
315
  def enter_list_val(self, node: ast.ListVal) -> None:
@@ -332,7 +362,7 @@ class FuseTypeInfoPass(Pass):
332
362
  @__handle_node
333
363
  def enter_index_slice(self, node: ast.IndexSlice) -> None:
334
364
  """Pass handler for IndexSlice nodes."""
335
- self.__debug_print("Getting type not supported in", type(node))
365
+ self.__debug_print(f"Getting type not supported in {type(node)}")
336
366
 
337
367
  @__handle_node
338
368
  def enter_arch_ref(self, node: ast.ArchRef) -> None:
@@ -346,8 +376,8 @@ class FuseTypeInfoPass(Pass):
346
376
  self.__call_type_handler(node, mypy_node2.type)
347
377
  else:
348
378
  self.__debug_print(
349
- f"{node.loc}: Can't get ArchRef value from mypyNode other than ClassDef",
350
- type(node.gen.mypy_ast[0]),
379
+ f"{node.loc}: Can't get ArchRef value from mypyNode other than ClassDef "
380
+ f"type(node.gen.mypy_ast[0])"
351
381
  )
352
382
 
353
383
  @__handle_node
@@ -363,12 +393,12 @@ class FuseTypeInfoPass(Pass):
363
393
  @__handle_node
364
394
  def enter_filter_compr(self, node: ast.FilterCompr) -> None:
365
395
  """Pass handler for FilterCompr nodes."""
366
- self.__debug_print("Getting type not supported in", type(node))
396
+ self.__debug_print(f"Getting type not supported in {type(node)}")
367
397
 
368
398
  @__handle_node
369
399
  def enter_assign_compr(self, node: ast.AssignCompr) -> None:
370
400
  """Pass handler for AssignCompr nodes."""
371
- self.__debug_print("Getting type not supported in", type(node))
401
+ self.__debug_print(f"Getting type not supported in {type(node)}")
372
402
 
373
403
  @__handle_node
374
404
  def enter_int(self, node: ast.Int) -> None:
@@ -405,7 +435,7 @@ class FuseTypeInfoPass(Pass):
405
435
  self, node: ast.AstSymbolNode, mypy_type: MypyTypes.CallableType
406
436
  ) -> None:
407
437
  """Get type info from mypy type CallableType."""
408
- node.name_spec.sym_type = str(mypy_type.ret_type)
438
+ self.__call_type_handler(node, mypy_type.ret_type)
409
439
 
410
440
  # TODO: Which overloaded function to get the return value from?
411
441
  def get_type_from_overloaded(
@@ -432,6 +462,12 @@ class FuseTypeInfoPass(Pass):
432
462
  """Get type info from mypy type TupleType."""
433
463
  node.name_spec.sym_type = "builtins.tuple"
434
464
 
465
+ def get_type_from_type_type(
466
+ self, node: ast.AstSymbolNode, mypy_type: MypyTypes.TypeType
467
+ ) -> None:
468
+ """Get type info from mypy type TypeType."""
469
+ node.name_spec.sym_type = str(mypy_type.item)
470
+
435
471
  def exit_assignment(self, node: ast.Assignment) -> None:
436
472
  """Add new symbols in the symbol table in case of self."""
437
473
  # This will fix adding new items to the class through self
@@ -447,20 +483,85 @@ class FuseTypeInfoPass(Pass):
447
483
  if self_obj.type_sym_tab and isinstance(right_obj, ast.AstSymbolNode):
448
484
  self_obj.type_sym_tab.def_insert(right_obj)
449
485
 
450
- def exit_name(self, node: ast.Name) -> None:
451
- """Add new symbols in the symbol table in case of atom trailer."""
452
- if isinstance(node.parent, ast.AtomTrailer):
453
- target_node = node.parent.target
454
- if isinstance(target_node, ast.AstSymbolNode):
455
- parent_symbol_table = target_node.type_sym_tab
456
- if isinstance(parent_symbol_table, SymbolTable):
457
- node.sym = parent_symbol_table.lookup(node.sym_name)
458
-
459
- # def exit_in_for_stmt(self, node: ast.InForStmt):
460
- # print(node.loc.mod_path, node.loc)
461
- # print(node.target, node.target.loc)
462
- # # node.sym_tab.def_insert()
463
- # # exit()
464
-
465
- # def after_pass(self) -> None:
466
- # exit()
486
+ def expand_atom_trailer(self, node_list: list[ast.AstNode]) -> list[ast.AstNode]:
487
+ """Expand the atom trailer object in a list of AstNode."""
488
+ out: list[ast.AstNode] = []
489
+ for i in node_list:
490
+ if isinstance(i, ast.AtomTrailer):
491
+ out.append(i.target)
492
+ out.append(i.right)
493
+ elif isinstance(i, ast.FuncCall):
494
+ out.append(i.target)
495
+ else:
496
+ out.append(i)
497
+ return out
498
+
499
+ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None:
500
+ """Adding symbol links to AtomTrailer right nodes."""
501
+ # This will fix adding the symbol links to nodes in atom trailer
502
+ # self.x.z = 5 # will add symbol links to both x and z
503
+
504
+ # This function originally used `as_attr_list` in AtomTrailer
505
+ # but an issue happened when doing stuff like fool_me().CONST_VALUE2
506
+ # The issue was due to the way `as_attr_list` implemented so the fix
507
+ # was to implement it again to get all items in AtomTrailer even if
508
+ # their type is not an AstSymbolNode
509
+ atom_trailer_unwind = self.expand_atom_trailer([node])
510
+ iteration_count = 0
511
+ while any(
512
+ isinstance(i, (ast.AtomTrailer, ast.FuncCall)) for i in atom_trailer_unwind
513
+ ):
514
+ atom_trailer_unwind = self.expand_atom_trailer(atom_trailer_unwind)
515
+ iteration_count += 1
516
+ if iteration_count > 50:
517
+ break
518
+
519
+ for i in range(1, len(atom_trailer_unwind)):
520
+ left = atom_trailer_unwind[i - 1]
521
+ right = atom_trailer_unwind[i]
522
+
523
+ assert isinstance(left, ast.AstSymbolNode)
524
+ assert isinstance(right, ast.AstSymbolNode)
525
+
526
+ if isinstance(right, ast.IndexSlice):
527
+ # In case of index slice, left won't have a symbol table as it's a list/dict/set
528
+ node_type: str = ""
529
+
530
+ # left type is a list
531
+ if left.sym_type.startswith("builtins.list["):
532
+ node_type = left.sym_type[len("builtins.list[") : -1]
533
+
534
+ # right index slice is a range then it's type is the same as left
535
+ if right.is_range:
536
+ right.name_spec.sym_type = left.sym_type
537
+ continue
538
+
539
+ # left type is a dictionary
540
+ elif left.sym_type.startswith("builtins.dict["):
541
+ node_type = (
542
+ left.sym_type[len("builtins.dict[") : -1].split(",")[1].strip()
543
+ )
544
+
545
+ # unsupported type
546
+ else:
547
+ continue
548
+
549
+ right.name_spec.sym_type = node_type
550
+
551
+ # Getting the correct symbol table and link it
552
+ type_symtab: Optional[SymbolTable] = self.ir.sym_tab
553
+ assert isinstance(self.ir, ast.Module)
554
+ assert isinstance(type_symtab, SymbolTable)
555
+ for j in node_type.split("."):
556
+ if j == self.ir.name:
557
+ continue
558
+ type_symtab = type_symtab.find_scope(j)
559
+ if type_symtab is None:
560
+ break
561
+ right.name_spec.type_sym_tab = type_symtab
562
+
563
+ else:
564
+ if left.type_sym_tab:
565
+ right.name_spec.sym = left.type_sym_tab.lookup(right.sym_name)
566
+ if right.name_spec.sym:
567
+ right.name_spec.sym.add_use(right.name_spec)
@@ -6,17 +6,17 @@ symbols are available for matching.
6
6
  """
7
7
 
8
8
  import ast as py_ast
9
- import importlib.util
10
9
  import os
11
- import sys
12
10
  from typing import Optional
13
11
 
14
12
 
15
13
  import jaclang.compiler.absyntree as ast
16
14
  from jaclang.compiler.passes import Pass
17
- from jaclang.compiler.passes.main import SubNodeTabPass
18
- from jaclang.settings import settings
19
- from jaclang.utils.helpers import import_target_to_relative_path, is_standard_lib_module
15
+ from jaclang.compiler.passes.main import SubNodeTabPass, SymTabBuildPass
16
+ from jaclang.utils.log import logging
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  class JacImportPass(Pass):
@@ -25,40 +25,29 @@ class JacImportPass(Pass):
25
25
  def before_pass(self) -> None:
26
26
  """Run once before pass."""
27
27
  self.import_table: dict[str, ast.Module] = {}
28
- self.__py_imports: set[str] = set()
29
- self.py_resolve_list: set[str] = set()
30
28
 
31
29
  def enter_module(self, node: ast.Module) -> None:
32
30
  """Run Importer."""
33
31
  self.cur_node = node
34
32
  self.import_table[node.loc.mod_path] = node
35
- self.__py_imports.clear()
36
- self.__annex_impl(node)
33
+ self.annex_impl(node)
37
34
  self.terminate() # Turns off auto traversal for deliberate traversal
38
35
  self.run_again = True
39
36
  while self.run_again:
40
37
  self.run_again = False
41
38
  all_imports = self.get_all_sub_nodes(node, ast.ModulePath)
42
39
  for i in all_imports:
43
- self.process_import(node, i)
40
+ self.process_import(i)
44
41
  self.enter_module_path(i)
45
42
  SubNodeTabPass(prior=self, input_ir=node)
46
43
 
47
- for i in self.get_all_sub_nodes(node, ast.AtomTrailer):
48
- self.enter_atom_trailer(i)
49
-
50
- node.mod_deps = self.import_table
44
+ node.mod_deps.update(self.import_table)
51
45
 
52
- def process_import(self, node: ast.Module, i: ast.ModulePath) -> None:
46
+ def process_import(self, i: ast.ModulePath) -> None:
53
47
  """Process an import."""
54
- lang = i.parent_of_type(ast.Import).hint.tag.value
55
- if lang == "jac" and not i.sub_module:
56
- self.import_jac_module(
57
- node=i,
58
- mod_path=node.loc.mod_path,
59
- )
60
- elif lang == "py":
61
- self.__py_imports.add(i.path_str)
48
+ imp_node = i.parent_of_type(ast.Import)
49
+ if imp_node.is_jac and not i.sub_module:
50
+ self.import_jac_module(node=i)
62
51
 
63
52
  def attach_mod_to_node(
64
53
  self, node: ast.ModulePath | ast.ModuleItem, mod: ast.Module | None
@@ -67,10 +56,11 @@ class JacImportPass(Pass):
67
56
  if mod:
68
57
  self.run_again = True
69
58
  node.sub_module = mod
70
- self.__annex_impl(mod)
59
+ self.annex_impl(mod)
71
60
  node.add_kids_right([mod], pos_update=False)
61
+ mod.parent = node
72
62
 
73
- def __annex_impl(self, node: ast.Module) -> None:
63
+ def annex_impl(self, node: ast.Module) -> None:
74
64
  """Annex impl and test modules."""
75
65
  if node.stub_only:
76
66
  return
@@ -131,19 +121,10 @@ class JacImportPass(Pass):
131
121
  node.sub_module.name = node.alias.value
132
122
  # Items matched during def/decl pass
133
123
 
134
- def enter_atom_trailer(self, node: ast.AtomTrailer) -> None:
135
- """Iterate on AtomTrailer nodes to get python paths to resolve."""
136
- if node.as_attr_list[0].sym_name in self.__py_imports:
137
- self.py_resolve_list.add(".".join([i.sym_name for i in node.as_attr_list]))
138
-
139
- def import_jac_module(self, node: ast.ModulePath, mod_path: str) -> None:
124
+ def import_jac_module(self, node: ast.ModulePath) -> None:
140
125
  """Import a module."""
141
126
  self.cur_node = node # impacts error reporting
142
- target = import_target_to_relative_path(
143
- level=node.level,
144
- target=node.path_str,
145
- base_path=os.path.dirname(node.loc.mod_path),
146
- )
127
+ target = node.resolve_relative_path()
147
128
  # If the module is a package (dir)
148
129
  if os.path.isdir(target):
149
130
  self.attach_mod_to_node(node, self.import_jac_mod_from_dir(target))
@@ -153,11 +134,7 @@ class JacImportPass(Pass):
153
134
  # Import all from items as modules or packages
154
135
  for i in import_node.items.items:
155
136
  if isinstance(i, ast.ModuleItem):
156
- from_mod_target = import_target_to_relative_path(
157
- level=node.level,
158
- target=node.path_str + "." + i.name.value,
159
- base_path=os.path.dirname(node.loc.mod_path),
160
- )
137
+ from_mod_target = node.resolve_relative_path(i.name.value)
161
138
  # If package
162
139
  if os.path.isdir(from_mod_target):
163
140
  self.attach_mod_to_node(
@@ -204,7 +181,7 @@ class JacImportPass(Pass):
204
181
  self.warnings_had += mod_pass.warnings_had
205
182
  mod = mod_pass.ir
206
183
  except Exception as e:
207
- print(e)
184
+ logger.info(e)
208
185
  mod = None
209
186
  if isinstance(mod, ast.Module):
210
187
  self.import_table[target] = mod
@@ -222,55 +199,102 @@ class PyImportPass(JacImportPass):
222
199
  def before_pass(self) -> None:
223
200
  """Only run pass if settings are set to raise python."""
224
201
  super().before_pass()
225
- if not settings.py_raise:
226
- self.terminate()
227
202
 
228
- def process_import(self, node: ast.Module, i: ast.ModulePath) -> None:
203
+ def __get_current_module(self, node: ast.AstNode) -> str:
204
+ parent = node.find_parent_of_type(ast.Module)
205
+ mod_list = []
206
+ while parent is not None:
207
+ mod_list.append(parent)
208
+ parent = parent.find_parent_of_type(ast.Module)
209
+ mod_list.reverse()
210
+ return ".".join(p.name for p in mod_list)
211
+
212
+ def process_import(self, i: ast.ModulePath) -> None:
229
213
  """Process an import."""
230
- lang = i.parent_of_type(ast.Import).hint.tag.value
231
- if lang == "py" and not i.sub_module and not is_standard_lib_module(i.path_str):
232
- mod = self.import_py_module(node=i, mod_path=node.loc.mod_path)
233
- if mod:
234
- i.sub_module = mod
235
- i.add_kids_right([mod], pos_update=False)
236
- if settings.py_raise_deep:
237
- self.run_again = True
214
+ # Process import is orginally implemented to handle ModulePath in Jaclang
215
+ # This won't work with py imports as this will fail to import stuff in form of
216
+ # from a import b
217
+ # from a import (c, d, e)
218
+ # Solution to that is to get the import node and check the from loc then
219
+ # handle it based on if there a from loc or not
220
+ imp_node = i.parent_of_type(ast.Import)
221
+ if imp_node.is_py and not i.sub_module:
222
+ if imp_node.from_loc:
223
+ for j in imp_node.items.items:
224
+ assert isinstance(j, ast.ModuleItem)
225
+ mod_path = f"{imp_node.from_loc.dot_path_str}.{j.name.sym_name}"
226
+ self.import_py_module(
227
+ parent_node=j,
228
+ mod_path=mod_path,
229
+ imported_mod_name=(
230
+ j.name.sym_name if not j.alias else j.alias.sym_name
231
+ ),
232
+ )
233
+ else:
234
+ for j in imp_node.items.items:
235
+ assert isinstance(j, ast.ModulePath)
236
+ self.import_py_module(
237
+ parent_node=j,
238
+ mod_path=j.dot_path_str,
239
+ imported_mod_name=(
240
+ j.dot_path_str.replace(".", "")
241
+ if not j.alias
242
+ else j.alias.sym_name
243
+ ),
244
+ )
238
245
 
239
246
  def import_py_module(
240
- self, node: ast.ModulePath, mod_path: str
247
+ self,
248
+ parent_node: ast.ModulePath | ast.ModuleItem,
249
+ imported_mod_name: str,
250
+ mod_path: str,
241
251
  ) -> Optional[ast.Module]:
242
252
  """Import a module."""
243
253
  from jaclang.compiler.passes.main import PyastBuildPass
244
254
 
245
- base_dir = os.path.dirname(mod_path)
246
- sys.path.append(base_dir)
255
+ assert isinstance(self.ir, ast.Module)
256
+
257
+ python_raise_map = self.ir.py_raise_map
258
+ file_to_raise = None
259
+
260
+ if mod_path in python_raise_map:
261
+ file_to_raise = python_raise_map[mod_path]
262
+ else:
263
+ resolved_mod_path = (
264
+ f"{self.__get_current_module(parent_node)}.{imported_mod_name}"
265
+ )
266
+ assert isinstance(self.ir, ast.Module)
267
+ resolved_mod_path = resolved_mod_path.replace(f"{self.ir.name}.", "")
268
+ file_to_raise = python_raise_map.get(resolved_mod_path)
269
+
270
+ if file_to_raise is None:
271
+ return None
272
+
247
273
  try:
248
- # Dynamically import the module
249
- spec = importlib.util.find_spec(node.path_str)
250
- sys.path.remove(base_dir)
251
- if spec and spec.origin and spec.origin not in {None, "built-in", "frozen"}:
252
- if spec.origin in self.import_table:
253
- return self.import_table[spec.origin]
254
- with open(spec.origin, "r", encoding="utf-8") as f:
255
- # print(f"\nImporting python module {node.path_str}")
274
+ if file_to_raise not in {None, "built-in", "frozen"}:
275
+ if file_to_raise in self.import_table:
276
+ return self.import_table[file_to_raise]
277
+
278
+ with open(file_to_raise, "r", encoding="utf-8") as f:
256
279
  mod = PyastBuildPass(
257
280
  input_ir=ast.PythonModuleAst(
258
- py_ast.parse(f.read()), mod_path=spec.origin
281
+ py_ast.parse(f.read()), mod_path=file_to_raise
259
282
  ),
260
283
  ).ir
284
+ SubNodeTabPass(input_ir=mod, prior=self)
261
285
  if mod:
262
- self.import_table[spec.origin] = mod
286
+ mod.name = imported_mod_name
287
+ self.import_table[file_to_raise] = mod
288
+ self.attach_mod_to_node(parent_node, mod)
289
+ SymTabBuildPass(input_ir=mod, prior=self)
263
290
  return mod
264
291
  else:
265
- raise self.ice(
266
- f"Failed to import python module {node.path_str}: {spec.origin}"
267
- )
292
+ raise self.ice(f"Failed to import python module {mod_path}")
268
293
  except Exception as e:
269
- if "Empty kid for Token ModulePath" in str(e) or "utf-8" in str(e): # FIXME
270
- return None
271
- self.error(
272
- f"Failed to import python module {node.path_str}: {e}",
273
- node_override=node,
274
- )
294
+ self.error(f"Failed to import python module {mod_path}")
275
295
  raise e
276
296
  return None
297
+
298
+ def annex_impl(self, node: ast.Module) -> None:
299
+ """Annex impl and test modules."""
300
+ return None