jaclang 0.2.5__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 (72) hide show
  1. jaclang/__init__.py +3 -3
  2. jaclang/cli/__init__.py +0 -1
  3. jaclang/cli/__jac_gen__/cli.py +4 -4
  4. jaclang/cli/__jac_gen__/cmds.py +1 -1
  5. jaclang/cli/__jac_gen__/cmds_impl.py +1 -1
  6. jaclang/core/__init__.py +5 -11
  7. jaclang/core/__jac_gen__/corelib.py +289 -0
  8. jaclang/core/__jac_gen__/corelib_impl.py +220 -0
  9. jaclang/core/corelib.jac +21 -34
  10. jaclang/core/corelib_impl.jac +317 -0
  11. jaclang/jac/__init__.py +1 -0
  12. jaclang/jac/__jac_gen__/jac_parser.py +2 -2
  13. jaclang/jac/absyntree.py +28 -8
  14. jaclang/jac/constant.py +3 -7
  15. jaclang/jac/parser.py +13 -9
  16. jaclang/jac/passes/main/__init__.py +2 -0
  17. jaclang/jac/passes/main/def_use_pass.py +3 -2
  18. jaclang/jac/passes/main/pyast_gen_pass.py +99 -34
  19. jaclang/jac/passes/main/schedules.py +6 -0
  20. jaclang/jac/passes/main/sym_tab_build_pass.py +3 -5
  21. jaclang/jac/passes/main/tests/test_jac_format_pass.py +22 -4
  22. jaclang/jac/passes/main/tests/test_type_check_pass.py +42 -0
  23. jaclang/jac/passes/main/type_check_pass.py +103 -0
  24. jaclang/jac/passes/tool/fuse_comments_pass.py +57 -39
  25. jaclang/jac/passes/tool/jac_formatter_pass.py +419 -192
  26. jaclang/jac/passes/transform.py +0 -39
  27. jaclang/jac/passes/utils/__init__.py +1 -0
  28. jaclang/jac/passes/utils/mypy_ast_build.py +302 -0
  29. jaclang/jac/plugin/__init__.py +5 -2
  30. jaclang/jac/plugin/default.py +20 -4
  31. jaclang/jac/plugin/feature.py +15 -6
  32. jaclang/jac/plugin/spec.py +34 -6
  33. jaclang/jac/tests/test_workspace.py +45 -5
  34. jaclang/jac/transpiler.py +4 -9
  35. jaclang/utils/helpers.py +0 -33
  36. jaclang/utils/lang_tools.py +3 -0
  37. jaclang/utils/test.py +3 -1
  38. jaclang/vendor/lark/py.typed +0 -0
  39. jaclang/vendor/mypy/checker.py +19 -12
  40. jaclang/vendor/mypy/checkexpr.py +31 -10
  41. jaclang/vendor/mypy/constraints.py +56 -38
  42. jaclang/vendor/mypy/expandtype.py +1 -0
  43. jaclang/vendor/mypy/meet.py +10 -1
  44. jaclang/vendor/mypy/messages.py +16 -4
  45. jaclang/vendor/mypy/moduleinspect.py +10 -4
  46. jaclang/vendor/mypy/py.typed +1 -0
  47. jaclang/vendor/mypy/semanal.py +18 -17
  48. jaclang/vendor/mypy/semanal_enum.py +7 -4
  49. jaclang/vendor/mypy/semanal_namedtuple.py +11 -1
  50. jaclang/vendor/mypy/semanal_typeddict.py +25 -11
  51. jaclang/vendor/mypy/stubdoc.py +18 -4
  52. jaclang/vendor/mypy/stubgen.py +80 -1
  53. jaclang/vendor/mypy/stubgenc.py +47 -5
  54. jaclang/vendor/mypy/stubtest.py +53 -3
  55. jaclang/vendor/mypy/stubutil.py +9 -9
  56. jaclang/vendor/mypy/test/testipc.py +16 -7
  57. jaclang/vendor/mypy/test/teststubtest.py +20 -2
  58. jaclang/vendor/mypy/types.py +1 -1
  59. jaclang/vendor/mypyc/irbuild/prebuildvisitor.py +2 -1
  60. jaclang/vendor/mypyc/test/test_run.py +2 -4
  61. jaclang/vendor/pluggy/py.typed +0 -0
  62. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/METADATA +1 -1
  63. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/RECORD +67 -62
  64. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/WHEEL +1 -1
  65. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/entry_points.txt +3 -0
  66. jaclang/core/arch_impl.jac +0 -131
  67. jaclang/core/element_impl.jac +0 -109
  68. jaclang/core/exec_ctx_impl.jac +0 -14
  69. jaclang/core/memory_impl.jac +0 -57
  70. jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +0 -5
  71. /jaclang/{jac/tests/fixtures → core}/__jac_gen__/__init__.py +0 -0
  72. {jaclang-0.2.5.dist-info → jaclang-0.3.0.dist-info}/top_level.txt +0 -0
