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
@@ -94,18 +94,18 @@ class Constants(StrEnum):
94
94
  HERE = "_jac_here_"
95
95
  JAC_FEATURE = "_Jac"
96
96
  ROOT = f"{JAC_FEATURE}.get_root()"
97
- EDGES_TO_NODE = "_jac_.edges_to_nodes"
98
- EDGE_REF = "_jac_.edge_ref"
99
- CONNECT_NODE = "_jac_.connect_node"
100
- DISCONNECT_NODE = "_jac_.disconnect_node"
101
- WALKER_VISIT = "_jac_.visit_node"
102
- WALKER_IGNORE = "_jac_.ignore_node"
103
- DISENGAGE = "_jac_.disengage_now"
97
+ EDGES_TO_NODE = "__jac__.edges_to_nodes"
98
+ EDGE_REF = "__jac__.edge_ref"
99
+ CONNECT_NODE = "__jac__.connect_node"
100
+ DISCONNECT_NODE = "__jac__.disconnect_node"
101
+ WALKER_VISIT = "__jac__.visit_node"
102
+ WALKER_IGNORE = "__jac__.ignore_node"
103
+ DISENGAGE = "__jac__.disengage_now"
104
104
  OBJECT_CLASS = "_jac_Object_"
105
105
  NODE_CLASS = "_jac_Node_"
106
106
  EDGE_CLASS = "_jac_Edge_"
107
107
  WALKER_CLASS = "_jac_Walker_"
108
- WITH_DIR = "_jac_.apply_dir"
108
+ WITH_DIR = "__jac__.apply_dir"
109
109
  EDGE_DIR = "_jac_Edge_Dir"
110
110
  ON_ENTRY = "_jac_ds_.on_entry"
111
111
  ON_EXIT = "_jac_ds_.on_exit"
jaclang/compiler/jac.lark CHANGED
@@ -16,8 +16,9 @@ element: import_stmt
16
16
  | test
17
17
 
18
18
  // Import/Include Statements
19
- import_stmt: KW_IMPORT sub_name KW_FROM from_path COMMA import_items SEMI
20
- | KW_IMPORT sub_name import_path (COMMA import_path)* SEMI
19
+ import_stmt: KW_IMPORT sub_name? KW_FROM from_path LBRACE import_items RBRACE
20
+ | KW_IMPORT sub_name? KW_FROM from_path COMMA import_items SEMI //Deprecated
21
+ | KW_IMPORT sub_name? import_path (COMMA import_path)* SEMI
21
22
  | include_stmt
22
23
 
23
24
  from_path: (DOT | ELLIPSIS)* import_path
@@ -27,7 +28,7 @@ import_path: named_ref (DOT named_ref)* (KW_AS NAME)?
27
28
  import_items: (import_item COMMA)* import_item
28
29
  import_item: named_ref (KW_AS NAME)?
29
30
  sub_name: COLON NAME
30
- include_stmt: KW_INCLUDE sub_name import_path SEMI
31
+ include_stmt: KW_INCLUDE sub_name? import_path SEMI
31
32
 
32
33
  // Architypes
33
34
  architype: decorators? architype_decl
@@ -163,7 +163,11 @@ class JacParser(Pass):
163
163
  body=body,
164
164
  is_imported=False,
165
165
  terminals=self.terminals,
166
- kid=kid if len(kid) else [ast.EmptyToken()],
166
+ kid=(
167
+ kid
168
+ if len(kid)
169
+ else [ast.EmptyToken(file_path=self.parse_ref.mod_path)]
170
+ ),
167
171
  )
168
172
  return self.nu(mod)
169
173
 
@@ -298,26 +302,29 @@ class JacParser(Pass):
298
302
  def import_stmt(self, kid: list[ast.AstNode]) -> ast.Import:
299
303
  """Grammar rule.
300
304
 
301
- import_stmt: KW_IMPORT sub_name KW_FROM from_path COMMA import_items SEMI
302
- | KW_IMPORT sub_name import_path (COMMA import_path)* SEMI
305
+ import_stmt: KW_IMPORT sub_name? KW_FROM from_path LBRACE import_items RBRACE
306
+ | KW_IMPORT sub_name? KW_FROM from_path COMMA import_items SEMI //Deprecated
307
+ | KW_IMPORT sub_name? import_path (COMMA import_path)* SEMI
303
308
  | include_stmt
304
309
  """
305
310
  if len(kid) == 1 and isinstance(kid[0], ast.Import):
