jaclang 0.8.0__py3-none-any.whl → 0.8.1__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 (77) hide show
  1. jaclang/cli/cli.py +11 -9
  2. jaclang/compiler/jac.lark +2 -12
  3. jaclang/compiler/larkparse/jac_parser.py +1 -1
  4. jaclang/compiler/parser.py +360 -521
  5. jaclang/compiler/passes/main/cfg_build_pass.py +2 -2
  6. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  7. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  8. jaclang/compiler/passes/main/import_pass.py +3 -3
  9. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +196 -218
  11. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  13. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  14. jaclang/compiler/passes/main/sym_tab_link_pass.py +4 -4
  15. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  16. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  17. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +4 -2
  18. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +197 -120
  19. jaclang/compiler/program.py +2 -7
  20. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  21. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  22. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  23. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  24. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  25. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +11 -0
  26. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +7 -0
  27. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  28. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  29. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  30. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  31. jaclang/compiler/tests/test_importer.py +20 -0
  32. jaclang/compiler/tests/test_parser.py +1 -0
  33. jaclang/compiler/unitree.py +456 -304
  34. jaclang/langserve/engine.jac +498 -0
  35. jaclang/langserve/sem_manager.jac +309 -0
  36. jaclang/langserve/server.jac +186 -0
  37. jaclang/langserve/tests/server_test/test_lang_serve.py +6 -7
  38. jaclang/langserve/tests/server_test/utils.py +4 -1
  39. jaclang/langserve/tests/session.jac +294 -0
  40. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  41. jaclang/langserve/tests/test_server.py +12 -7
  42. jaclang/langserve/utils.jac +51 -30
  43. jaclang/runtimelib/archetype.py +1 -1
  44. jaclang/runtimelib/builtin.py +17 -14
  45. jaclang/runtimelib/importer.py +26 -8
  46. jaclang/runtimelib/machine.py +96 -55
  47. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  48. jaclang/runtimelib/utils.py +3 -3
  49. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  50. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  51. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  52. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  53. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  54. jaclang/tests/fixtures/concurrency.jac +1 -1
  55. jaclang/tests/fixtures/edge_ability.jac +49 -0
  56. jaclang/tests/fixtures/guess_game.jac +1 -1
  57. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  58. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  59. jaclang/tests/fixtures/node_del.jac +30 -36
  60. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  61. jaclang/tests/test_cli.py +12 -7
  62. jaclang/tests/test_language.py +91 -16
  63. jaclang/utils/helpers.py +14 -6
  64. jaclang/utils/lang_tools.py +2 -3
  65. jaclang/utils/tests/test_lang_tools.py +2 -1
  66. jaclang/utils/treeprinter.py +3 -4
  67. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/METADATA +4 -3
  68. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/RECORD +71 -55
  69. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/WHEEL +1 -1
  70. jaclang/langserve/engine.py +0 -553
  71. jaclang/langserve/sem_manager.py +0 -383
  72. jaclang/langserve/server.py +0 -167
  73. jaclang/langserve/tests/session.py +0 -255
  74. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  75. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  76. /jaclang/langserve/{__init__.py → __init__.jac} +0 -0
  77. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,294 @@