@@ -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, T
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()
@@ -8,7 +8,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 AT, 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
- 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()
@@ -1,7 +1,7 @@
1
1
  """Jac Language Features."""
2
2
  from __future__ import annotations
3
3
 
4
- from typing import Any, Callable, Optional, Protocol, TypeVar
4
+ from typing import Any, Callable, Optional, Protocol, Type, TypeVar
5
5
 
6
6
  from jaclang.jac.constant import EdgeDir
7
7
 
@@ -10,14 +10,26 @@ import pluggy
10
10
  hookspec = pluggy.HookspecMarker("jac")
11
11
 
12
12
 
13
- class ArchitypeProtocol(Protocol):
13
+ class Architype:
14
14
  """Architype Protocol."""
15
15
 
16
- _jac_: None
16
+ class ArchitypeProtocol(Protocol):
17
+ """Architype Protocol."""
18
+
19
+ _jac_: ArchitypeProtocol
20
+
21
+ def __call__(self, target: Architype) -> None:
22
+ """Call the architype's data spatial behavior."""
23
+ if callable(self._jac_):
24
+ return self._jac_(target)
25
+
26
+
27
+ class AbsRootHook:
28
+ """Abstract Root Node."""
17
29
 
18
30
 
19
31
  T = TypeVar("T")
20
- AT = TypeVar("AT", bound=ArchitypeProtocol)
32
+ AT = TypeVar("AT", bound=Architype)
21
33
 
22
34
 
23
35
  class JacFeatureSpec:
@@ -25,39 +37,45 @@ class JacFeatureSpec:
25
37
 
26
38
  @staticmethod
27
39
  @hookspec(firstresult=True)
28
- def bind_architype(arch: AT) -> None:
40
+ def bind_architype(arch: Type[AT], arch_type: str) -> bool:
29
41
  """Create a new architype."""
42
+ raise NotImplementedError
30
43
 
31
44
  @staticmethod
32
45
  @hookspec(firstresult=True)
33
46
  def make_ds_ability(event: str, trigger: Optional[type]) -> Callable[[type], type]:
34
47
  """Create a new architype."""
48
+ raise NotImplementedError
35
49
 
36
50
  @staticmethod
37
51
  @hookspec(firstresult=True)
38
52
  def elvis(op1: Optional[T], op2: T) -> T: # noqa: ANN401
39
53
  """Jac's elvis operator feature."""
54
+ raise NotImplementedError
40
55
 
41
56
  @staticmethod
42
57
  @hookspec(firstresult=True)
43
58
  def report(expr: Any) -> Any: # noqa: ANN401
44
59
  """Jac's report stmt feature."""
60
+ raise NotImplementedError
45
61
 
46
62
  @staticmethod
47
63
  @hookspec(firstresult=True)
48
64
  def ignore(walker_obj: Any, expr: Any) -> bool: # noqa: ANN401
49
65
  """Jac's ignore stmt feature."""
66
+ raise NotImplementedError
50
67
 
51
68
  @staticmethod
52
69
  @hookspec(firstresult=True)
53
70
  def visit(walker_obj: Any, expr: Any) -> bool: # noqa: ANN401
54
71
  """Jac's visit stmt feature."""
55
- return True
72
+ raise NotImplementedError
56
73
 