306
311
  return self.nu(kid[0])
307
- lang = kid[1]
308
- from_path = kid[3] if isinstance(kid[3], ast.ModulePath) else None
312
+ chomp = [*kid]
313
+ lang = kid[1] if isinstance(kid[1], ast.SubTag) else None
314
+ chomp = chomp[2:] if lang else chomp[1:]
315
+ from_path = chomp[1] if isinstance(chomp[1], ast.ModulePath) else None
309
316
  if from_path:
310
317
  items = kid[-2] if isinstance(kid[-2], ast.SubNodeList) else None
311
318
  else:
312
319
  paths = [i for i in kid if isinstance(i, ast.ModulePath)]
313
320
  items = ast.SubNodeList[ast.ModulePath](
314
- items=paths, delim=Tok.COMMA, kid=kid[2:-1]
321
+ items=paths, delim=Tok.COMMA, kid=kid[2 if lang else 1 : -1]
315
322
  )
316
- kid = kid[:2] + [items] + kid[-1:]
323
+ kid = (kid[:2] if lang else kid[:1]) + [items] + kid[-1:]
317
324
 
318
325
  is_absorb = False
319
- if isinstance(lang, ast.SubTag) and (isinstance(items, ast.SubNodeList)):
320
- return self.nu(
326
+ if isinstance(items, ast.SubNodeList):
327
+ ret = self.nu(
321
328
  ast.Import(
322
329
  hint=lang,
323
330
  from_loc=from_path,
@@ -326,7 +333,15 @@ class JacParser(Pass):
326
333
  kid=kid,
327
334
  )
328
335
  )
329
-
336
+ if (
337
+ from_path
338
+ and isinstance(kid[-1], ast.Token)
339
+ and kid[-1].name == Tok.SEMI
340
+ ):
341
+ self.parse_ref.warning(
342
+ "Deprecated syntax, use braces for multiple imports (e.g, import from mymod {a, b, c})",
343
+ )
344
+ return ret
330
345
  else:
331
346
  raise self.ice()
332
347
 
@@ -364,27 +379,24 @@ class JacParser(Pass):
364
379
 
365
380
  include_stmt: KW_INCLUDE sub_name import_path SEMI
366
381
  """
367
- lang = kid[1]
368
- from_path = kid[2]
382
+ lang = kid[1] if isinstance(kid[1], ast.SubTag) else None
383
+ from_path = kid[2] if lang else kid[1]
369
384
  if not isinstance(from_path, ast.ModulePath):
370
385
  raise self.ice()
371
386
  items = ast.SubNodeList[ast.ModulePath](
372
387
  items=[from_path], delim=Tok.COMMA, kid=[from_path]
373
388
  )
374
- kid = kid[:2] + [items] + kid[3:]
389
+ kid = (kid[:2] if lang else kid[:1]) + [items] + kid[-1:]
375
390
  is_absorb = True
376
- if isinstance(lang, ast.SubTag):
377
- return self.nu(
378
- ast.Import(
379
- hint=lang,
380
- from_loc=None,
381
- items=items,
382
- is_absorb=is_absorb,
383
- kid=kid,
384
- )
391
+ return self.nu(
392
+ ast.Import(
393
+ hint=lang,
394
+ from_loc=None,
395
+ items=items,
396
+ is_absorb=is_absorb,
397
+ kid=kid,
385
398
  )
386
- else:
387
- raise self.ice()
399
+ )
388
400
 
389
401
  def import_path(self, kid: list[ast.AstNode]) -> ast.ModulePath:
390
402
  """Grammar rule.
@@ -995,7 +1007,11 @@ class JacParser(Pass):
995
1007
  semstr=semstr,
996
1008
  params=params,
997
1009
  return_type=return_spec,
998
- kid=kid if len(kid) else [ast.EmptyToken()],
1010
+ kid=(
1011
+ kid
1012
+ if len(kid)
1013
+ else [ast.EmptyToken(file_path=self.parse_ref.mod_path)]
1014
+ ),
999
1015
  )
1000
1016
  )
1001
1017
  else:
@@ -74,7 +74,7 @@ class Pass(Transform[T]):
74
74
  return result
75
75
 
76
76
  @staticmethod
77
- def has_parent_of_type(node: ast.AstNode, typ: Type[T]) -> Optional[T]:
77
+ def find_parent_of_type(node: ast.AstNode, typ: Type[T]) -> Optional[T]:
78
78
  """Check if node has parent of type."""
