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.

Files changed (82) hide show
  1. jaclang/__init__.py +9 -3
  2. jaclang/cli/__init__.py +0 -1
  3. jaclang/cli/__jac_gen__/cli.py +6 -6
  4. jaclang/cli/__jac_gen__/cli_impl.py +2 -2
  5. jaclang/cli/__jac_gen__/cmds.py +2 -3
  6. jaclang/cli/__jac_gen__/cmds_impl.py +2 -3
  7. jaclang/cli/cmds.jac +1 -1
  8. jaclang/cli/cmds_impl.jac +2 -3
  9. jaclang/core/__init__.py +5 -11
  10. jaclang/core/__jac_gen__/corelib.py +289 -0
  11. jaclang/core/__jac_gen__/corelib_impl.py +220 -0
  12. jaclang/core/corelib.jac +21 -34
  13. jaclang/core/corelib_impl.jac +317 -0
  14. jaclang/jac/__init__.py +1 -0
  15. jaclang/jac/__jac_gen__/jac_parser.py +2 -2
  16. jaclang/jac/absyntree.py +32 -62
  17. jaclang/jac/constant.py +3 -7
  18. jaclang/jac/importer.py +1 -1
  19. jaclang/jac/parser.py +14 -10
  20. jaclang/jac/passes/main/__init__.py +2 -0
  21. jaclang/jac/passes/main/def_use_pass.py +4 -7
  22. jaclang/jac/passes/main/pyast_gen_pass.py +116 -35
  23. jaclang/jac/passes/main/schedules.py +6 -0
  24. jaclang/jac/passes/main/sym_tab_build_pass.py +40 -19
  25. jaclang/jac/passes/main/tests/test_jac_format_pass.py +22 -4
  26. jaclang/jac/passes/main/tests/test_pyast_gen_pass.py +3 -1
  27. jaclang/jac/passes/main/tests/test_type_check_pass.py +42 -0
  28. jaclang/jac/passes/main/type_check_pass.py +103 -0
  29. jaclang/jac/passes/tool/ast_printer_pass.py +8 -2
  30. jaclang/jac/passes/tool/fuse_comments_pass.py +57 -39
  31. jaclang/jac/passes/tool/jac_formatter_pass.py +419 -192
  32. jaclang/jac/passes/tool/sym_tab_printer_pass.py +10 -93
  33. jaclang/jac/passes/tool/tests/test_ast_print_pass.py +2 -1
  34. jaclang/jac/passes/transform.py +0 -39
  35. jaclang/jac/passes/utils/__init__.py +1 -0
  36. jaclang/jac/passes/utils/mypy_ast_build.py +302 -0
  37. jaclang/jac/plugin/__init__.py +5 -2
  38. jaclang/jac/plugin/default.py +20 -4
  39. jaclang/jac/plugin/feature.py +16 -7
  40. jaclang/jac/plugin/spec.py +34 -6
  41. jaclang/jac/symtable.py +6 -0
  42. jaclang/jac/tests/test_workspace.py +55 -1
  43. jaclang/jac/transpiler.py +4 -9
  44. jaclang/utils/helpers.py +0 -33
  45. jaclang/utils/lang_tools.py +3 -0
  46. jaclang/utils/test.py +3 -1
  47. jaclang/utils/treeprinter.py +171 -0
  48. jaclang/vendor/lark/py.typed +0 -0
  49. jaclang/vendor/mypy/checker.py +19 -12
  50. jaclang/vendor/mypy/checkexpr.py +31 -10
  51. jaclang/vendor/mypy/constraints.py +56 -38
  52. jaclang/vendor/mypy/expandtype.py +1 -0
  53. jaclang/vendor/mypy/meet.py +10 -1
  54. jaclang/vendor/mypy/messages.py +16 -4
  55. jaclang/vendor/mypy/moduleinspect.py +10 -4
  56. jaclang/vendor/mypy/py.typed +1 -0
  57. jaclang/vendor/mypy/semanal.py +18 -17
  58. jaclang/vendor/mypy/semanal_enum.py +7 -4
  59. jaclang/vendor/mypy/semanal_namedtuple.py +11 -1
  60. jaclang/vendor/mypy/semanal_typeddict.py +25 -11
  61. jaclang/vendor/mypy/stubdoc.py +18 -4
  62. jaclang/vendor/mypy/stubgen.py +80 -1
  63. jaclang/vendor/mypy/stubgenc.py +47 -5
  64. jaclang/vendor/mypy/stubtest.py +53 -3
  65. jaclang/vendor/mypy/stubutil.py +9 -9
  66. jaclang/vendor/mypy/test/testipc.py +16 -7
  67. jaclang/vendor/mypy/test/teststubtest.py +20 -2
  68. jaclang/vendor/mypy/types.py +1 -1
  69. jaclang/vendor/mypyc/irbuild/prebuildvisitor.py +2 -1
  70. jaclang/vendor/mypyc/test/test_run.py +2 -4
  71. jaclang/vendor/pluggy/py.typed +0 -0
  72. {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/METADATA +1 -1
  73. {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/RECORD +77 -71
  74. {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/WHEEL +1 -1
  75. {jaclang-0.2.4.dist-info → jaclang-0.3.0.dist-info}/entry_points.txt +3 -0
  76. jaclang/core/arch_impl.jac +0 -131
  77. jaclang/core/element_impl.jac +0 -109
  78. jaclang/core/exec_ctx_impl.jac +0 -14
  79. jaclang/core/memory_impl.jac +0 -57
  80. jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +0 -5
  81. /jaclang/{jac/tests/fixtures → core}/__jac_gen__/__init__.py +0 -0
  82. {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 List, Optional
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
- class _SymbolTree:
11
- def __init__(
12
- self,
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
- root = _build_symbol_tree_common(self.ir.sym_tab)
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: _SymbolTree) -> int:
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: _SymbolTree) -> str:
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: _SymbolTree) -> None:
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())
@@ -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
+ ]
@@ -1,4 +1,7 @@
1
1
  """Plugin interface for Jac."""
2
- import pluggy
2
+ from __future__ import annotations
3
3
 
4
- hookimpl = pluggy.HookimplMarker("jac")
4
+ from .default import hookimpl
5
+ from .spec import AbsRootHook, Architype
6
+
7
+ __all__ = ["Architype", "AbsRootHook", "hookimpl"]
@@ -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 hookimpl
9
- from jaclang.jac.plugin.spec import AT
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) -> None:
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()
@@ -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, AT
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
- JacFeature.bind_architype(cls)
47
- return dataclass(cls)
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) -> None:
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()