jaclang 0.8.0__py3-none-any.whl → 0.8.2__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 (124) hide show
  1. jaclang/__init__.py +6 -0
  2. jaclang/cli/cli.py +23 -50
  3. jaclang/compiler/codeinfo.py +0 -1
  4. jaclang/compiler/jac.lark +14 -22
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +378 -531
  7. jaclang/compiler/passes/main/__init__.py +0 -14
  8. jaclang/compiler/passes/main/annex_pass.py +2 -8
  9. jaclang/compiler/passes/main/cfg_build_pass.py +39 -13
  10. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  11. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  12. jaclang/compiler/passes/main/import_pass.py +6 -14
  13. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +428 -799
  15. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  16. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  18. jaclang/compiler/passes/main/sym_tab_link_pass.py +6 -9
  19. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  20. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  21. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +5 -9
  22. jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
  23. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
  24. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
  25. jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
  26. jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
  27. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +425 -216
  28. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
  29. jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
  30. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
  31. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
  32. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
  33. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
  34. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +18 -3
  35. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
  36. jaclang/compiler/program.py +22 -66
  37. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  38. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  39. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  40. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  41. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  42. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +5 -0
  43. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +3 -0
  44. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  45. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  46. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  47. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  48. jaclang/compiler/tests/test_importer.py +30 -13
  49. jaclang/compiler/tests/test_parser.py +1 -0
  50. jaclang/compiler/unitree.py +488 -320
  51. jaclang/langserve/__init__.jac +1 -0
  52. jaclang/langserve/engine.jac +503 -0
  53. jaclang/langserve/sem_manager.jac +309 -0
  54. jaclang/langserve/server.jac +201 -0
  55. jaclang/langserve/tests/server_test/test_lang_serve.py +139 -48
  56. jaclang/langserve/tests/server_test/utils.py +35 -6
  57. jaclang/langserve/tests/session.jac +294 -0
  58. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  59. jaclang/langserve/tests/test_server.py +8 -7
  60. jaclang/langserve/utils.jac +51 -30
  61. jaclang/runtimelib/archetype.py +128 -6
  62. jaclang/runtimelib/builtin.py +17 -14
  63. jaclang/runtimelib/importer.py +51 -76
  64. jaclang/runtimelib/machine.py +469 -305
  65. jaclang/runtimelib/meta_importer.py +86 -0
  66. jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
  67. jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
  68. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  69. jaclang/runtimelib/tests/test_jaseci.py +3 -1
  70. jaclang/runtimelib/utils.py +3 -3
  71. jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
  72. jaclang/tests/fixtures/async_ability.jac +43 -10
  73. jaclang/tests/fixtures/async_function.jac +18 -0
  74. jaclang/tests/fixtures/async_walker.jac +17 -12
  75. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  76. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  77. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  78. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  79. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  80. jaclang/tests/fixtures/concurrency.jac +1 -1
  81. jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
  82. jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
  83. jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
  84. jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
  85. jaclang/tests/fixtures/deep/one_lev.jac +2 -2
  86. jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
  87. jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
  88. jaclang/tests/fixtures/edge_ability.jac +49 -0
  89. jaclang/tests/fixtures/foo.jac +14 -22
  90. jaclang/tests/fixtures/guess_game.jac +1 -1
  91. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  92. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  93. jaclang/tests/fixtures/jac_from_py.py +1 -1
  94. jaclang/tests/fixtures/jp_importer.jac +6 -6
  95. jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
  96. jaclang/tests/fixtures/node_del.jac +30 -36
  97. jaclang/tests/fixtures/unicode_strings.jac +24 -0
  98. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  99. jaclang/tests/fixtures/walker_update.jac +5 -7
  100. jaclang/tests/test_cli.py +12 -7
  101. jaclang/tests/test_language.py +218 -145
  102. jaclang/tests/test_reference.py +9 -4
  103. jaclang/tests/test_typecheck.py +13 -26
  104. jaclang/utils/helpers.py +14 -6
  105. jaclang/utils/lang_tools.py +9 -8
  106. jaclang/utils/module_resolver.py +23 -0
  107. jaclang/utils/tests/test_lang_tools.py +2 -1
  108. jaclang/utils/treeprinter.py +3 -4
  109. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/METADATA +4 -3
  110. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/RECORD +112 -94
  111. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/WHEEL +1 -1
  112. jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
  113. jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
  114. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
  115. jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
  116. jaclang/langserve/__init__.py +0 -1
  117. jaclang/langserve/engine.py +0 -553
  118. jaclang/langserve/sem_manager.py +0 -383
  119. jaclang/langserve/server.py +0 -167
  120. jaclang/langserve/tests/session.py +0 -255
  121. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  122. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  123. jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
  124. {jaclang-0.8.0.dist-info → jaclang-0.8.2.dist-info}/entry_points.txt +0 -0