79
79
  while node.parent:
80
80
  if isinstance(node.parent, typ):
@@ -115,7 +115,7 @@ class Pass(Transform[T]):
115
115
  self.after_pass()
116
116
  self.time_taken = time.time() - start_time
117
117
  if settings.pass_timer:
118
- print(
118
+ self.log_info(
119
119
  f"Time taken in {self.__class__.__name__}: {self.time_taken:.4f} seconds"
120
120
  )
121
121
  return self.ir
@@ -136,21 +136,12 @@ class Pass(Transform[T]):
136
136
  self.exit_node(node)
137
137
  return node
138
138
 
139
- def update_code_loc(self, node: Optional[ast.AstNode] = None) -> None:
140
- """Update code location."""
141
- if node is None:
142
- node = self.cur_node
143
- if not isinstance(node, ast.AstNode):
144
- self.ice("Current node is not an AstNode.")
145
-
146
139
  def error(self, msg: str, node_override: Optional[ast.AstNode] = None) -> None:
147
140
  """Pass Error."""
148
- self.update_code_loc(node_override)
149
141
  self.log_error(f"{msg}", node_override=node_override)
150
142
 
151
143
  def warning(self, msg: str, node_override: Optional[ast.AstNode] = None) -> None:
152
144
  """Pass Error."""
153
- self.update_code_loc(node_override)
154
145
  self.log_warning(f"{msg}", node_override=node_override)
155
146
 
156
147
  def ice(self, msg: str = "Something went horribly wrong!") -> RuntimeError:
@@ -166,10 +157,10 @@ class PrinterPass(Pass):
166
157
 
167
158
  def enter_node(self, node: ast.AstNode) -> None:
168
159
  """Run on entering node."""
169
- print("Entering:", node)
160
+ self.log_info(f"Entering: {node.__class__.__name__}: {node.loc}")
170
161
  super().enter_node(node)
171
162
 
172
163
  def exit_node(self, node: ast.AstNode) -> None:
173
164
  """Run on exiting node."""
174
165
  super().exit_node(node)
175
- print("Exiting:", node)
166
+ self.log_info(f"Exiting: {node.__class__.__name__}: {node.loc}")
@@ -1,8 +1,8 @@
1
1
  """Collection of passes for Jac IR."""
2
2
 
3
3
  from .sub_node_tab_pass import SubNodeTabPass
4
- from .import_pass import JacImportPass, PyImportPass # noqa: I100
5
4
  from .sym_tab_build_pass import SymTabBuildPass # noqa: I100
5
+ from .import_pass import JacImportPass, PyImportPass # noqa: I100
6
6
  from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
7
7
  from .def_use_pass import DefUsePass # noqa: I100
8
8
  from .pyout_pass import PyOutPass # noqa: I100
@@ -3,162 +3,56 @@
3
3
  This pass checks for access to attributes in the Jac language.
4
4
  """
5
5
 
6
- import os
7
6
  from typing import Optional
8
7
 
9
8
  import jaclang.compiler.absyntree as ast
10
9
  from jaclang.compiler.constant import SymbolAccess
11
10
  from jaclang.compiler.passes import Pass
12
- from jaclang.compiler.symtable import SymbolTable
11
+ from jaclang.compiler.symtable import Symbol
13
12
  from jaclang.settings import settings
14
13
 
15
14
 
16
15
  class AccessCheckPass(Pass):
17
16
  """Jac Ast Access Check pass."""
18
17
 
19
- def after_pass(self) -> None:
20
- """After pass."""
21
- pass
22
-
23
- def exit_node(self, node: ast.AstNode) -> None:
18
+ # NOTE: This method is a hacky way to detect if the drivied class is inherit from base class, it
19
+ # doesn't work if the base class was provided as an expression (ex. obj Dri :module.Base: {...}).
20
+ def is_class_inherited_from(
21
+ self, dri_class: ast.Architype, base_class: ast.Architype
22
+ ) -> bool:
23
+ """Return true if the dri_class inherited from base_class."""
24
+ if dri_class.base_classes is None:
25
+ return False
26
+ for expr in dri_class.base_classes.items:
27
+ if not isinstance(expr, ast.Name):
28
+ continue
29
+ if not isinstance(expr.name_of, ast.Architype):
30
+ continue # Unlikely.
31
+ if expr.name_of == base_class:
32
+ return True
33
+ if self.is_class_inherited_from(expr.name_of, base_class):
34
+ return True
35
+ return False
36
+
37
+ def report_error(self, message: str, node: Optional[ast.AstNode] = None) -> None:
38
+ """Report error message related to illegal access of attributes and objects."""
39
+ self.error(message, node)
40
+
41
+ def exit_node(self, node: ast.AstNode) -> None: # TODO: Move to debug pass
24
42
  """Exit node."""
25
43
  super().exit_node(node)
26
- if settings.lsp_debug and isinstance(node, ast.NameAtom) and not node.sym:
27
- self.warning(f"Name {node.sym_name} not present in symbol table")
28
-
29
- def access_check(self, node: ast.Name) -> None:
30
- """Access check."""
31
- node_info = (
32
- node.sym_tab.lookup(node.sym_name)
33
- if isinstance(node.sym_tab, SymbolTable)
34
- else None
35
- )
36
-
37
- if node.sym:
38
- decl_package_path = os.path.dirname(
39
- os.path.abspath(node.sym.defn[-1].loc.mod_path)
40
- )
41
- use_package_path = os.path.dirname(os.path.abspath(node.loc.mod_path))
42
- else:
43
- decl_package_path = use_package_path = ""
44
-
45
44
  if (
46
- node_info
47
- and node.sym
48
- and node_info.access == SymbolAccess.PROTECTED
49
- and decl_package_path != use_package_path
50
- ):
51
- return self.error(
52
- f'Can not access protected variable "{node.sym_name}" from {decl_package_path}'
53
- f" to {use_package_path}."
45
+ settings.lsp_debug
46
+ and isinstance(node, ast.NameAtom)
47
+ and not node.sym
48
+ and not node.parent_of_type(ast.Module).is_raised_from_py
49
+ and not (
50
+ node.sym_name == "py"
51
+ and node.parent
52
+ and isinstance(node.parent.parent, ast.Import)
54
53
  )
55
-
56
- if (
57
- node_info
58
- and node.sym
59
- and node_info.access == SymbolAccess.PRIVATE
60
- and node.sym.defn[-1].loc.mod_path != node.loc.mod_path
61
54
  ):
62
- return self.error(
63
- f'Can not access private variable "{node.sym_name}" from {node.sym.defn[-1].loc.mod_path}'
64
- f" to {node.loc.mod_path}."
65
- )
66
-
67
- def access_register(
68
- self, node: ast.AstSymbolNode, acc_tag: Optional[SymbolAccess] = None
69
- ) -> None:
70
- """Access register."""
71
-
72
- def enter_global_vars(self, node: ast.GlobalVars) -> None:
73
- """Sub objects.
74
-
75
- access: Optional[SubTag[Token]],
76
- assignments: SubNodeList[Assignment],
77
- is_frozen: bool,
78
- """
79
- pass
80
-
81
- def enter_module(self, node: ast.Module) -> None:
82
- """Sub objects.
83
-
84
- name: str,
85
- doc: Token,
86
- body: Optional['Elements'],
87
- mod_path: str,
88
- is_imported: bool,
89
- """
90
-
91
- def enter_architype(self, node: ast.Architype) -> None:
92
- """Sub objects.
93
-
94
- name: Name,
95
- arch_type: Token,
96
- access: Optional[SubTag[Token]],
97
- base_classes: Optional[SubNodeList[Expr]],
98
- body: Optional[SubNodeList[ArchBlockStmt] | ArchDef],
99
- decorators: Optional[SubNodeList[Expr]] = None,
100
- """
101
- pass
102
-
103
- def enter_enum(self, node: ast.Enum) -> None:
104
- """Sub objects.
105
-
106
- name: Name,
107
- access: Optional[SubTag[Token]],
108
- base_classes: Optional[SubNodeList[Expr]],
109
- body: Optional[SubNodeList[EnumBlockStmt] | EnumDef],
110
- decorators: Optional[SubNodeList[Expr]] = None,
111
- """
112
- pass
113
-
114
- def enter_ability(self, node: ast.Ability) -> None:
115
- """Sub objects.
116
-
117
- name_ref: NameSpec,
118
- is_func: bool,
119
- is_async: bool,
120
- is_override: bool,
121
- is_static: bool,
122
- is_abstract: bool,
123
- access: Optional[SubTag[Token]],
124
- signature: Optional[FuncSignature | EventSignature],
125
- body: Optional[SubNodeList[CodeBlockStmt] | AbilityDef],
126
- decorators: Optional[SubNodeList[Expr]] = None,
127
- """
128
- pass
129
-
130
- def enter_sub_node_list(self, node: ast.SubNodeList) -> None:
131
- """Sub objects.
132
-
133
- items: list[T]
134
- """
135
-
136
- def enter_arch_has(self, node: ast.ArchHas) -> None:
137
- """Sub objects.
138
-
139
- is_static: bool,
140
- access: Optional[SubTag[Token]],
141
- vars: SubNodeList[HasVar],
142
- is_frozen: bool,
143
- """
144
- pass
145
-
146
- def enter_atom_trailer(self, node: ast.AtomTrailer) -> None:
147
- """Sub objects.
148
-
149
- access: Optional[SubTag[Token]],
150
- """
151
- pass
152
-
153
- def enter_func_call(self, node: ast.FuncCall) -> None:
154
- """Sub objects.
155
-
156
- target: Expr,
157
- params: Optional[SubNodeList[Expr | KWPair]],
158
- genai_call: Optional[FuncCall],
159
- kid: Sequence[AstNode],
160
- """
161
- pass
55
+ self.warning(f"Name {node.sym_name} not present in symbol table")
162
56
 
163
57
  def enter_name(self, node: ast.Name) -> None:
164
58
  """Sub objects.
@@ -170,12 +64,67 @@ class AccessCheckPass(Pass):
170
64
  pos_start: int,
171
65
  pos_end: int,
172
66
  """
173
- from jaclang.compiler.passes import Pass
67
+ # TODO: Enums are not considered at the moment, I'll need to test and add them bellow.
174
68
 
175
- if isinstance(node.parent, ast.FuncCall):
176
- self.access_check(node)
69
+ # If the current node is a global variable's name there is no access, it's just the declaration.
70
+ if Pass.find_parent_of_type(node, ast.GlobalVars) is not None:
71
+ return
177
72
 
178
- if node.sym and Pass.has_parent_of_type(
179
- node=node.sym.defn[-1], typ=ast.GlobalVars
180
- ):
181
- self.access_check(node)
73
+ # Class name, and ability name are declarations and there is no access here as well.
74
+ if isinstance(node.name_of, (ast.Ability, ast.Architype, ast.Enum)):
75
+ return
76
+
77
+ # Get the context to check the access.
78
+ curr_class: Optional[ast.Architype] = Pass.find_parent_of_type(
79
+ node, ast.Architype
80
+ )
81
+ curr_module: Optional[ast.Module] = Pass.find_parent_of_type(node, ast.Module)
82
+ if curr_module is None:
83
+ return
84
+
85
+ # Note that currently we can only check for name + symbols, because expressions are not associated with the
86
+ # typeinfo thus they don't have a symbol. In the future the name nodes will become expression nodes.
87
+ if not isinstance(node.sym, Symbol):
88
+ return
89
+
90
+ # Public symbols are fine.
91
+ if node.sym.access == SymbolAccess.PUBLIC:
92
+ return
93
+
94
+ # Note that from bellow the access is either private or protected.
95
+ is_portect = node.sym.access == SymbolAccess.PROTECTED
96
+ access_type = "protected" if is_portect else "private"
97
+
98
+ # The class we're currently in (None if we're not inside any).
99
+ sym_owner: ast.AstNode = node.sym.parent_tab.owner
100
+
101
+ # If the symbol belongs to a class, we need to check if the access used properly
102
+ # within the class and in it's inherited classes.
103
+ if isinstance(sym_owner, ast.Architype):
104
+
105
+ # Accessing a private/protected member within the top level scope illegal.
106
+ if curr_class is None:
107
+ return self.report_error(
108
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
109
+ node,
110
+ )
111
+
112
+ if curr_class != node.sym.parent_tab.owner:
113
+ if not is_portect: # private member accessed in a different class.
114
+ return self.report_error(
115
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
116
+ node,
117
+ )
118
+ else: # Accessing a protected member, check we're in an inherited class.
119
+ if not self.is_class_inherited_from(curr_class, sym_owner):
120
+ return self.report_error(
121
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
122
+ node,
123
+ )
124
+
125
+ elif isinstance(sym_owner, ast.Module) and sym_owner != curr_module:
126
+ # Accessing a private/public member in a different module.
127
+ return self.report_error(
128
+ f'Error: Invalid access of {access_type} member "{node.sym_name}".',
129
+ node,
130
+ )