1
+ """Provides LSP session helpers for testing."""
2
+
3
+ import os;
4
+ import subprocess;
5
+ import sys;
6
+ import from concurrent.futures { Future, ThreadPoolExecutor }
7
+ import from threading { Event }
8
+
9
+ import from pylsp_jsonrpc.dispatchers { MethodDispatcher }
10
+ import from pylsp_jsonrpc.endpoint { Endpoint }
11
+ import from pylsp_jsonrpc.streams { JsonRpcStreamReader, JsonRpcStreamWriter }
12
+
13
+ import defaults;
14
+
15
+
16
+ with entry {
17
+ LSP_EXIT_TIMEOUT = 5000;
18
+ PUBLISH_DIAGNOSTICS = 'textDocument/publishDiagnostics';
19
+ WINDOW_LOG_MESSAGE = 'window/logMessage';
20
+ WINDOW_SHOW_MESSAGE = 'window/showMessage';
21
+ }
22
+
23
+
24
+ """Send and Receive messages over LSP as a test LS Client."""
25
+ class LspSession (MethodDispatcher) {
26
+ def init(self: LspSession, cwd: Any = None) {
27
+ self.cwd = cwd if cwd else os.getcwd() ;
28
+ self._thread_pool = ThreadPoolExecutor();
29
+ self._sub = None;
30
+ self._writer = None;
31
+ self._reader = None;
32
+ self._endpoint = None;
33
+ self._notification_callbacks = {};
34
+ }
35
+
36
+ """Context manager entrypoint.\n\n shell=True needed for pytest-cov to work in subprocess.\n """
37
+ def __enter__(self: LspSession) {
38
+ self._sub = subprocess.Popen(
39
+ ["jac", "run",
40
+ os.path.join(os.path.dirname(os.path.dirname(__file__)), 'server.jac')],
41
+ stdout=subprocess.PIPE,
42
+ stdin=subprocess.PIPE,
43
+ stderr=subprocess.DEVNULL,
44
+ bufsize=0,
45
+ cwd=self.cwd,
46
+ env=os.environ,
47
+ shell=('WITH_COVERAGE' in os.environ)
48
+ );
49
+ self._writer = JsonRpcStreamWriter(os.fdopen(self._sub.stdin.fileno(), 'wb'));
50
+ self._reader = JsonRpcStreamReader(os.fdopen(self._sub.stdout.fileno(), 'rb'));
51
+ dispatcher =
52
+ {PUBLISH_DIAGNOSTICS : self._publish_diagnostics , WINDOW_SHOW_MESSAGE : self._window_show_message , WINDOW_LOG_MESSAGE : self._window_log_message };
53
+ self._endpoint = Endpoint(dispatcher, self._writer.write);
54
+ self._thread_pool.submit(self._reader.listen, self._endpoint.consume);
55
+ return self;
56
+ }
57
+
58
+ def __exit__(self: LspSession, typ: Any, value: Any, _tb: Any) {
59
+ self.shutdown(True);
60
+ try {
61
+ self._sub.terminate();
62
+ } except Exception as e {
63
+
64
+ }
65
+
66
+ self._endpoint.shutdown();
67
+ self._thread_pool.shutdown();
68
+ }
69
+
70
+ """Sends the initialize request to LSP server."""
71
+ def initialize(
72
+ self: LspSession,
73
+ initialize_params: Any = None,
74
+ process_server_capabilities: Any = None
75
+ ) {
76
+ server_initialized = Event();
77
+ def _after_initialize(fut: Any) {
78
+ if process_server_capabilities {
79
+ process_server_capabilities(fut.result());
80
+ }
81
+ self.initialized();
82
+ server_initialized.set();
83
+ }
84
+ self._send_request(
85
+ 'initialize',
86
+ params=initialize_params
87
+ if (initialize_params is not None)
88
+ else defaults.VSCODE_DEFAULT_INITIALIZE
89
+ ,
90
+ handle_response=_after_initialize
91
+ );
92
+ server_initialized.wait();
93
+ }
94
+
95
+ """Sends the initialized notification to LSP server."""
96
+ def initialized(self: LspSession, initialized_params: Any = None) {
97
+ if (initialized_params is None) {
98
+ initialized_params = {};
99
+ }
100
+ self._endpoint.notify('initialized', initialized_params);
101
+ }
102
+
103
+ """Sends the shutdown request to LSP server."""
104
+ def shutdown(
105
+ self: LspSession,
106
+ should_exit: Any,
107
+ exit_timeout: Any = LSP_EXIT_TIMEOUT
108
+ ) {
109
+ def _after_shutdown(_: Any) {
110
+ if should_exit {
111
+ self.exit_lsp(exit_timeout);
112
+ }
113
+ }
114
+ self._send_request('shutdown', handle_response=_after_shutdown);
115
+ }
116
+
117
+ """Handles LSP server process exit."""
118
+ def exit_lsp(self: LspSession, exit_timeout: Any = LSP_EXIT_TIMEOUT) {
119
+ self._endpoint.notify('exit');
120
+ assert (self._sub.wait(exit_timeout) == 0) ;
121
+ }
122
+
123
+ """Sends text document completion request to LSP server."""
124
+ def text_document_completion(self: LspSession, completion_params: Any) {
125
+ fut = self._send_request('textDocument/completion', params=completion_params);
126
+ return fut.result();
127
+ }
128
+
129
+ """Sends text document rename request to LSP server."""
130
+ def text_document_rename(self: LspSession, rename_params: Any) {
131
+ fut = self._send_request('textDocument/rename', params=rename_params);
132
+ return fut.result();
133
+ }
134
+
135
+ """Sends text document code action request to LSP server."""
136
+ def text_document_code_action(self: LspSession, code_action_params: Any) {
137
+ fut = self._send_request('textDocument/codeAction', params=code_action_params);
138
+ return fut.result();
139
+ }
140
+
141
+ """Sends text document hover request to LSP server."""
142
+ def text_document_hover(self: LspSession, hover_params: Any) {
143
+ fut = self._send_request('textDocument/hover', params=hover_params);
144
+ return fut.result();
145
+ }
146
+
147
+ """Sends text document formatting request to LSP server."""
148
+ def text_document_formatting(self: LspSession, formatting_params: Any) {
149
+ fut = self._send_request('textDocument/formatting', params=formatting_params);
150
+ return fut.result();
151
+ }
152
+
153
+ """Sends text document hover request to LSP server."""
154
+ def text_document_signature_help(self: LspSession, signature_help_params: Any) {
155
+ fut = self._send_request(
156
+ 'textDocument/signatureHelp',
157
+ params=signature_help_params
158
+ );
159
+ return fut.result();
160
+ }
161
+
162
+ """Sends text document declaration request to LSP server."""
163
+ def text_document_declaration(self: LspSession, declaration_params: Any) {
164
+ fut = self._send_request('textDocument/declaration', params=declaration_params);
165
+ return fut.result();
166
+ }
167
+
168
+ """Sends text document definition request to LSP server."""
169
+ def text_document_definition(self: LspSession, definition_params: Any) {
170
+ fut = self._send_request('textDocument/definition', params=definition_params);
171
+ return fut.result();
172
+ }
173
+
174
+ """Sends text document symbol request to LSP server."""
175
+ def text_document_symbol(self: LspSession, document_symbol_params: Any) {
176
+ fut = self._send_request(
177
+ 'textDocument/documentSymbol',
178
+ params=document_symbol_params
179
+ );
180
+ return fut.result();
181
+ }
182
+
183
+ """Sends text document highlight request to LSP server."""
184
+ def text_document_highlight(self: LspSession, document_highlight_params: Any) {
185
+ fut = self._send_request(
186
+ 'textDocument/documentHighlight',
187
+ params=document_highlight_params
188
+ );
189
+ return fut.result();
190
+ }
191
+
192
+ """Sends text document references request to LSP server."""
193
+ def text_document_references(self: LspSession, references_params: Any) {
194
+ fut = self._send_request('textDocument/references', params=references_params);
195
+ return fut.result();
196
+ }
197
+
198
+ """Sends workspace symbol request to LSP server."""
199
+ def workspace_symbol(self: LspSession, workspace_symbol_params: Any) {
200
+ fut = self._send_request('workspace/symbol', params=workspace_symbol_params);
201
+ return fut.result();
202
+ }
203
+
204
+ """Sends completion item resolve request to LSP server."""
205
+ def completion_item_resolve(self: LspSession, resolve_params: Any) {
206
+ fut = self._send_request('completionItem/resolve', params=resolve_params);
207
+ return fut.result();
208
+ }
209
+
210
+ """Sends did change notification to LSP Server."""
211
+ def notify_did_change(self: LspSession, did_change_params: Any) {
212
+ self._send_notification('textDocument/didChange', params=did_change_params);
213
+ }
214
+
215
+ """Sends did save notification to LSP Server."""
216
+ def notify_did_save(self: LspSession, did_save_params: Any) {
217
+ self._send_notification('textDocument/didSave', params=did_save_params);
218
+ }
219
+
220
+ """Sends did open notification to LSP Server."""
221
+ def notify_did_open(self: LspSession, did_open_params: Any) {
222
+ self._send_notification('textDocument/didOpen', params=did_open_params);
223
+ }
224
+
225
+ """Set custom LS notification handler."""
226
+ def set_notification_callback(
227
+ self: LspSession,
228
+ notification_name: Any,
229
+ callback: Any
230
+ ) {
231
+ self._notification_callbacks[notification_name] = callback;
232
+ }
233
+
234
+ """Gets callback if set or default callback for a given LS notification."""
235
+ def get_notification_callback(self: LspSession, notification_name: Any) {
236
+ try {
237
+ return self._notification_callbacks[notification_name];
238
+ } except KeyError {
239
+ def _default_handler(_params: Any) -> None {}
240
+ return _default_handler;
241
+ }
242
+
243
+ }
244
+
245
+ """Internal handler for text document publish diagnostics."""
246
+ def _publish_diagnostics(self: LspSession, publish_diagnostics_params: Any) {
247
+ return self._handle_notification(
248
+ PUBLISH_DIAGNOSTICS,
249
+ publish_diagnostics_params
250
+ );
251
+ }
252
+
253
+ """Internal handler for window log message."""
254
+ def _window_log_message(self: LspSession, window_log_message_params: Any) {
255
+ return self._handle_notification(WINDOW_LOG_MESSAGE, window_log_message_params);
256
+ }
257
+
258
+ """Internal handler for window show message."""
259
+ def _window_show_message(self: LspSession, window_show_message_params: Any) {
260
+ return self._handle_notification(
261
+ WINDOW_SHOW_MESSAGE,
262
+ window_show_message_params
263
+ );
264
+ }
265
+
266
+ """Internal handler for notifications."""
267
+ def _handle_notification(self: LspSession, notification_name: Any, params: Any) {
268
+ fut = Future();
269
+ def _handler() {
270
+ callback = self.get_notification_callback(notification_name);
271
+ callback(params);
272
+ fut.set_result(None);
273
+ }
274
+ self._thread_pool.submit(_handler);
275
+ return fut;
276
+ }
277
+
278
+ """Sends {name} request to the LSP server."""
279
+ def _send_request(
280
+ self: LspSession,
281
+ name: Any,
282
+ params: Any = None,
283
+ handle_response: Any = lambda f: Any: f.done()
284
+ ) {
285
+ fut = self._endpoint.request(name, params);
286
+ fut.add_done_callback(handle_response);
287
+ return fut;
288
+ }
289
+
290
+ """Sends {name} notification to the LSP server."""
291
+ def _send_notification(self: LspSession, name: Any, params: Any = None) {
292
+ self._endpoint.notify(name, params);
293
+ }
294
+ }
@@ -2,12 +2,12 @@
2
2
 