@@ -1,11 +1,12 @@
1
1
  from jaclang.utils.test import TestCase
2
2
  from jaclang.vendor.pygls import uris
3
3
  from jaclang.vendor.pygls.workspace import Workspace
4
- from jaclang.langserve.engine import JacLangServer
5
- from .session import LspSession
6
4
 
7
5
  import lsprotocol.types as lspt
8
6
  import pytest
7
+ from jaclang import JacMachineInterface as _
8
+ from jaclang.langserve.engine import JacLangServer
9
+ from .session import LspSession
9
10
 
10
11
 
11
12
  class TestJacLangServer(TestCase):
@@ -62,7 +63,7 @@ class TestJacLangServer(TestCase):
62
63
  self.assertIn(
63
64
  # "ability) calculate_area: float",
64
65
  "ability) calculate_area\n( radius : float ) -> float",
65
- lsp.get_hover_info(circle_impl_file, pos).contents.value,
66
+ lsp.get_hover_info(circle_impl_file, pos).contents.value.replace("'", ""),
66
67
  )
67
68
 
68
69
  def test_impl_auto_discover(self) -> None:
@@ -80,7 +81,7 @@ class TestJacLangServer(TestCase):
80
81
  self.assertIn(
81
82
  # "ability) calculate_area: float",
82
83
  "(public ability) calculate_area\n( radius : float ) -> float",
83
- lsp.get_hover_info(circle_impl_file, pos).contents.value,
84
+ lsp.get_hover_info(circle_impl_file, pos).contents.value.replace("'", ""),
84
85
  )
85
86
 
86
87
  @pytest.mark.xfail(reason="TODO: Fix when we have the type checker")
@@ -133,12 +134,12 @@ class TestJacLangServer(TestCase):
133
134
  workspace = Workspace(workspace_path, lsp)
134
135
  lsp.lsp._workspace = workspace
135
136
  guess_game_file = uris.from_fs_path(
136
- self.examples_abs_path("guess_game/guess_game4.jac")
137
+ self.examples_abs_path("guess_game/guess_game4.impl.jac")
137
138
  )
138
139
  lsp.deep_check(guess_game_file)
139
140
  self.assertIn(
140
- "guess_game4.jac:27:8-27:21",
141
- str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
141
+ "guess_game4.jac:16:8-16:21",
142
+ str(lsp.get_definition(guess_game_file, lspt.Position(15, 45))),
142
143
  )
143
144
 
144
145
  def test_go_to_definition_method_manual_impl(self) -> None:
@@ -24,10 +24,8 @@ import from jaclang.vendor.pygls { uris }
24
24
  import lsprotocol.types as lspt;
25
25
 
26
26
 
27
- with entry {
28
- P = ParamSpec('P');
29
- T = TypeVar('T', bound=Callable[(P, Coroutine[(Any, Any, Any)])]);
30
- }
27
+ glob P = ParamSpec('P');
28
+ glob T = TypeVar('T', bound=Callable[(P, Coroutine[(Any, Any, Any)])]);
31
29
 
32
30
 
33
31
  """Return diagnostics."""