57
74
  @staticmethod
58
75
  @hookspec(firstresult=True)
59
76
  def disengage(walker_obj: Any) -> bool: # noqa: ANN401
60
77
  """Jac's disengage stmt feature."""
78
+ raise NotImplementedError
61
79
 
62
80
  @staticmethod
63
81
  @hookspec(firstresult=True)
@@ -65,6 +83,7 @@ class JacFeatureSpec:
65
83
  node_obj: Any, dir: EdgeDir, filter_type: Optional[type] # noqa: ANN401
66
84
  ) -> list[Any]:
67
85
  """Jac's apply_dir stmt feature."""
86
+ raise NotImplementedError
68
87
 
69
88
  @staticmethod
70
89
  @hookspec(firstresult=True)
@@ -73,11 +92,13 @@ class JacFeatureSpec:
73
92
 
74
93
  Note: connect needs to call assign compr with tuple in op
75
94
  """
95
+ raise NotImplementedError
76
96
 
77
97
  @staticmethod
78
98
  @hookspec(firstresult=True)
79
99
  def disconnect(op1: Optional[T], op2: T, op: Any) -> T: # noqa: ANN401
80
100
  """Jac's connect operator feature."""
101
+ raise NotImplementedError
81
102
 
82
103
  @staticmethod
83
104
  @hookspec(firstresult=True)
@@ -85,3 +106,10 @@ class JacFeatureSpec:
85
106
  target: list[T], attr_val: tuple[tuple[str], tuple[Any]]
86
107
  ) -> list[T]:
87
108
  """Jac's assign comprehension feature."""
109
+ raise NotImplementedError
110
+
111
+ @staticmethod
112
+ @hookspec(firstresult=True)
113
+ def get_root() -> Architype:
114
+ """Jac's root getter."""
115
+ raise NotImplementedError
@@ -40,13 +40,53 @@ class TestWorkspace(TestCase):
40
40
  def test_man_code_dir(self) -> None:
41
41
  """Test of circle workspace."""
42
42
  loc = os.path.join(os.path.dirname(__file__))
43
- # print(loc)
44
43
  ws = Workspace(path=loc + "/../../../examples/manual_code")
45
44
  key = [i for i in ws.modules.keys() if "circle.jac" in i][0]
46
45
  # print(ws.modules[key].ir.sym_tab.pp())
47
46
  # for i in ws.get_symbols(key):
48
47
  # print(i.decl.pp(depth=2))
49
- # for i in ws.get_uses(key):
50
- # print(i.pp(depth=2))
51
- # print(ws.get_uses(key))
52
- self.assertGreater(len(ws.get_uses(key)), 5)
48
+ out = ""
49
+ for i in ws.get_uses(key):
50
+ # print(i.pp(depth=2).strip())
51
+ out += i.pp(depth=2)
52
+ for i in [
53
+ "math",
54
+ "calculate_area",
55
+ "RAD",
56
+ "expected_area",
57
+ "Circle",
58
+ "c",
59
+ "ShapeType",
60
+ "float",
61
+ "radius",
62
+ "CIRCLE",
63
+ "Shape",
64
+ "__init__",
65
+ "print",
66
+ ]:
67
+ self.assertIn(i, out)
68
+
69
+ # def test_decl_impl(self) -> None:
70
+ # """Test of circle workspace."""
71
+ # loc = os.path.join(os.path.dirname(__file__))
72
+ # ws = Workspace(path=loc + "/../../../examples/manual_code")
73
+ # key = [i for i in ws.modules.keys() if "circle_clean.jac" in i][0]
74
+ # out = ""
75
+ # for i in ws.get_uses(key):
76
+ # out += i.pp(depth=2)
77
+ # for i in [
78
+ # "math",
79
+ # "calculate_area",
80
+ # "RAD",
81
+ # "expected_area",
82
+ # "Circle",
83
+ # "c",
84
+ # "ShapeType",
85
+ # "float",
86
+ # "radius",
87
+ # "CIRCLE",
88
+ # "Shape",
89
+ # "__init__",
90
+ # "print",
91
+ # ]:
92
+ # self.assertIn(i, out)