3
3
  import copy
4
4
  import lsprotocol.types as lspt
5
-
6
- from jaclang.langserve.sem_manager import SemTokManager
5
+ from jaclang import JacMachineInterface as _
7
6
  from jaclang.utils.test import TestCase
8
7
 
9
8
  from typing import Tuple
10
9
 
10
+ SemTokManager = _.py_jac_import("..sem_manager", __file__, items={"SemTokManager": None})[0]
11
11
 
12
12
  class TestUpdateSemTokens(TestCase):
13
13
  """Test update semantic tokens"""
@@ -1,12 +1,17 @@
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 _
9
8
 
9
+ JacLangServer = _.py_jac_import(
10
+ "...langserve.engine", __file__, items={"JacLangServer": None}
11
+ )[0]
12
+ LspSession = _.py_jac_import(
13
+ "session", __file__, items={"LspSession": None}
14
+ )[0]
10
15
 
11
16
  class TestJacLangServer(TestCase):
12
17
 
@@ -62,7 +67,7 @@ class TestJacLangServer(TestCase):
62
67
  self.assertIn(
63
68
  # "ability) calculate_area: float",
64
69
  "ability) calculate_area\n( radius : float ) -> float",
65
- lsp.get_hover_info(circle_impl_file, pos).contents.value,
70
+ lsp.get_hover_info(circle_impl_file, pos).contents.value.replace("'", ""),
66
71
  )