@@ -36,11 +34,15 @@ def gen_diagnostics(
36
34
  errors: <>list[Alert],
37
35
  warnings: <>list[Alert]
38
36
  ) -> <>list[lspt.Diagnostic] {
39
- return ([ lspt.Diagnostic(range=create_range(error.loc),
40
- message=error.msg,
41
- severity=lspt.DiagnosticSeverity.Error) for error in errors if error.loc.mod_path == uris.to_fs_path(from_path) ] + [ lspt.Diagnostic(range=create_range(warning.loc),
42
- message=warning.msg,
43
- severity=lspt.DiagnosticSeverity.Warning) for warning in warnings if warning.loc.mod_path == uris.to_fs_path(from_path) ]);
37
+ return [ lspt.Diagnostic(
38
+ range=create_range(error.loc),
39
+ message=error.msg,
40
+ severity=lspt.DiagnosticSeverity.Error
41
+ ) for error in errors if error.loc.mod_path == uris.to_fs_path(from_path) ] + [ lspt.Diagnostic(
42
+ range=create_range(warning.loc),
43
+ message=warning.msg,
44
+ severity=lspt.DiagnosticSeverity.Warning
45
+ ) for warning in warnings if warning.loc.mod_path == uris.to_fs_path(from_path) ];
44
46
  }
45
47
 
46
48
 
@@ -156,25 +158,31 @@ def get_symbols_for_outline(<>node: UniScopeNode) -> <>list[lspt.DocumentSymbol]
156
158
  continue;
157
159
  }
158
160
  pos = create_range(item.decl.loc);
159
- symbol = lspt.DocumentSymbol(name=key,
160
- kind=kind_map(item.decl),
161
- range=pos,
162
- selection_range=pos,
163
- children=[]);
161
+ symbol = lspt.DocumentSymbol(
162
+ name=key,
163
+ kind=kind_map(item.decl),
164
+ range=pos,
165
+ selection_range=pos,
166
+ children=[]
167
+ );
164
168
  symbols.append(symbol);
165
169
  }
