jaclang 0.2.4__py3-none-any.whl → 0.3.0__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/__init__.py +9 -3
- jaclang/cli/__init__.py +0 -1
- jaclang/cli/__jac_gen__/cli.py +6 -6
- jaclang/cli/__jac_gen__/cli_impl.py +2 -2
- jaclang/cli/__jac_gen__/cmds.py +2 -3
- jaclang/cli/__jac_gen__/cmds_impl.py +2 -3
- jaclang/cli/cmds.jac +1 -1
- jaclang/cli/cmds_impl.jac +2 -3
- jaclang/core/__init__.py +5 -11
- jaclang/core/__jac_gen__/corelib.py +289 -0
- jaclang/core/__jac_gen__/corelib_impl.py +220 -0
- jaclang/core/corelib.jac +21 -34
- jaclang/core/corelib_impl.jac +317 -0
- jaclang/jac/__init__.py +1 -0
- jaclang/jac/__jac_gen__/jac_parser.py +2 -2
- jaclang/jac/absyntree.py +32 -62
- jaclang/jac/constant.py +3 -7
- jaclang/jac/importer.py +1 -1
- jaclang/jac/parser.py +14 -10
- jaclang/jac/passes/main/__init__.py +2 -0
- jaclang/jac/passes/main/def_use_pass.py +4 -7
- jaclang/jac/passes/main/pyast_gen_pass.py +116 -35
- jaclang/jac/passes/main/schedules.py +6 -0
- jaclang/jac/passes/main/sym_tab_build_pass.py +40 -19
- jaclang/jac/passes/main/tests/test_jac_format_pass.py +22 -4
- jaclang/jac/passes/main/tests/test_pyast_gen_pass.py +3 -1
- jaclang/jac/passes/main/tests/test_type_check_pass.py +42 -0
- jaclang/jac/passes/main/type_check_pass.py +103 -0
- jaclang/jac/passes/tool/ast_printer_pass.py +8 -2
- jaclang/jac/passes/tool/fuse_comments_pass.py +57 -39
- jaclang/jac/passes/tool/jac_formatter_pass.py +419 -192
- jaclang/jac/passes/tool/sym_tab_printer_pass.py +10 -93
- jaclang/jac/passes/tool/tests/test_ast_print_pass.py +2 -1
- jaclang/jac/passes/transform.py +0 -39
- jaclang/jac/passes/utils/__init__.py +1 -0
- jaclang/jac/passes/utils/mypy_ast_build.py +302 -0
- jaclang/jac/plugin/__init__.py +5 -2
- jaclang/jac/plugin/default.py +20 -4
- jaclang/jac/plugin/feature.py +16 -7
- jaclang/jac/plugin/spec.py +34 -6
- jaclang/jac/symtable.py +6 -0
- jaclang/jac/tests/test_workspace.py +55 -1
- jaclang/jac/transpiler.py +4 -9
- jaclang/utils/helpers.py +0 -33
- jaclang/utils/lang_tools.py +3 -0
- jaclang/utils/test.py +3 -1
- jaclang/utils/treeprinter.py +171 -0
- jaclang/vendor/lark/py.typed +0 -0
- jaclang/vendor/mypy/checker.py +19 -12
- jaclang/vendor/mypy/checkexpr.py +31 -10
- jaclang/vendor/mypy/constraints.py +56 -38
- jaclang/vendor/mypy/expandtype.py +1 -0
- jaclang/vendor/mypy/meet.py +10 -1
- jaclang/vendor/mypy/messages.py +16 -4
- jaclang/vendor/mypy/moduleinspect.py +10 -4
- jaclang/vendor/mypy/py.typed +1 -0
- jaclang/vendor/mypy/semanal.py +18 -17
- jaclang/vendor/mypy/semanal_enum.py +7 -4
- jaclang/vendor/mypy/semanal_namedtuple.py +11 -1
- jaclang/vendor/mypy/semanal_typeddict.py +25 -11
- jaclang/vendor/mypy/stubdoc.py +18 -4
- jaclang/vendor/mypy/stubgen.py +80 -1
- jaclang/vendor/mypy/stubgenc.py +47 -5
- jaclang/vendor/mypy/stubtest.py +53 -3
- jaclang/vendor/mypy/stubutil.py +9 -9
- jaclang/vendor/mypy/test/testipc.py +16 -7
- jaclang/vendor/mypy/test/teststubtest.py +20 -2
- jaclang/vendor/mypy/types.py +1 -1
- jaclang/vendor/mypyc/irbuild/prebuildvisitor.py +2 -1
- jaclang/vendor/mypyc/test/test_run.py +2 -4
- jaclang/vendor/pluggy/py.typed +0 -0
- {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/METADATA +1 -1
- {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/RECORD +77 -71
- {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/WHEEL +1 -1
- {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/entry_points.txt +3 -0
- jaclang/core/arch_impl.jac +0 -131
- jaclang/core/element_impl.jac +0 -109
- jaclang/core/exec_ctx_impl.jac +0 -14
- jaclang/core/memory_impl.jac +0 -57
- jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +0 -5
- /jaclang/{jac/tests/fixtures → core}/__jac_gen__/__init__.py +0 -0
- {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,64 +1,15 @@
|
|
|
1
1
|
"""Jac Blue pass for drawing AST."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from jaclang.jac.passes import Pass
|
|
7
7
|
from jaclang.jac.symtable import SymbolTable
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
node_name: str,
|
|
14
|
-
parent: Optional["_SymbolTree"] = None,
|
|
15
|
-
children: Optional[List["_SymbolTree"]] = None,
|
|
16
|
-
) -> None:
|
|
17
|
-
self.parent = parent
|
|
18
|
-
self.kid = children if children is not None else []
|
|
19
|
-
self.name = node_name
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def parent(self) -> Optional["_SymbolTree"]:
|
|
23
|
-
return self.__parent
|
|
24
|
-
|
|
25
|
-
@parent.setter
|
|
26
|
-
def parent(self, parent_node: Optional["_SymbolTree"]) -> None:
|
|
27
|
-
if parent_node:
|
|
28
|
-
self.__parent = parent_node
|
|
29
|
-
parent_node.kid.append(self)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _build_symbol_tree_common(
|
|
33
|
-
node: SymbolTable, parent_node: Optional[_SymbolTree] = None
|
|
34
|
-
) -> _SymbolTree:
|
|
35
|
-
root = _SymbolTree(
|
|
36
|
-
node_name=f"SymTable::{node.owner.__class__.__name__}({node.name})",
|
|
37
|
-
parent=parent_node,
|
|
38
|
-
)
|
|
39
|
-
symbols = _SymbolTree(node_name="Symbols", parent=root)
|
|
40
|
-
children = _SymbolTree(node_name="Sub Tables", parent=root)
|
|
41
|
-
|
|
42
|
-
for sym in node.tab.values():
|
|
43
|
-
symbol_node = _SymbolTree(node_name=f"{sym.sym_name}", parent=symbols)
|
|
44
|
-
_SymbolTree(node_name=f"{sym.access} {sym.sym_type}", parent=symbol_node)
|
|
45
|
-
|
|
46
|
-
if sym.decl:
|
|
47
|
-
_SymbolTree(
|
|
48
|
-
node_name=f"decl: line {sym.decl.loc.first_line}, col {sym.decl.loc.col_start}",
|
|
49
|
-
parent=symbol_node,
|
|
50
|
-
)
|
|
51
|
-
defn = _SymbolTree(node_name="defn", parent=symbol_node)
|
|
52
|
-
[
|
|
53
|
-
_SymbolTree(
|
|
54
|
-
node_name=f"line {n.loc.first_line}, col {n.loc.col_start}", parent=defn
|
|
55
|
-
)
|
|
56
|
-
for n in sym.defn
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
for k in node.kid:
|
|
60
|
-
_build_symbol_tree_common(k, children)
|
|
61
|
-
return root
|
|
8
|
+
from jaclang.utils.treeprinter import (
|
|
9
|
+
SymbolTree,
|
|
10
|
+
_build_symbol_tree_common,
|
|
11
|
+
print_symtab_tree,
|
|
12
|
+
)
|
|
62
13
|
|
|
63
14
|
|
|
64
15
|
class SymbolTablePrinterPass(Pass):
|
|
@@ -69,44 +20,10 @@ class SymbolTablePrinterPass(Pass):
|
|
|
69
20
|
def before_pass(self) -> None:
|
|
70
21
|
"""Initialize pass."""
|
|
71
22
|
if isinstance(self.ir.sym_tab, SymbolTable):
|
|
72
|
-
|
|
73
|
-
self._print_tree(root)
|
|
23
|
+
print_symtab_tree(self.ir.sym_tab, output_file=self.SAVE_OUTPUT)
|
|
74
24
|
self.terminate()
|
|
75
25
|
return super().before_pass()
|
|
76
26
|
|
|
77
|
-
def _print_tree(
|
|
78
|
-
self,
|
|
79
|
-
root: _SymbolTree,
|
|
80
|
-
marker: str = "+-- ",
|
|
81
|
-
level_markers: Optional[List[bool]] = None,
|
|
82
|
-
) -> None:
|
|
83
|
-
"""Recursive function that prints the hierarchical structure of a tree."""
|
|
84
|
-
if root is None:
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
empty_str = " " * len(marker)
|
|
88
|
-
connection_str = "|" + empty_str[:-1]
|
|
89
|
-
if not level_markers:
|
|
90
|
-
level_markers = []
|
|
91
|
-
level = len(level_markers) # recursion level
|
|
92
|
-
|
|
93
|
-
def mapper(draw: bool) -> str:
|
|
94
|
-
return connection_str if draw else empty_str
|
|
95
|
-
|
|
96
|
-
markers = "".join(map(mapper, level_markers[:-1]))
|
|
97
|
-
markers += marker if level > 0 else ""
|
|
98
|
-
if self.SAVE_OUTPUT:
|
|
99
|
-
with open(self.SAVE_OUTPUT, "a+") as f:
|
|
100
|
-
print(f"{markers}{root.name}", file=f)
|
|
101
|
-
else:
|
|
102
|
-
print(f"{markers}{root.name}")
|
|
103
|
-
# After root has been printed, recurse down (depth-first) the child nodes.
|
|
104
|
-
for i, child in enumerate(root.kid):
|
|
105
|
-
# The last child will not need connection markers on the current level
|
|
106
|
-
# (see example above)
|
|
107
|
-
is_last = i == len(root.kid) - 1
|
|
108
|
-
self._print_tree(child, marker, [*level_markers, not is_last])
|
|
109
|
-
|
|
110
27
|
|
|
111
28
|
class SymbolTableDotGraphPass(Pass):
|
|
112
29
|
"""Jac AST conversion to DOT graph."""
|
|
@@ -123,13 +40,13 @@ class SymbolTableDotGraphPass(Pass):
|
|
|
123
40
|
self.terminate()
|
|
124
41
|
return super().before_pass()
|
|
125
42
|
|
|
126
|
-
def __gen_node_id(self, node:
|
|
43
|
+
def __gen_node_id(self, node: SymbolTree) -> int:
|
|
127
44
|
if id(node) not in self.__id_map:
|
|
128
45
|
self.__id_map[id(node)] = self.__lase_id_used
|
|
129
46
|
self.__lase_id_used += 1
|
|
130
47
|
return self.__id_map[id(node)]
|
|
131
48
|
|
|
132
|
-
def __gen_node_parameters(self, node:
|
|
49
|
+
def __gen_node_parameters(self, node: SymbolTree) -> str:
|
|
133
50
|
shape = ""
|
|
134
51
|
fillcolor = ""
|
|
135
52
|
style = ""
|
|
@@ -137,7 +54,7 @@ class SymbolTableDotGraphPass(Pass):
|
|
|
137
54
|
label = f"{label} {shape} {style} {fillcolor}".strip()
|
|
138
55
|
return f"[label={label}]"
|
|
139
56
|
|
|
140
|
-
def __gen_dot_graph(self, node:
|
|
57
|
+
def __gen_dot_graph(self, node: SymbolTree) -> None:
|
|
141
58
|
self.__dot_lines.append(
|
|
142
59
|
f"{self.__gen_node_id(node)} {self.__gen_node_parameters(node)};"
|
|
143
60
|
)
|
|
@@ -67,6 +67,7 @@ class AstPrinterPassTest(TestCase):
|
|
|
67
67
|
with open("out.txt") as f:
|
|
68
68
|
res_lines = "".join(f.readlines())
|
|
69
69
|
|
|
70
|
+
# print(res_lines)
|
|
70
71
|
with open(self.fixture_abs_path("multi_def_err.txt")) as f:
|
|
71
72
|
ref_lines = "".join(f.readlines())
|
|
72
|
-
self.assertEqual(res_lines, ref_lines)
|
|
73
|
+
self.assertEqual(res_lines.split(), ref_lines.split())
|
jaclang/jac/passes/transform.py
CHANGED
|
@@ -6,8 +6,6 @@ from typing import Optional
|
|
|
6
6
|
|
|
7
7
|
from jaclang.jac.absyntree import AstNode
|
|
8
8
|
from jaclang.jac.codeloc import CodeLocInfo
|
|
9
|
-
from jaclang.jac.constant import Constants as Con, Values as Val
|
|
10
|
-
from jaclang.utils.helpers import add_line_numbers, clip_code_section
|
|
11
9
|
from jaclang.utils.log import logging
|
|
12
10
|
|
|
13
11
|
|
|
@@ -31,37 +29,6 @@ class Alert:
|
|
|
31
29
|
return self.__str__()
|
|
32
30
|
|
|
33
31
|
|
|
34
|
-
class TransformError(Exception):
|
|
35
|
-
"""Error during transformation."""
|
|
36
|
-
|
|
37
|
-
def __init__(
|
|
38
|
-
self, message: str, errors: list[Alert], warnings: list[Alert]
|
|
39
|
-
) -> None:
|
|
40
|
-
"""Initialize error."""
|
|
41
|
-
self.errors = errors
|
|
42
|
-
self.warnings = warnings
|
|
43
|
-
if len(errors):
|
|
44
|
-
message += "\nErrors:"
|
|
45
|
-
for i in self.errors:
|
|
46
|
-
message += "\n" + str(i)
|
|
47
|
-
if len(warnings):
|
|
48
|
-
message += "\nWarnings:"
|
|
49
|
-
for i in self.warnings:
|
|
50
|
-
message += "\n" + str(i)
|
|
51
|
-
if len(errors) or len(warnings):
|
|
52
|
-
jac_err_line = (
|
|
53
|
-
errors[0].loc.first_line if len(errors) else warnings[0].loc.first_line
|
|
54
|
-
)
|
|
55
|
-
with open(errors[0].loc.mod_path, "r") as file:
|
|
56
|
-
jac_code_string = file.read()
|
|
57
|
-
message += f"\n{Con.JAC_ERROR_PREAMBLE}\n" + clip_code_section(
|
|
58
|
-
add_line_numbers(jac_code_string),
|
|
59
|
-
jac_err_line,
|
|
60
|
-
Val.JAC_ERROR_LINE_RANGE,
|
|
61
|
-
)
|
|
62
|
-
super().__init__(message)
|
|
63
|
-
|
|
64
|
-
|
|
65
32
|
class Transform(ABC):
|
|
66
33
|
"""Abstract class for IR passes."""
|
|
67
34
|
|
|
@@ -93,9 +60,3 @@ class Transform(ABC):
|
|
|
93
60
|
alrt = Alert(msg, self.cur_node.loc if not node_override else node_override.loc)
|
|
94
61
|
self.warnings_had.append(alrt)
|
|
95
62
|
self.logger.warning(str(alrt))
|
|
96
|
-
|
|
97
|
-
def gen_exception(
|
|
98
|
-
self, msg: str = "Error in code transform, see above for details."
|
|
99
|
-
) -> TransformError:
|
|
100
|
-
"""Raise error."""
|
|
101
|
-
return TransformError(msg, self.errors_had, self.warnings_had)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utility packages for passes."""
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Overrides to mypy build manager for direct AST pass through."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import ast
|
|
5
|
+
|
|
6
|
+
import jaclang.vendor.mypy.build as myb
|
|
7
|
+
from jaclang.vendor.mypy.build import BuildSource
|
|
8
|
+
from jaclang.vendor.mypy.build import BuildSourceSet
|
|
9
|
+
from jaclang.vendor.mypy.build import FileSystemCache
|
|
10
|
+
from jaclang.vendor.mypy.build import compute_search_paths
|
|
11
|
+
from jaclang.vendor.mypy.build import load_graph
|
|
12
|
+
from jaclang.vendor.mypy.build import load_plugins
|
|
13
|
+
from jaclang.vendor.mypy.build import process_graph
|
|
14
|
+
from jaclang.vendor.mypy.errors import Errors
|
|
15
|
+
from jaclang.vendor.mypy.fastparse import ASTConverter
|
|
16
|
+
from jaclang.vendor.mypy.options import Options
|
|
17
|
+
from jaclang.vendor.mypy.semanal_main import semantic_analysis_for_scc
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BuildManager(myb.BuildManager):
|
|
21
|
+
"""Overrides to mypy build manager for direct AST pass through."""
|
|
22
|
+
|
|
23
|
+
def parse_file(
|
|
24
|
+
self,
|
|
25
|
+
id: str,
|
|
26
|
+
path: str,
|
|
27
|
+
source: str,
|
|
28
|
+
ignore_errors: bool,
|
|
29
|
+
options: myb.Options,
|
|
30
|
+
ast_override: ast.AST | None = None,
|
|
31
|
+
) -> myb.MypyFile:
|
|
32
|
+
"""Parse the source of a file with the given name.
|
|
33
|
+
|
|
34
|
+
Raise CompileError if there is a parse error.
|
|
35
|
+
"""
|
|
36
|
+
t0 = myb.time.time()
|
|
37
|
+
if ignore_errors:
|
|
38
|
+
self.errors.ignored_files.add(path)
|
|
39
|
+
tree = (
|
|
40
|
+
ast_override
|
|
41
|
+
if ast_override
|
|
42
|
+
else myb.parse(source, path, id, self.errors, options=options)
|
|
43
|
+
)
|
|
44
|
+
tree._fullname = id
|
|
45
|
+
self.add_stats(
|
|
46
|
+
files_parsed=1,
|
|
47
|
+
modules_parsed=int(not tree.is_stub),
|
|
48
|
+
stubs_parsed=int(tree.is_stub),
|
|
49
|
+
parse_time=myb.time.time() - t0,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if self.errors.is_blockers():
|
|
53
|
+
self.log("Bailing due to parse errors")
|
|
54
|
+
self.errors.raise_error()
|
|
55
|
+
|
|
56
|
+
self.errors.set_file_ignored_lines(path, tree.ignored_lines, ignore_errors)
|
|
57
|
+
return tree
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class State(myb.State):
|
|
61
|
+
"""Overrides to mypy state for direct AST pass through."""
|
|
62
|
+
|
|
63
|
+
manager: BuildManager
|
|
64
|
+
tree: myb.MypyFile | None = None
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
id: str | None,
|
|
69
|
+
path: str | None,
|
|
70
|
+
source: str | None,
|
|
71
|
+
manager: BuildManager,
|
|
72
|
+
caller_state: myb.State | None = None,
|
|
73
|
+
caller_line: int = 0,
|
|
74
|
+
ancestor_for: myb.State | None = None,
|
|
75
|
+
root_source: bool = False,
|
|
76
|
+
# If `temporary` is True, this State is being created to just
|
|
77
|
+
# quickly parse/load the tree, without an intention to further
|
|
78
|
+
# process it. With this flag, any changes to external state as well
|
|
79
|
+
# as error reporting should be avoided.
|
|
80
|
+
temporary: bool = False,
|
|
81
|
+
ast_override: ast.AST | None = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Override to mypy state for AST pass through."""
|
|
84
|
+
if not temporary:
|
|
85
|
+
assert id or path or source is not None, "Neither id, path nor source given"
|
|
86
|
+
self.manager = manager
|
|
87
|
+
State.order_counter += 1
|
|
88
|
+
self.order = State.order_counter
|
|
89
|
+
self.caller_state = caller_state
|
|
90
|
+
self.caller_line = caller_line
|
|
91
|
+
if caller_state:
|
|
92
|
+
self.import_context = caller_state.import_context.copy()
|
|
93
|
+
self.import_context.append((caller_state.xpath, caller_line))
|
|
94
|
+
else:
|
|
95
|
+
self.import_context = []
|
|
96
|
+
self.id = id or "__main__"
|
|
97
|
+
self.options = manager.options.clone_for_module(self.id)
|
|
98
|
+
self.early_errors: list[myb.ErrorInfo] = []
|
|
99
|
+
self._type_checker = None
|
|
100
|
+
if not path and source is None:
|
|
101
|
+
assert id is not None
|
|
102
|
+
try:
|
|
103
|
+
path, follow_imports = myb.find_module_and_diagnose(
|
|
104
|
+
manager,
|
|
105
|
+
id,
|
|
106
|
+
self.options,
|
|
107
|
+
caller_state,
|
|
108
|
+
caller_line,
|
|
109
|
+
ancestor_for,
|
|
110
|
+
root_source,
|
|
111
|
+
skip_diagnose=temporary,
|
|
112
|
+
)
|
|
113
|
+
except myb.ModuleNotFound:
|
|
114
|
+
if not temporary:
|
|
115
|
+
manager.missing_modules.add(id)
|
|
116
|
+
raise
|
|
117
|
+
if follow_imports == "silent":
|
|
118
|
+
self.ignore_all = True
|
|
119
|
+
elif path and myb.is_silent_import_module(manager, path) and not root_source:
|
|
120
|
+
self.ignore_all = True
|
|
121
|
+
self.path = path
|
|
122
|
+
if path:
|
|
123
|
+
self.abspath = myb.os.path.abspath(path)
|
|
124
|
+
self.xpath = path or "<string>"
|
|
125
|
+
if path and source is None and self.manager.cache_enabled:
|
|
126
|
+
self.meta = myb.find_cache_meta(self.id, path, manager)
|
|
127
|
+
# TODO: Get mtime if not cached.
|
|
128
|
+
if self.meta is not None:
|
|
129
|
+
self.interface_hash = self.meta.interface_hash
|
|
130
|
+
self.meta_source_hash = self.meta.hash
|
|
131
|
+
if path and source is None and self.manager.fscache.isdir(path):
|
|
132
|
+
source = ""
|
|
133
|
+
self.source = source
|
|
134
|
+
self.add_ancestors()
|
|
135
|
+
self.per_line_checking_time_ns = myb.collections.defaultdict(int)
|
|
136
|
+
t0 = myb.time.time()
|
|
137
|
+
self.meta = myb.validate_meta(
|
|
138
|
+
self.meta, self.id, self.path, self.ignore_all, manager
|
|
139
|
+
)
|
|
140
|
+
self.manager.add_stats(validate_meta_time=myb.time.time() - t0)
|
|
141
|
+
if self.meta:
|
|
142
|
+
# Make copies, since we may modify these and want to
|
|
143
|
+
# compare them to the originals later.
|
|
144
|
+
self.dependencies = list(self.meta.dependencies)
|
|
145
|
+
self.dependencies_set = set(self.dependencies)
|
|
146
|
+
self.suppressed = list(self.meta.suppressed)
|
|
147
|
+
self.suppressed_set = set(self.suppressed)
|
|
148
|
+
all_deps = self.dependencies + self.suppressed
|
|
149
|
+
assert len(all_deps) == len(self.meta.dep_prios)
|
|
150
|
+
self.priorities = dict(zip(all_deps, self.meta.dep_prios))
|
|
151
|
+
|
|
152
|
+
assert len(all_deps) == len(self.meta.dep_lines)
|
|
153
|
+
self.dep_line_map = dict(zip(all_deps, self.meta.dep_lines))
|
|
154
|
+
if temporary:
|
|
155
|
+
self.load_tree(temporary=True)
|
|
156
|
+
if not manager.use_fine_grained_cache() and myb.exist_added_packages(
|
|
157
|
+
self.suppressed, manager, self.options
|
|
158
|
+
):
|
|
159
|
+
# Special case: if there were a previously missing package imported here
|
|
160
|
+
# and it is not present, then we need to re-calculate dependencies.
|
|
161
|
+
# This is to support patterns like this:
|
|
162
|
+
# from missing_package import missing_module # type: ignore
|
|
163
|
+
# At first mypy doesn't know that `missing_module` is a module
|
|
164
|
+
# (it may be a variable, a class, or a function), so it is not added to
|
|
165
|
+
# suppressed dependencies. Therefore, when the package with module is added,
|
|
166
|
+
# we need to re-calculate dependencies.
|
|
167
|
+
# NOTE: see comment below for why we skip this in fine grained mode.
|
|
168
|
+
self.parse_file(
|
|
169
|
+
ast_override=ast_override
|
|
170
|
+
) # This is safe because the cache is anyway stale.
|
|
171
|
+
self.compute_dependencies()
|
|
172
|
+
else:
|
|
173
|
+
# When doing a fine-grained cache load, pretend we only
|
|
174
|
+
# know about modules that have cache information and defer
|
|
175
|
+
# handling new modules until the fine-grained update.
|
|
176
|
+
if manager.use_fine_grained_cache():
|
|
177
|
+
manager.log(f"Deferring module to fine-grained update {path} ({id})")
|
|
178
|
+
raise myb.ModuleNotFound
|
|
179
|
+
|
|
180
|
+
# Parse the file (and then some) to get the dependencies.
|
|
181
|
+
self.parse_file(temporary=temporary, ast_override=ast_override)
|
|
182
|
+
self.compute_dependencies()
|
|
183
|
+
|
|
184
|
+
def parse_file(
|
|
185
|
+
self, *, temporary: bool = False, ast_override: ast.AST | None = None
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Parse file and run first pass of semantic analysis.
|
|
188
|
+
|
|
189
|
+
Everything done here is local to the file. Don't depend on imported
|
|
190
|
+
modules in any way. Also record module dependencies based on imports.
|
|
191
|
+
"""
|
|
192
|
+
if self.tree is not None:
|
|
193
|
+
# The file was already parsed (in __init__()).
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
manager = self.manager
|
|
197
|
+
|
|
198
|
+
# Can we reuse a previously parsed AST? This avoids redundant work in daemon.
|
|
199
|
+
cached = self.id in manager.ast_cache
|
|
200
|
+
modules = manager.modules
|
|
201
|
+
if not cached:
|
|
202
|
+
manager.log(f"Parsing {self.xpath} ({self.id})")
|
|
203
|
+
else:
|
|
204
|
+
manager.log(f"Using cached AST for {self.xpath} ({self.id})")
|
|
205
|
+
|
|
206
|
+
t0 = myb.time_ref()
|
|
207
|
+
|
|
208
|
+
with self.wrap_context():
|
|
209
|
+
source = self.source
|
|
210
|
+
self.source = None # We won't need it again.
|
|
211
|
+
if self.path and source is None:
|
|
212
|
+
try:
|
|
213
|
+
path = manager.maybe_swap_for_shadow_path(self.path)
|
|
214
|
+
source = myb.decode_python_encoding(manager.fscache.read(path))
|
|
215
|
+
self.source_hash = manager.fscache.hash_digest(path)
|
|
216
|
+
except OSError as ioerr:
|
|
217
|
+
# ioerr.strerror differs for os.stat failures between Windows and
|
|
218
|
+
# other systems, but os.strerror(ioerr.errno) does not, so we use that.
|
|
219
|
+
# (We want the error messages to be platform-independent so that the
|
|
220
|
+
# tests have predictable output.)
|
|
221
|
+
raise myb.CompileError(
|
|
222
|
+
[
|
|
223
|
+
"mypy: can't read file '{}': {}".format(
|
|
224
|
+
self.path, myb.os.strerror(ioerr.errno)
|
|
225
|
+
)
|
|
226
|
+
],
|
|
227
|
+
module_with_blocker=self.id,
|
|
228
|
+
) from ioerr
|
|
229
|
+
except (UnicodeDecodeError, myb.DecodeError) as decodeerr:
|
|
230
|
+
if self.path.endswith(".pyd"):
|
|
231
|
+
err = (
|
|
232
|
+
f"mypy: stubgen does not support .pyd files: '{self.path}'"
|
|
233
|
+
)
|
|
234
|
+
else:
|
|
235
|
+
err = f"mypy: can't decode file '{self.path}': {str(decodeerr)}"
|
|
236
|
+
raise myb.CompileError(
|
|
237
|
+
[err], module_with_blocker=self.id
|
|
238
|
+
) from decodeerr
|
|
239
|
+
elif self.path and self.manager.fscache.isdir(self.path):
|
|
240
|
+
source = ""
|
|
241
|
+
self.source_hash = ""
|
|
242
|
+
else:
|
|
243
|
+
assert source is not None
|
|
244
|
+
self.source_hash = myb.compute_hash(source)
|
|
245
|
+
|
|
246
|
+
self.parse_inline_configuration(source)
|
|
247
|
+
if not cached:
|
|
248
|
+
self.tree = manager.parse_file(
|
|
249
|
+
self.id,
|
|
250
|
+
self.xpath,
|
|
251
|
+
source,
|
|
252
|
+
self.ignore_all or self.options.ignore_errors,
|
|
253
|
+
self.options,
|
|
254
|
+
ast_override=ast_override,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
else:
|
|
258
|
+
# Reuse a cached AST
|
|
259
|
+
self.tree = manager.ast_cache[self.id][0]
|
|
260
|
+
manager.errors.set_file_ignored_lines(
|
|
261
|
+
self.xpath,
|
|
262
|
+
self.tree.ignored_lines,
|
|
263
|
+
self.ignore_all or self.options.ignore_errors,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
self.time_spent_us += myb.time_spent_us(t0)
|
|
267
|
+
|
|
268
|
+
if not cached:
|
|
269
|
+
# Make a copy of any errors produced during parse time so that
|
|
270
|
+
# fine-grained mode can repeat them when the module is
|
|
271
|
+
# reprocessed.
|
|
272
|
+
self.early_errors = list(manager.errors.error_info_map.get(self.xpath, []))
|
|
273
|
+
else:
|
|
274
|
+
self.early_errors = manager.ast_cache[self.id][1]
|
|
275
|
+
|
|
276
|
+
if not temporary:
|
|
277
|
+
modules[self.id] = self.tree
|
|
278
|
+
|
|
279
|
+
if not cached:
|
|
280
|
+
self.semantic_analysis_pass1()
|
|
281
|
+
|
|
282
|
+
if not temporary:
|
|
283
|
+
self.check_blockers()
|
|
284
|
+
|
|
285
|
+
manager.ast_cache[self.id] = (self.tree, self.early_errors)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
__all__ = [
|
|
289
|
+
"BuildManager",
|
|
290
|
+
"State",
|
|
291
|
+
"BuildSource",
|
|
292
|
+
"BuildSourceSet",
|
|
293
|
+
"FileSystemCache",
|
|
294
|
+
"compute_search_paths",
|
|
295
|
+
"load_graph",
|
|
296
|
+
"load_plugins",
|
|
297
|
+
"process_graph",
|
|
298
|
+
"Errors",
|
|
299
|
+
"Options",
|
|
300
|
+
"ASTConverter",
|
|
301
|
+
"semantic_analysis_for_scc",
|
|
302
|
+
]
|
jaclang/jac/plugin/__init__.py
CHANGED
jaclang/jac/plugin/default.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
from typing import Any, Callable, Optional
|
|
5
|
+
from typing import Any, Callable, Optional, Type
|
|
6
6
|
|
|
7
7
|
from jaclang.jac.constant import EdgeDir
|
|
8
|
-
from jaclang.jac.plugin import
|
|
9
|
-
|
|
8
|
+
from jaclang.jac.plugin.spec import AT, Architype, T
|
|
9
|
+
|
|
10
|
+
import pluggy
|
|
11
|
+
|
|
12
|
+
hookimpl = pluggy.HookimplMarker("jac")
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class JacFeatureDefaults:
|
|
@@ -14,9 +17,10 @@ class JacFeatureDefaults:
|
|
|
14
17
|
|
|
15
18
|
@staticmethod
|
|
16
19
|
@hookimpl
|
|
17
|
-
def bind_architype(arch: AT) ->
|
|
20
|
+
def bind_architype(arch: Type[AT], arch_type: str) -> bool:
|
|
18
21
|
"""Create a new architype."""
|
|
19
22
|
arch._jac_ = None
|
|
23
|
+
return True
|
|
20
24
|
|
|
21
25
|
@staticmethod
|
|
22
26
|
@hookimpl
|
|
@@ -44,6 +48,7 @@ class JacFeatureDefaults:
|
|
|
44
48
|
@hookimpl
|
|
45
49
|
def ignore(walker_obj: Any, expr: Any) -> bool: # noqa: ANN401
|
|
46
50
|
"""Jac's ignore stmt feature."""
|
|
51
|
+
return True
|
|
47
52
|
|
|
48
53
|
@staticmethod
|
|
49
54
|
@hookimpl
|
|
@@ -55,6 +60,7 @@ class JacFeatureDefaults:
|
|
|
55
60
|
@hookimpl
|
|
56
61
|
def disengage(walker_obj: Any) -> bool: # noqa: ANN401
|
|
57
62
|
"""Jac's disengage stmt feature."""
|
|
63
|
+
return True
|
|
58
64
|
|
|
59
65
|
@staticmethod
|
|
60
66
|
@hookimpl
|
|
@@ -88,3 +94,13 @@ class JacFeatureDefaults:
|
|
|
88
94
|
) -> list[T]:
|
|
89
95
|
"""Jac's assign comprehension feature."""
|
|
90
96
|
return target
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
@hookimpl
|
|
100
|
+
def get_root() -> Architype:
|
|
101
|
+
"""Jac's assign comprehension feature."""
|
|
102
|
+
|
|
103
|
+
class Blank(Architype):
|
|
104
|
+
_jac_: Any = None
|
|
105
|
+
|
|
106
|
+
return Blank()
|
jaclang/jac/plugin/feature.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""Jac Language Features."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from dataclasses import dataclass
|
|
5
4
|
import inspect
|
|
5
|
+
from dataclasses import dataclass
|
|
6
6
|
from types import FunctionType, MethodType
|
|
7
7
|
from typing import Any, Callable, Optional, Type
|
|
8
8
|
|
|
9
9
|
from jaclang.jac.constant import EdgeDir
|
|
10
10
|
from jaclang.jac.plugin.default import JacFeatureDefaults
|
|
11
|
-
from jaclang.jac.plugin.spec import JacFeatureSpec, T
|
|
11
|
+
from jaclang.jac.plugin.spec import AT, AbsRootHook, Architype, JacFeatureSpec, T
|
|
12
12
|
|
|
13
13
|
import pluggy
|
|
14
14
|
|
|
@@ -18,9 +18,10 @@ class JacFeature:
|
|
|
18
18
|
|
|
19
19
|
pm = pluggy.PluginManager("jac")
|
|
20
20
|
pm.add_hookspecs(JacFeatureSpec)
|
|
21
|
-
pm.load_setuptools_entrypoints("jac")
|
|
22
21
|
pm.register(JacFeatureDefaults)
|
|
23
22
|
|
|
23
|
+
RootType: Type[AbsRootHook] = AbsRootHook
|
|
24
|
+
|
|
24
25
|
@staticmethod
|
|
25
26
|
def make_architype(arch_type: str) -> Callable[[type], type]:
|
|
26
27
|
"""Create a new architype."""
|
|
@@ -43,15 +44,18 @@ class JacFeature:
|
|
|
43
44
|
for k, v in cls_module_globals.items(): # Risky!
|
|
44
45
|
if k not in func_module_globals and not k.startswith("__"):
|
|
45
46
|
func_module_globals[k] = v
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
cls = dataclass(cls)
|
|
48
|
+
if not issubclass(cls, Architype):
|
|
49
|
+
cls = type(cls.__name__, (cls, Architype), {})
|
|
50
|
+
JacFeature.bind_architype(cls, arch_type)
|
|
51
|
+
return cls
|
|
48
52
|
|
|
49
53
|
return decorator
|
|
50
54
|
|
|
51
55
|
@staticmethod
|
|
52
|
-
def bind_architype(arch: AT) ->
|
|
56
|
+
def bind_architype(arch: Type[AT], arch_type: str) -> bool:
|
|
53
57
|
"""Create a new architype."""
|
|
54
|
-
return JacFeature.pm.hook.bind_architype(arch=arch)
|
|
58
|
+
return JacFeature.pm.hook.bind_architype(arch=arch, arch_type=arch_type)
|
|
55
59
|
|
|
56
60
|
@staticmethod
|
|
57
61
|
def make_ds_ability(event: str, trigger: Optional[type]) -> Callable[[type], type]:
|
|
@@ -113,3 +117,8 @@ class JacFeature:
|
|
|
113
117
|
) -> list[T]:
|
|
114
118
|
"""Jac's assign comprehension feature."""
|
|
115
119
|
return JacFeature.pm.hook.assign_compr(target=target, attr_val=attr_val)
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def get_root() -> Architype:
|
|
123
|
+
"""Jac's assign comprehension feature."""
|
|
124
|
+
return JacFeature.pm.hook.get_root()
|