jaclang 0.5.6__py3-none-any.whl → 0.5.8__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 (51) hide show
  1. jaclang/__init__.py +6 -1
  2. jaclang/cli/cli.py +63 -20
  3. jaclang/cli/cmdreg.py +42 -12
  4. jaclang/compiler/__init__.py +6 -3
  5. jaclang/compiler/__jac_gen__/jac_parser.py +2 -2
  6. jaclang/compiler/absyntree.py +1740 -61
  7. jaclang/compiler/codeloc.py +7 -0
  8. jaclang/compiler/compile.py +4 -5
  9. jaclang/compiler/constant.py +52 -6
  10. jaclang/compiler/parser.py +220 -129
  11. jaclang/compiler/passes/main/def_impl_match_pass.py +19 -3
  12. jaclang/compiler/passes/main/def_use_pass.py +1 -1
  13. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +357 -0
  14. jaclang/compiler/passes/main/import_pass.py +7 -3
  15. jaclang/compiler/passes/main/pyast_gen_pass.py +333 -93
  16. jaclang/compiler/passes/main/pyast_load_pass.py +1779 -206
  17. jaclang/compiler/passes/main/pyout_pass.py +2 -2
  18. jaclang/compiler/passes/main/schedules.py +2 -1
  19. jaclang/compiler/passes/main/sym_tab_build_pass.py +20 -28
  20. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
  21. jaclang/compiler/passes/main/tests/test_pyast_build_pass.py +14 -5
  22. jaclang/compiler/passes/main/tests/test_sym_tab_build_pass.py +8 -8
  23. jaclang/compiler/passes/main/tests/test_typeinfo_pass.py +7 -0
  24. jaclang/compiler/passes/main/type_check_pass.py +0 -1
  25. jaclang/compiler/passes/tool/jac_formatter_pass.py +8 -17
  26. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +43 -0
  27. jaclang/compiler/passes/utils/mypy_ast_build.py +28 -14
  28. jaclang/compiler/symtable.py +23 -2
  29. jaclang/compiler/tests/test_parser.py +53 -0
  30. jaclang/compiler/workspace.py +52 -26
  31. jaclang/core/aott.py +68 -0
  32. jaclang/core/construct.py +58 -6
  33. jaclang/core/importer.py +9 -10
  34. jaclang/core/utils.py +65 -3
  35. jaclang/plugin/builtin.py +42 -0
  36. jaclang/plugin/default.py +163 -18
  37. jaclang/plugin/feature.py +38 -10
  38. jaclang/plugin/spec.py +33 -6
  39. jaclang/utils/helpers.py +25 -0
  40. jaclang/utils/lang_tools.py +4 -1
  41. jaclang/utils/test.py +1 -0
  42. jaclang/utils/tests/test_lang_tools.py +12 -15
  43. jaclang/utils/treeprinter.py +10 -2
  44. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/METADATA +1 -1
  45. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/RECORD +48 -46
  46. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/WHEEL +1 -1
  47. jaclang/compiler/tests/fixtures/__jac_gen__/__init__.py +0 -0
  48. jaclang/compiler/tests/fixtures/__jac_gen__/hello_world.py +0 -5
  49. jaclang/core/jacbuiltins.py +0 -10
  50. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/entry_points.txt +0 -0
  51. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import os
6
- from typing import Sequence
6
+ from typing import Optional, Sequence
7
7
 
8
8
  import jaclang.compiler.absyntree as ast
9
9
  from jaclang.compiler.compile import jac_str_to_pass
@@ -33,7 +33,7 @@ class ModuleInfo:
33
33
 
34
34
  def __init__(
35
35
  self,
36
- ir: ast.Module,
36
+ ir: Optional[ast.Module],
37
37
  errors: Sequence[Alert],
38
38
  warnings: Sequence[Alert],
39
39
  ) -> None:
@@ -46,10 +46,11 @@ class ModuleInfo:
46
46
  class Workspace:
47
47
  """Class for managing workspace."""
48
48
 
49
- def __init__(self, path: str) -> None:
49
+ def __init__(self, path: str, lazy_parse: bool = False) -> None:
50
50
  """Initialize workspace."""
51
51
  self.path = path
52
52
  self.modules: dict[str, ModuleInfo] = {}