166
170
  for sub_tab in [ i for i in <>node.kid_scope if i.loc.mod_path == <>node.loc.mod_path ] {
167
171
  sub_symbols = get_symbols_for_outline(sub_tab);
168
- if isinstance(sub_tab,
169
- (uni.IfStmt, uni.ElseStmt, uni.WhileStmt, uni.IterForStmt, uni.InForStmt)) {
172
+ if isinstance(
173
+ sub_tab,
174
+ (uni.IfStmt, uni.ElseStmt, uni.WhileStmt, uni.IterForStmt, uni.InForStmt)
175
+ ) {
170
176
  symbols.extend(sub_symbols);
171
177
  } else {
172
178
  sub_pos = create_range(sub_tab.loc);
173
- symbol = lspt.DocumentSymbol(name=sub_tab.scope_name,
174
- kind=kind_map(sub_tab),
175
- range=sub_pos,
176
- selection_range=sub_pos,
177
- children=sub_symbols);
179
+ symbol = lspt.DocumentSymbol(
180
+ name=sub_tab.scope_name,
181
+ kind=kind_map(sub_tab),
182
+ range=sub_pos,
183
+ selection_range=sub_pos,
184
+ children=sub_symbols
185
+ );
178
186
  symbols.append(symbol);
179
187
  }
180
188
  }
@@ -193,10 +201,16 @@ def owner_sym(table: UniScopeNode) -> Optional[Symbol] {
193
201
 
194
202
  """Create an lspt.Range from a location object."""
195
203
  def create_range(loc: CodeLocInfo) -> lspt.Range {
196
- return lspt.Range(start=lspt.Position(line=(loc.first_line - 1) if loc.first_line > 0 else 0 ,
197
- character=(loc.col_start - 1) if loc.col_start > 0 else 0 ),
198
- end=lspt.Position(line=(loc.last_line - 1) if loc.last_line > 0 else 0 ,
199
- character=(loc.col_end - 1) if loc.col_end > 0 else 0 ));
204
+ return lspt.Range(
205
+ start=lspt.Position(
206
+ line=(loc.first_line - 1) if loc.first_line > 0 else 0 ,
207
+ character=(loc.col_start - 1) if loc.col_start > 0 else 0
208
+ ),
209
+ end=lspt.Position(
210
+ line=(loc.last_line - 1) if loc.last_line > 0 else 0 ,
211
+ character=(loc.col_end - 1) if loc.col_end > 0 else 0
212
+ )
213
+ );
200
214
  }
201
215
 
202
216
 
@@ -263,8 +277,9 @@ def collect_all_symbols_in_scope(
263
277
  visited.add(current_tab);
264
278
  for (name, symbol) in current_tab.names_in_scope.items() {
265
279
  if name not in dir(builtins) and symbol.sym_type != SymbolType.IMPL {
266
- symbols.append(lspt.CompletionItem(label=name,
267
- kind=label_map(symbol.sym_type)));
280
+ symbols.append(
281
+ lspt.CompletionItem(label=name, kind=label_map(symbol.sym_type))
282
+ );
268
283
  }
269
284
  }
270
285
  if not up_tree {
@@ -284,8 +299,12 @@ def collect_child_tabs(sym_tab: UniScopeNode) -> <>list[lspt.CompletionItem] {
284
299
  symbols : <>list[lspt.CompletionItem] = [];
285
300
  for tab in sym_tab.kid_scope {
286
301
  if tab.scope_name not in [ i.label for i in symbols ] {
287
- symbols.append(lspt.CompletionItem(label=tab.scope_name,
288
- kind=label_map(tab.get_type())));
302
+ symbols.append(
303
+ lspt.CompletionItem(
304
+ label=tab.scope_name,
305
+ kind=label_map(tab.get_type())
306
+ )
307
+ );
289
308
  }
290
309
  }
291
310
  return symbols;
@@ -368,7 +387,9 @@ def find_surrounding_tokens(
368
387
  prev_token_index = None;
369
388
  next_token_index = None;
370
389
  inside_tok = False;
371
- for (i, tok) in enumerate([ get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5) ][ 0 : ]) {
390
+ for (i, tok) in enumerate(
391
+ [ get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5) ][ 0 : ]
392
+ ) {
372
393
  if (not (prev_token_index is None or next_token_index is None ))
373
394
  and (tok[0] > change_end_line
374
395
  or (tok[0] == change_end_line and tok[1] > change_end_char )
@@ -2,21 +2,24 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import inspect
6
5
  from dataclasses import asdict, dataclass, field, fields, is_dataclass
7
6
  from enum import IntEnum
8
7
  from functools import cached_property
8
+ from inspect import _empty, signature
9
9
  from logging import getLogger
10
10
  from pickle import dumps
11
11
  from types import UnionType
12
- from typing import Any, Callable, ClassVar, Optional, TypeVar
12
+ from typing import Any, Callable, ClassVar, Optional, TypeAlias, TypeVar
13
13
  from uuid import UUID, uuid4
14
14
 
15
+ from ..compiler.constant import EdgeDir
16
+
15
17
 
16
18
  logger = getLogger(__name__)
17
19
 
18
20
  TARCH = TypeVar("TARCH", bound="Archetype")
19
21
  TANCH = TypeVar("TANCH", bound="Anchor")
22
+ T = TypeVar("T")
20
23
 
21
24
 
22
25
  class AccessLevel(IntEnum):
@@ -66,6 +69,125 @@ class AnchorReport:
66
69
  context: dict[str, Any]
67
70
 
68
71
 
72
+ DataSpatialFilter: TypeAlias = (
73
+ Callable[["Archetype"], bool] | "Archetype" | list["Archetype"] | None
74
+ )
75
+
76
+
77
+ @dataclass(eq=False, repr=False)
78
+ class DataSpatialDestination:
79
+ """Object-Spatial Destination."""
80
+
81
+ direction: EdgeDir
82
+ edge: Callable[["Archetype"], bool] | None = None
83
+ node: Callable[["Archetype"], bool] | None = None
84
+
85
+ def edge_filter(self, arch: Archetype) -> bool:
86
+ """Filter edge."""
87
+ return not self.edge or self.edge(arch)
88
+
89
+ def node_filter(self, arch: Archetype) -> bool:
90
+ """Filter node."""
91
+ return not self.node or self.node(arch)
92
+
93
+
94
+ @dataclass(eq=False, repr=False)
95
+ class DataSpatialPath:
96
+ """Object-Spatial Path."""
97
+
98
+ origin: list[NodeArchetype]
99
+ destinations: list[DataSpatialDestination]
100
+ edge_only: bool
101
+ from_visit: bool
102
+
103
+ def __init__(
104
+ self,
105
+ origin: NodeArchetype | list[NodeArchetype],
106
+ destinations: list[DataSpatialDestination] | None = None,
107
+ ) -> None:
108
+ """Override Init."""
109
+ if not isinstance(origin, list):
110
+ origin = [origin]
111
+ self.origin = origin
112
+ self.destinations = [] if destinations is None else destinations
113
+ self.edge_only = False
114
+ self.from_visit = False
115
+
116
+ def convert(
117
+ self,
118
+ filter: DataSpatialFilter,
119
+ ) -> Callable[["Archetype"], bool] | None:
120
+ """Convert filter."""
121
+ if not filter:
122
+ return None
123
+ if callable(filter):
124
+ return filter
125
+ elif isinstance(filter, list):
126
+ return lambda i: i in filter
127
+ return lambda i: i == filter
128
+
129
+ def append(
130
+ self,
131
+ direction: EdgeDir,
132
+ edge: DataSpatialFilter,
133
+ node: DataSpatialFilter,
134
+ ) -> DataSpatialPath:
135
+ """Append destination."""
136
+ self.destinations.append(
137
+ DataSpatialDestination(direction, self.convert(edge), self.convert(node))
138
+ )
139
+ return self
140
+
141
+ def _out(
142
+ self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
143
+ ) -> DataSpatialPath:
144
+ """Override greater than function."""
145
+ return self.append(EdgeDir.OUT, edge, node)
146
+
147
+ def _in(
148
+ self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
149
+ ) -> DataSpatialPath:
150
+ """Override greater than function."""
151
+ return self.append(EdgeDir.IN, edge, node)
152
+
153
+ def _any(
154
+ self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
155
+ ) -> DataSpatialPath:
156
+ """Override greater than function."""
157
+ return self.append(EdgeDir.ANY, edge, node)
158
+
159
+ def edge(self) -> DataSpatialPath:
160
+ """Set edge only."""
161
+ self.edge_only = True
162
+ return self
163
+
164
+ def visit(self) -> DataSpatialPath:
165
+ """Set from visit."""
166
+ self.from_visit = True
167
+ return self
168
+
169
+ def repr_builder(self, repr: str, dest: DataSpatialDestination, mark: str) -> str:
170
+ """Repr builder."""
171
+ repr += mark
172
+ repr += f' (edge{" filter" if dest.edge else ""}) '
173
+ repr += mark
174
+ repr += f' (node{" filter" if dest.node else ""}) '
175
+ return repr
176
+
177
+ def __repr__(self) -> str:
178
+ """Override repr."""
179
+ repr = "nodes "
180
+ for dest in self.destinations:
181
+ match dest.direction:
182
+ case EdgeDir.IN:
183
+ repr = self.repr_builder(repr, dest, "<<")
184
+ case EdgeDir.OUT:
185
+ repr = self.repr_builder(repr, dest, ">>")
186
+ case _:
187
+ repr = self.repr_builder(repr, dest, "--")
188
+ return repr.strip()
189
+
190
+
69
191
  @dataclass(eq=False, repr=False, kw_only=True)
70
192
  class Anchor:
71
193
  """Object Anchor."""
@@ -223,7 +345,7 @@ class WalkerAnchor(Anchor):
223
345
 
224
346
  archetype: WalkerArchetype
225
347
  path: list[NodeAnchor] = field(default_factory=list)
226
- next: list[NodeAnchor] = field(default_factory=list)
348
+ next: list[NodeAnchor | EdgeAnchor] = field(default_factory=list)
227
349
  ignores: list[NodeAnchor] = field(default_factory=list)
228
350
  disengaged: bool = False
229
351
 
@@ -329,7 +451,7 @@ class Root(NodeArchetype):
329
451
 
330
452
  @dataclass(eq=False)
331
453
  class DataSpatialFunction:
332
- """Data Spatial Function."""
454
+ """Object-Spatial Function."""
333
455
 
334
456
  name: str
335
457
  func: Callable[[Any, Any], Any]
@@ -337,9 +459,9 @@ class DataSpatialFunction:
337
459
  @cached_property
338
460
  def trigger(self) -> type | UnionType | tuple[type | UnionType, ...] | None:
339
461
  """Get function parameter annotations."""
340
- parameters = inspect.signature(self.func, eval_str=True).parameters
462
+ parameters = signature(self.func, eval_str=True).parameters
341
463
  if len(parameters) >= 2:
342
464
  second_param = list(parameters.values())[1]
343
465
  ty = second_param.annotation
344
- return ty if ty != inspect._empty else None
466
+ return ty if ty != _empty else None
345
467
  return None
@@ -10,10 +10,7 @@ from jaclang.runtimelib.constructs import Archetype, NodeArchetype, Root
10
10
  from jaclang.runtimelib.machine import JacMachineInterface as Jac
11
11
 
12
12
 
13
- # FIXME: Retname this to something common, doing this way so this doesn't break
14
- # the existing code. Currently it can return the jac graph in both dot and json format.
15
- # So the name shuouldn't be dotgen but something more generic.
16
- def dotgen(
13
+ def printgraph(
17
14
  node: Optional[NodeArchetype] = None,
18
15
  depth: int = -1,
19
16
  traverse: bool = False,
@@ -21,16 +18,17 @@ def dotgen(
21
18
  bfs: bool = True,
22
19
  edge_limit: int = 512,
23
20
  node_limit: int = 512,
24
- dot_file: Optional[str] = None,
25
- as_json: bool = False,
21
+ file: Optional[str] = None,
22
+ format: str = "dot",
26
23
  ) -> str:
27
- """Print the dot graph."""
24
+ """Print the graph in different formats."""
28
25
  from jaclang.runtimelib.machine import JacMachineInterface as Jac
29
26
 
30
- if as_json:
31
- return _jac_graph_json()
27
+ fmt = format.lower()
28
+ if fmt == "json":
29
+ return _jac_graph_json(file)
32
30
 
33
- return Jac.dotgen(
31
+ return Jac.printgraph(
34
32
  edge_type=edge_type,
35
33
  node=node or Jac.root(),
36
34
  depth=depth,
@@ -38,7 +36,8 @@ def dotgen(
38
36
  bfs=bfs,
39
37
  edge_limit=edge_limit,
40
38
  node_limit=node_limit,
41
- dot_file=dot_file,
39
+ file=file,
40
+ format=fmt,
42
41
  )
43
42
 
44
43
 
@@ -52,7 +51,7 @@ def jobj(id: str) -> Archetype | None:
52
51
  return Jac.get_object(id)
53
52
 
54
53
 
55
- def _jac_graph_json() -> str:
54
+ def _jac_graph_json(file: Optional[str] = None) -> str:
56
55
  """Get the graph in json string."""
57
56
  processed: list[Root | NodeArchetype] = []
58
57
  nodes: list[dict] = []
@@ -73,20 +72,24 @@ def _jac_graph_json() -> str:
73
72
  for ref in Jac.refs(end):
74
73
  if ref not in processed:
75
74
  working_set.append((end, ref))
76
- return json.dumps(
75
+ output = json.dumps(
77
76
  {
78
77
  "version": "1.0",
79
78
  "nodes": nodes,
80
79
  "edges": edges,
81
80
  }
82
81
  )
82
+ if file:
83
+ with open(file, "w") as f:
84
+ f.write(output)
85
+ return output
83
86
 
84
87
 
85
88
  __all__ = [
86
89
  "abstractmethod",
87
90
  "ClassVar",
88
91
  "override",
89
- "dotgen",
92
+ "printgraph",
90
93
  "jid",
91
94
  "jobj",
92
95
  ]