67
72
 
68
73
  def test_impl_auto_discover(self) -> None:
@@ -80,7 +85,7 @@ class TestJacLangServer(TestCase):
80
85
  self.assertIn(
81
86
  # "ability) calculate_area: float",
82
87
  "(public ability) calculate_area\n( radius : float ) -> float",
83
- lsp.get_hover_info(circle_impl_file, pos).contents.value,
88
+ lsp.get_hover_info(circle_impl_file, pos).contents.value.replace("'", ""),
84
89
  )
85
90
 
86
91
  @pytest.mark.xfail(reason="TODO: Fix when we have the type checker")
@@ -133,12 +138,12 @@ class TestJacLangServer(TestCase):
133
138
  workspace = Workspace(workspace_path, lsp)
134
139
  lsp.lsp._workspace = workspace
135
140
  guess_game_file = uris.from_fs_path(
136
- self.examples_abs_path("guess_game/guess_game4.jac")
141
+ self.examples_abs_path("guess_game/guess_game4.impl.jac")
137
142
  )
138
143
  lsp.deep_check(guess_game_file)
139
144
  self.assertIn(
140
- "guess_game4.jac:27:8-27:21",
141
- str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
145
+ "guess_game4.jac:16:8-16:21",
146
+ str(lsp.get_definition(guess_game_file, lspt.Position(14, 45))),
142
147
  )