53
+ self.lazy_parse = lazy_parse
53
54
  self.rebuild_workspace()
54
55
 
55
56
  def rebuild_workspace(self) -> None:
@@ -63,6 +64,15 @@ class Workspace:
63
64
  ]:
64
65
  if file in self.modules:
65
66
  continue
67
+ if self.lazy_parse:
68
+ # If lazy_parse is True, add the file to modules with empty IR
69
+ self.modules[file] = ModuleInfo(
70
+ ir=None,
71
+ errors=[],
72
+ warnings=[],
73
+ )
74
+ continue
75
+
66
76
  with open(file, "r") as f:
67
77
  source = f.read()
68
78
  build = jac_str_to_pass(
@@ -98,10 +108,13 @@ class Workspace:
98
108
  warnings=build.warnings_had,
99
109
  )
100
110
 
101
- def rebuild_file(self, file_path: str, deep: bool = False) -> bool:
111
+ def rebuild_file(
112
+ self, file_path: str, deep: bool = False, source: str = ""
113
+ ) -> bool:
102
114
  """Rebuild a file."""
103
- with open(file_path, "r") as f:
104
- source = f.read()
115
+ if source == "":
116
+ with open(file_path, "r") as f:
117
+ source = f.read()
105
118
  build = jac_str_to_pass(
106
119
  jac_str=source,
107
120
  file_path=file_path,
@@ -152,30 +165,40 @@ class Workspace:
152
165
  self, file_path: str, deep: bool = False
153
166
  ) -> list[ast.ModulePath]:
154
167
  """Return a list of dependencies for a file."""
168
+ mod_ir = self.modules[file_path].ir
155
169
  if deep:
156
- return [
157
- i
158
- for i in self.modules[file_path].ir.get_all_sub_nodes(ast.ModulePath)
159
- if i.parent
160
- and isinstance(i.parent, ast.Import)
161
- and i.parent.lang.tag.value == "jac"
162
- ]
170
+ return (
171
+ [
172
+ i
173
+ for i in mod_ir.get_all_sub_nodes(ast.ModulePath)
174
+ if i.parent
175
+ and isinstance(i.parent, ast.Import)
176
+ and i.parent.lang.tag.value == "jac"
177
+ ]
178
+ if mod_ir
179
+ else []
180
+ )
163
181
  else:
164
- return [
165
- i
166
- for i in self.modules[file_path].ir.get_all_sub_nodes(ast.ModulePath)
167
- if i.loc.mod_path == file_path
168
- and i.parent
169
- and isinstance(i.parent, ast.Import)
170
- and i.parent.lang.tag.value == "jac"
171
- ]
182
+ return (
183
+ [
184
+ i
185
+ for i in mod_ir.get_all_sub_nodes(ast.ModulePath)
186
+ if i.loc.mod_path == file_path
187
+ and i.parent
188
+ and isinstance(i.parent, ast.Import)
189
+ and i.parent.lang.tag.value == "jac"
190
+ ]
191
+ if mod_ir
192
+ else []
193
+ )
172
194
 
173
195
  def get_symbols(self, file_path: str) -> Sequence[Symbol]:
174
196
  """Return a list of symbols for a file."""
175
197
  symbols = []
198
+ mod_ir = self.modules[file_path].ir
176
199
  if file_path in self.modules:
177
- root_table = self.modules[file_path].ir.sym_tab
178
- if file_path in self.modules and isinstance(root_table, SymbolTable):
200
+ root_table = mod_ir.sym_tab if mod_ir else None
201
+ if file_path in self.modules and root_table:
179
202
  for i in sym_tab_list(sym_tab=root_table, file_path=file_path):
180
203
  symbols += list(i.tab.values())
181
204
  return symbols
@@ -191,10 +214,13 @@ class Workspace:
191
214
 
192
215
  def get_uses(self, file_path: str) -> Sequence[ast.AstSymbolNode]: # need test
193
216
  """Return a list of definitions for a file."""
194
- uses = []
217
+ mod_ir = self.modules[file_path].ir
218
+ uses: list[ast.AstSymbolNode] = []
219
+ if self.lazy_parse:
220
+ return uses
195
221
  if file_path in self.modules:
196
- root_table = self.modules[file_path].ir.sym_tab
197
- if file_path in self.modules and isinstance(root_table, SymbolTable):
222
+ root_table = mod_ir.sym_tab if mod_ir else None
223
+ if file_path in self.modules and root_table:
198
224
  for i in sym_tab_list(sym_tab=root_table, file_path=file_path):
199
225
  uses += i.uses
200
226
  return uses
jaclang/core/aott.py ADDED
@@ -0,0 +1,68 @@
1
+ """
2
+ AOTT: Automated Operational Type Transformation.
3
+
4
+ This has all the necessary functions to perform the AOTT operations.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+
10
+ prompt_template = """
11
+ [System Prompt]
12
+ This is an operation you must perform and return the output values. Neither, the methodology,
13
+ extra sentences nor the code are not needed.
14
+
15
+ [Information]
16
+ {information_str}
17
+
18
+ [Inputs and Input Type Information]
19
+ {input_types_n_information_str}
20
+
21
+ [Output Type]
22
+ {output_type_str}
23
+
24
+ [Output Type Explanations]
25
+ {output_type_info_str}
26
+
27
+ [Action]
28
+ {action}
29
+
30
+ {reason_suffix}
31
+ """
32
+
33
+ with_reason_suffix = """
34
+ Reason and return the output result(s) only, adhering to the provided Type in the following format
35
+
36
+ [Reasoning] <Reason>
37
+ [Output] <Result>
38
+ """
39
+
40
+ without_reason_suffix = """Generate and return the output result(s) only, adhering to the provided Type in the
41
+ following format
42
+
43
+ [Output] <result>
44
+ """
45
+
46
+
47
+ def aott_raise(
48
+ information_str: str,
49
+ input_types_n_information_str: str,
50
+ output_type_str: str,
51
+ output_type_info_str: str,
52
+ action: str,
53
+ reason: bool,
54
+ ) -> str:
55
+ """AOTT Raise uses the information (Meanings types values) provided to generate a prompt(meaning in)."""
56
+ return prompt_template.format(
57
+ information_str=information_str,
58
+ input_types_n_information_str=input_types_n_information_str,
59
+ output_type_str=output_type_str,
60
+ output_type_info_str=output_type_info_str,
61
+ action=action,
62
+ reason_suffix=with_reason_suffix if reason else without_reason_suffix,
63
+ )
64
+
65
+
66
+ def aott_lower(meaning_out: str, output_type_info: tuple) -> Any: # noqa: ANN401
67
+ """AOTT Lower uses the meaning out provided by the language model and return the result in the desired type."""
68
+ return meaning_out
jaclang/core/construct.py CHANGED
@@ -141,14 +141,14 @@ class EdgeAnchor(ObjectAnchor):
141
141
 
142
142
  def detach(
143
143
  self, src: NodeArchitype, trg: NodeArchitype, is_undirected: bool = False
144
- ) -> EdgeAnchor:
144
+ ) -> None:
145
145
  """Detach edge from nodes."""
146
- self.source = src # TODO: Delete me, don't keep attached
147
- self.target = trg # TODO: Delete me, don't keep attached
148
146
  self.is_undirected = is_undirected
149
147
  src._jac_.edges.remove(self.obj)
150
148
  trg._jac_.edges.remove(self.obj)
151
- return self
149
+ self.source = None
150
+ self.target = None
151
+ del self
152
152
 
153
153
  def spawn_call(self, walk: WalkerArchitype) -> WalkerArchitype:
154
154
  """Invoke data spatial call."""
@@ -328,11 +328,57 @@ class DSFunc:
328
328
  self.func = getattr(cls, self.name)
329
329
 
330
330
 
331
+ class JacTestResult(unittest.TextTestResult):
332
+ """Jac test result class."""
333
+
334
+ def __init__(
335
+ self,
336
+ stream, # noqa
337
+ descriptions, # noqa
338
+ verbosity: int,
339
+ max_failures: Optional[int] = None,
340
+ ) -> None:
341
+ """Initialize FailFastTestResult object."""
342
+ super().__init__(stream, descriptions, verbosity) # noqa
343
+ self.failures_count = 0
344
+ self.max_failures = max_failures
345
+
346
+ def addFailure(self, test, err) -> None: # noqa
347
+ """Count failures and stop."""
348
+ super().addFailure(test, err)
349
+ self.failures_count += 1
350
+ if self.max_failures is not None and self.failures_count >= self.max_failures:
351
+ self.stop()
352
+
353
+ def stop(self) -> None:
354
+ """Stop the test execution."""
355
+ self.shouldStop = True
356
+
357
+
358
+ class JacTextTestRunner(unittest.TextTestRunner):
359
+ """Jac test runner class."""
360
+
361
+ def __init__(self, max_failures: Optional[int] = None, **kwargs) -> None: # noqa
362
+ """Initialize JacTextTestRunner object."""
363
+ self.max_failures = max_failures
364
+ super().__init__(**kwargs)
365
+
366
+ def _makeResult(self) -> JacTestResult: # noqa
367
+ """Override the method to return an instance of JacTestResult."""
368
+ return JacTestResult(
369
+ self.stream,
370
+ self.descriptions,
371
+ self.verbosity,
372
+ max_failures=self.max_failures,
373
+ )
374
+
375
+
331
376
  class JacTestCheck:
332
377
  """Jac Testing and Checking."""
333
378
 
334
379
  test_case = unittest.TestCase()
335
380
  test_suite = unittest.TestSuite()
381
+ breaker = False
336
382
 
337
383
  @staticmethod
338
384
  def reset() -> None:
@@ -341,9 +387,15 @@ class JacTestCheck:
341
387
  JacTestCheck.test_suite = unittest.TestSuite()
342
388
 
343
389
  @staticmethod
344
- def run_test() -> None:
390
+ def run_test(xit: bool, maxfail: int | None, verbose: bool) -> None:
345
391
  """Run the test suite."""
346
- unittest.TextTestRunner().run(JacTestCheck.test_suite)
392
+ verb = 2 if verbose else 1
393
+ runner = JacTextTestRunner(max_failures=maxfail, failfast=xit, verbosity=verb)
394
+ result = runner.run(JacTestCheck.test_suite)
395
+ if result.wasSuccessful():
396
+ print("Passed successfully.")
397
+ else:
398
+ JacTestCheck.breaker = True
347
399
 
348
400
  @staticmethod
349
401
  def add_test(test_fun: Callable) -> None:
jaclang/core/importer.py CHANGED
@@ -46,24 +46,23 @@ def jac_importer(
46
46
  codeobj = marshal.loads(codeobj)
47
47
  else:
48
48
  gen_dir = path.join(caller_dir, Con.JAC_GEN_DIR)
49
- py_file_path = path.join(gen_dir, module_name + ".py")
50
49
  pyc_file_path = path.join(gen_dir, module_name + ".jbc")
51
50
  if (
52
51
  cachable
53
- and path.exists(py_file_path)
54
- and path.getmtime(py_file_path) > path.getmtime(full_target)
52
+ and path.exists(pyc_file_path)
53
+ and path.getmtime(pyc_file_path) > path.getmtime(full_target)
55
54
  ):
56
55
  with open(pyc_file_path, "rb") as f:
57
56
  codeobj = marshal.load(f)
58
57
  else:
59
- if error := compile_jac(full_target):
60
- if error:
61
- for e in error:
62
- print(e)
63
- logging.error(e)
58
+ result = compile_jac(full_target, cache_result=cachable)
59
+ if result.errors_had or not result.ir.gen.py_bytecode:
60
+ for e in result.errors_had:
61
+ print(e)
62
+ logging.error(e)
64
63
  return None
65
- with open(pyc_file_path, "rb") as f:
66
- codeobj = marshal.load(f)
64
+ else:
65
+ codeobj = marshal.loads(result.ir.gen.py_bytecode)
67
66
 
68
67
  module_name = override_name if override_name else module_name
69
68
  module = types.ModuleType(module_name)
jaclang/core/utils.py CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING
5
+ from typing import Callable, TYPE_CHECKING
6
6
 
7
7
 
8
8
  if TYPE_CHECKING:
9
- from jaclang.core.construct import NodeAnchor
9
+ from jaclang.core.construct import NodeAnchor, NodeArchitype
10
10
 
11
11
 
12
12
  def collect_node_connections(
@@ -18,7 +18,6 @@ def collect_node_connections(
18
18
  if current_node not in visited_nodes:
19
19
  visited_nodes.add(current_node)
20
20
  edges = current_node.edges
21
-
22
21
  for edge_ in edges:
23
22
  target = edge_._jac_.target
24
23
  if target:
@@ -26,3 +25,66 @@ def collect_node_connections(
26
25
  (current_node.obj, target._jac_.obj, edge_.__class__.__name__)
27
26
  )
28
27
  collect_node_connections(target._jac_, visited_nodes, connections)
28
+
29
+
30
+ def traverse_graph(
31
+ node: NodeArchitype,
32
+ cur_depth: int,
33
+ depth: int,
34
+ edge_type: list[str],
35
+ traverse: bool,
36
+ connections: list,
37
+ node_depths: dict[NodeArchitype, int],
38
+ visited_nodes: list,
39
+ queue: list,
40
+ bfs: bool,
41
+ dfs: Callable,
42
+ node_limit: int,
43
+ edge_limit: int,
44
+ ) -> None:
45
+ """Traverse the graph using Breadth-First Search (BFS) or Depth-First Search (DFS)."""
46
+ for edge in node._jac_.edges:
47
+ is_self_loop = id(edge._jac_.source) == id(edge._jac_.target)
48
+ is_in_edge = edge._jac_.target == node
49
+ if (traverse and is_in_edge) or edge._jac_.obj.__class__.__name__ in edge_type:
50
+ continue
51
+ if is_self_loop:
52
+ continue # lets skip self loop for a while, need to handle it later
53
+ else:
54
+ other_nd = edge._jac_.target if not is_in_edge else edge._jac_.source
55
+ new_con = (
56
+ (node, other_nd, edge) if not is_in_edge else (other_nd, node, edge)
57
+ )
58
+ if node in node_depths and node_depths[node] is not None:
59
+ if other_nd in node_depths:
60
+ node_depths[node] = min(
61
+ cur_depth, node_depths[node], node_depths[other_nd] + 1
62
+ )
63
+ node_depths[other_nd] = min(
64
+ cur_depth + 1, node_depths[node] + 1, node_depths[other_nd]
65
+ )
66
+ else:
67
+ if other_nd:
68
+ node_depths[other_nd] = min(
69
+ cur_depth + 1, node_depths[node] + 1
70
+ )
71
+ else:
72
+ raise ValueError("Edge is detached from node in graph")
73
+ if (
74
+ other_nd
75
+ and new_con not in connections
76
+ and (
77
+ (
78
+ depth < 0
79
+ or min(node_depths[node], node_depths[other_nd]) + 1 <= depth
80
+ )
81
+ and node_limit > len(visited_nodes)
82
+ and edge_limit > len(connections)
83
+ )
84
+ ):
85
+ connections.append(new_con)
86
+ if bfs:
87
+ queue.append([other_nd, cur_depth + 1])
88
+ else:
89
+
90
+ dfs(other_nd, cur_depth + 1)
@@ -0,0 +1,42 @@
1
+ """Jac specific builtins."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import Optional
9
+ from jaclang.core.construct import NodeArchitype
10
+
11
+
12
+ def dotgen(
13
+ node: Optional[NodeArchitype] = None,
14
+ depth: Optional[int] = None,
15
+ traverse: Optional[bool] = None,
16
+ edge_type: Optional[list[str]] = None,
17
+ bfs: Optional[bool] = None,
18
+ edge_limit: Optional[int] = None,
19
+ node_limit: Optional[int] = None,
20
+ dot_file: Optional[str] = None,
21
+ ) -> str:
22
+ """Print the dot graph."""
23
+ from jaclang.core.construct import root
24
+ from jaclang.plugin.feature import pm
25
+
26
+ node = node if node is not None else root
27
+ depth = depth if depth is not None else -1
28
+ traverse = traverse if traverse is not None else False
29
+ bfs = bfs if bfs is not None else True
30
+ edge_limit = edge_limit if edge_limit is not None else 512
31
+ node_limit = node_limit if node_limit is not None else 512
32
+
33
+ return pm.hook.dotgen(
34
+ edge_type=edge_type,
35
+ node=node,
36
+ depth=depth,
37
+ traverse=traverse,
38
+ bfs=bfs,
39
+ edge_limit=edge_limit,
40
+ node_limit=node_limit,
41
+ dot_file=dot_file,
42
+ )
jaclang/plugin/default.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import fnmatch
5
6
  import os
