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.
- jaclang/cli/cli.py +147 -77
- jaclang/cli/cmdreg.py +9 -12
- jaclang/compiler/__init__.py +19 -53
- jaclang/compiler/absyntree.py +94 -16
- jaclang/compiler/constant.py +8 -8
- jaclang/compiler/jac.lark +4 -3
- jaclang/compiler/parser.py +41 -25
- jaclang/compiler/passes/ir_pass.py +4 -13
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +155 -54
- jaclang/compiler/passes/main/import_pass.py +99 -75
- jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +328 -565
- jaclang/compiler/passes/main/pyast_load_pass.py +33 -6
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
- jaclang/compiler/passes/main/registry_pass.py +37 -3
- jaclang/compiler/passes/main/schedules.py +9 -2
- jaclang/compiler/passes/main/sym_tab_build_pass.py +10 -6
- jaclang/compiler/passes/main/tests/__init__.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
- jaclang/compiler/passes/main/type_check_pass.py +22 -5
- jaclang/compiler/passes/tool/jac_formatter_pass.py +135 -89
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -42
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
- jaclang/compiler/passes/transform.py +4 -0
- jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
- jaclang/compiler/semtable.py +31 -7
- jaclang/compiler/symtable.py +16 -11
- jaclang/compiler/tests/test_importer.py +25 -10
- jaclang/langserve/engine.py +104 -118
- jaclang/langserve/sem_manager.py +379 -0
- jaclang/langserve/server.py +24 -11
- jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_sem_tokens.py +277 -0
- jaclang/langserve/tests/test_server.py +287 -17
- jaclang/langserve/utils.py +184 -98
- jaclang/plugin/builtin.py +1 -1
- jaclang/plugin/default.py +288 -92
- jaclang/plugin/feature.py +65 -27
- jaclang/plugin/spec.py +62 -23
- jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
- jaclang/plugin/tests/test_jaseci.py +414 -42
- jaclang/runtimelib/architype.py +650 -0
- jaclang/{core → runtimelib}/constructs.py +5 -8
- jaclang/{core → runtimelib}/context.py +86 -59
- jaclang/runtimelib/importer.py +361 -0
- jaclang/runtimelib/machine.py +158 -0
- jaclang/runtimelib/memory.py +158 -0
- jaclang/{core → runtimelib}/utils.py +30 -15
- jaclang/settings.py +5 -4
- jaclang/tests/fixtures/abc.jac +3 -3
- jaclang/tests/fixtures/access_checker.jac +12 -17
- jaclang/tests/fixtures/access_modifier.jac +88 -33
- jaclang/tests/fixtures/baddy.jac +3 -0
- jaclang/tests/fixtures/baddy.test.jac +3 -0
- jaclang/tests/fixtures/bar.jac +34 -0
- jaclang/tests/fixtures/byllmissue.jac +1 -5
- jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
- jaclang/tests/fixtures/cls_method.jac +41 -0
- jaclang/tests/fixtures/dblhello.jac +6 -0
- jaclang/tests/fixtures/deep/one_lev.jac +3 -3
- jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
- jaclang/tests/fixtures/deep_import_mods.jac +13 -0
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edge_ops.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/err.impl.jac +3 -0
- jaclang/tests/fixtures/err.jac +4 -2
- jaclang/tests/fixtures/err_runtime.jac +15 -0
- jaclang/tests/fixtures/foo.jac +43 -0
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/hello.jac +4 -0
- jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
- jaclang/tests/fixtures/impl_grab.jac +4 -1
- jaclang/tests/fixtures/import.jac +9 -0
- jaclang/tests/fixtures/index_slice.jac +30 -0
- jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/needs_import.jac +2 -2
- jaclang/tests/fixtures/pyfunc_1.py +1 -1
- jaclang/tests/fixtures/pyfunc_2.py +5 -2
- jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
- jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
- jaclang/tests/fixtures/registry.jac +9 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/fixtures/semstr.jac +1 -4
- jaclang/tests/fixtures/simple_archs.jac +1 -1
- jaclang/tests/test_cli.py +109 -3
- jaclang/tests/test_language.py +170 -68
- jaclang/tests/test_reference.py +2 -3
- jaclang/utils/helpers.py +45 -21
- jaclang/utils/test.py +9 -0
- jaclang/utils/treeprinter.py +30 -7
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/METADATA +3 -2
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/RECORD +126 -90
- jaclang/core/architype.py +0 -502
- jaclang/core/importer.py +0 -344
- jaclang/core/memory.py +0 -99
- jaclang/tests/fixtures/aott_raise.jac +0 -25
- jaclang/tests/fixtures/package_import.jac +0 -6
- /jaclang/{core → runtimelib}/__init__.py +0 -0
- /jaclang/{core → runtimelib}/test.py +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/entry_points.txt +0 -0
jaclang/compiler/constant.py
CHANGED
|
@@ -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 = "
|
|
98
|
-
EDGE_REF = "
|
|
99
|
-
CONNECT_NODE = "
|
|
100
|
-
DISCONNECT_NODE = "
|
|
101
|
-
WALKER_VISIT = "
|
|
102
|
-
WALKER_IGNORE = "
|
|
103
|
-
DISENGAGE = "
|
|
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 = "
|
|
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
|
|
20
|
-
| KW_IMPORT sub_name
|
|
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
|
jaclang/compiler/parser.py
CHANGED
|
@@ -163,7 +163,11 @@ class JacParser(Pass):
|
|
|
163
163
|
body=body,
|
|
164
164
|
is_imported=False,
|
|
165
165
|
terminals=self.terminals,
|
|
166
|
-
kid=
|
|
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
|
|
302
|
-
| KW_IMPORT sub_name
|
|
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
|
-
|
|
308
|
-
|
|
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
|
|
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(
|
|
320
|
-
|
|
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[
|
|
389
|
+
kid = (kid[:2] if lang else kid[:1]) + [items] + kid[-1:]
|
|
375
390
|
is_absorb = True
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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=
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
47
|
-
and node.
|
|
48
|
-
and
|
|
49
|
-
and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
# TODO: Enums are not considered at the moment, I'll need to test and add them bellow.
|
|
174
68
|
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
)
|