143
148
 
144
149
  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 )
@@ -223,7 +223,7 @@ class WalkerAnchor(Anchor):
223
223
 
224
224
  archetype: WalkerArchetype
225
225
  path: list[NodeAnchor] = field(default_factory=list)
226
- next: list[NodeAnchor] = field(default_factory=list)
226
+ next: list[NodeAnchor | EdgeAnchor] = field(default_factory=list)
227
227
  ignores: list[NodeAnchor] = field(default_factory=list)
228
228
  disengaged: bool = False
229
229
 
@@ -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
  ]
@@ -102,14 +102,34 @@ class ImportReturn:
102
102
  item = getattr(module, name)
103
103
  handle_item_loading(item, alias)
104
104
  except AttributeError:
105
+ found = False
105
106
  if lang == "jac":
106
107
  jac_file_path = (
107
108
  os.path.join(module.__path__[0], f"{name}.jac")
108
109
  if hasattr(module, "__path__")
109
110
  else module.__file__
110
111
  )
111
-
112
- if jac_file_path and os.path.isfile(jac_file_path):
112
+ if hasattr(module, "__path__"):
113
+ init_jac = os.path.join(module.__path__[0], "__init__.jac")
114
+ if os.path.isfile(init_jac):
115
+ package_name = module.__name__
116
+ init_mod = self.importer.jac_machine.loaded_modules.get(
117
+ package_name,
118
+ self.load_jac_mod_as_item(
119
+ module=module,
120
+ name="__init__",
121
+ jac_file_path=init_jac,
122
+ ),
123
+ )
124
+ # Attach all public names from __init__.jac to the package module
125
+ for k, v in init_mod.__dict__.items():
126
+ if not k.startswith("_"):
127
+ setattr(module, k, v)
128
+ if hasattr(module, name):
129
+ item = getattr(module, name)
130
+ handle_item_loading(item, alias)
131
+ found = True
132
+ if not found and jac_file_path and os.path.isfile(jac_file_path):
113
133
  item = self.load_jac_mod_as_item(
114
134
  module=module,
115
135
  name=name,
@@ -144,9 +164,8 @@ class ImportReturn:
144
164
  jac_file_path,
145
165
  ),
146
166
  )
147
- codeobj = self.importer.jac_machine.jac_program.get_bytecode(
148
- full_target=jac_file_path,
149
- full_compile=not self.importer.jac_machine.interp_mode,
167
+ codeobj = self.importer.jac_machine.program.get_bytecode(
168
+ full_target=jac_file_path
150
169
  )
151
170
  if not codeobj:
152
171
  raise ImportError(f"No bytecode found for {jac_file_path}")
@@ -379,9 +398,8 @@ class JacImporter(Importer):
379
398
  spec.package_path,
380
399
  spec.full_target,
381
400
  )
382
- codeobj = self.jac_machine.jac_program.get_bytecode(
383
- full_target=spec.full_target,
384
- full_compile=not self.jac_machine.interp_mode,
401
+ codeobj = self.jac_machine.program.get_bytecode(
402
+ full_target=spec.full_target
385
403
  )
386
404
 
387
405
  # Since this is a compile time error, we can safely raise an exception here.