6
7
  import types
7
8
  from dataclasses import field
@@ -9,7 +10,8 @@ from functools import wraps
9
10
  from typing import Any, Callable, Optional, Type
10
11
 
11
12
  from jaclang.compiler.absyntree import Module
12
- from jaclang.compiler.constant import EdgeDir
13
+ from jaclang.compiler.constant import EdgeDir, colors
14
+ from jaclang.core.aott import aott_lower, aott_raise
13
15
  from jaclang.core.construct import (
14
16
  Architype,
15
17
  DSFunc,
@@ -26,7 +28,7 @@ from jaclang.core.construct import (
26
28
  root,
27
29
  )
28
30
  from jaclang.core.importer import jac_importer
29
- from jaclang.core.jacbuiltins import dotgen
31
+ from jaclang.core.utils import traverse_graph
30
32
  from jaclang.plugin.feature import JacFeature as Jac
31
33
  from jaclang.plugin.spec import T
32
34
 
@@ -184,20 +186,57 @@ class JacFeatureDefaults:
184
186
 
185
187
  @staticmethod
186
188
  @hookimpl
187
- def run_test(filename: str) -> bool:
188
- """Run the test suite in the specified .jac file.
189
-
190
- :param filename: The path to the .jac file.
191
- """
192
- if filename.endswith(".jac"):
193
- base, mod_name = os.path.split(filename)
194
- base = base if base else "./"
195
- mod_name = mod_name[:-4]
196
- JacTestCheck.reset()
197
- Jac.jac_import(target=mod_name, base_path=base)
198
- JacTestCheck.run_test()
189
+ def run_test(
190
+ filepath: str,
191
+ filter: Optional[str],
192
+ xit: bool,
193
+ maxfail: Optional[int],
194
+ directory: Optional[str],
195
+ verbose: bool,
196
+ ) -> bool:
197
+ """Run the test suite in the specified .jac file."""
198
+ test_file = False
199
+ if filepath:
200
+ if filepath.endswith(".jac"):
201
+ base, mod_name = os.path.split(filepath)
202
+ base = base if base else "./"
203
+ mod_name = mod_name[:-4]
204
+ JacTestCheck.reset()
205
+ Jac.jac_import(target=mod_name, base_path=base)
206
+ JacTestCheck.run_test(xit, maxfail, verbose)
207
+ else:
208
+ print("Not a .jac file.")
199
209
  else:
200
- print("Not a .jac file.")
210
+ directory = directory if directory else os.getcwd()
211
+
212
+ if filter or directory:
213
+ current_dir = directory if directory else os.getcwd()
214
+ for root_dir, _, files in os.walk(current_dir, topdown=True):
215
+ files = (
216
+ [file for file in files if fnmatch.fnmatch(file, filter)]
217
+ if filter
218
+ else files
219
+ )
220
+ files = [
221
+ file
222
+ for file in files
223
+ if not file.endswith((".test.jac", ".impl.jac"))
224
+ ]
225
+ for file in files:
226
+ if file.endswith(".jac"):
227
+ test_file = True
228
+ print(f"\n\n\t\t* Inside {root_dir}" + "/" + f"{file} *")
229
+ JacTestCheck.reset()
230
+ Jac.jac_import(target=file[:-4], base_path=root_dir)
231
+ JacTestCheck.run_test(xit, maxfail, verbose)
232
+
233
+ if JacTestCheck.breaker and (xit or maxfail):
234
+ break
235
+ if JacTestCheck.breaker and (xit or maxfail):
236
+ break
237
+ JacTestCheck.breaker = False
238
+ print("No test files found.") if not test_file else None
239
+
201
240
  return True
202
241
 
203
242
  @staticmethod
@@ -385,15 +424,121 @@ class JacFeatureDefaults:
385
424
 
386
425
  return builder
387
426
 
427
+ @staticmethod
428
+ @hookimpl
429
+ def with_llm(
430
+ model: Any, # noqa: ANN401
431
+ model_params: dict[str, Any],
432
+ incl_info: tuple,
433
+ excl_info: tuple,
434
+ inputs: tuple,
435
+ outputs: tuple,
436
+ action: str,
437
+ ) -> Any: # noqa: ANN401
438
+ """Jac's with_llm feature."""
439
+ reason = False
440
+ if "reason" in model_params:
441
+ reason = model_params.pop("reason")
442
+ input_types_n_information_str = "" # TODO: We have to generate this
443
+ output_type_str = "" # TODO: We have to generate this
444
+ output_type_info_str = "" # TODO: We have to generate this
445
+ information_str = "" # TODO: We have to generate this
446
+ meaning_in = aott_raise(
447
+ information_str,
448
+ input_types_n_information_str,
449
+ output_type_str,
450
+ output_type_info_str,
451
+ action,
452
+ reason,
453
+ )
454
+ meaning_out = model.__infer__(meaning_in, **model_params)
455
+ output_type_info = (None, None) # TODO: We have to generate this
456
+ return aott_lower(meaning_out, output_type_info)
457
+
388
458
 
389
459
  class JacBuiltin:
390
460
  """Jac Builtins."""
391
461
 
392
462
  @staticmethod
393
463
  @hookimpl
394
- def dotgen(node: NodeArchitype, radius: int = 0) -> str:
395
- """Print the dot graph."""
396
- return dotgen(node, radius)
464
+ def dotgen(
465
+ node: NodeArchitype,
466
+ depth: int,
467
+ traverse: bool,
468
+ edge_type: list[str],
469
+ bfs: bool,
470
+ edge_limit: int,
471
+ node_limit: int,
472
+ dot_file: Optional[str],
473
+ ) -> str:
474
+ """Generate Dot file for visualizing nodes and edges."""
475
+ edge_type = edge_type if edge_type else []
476
+ visited_nodes: list[NodeArchitype] = []
477
+ node_depths: dict[NodeArchitype, int] = {node: 0}
478
+ queue: list = [[node, 0]]
479
+ connections: list[tuple[NodeArchitype, NodeArchitype, EdgeArchitype]] = []
480
+
481
+ def dfs(node: NodeArchitype, cur_depth: int) -> None:
482
+ """Depth first search."""
483
+ if node not in visited_nodes:
484
+ visited_nodes.append(node)
485
+ traverse_graph(
486
+ node,
487
+ cur_depth,
488
+ depth,
489
+ edge_type,
490
+ traverse,
491
+ connections,
492
+ node_depths,
493
+ visited_nodes,
494
+ queue,
495
+ bfs,
496
+ dfs,
497
+ node_limit,
498
+ edge_limit,
499
+ )
500
+
501
+ if bfs:
502
+ cur_depth = 0
503
+ while queue:
504
+ current_node, cur_depth = queue.pop(0)
505
+ if current_node not in visited_nodes:
506
+ visited_nodes.append(current_node)
507
+ traverse_graph(
508
+ current_node,
509
+ cur_depth,
510
+ depth,
511
+ edge_type,
512
+ traverse,
513
+ connections,
514
+ node_depths,
515
+ visited_nodes,
516
+ queue,
517
+ bfs,
518
+ dfs,
519
+ node_limit,
520
+ edge_limit,
521
+ )
522
+ else:
523
+ dfs(node, cur_depth=0)
524
+ dot_content = (
525
+ 'digraph {\nnode [style="filled", shape="ellipse", '
526
+ 'fillcolor="invis", fontcolor="black"];\n'
527
+ )
528
+ for source, target, edge in connections:
529
+ dot_content += (
530
+ f"{visited_nodes.index(source)} -> {visited_nodes.index(target)} "
531
+ f' [label="{edge._jac_.obj.__class__.__name__} "];\n'
532
+ )
533
+ for node_ in visited_nodes:
534
+ color = (
535
+ colors[node_depths[node_]] if node_depths[node_] < 25 else colors[24]
536
+ )
537
+ dot_content += f'{visited_nodes.index(node_)} [label="{node_._jac_.obj}" fillcolor="{color}"];\n'
538
+ if dot_file:
539
+ with open(dot_file, "w") as f:
540
+ f.write(dot_content + "}")
541
+ return dot_content + "}"
397
542
 
398
543
 
399
544
  class JacCmdDefaults: