jaclang 0.8.5__py3-none-any.whl → 0.8.7__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.
- jaclang/cli/cli.md +4 -3
- jaclang/cli/cli.py +63 -29
- jaclang/cli/cmdreg.py +1 -140
- jaclang/compiler/passes/main/__init__.py +2 -0
- jaclang/compiler/passes/main/cfg_build_pass.py +21 -1
- jaclang/compiler/passes/main/inheritance_pass.py +8 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +70 -11
- jaclang/compiler/passes/main/pyast_load_pass.py +14 -20
- jaclang/compiler/passes/main/sym_tab_build_pass.py +4 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_has_var.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_if_no_else.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_return.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_imported.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_importer.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/import_symbol_type_infer.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/infer_type_assignment.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac +8 -0
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +62 -24
- jaclang/compiler/passes/main/tests/test_checker_pass.py +161 -0
- jaclang/compiler/passes/main/type_checker_pass.py +147 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +1 -4
- jaclang/compiler/program.py +17 -3
- jaclang/compiler/type_system/__init__.py +1 -0
- jaclang/compiler/type_system/operations.py +104 -0
- jaclang/compiler/type_system/type_evaluator.py +560 -0
- jaclang/compiler/type_system/type_utils.py +41 -0
- jaclang/compiler/type_system/types.py +240 -0
- jaclang/compiler/unitree.py +15 -9
- jaclang/langserve/dev_engine.jac +645 -0
- jaclang/langserve/dev_server.jac +201 -0
- jaclang/langserve/engine.jac +135 -91
- jaclang/langserve/server.jac +21 -14
- jaclang/langserve/tests/server_test/test_lang_serve.py +2 -5
- jaclang/langserve/tests/test_dev_server.py +80 -0
- jaclang/langserve/tests/test_server.py +9 -2
- jaclang/langserve/utils.jac +44 -48
- jaclang/runtimelib/builtin.py +28 -39
- jaclang/runtimelib/importer.py +1 -1
- jaclang/runtimelib/machine.py +48 -64
- jaclang/runtimelib/memory.py +23 -5
- jaclang/runtimelib/tests/fixtures/savable_object.jac +10 -2
- jaclang/runtimelib/utils.py +13 -6
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
- jaclang/tests/fixtures/jac_run_py_import.py +13 -0
- jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
- jaclang/tests/fixtures/lambda_self.jac +18 -0
- jaclang/tests/fixtures/py_run.jac +8 -0
- jaclang/tests/fixtures/py_run.py +23 -0
- jaclang/tests/fixtures/pyfunc.py +2 -0
- jaclang/tests/test_cli.py +103 -14
- jaclang/tests/test_language.py +10 -4
- jaclang/utils/lang_tools.py +3 -0
- jaclang/utils/module_resolver.py +1 -1
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/METADATA +4 -2
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/RECORD +70 -37
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/WHEEL +1 -1
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/entry_points.txt +0 -0
|
@@ -16,7 +16,6 @@ from lsprotocol.types import (
|
|
|
16
16
|
from jaclang.langserve.tests.server_test.utils import (
|
|
17
17
|
create_temp_jac_file,
|
|
18
18
|
get_code,
|
|
19
|
-
get_jac_file_path,
|
|
20
19
|
get_simple_code,
|
|
21
20
|
create_ls_with_workspace, # new helper
|
|
22
21
|
)
|
|
@@ -26,8 +25,6 @@ from jaclang import JacMachineInterface as _
|
|
|
26
25
|
from jaclang.langserve.engine import JacLangServer
|
|
27
26
|
from jaclang.langserve.server import did_open, did_save, did_change, formatting
|
|
28
27
|
|
|
29
|
-
JAC_FILE = get_jac_file_path()
|
|
30
|
-
|
|
31
28
|
|
|
32
29
|
class TestLangServe:
|
|
33
30
|
"""Test class for Jac language server features."""
|
|
@@ -190,7 +187,7 @@ class TestLangServe:
|
|
|
190
187
|
await did_save(ls, params)
|
|
191
188
|
sem_tokens = ls.get_semantic_tokens(uri)
|
|
192
189
|
# semantic tokens should still be present even if there is a syntax error
|
|
193
|
-
assert len(sem_tokens.data) ==
|
|
190
|
+
assert len(sem_tokens.data) == 340
|
|
194
191
|
diagnostics = ls.diagnostics.get(uri, [])
|
|
195
192
|
assert isinstance(diagnostics, list)
|
|
196
193
|
assert len(diagnostics) == 1
|
|
@@ -254,7 +251,7 @@ class TestLangServe:
|
|
|
254
251
|
await did_change(ls, params)
|
|
255
252
|
sem_tokens = ls.get_semantic_tokens(uri)
|
|
256
253
|
# semantic tokens should still be present even if there is a syntax error
|
|
257
|
-
assert len(sem_tokens.data) ==
|
|
254
|
+
assert len(sem_tokens.data) == 340
|
|
258
255
|
diagnostics = ls.diagnostics.get(uri, [])
|
|
259
256
|
assert isinstance(diagnostics, list)
|
|
260
257
|
assert len(diagnostics) == 1
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from jaclang.utils.test import TestCase
|
|
2
|
+
from jaclang.vendor.pygls import uris
|
|
3
|
+
from jaclang.vendor.pygls.workspace import Workspace
|
|
4
|
+
|
|
5
|
+
import lsprotocol.types as lspt
|
|
6
|
+
from jaclang.langserve.engine import JacLangServer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestJacLangServer(TestCase):
|
|
10
|
+
|
|
11
|
+
def test_type_annotation_assignment_server(self) -> None:
|
|
12
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
13
|
+
lsp = JacLangServer()
|
|
14
|
+
workspace_path = self.fixture_abs_path("")
|
|
15
|
+
workspace = Workspace(workspace_path, lsp)
|
|
16
|
+
lsp.lsp._workspace = workspace
|
|
17
|
+
circle_file = uris.from_fs_path(
|
|
18
|
+
self.fixture_abs_path(
|
|
19
|
+
"../../../../jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac"
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
lsp.deep_check(circle_file)
|
|
23
|
+
self.assertIn(
|
|
24
|
+
"(public variable) should_fail1: int",
|
|
25
|
+
lsp.get_hover_info(circle_file, lspt.Position(1, 15)).contents.value,
|
|
26
|
+
)
|
|
27
|
+
self.assertIn(
|
|
28
|
+
"(public variable) should_pass2: str",
|
|
29
|
+
lsp.get_hover_info(circle_file, lspt.Position(2, 15)).contents.value,
|
|
30
|
+
)
|
|
31
|
+
self.assertIn(
|
|
32
|
+
"(public variable) should_fail2: str",
|
|
33
|
+
lsp.get_hover_info(circle_file, lspt.Position(3, 15)).contents.value,
|
|
34
|
+
)
|
|
35
|
+
diagnostics_list = list(lsp.diagnostics.values())[0]
|
|
36
|
+
self.assertEqual(len(diagnostics_list), 2)
|
|
37
|
+
self.assertIn(
|
|
38
|
+
"Cannot assign",
|
|
39
|
+
diagnostics_list[0].message,
|
|
40
|
+
)
|
|
41
|
+
self.assertEqual(
|
|
42
|
+
"1:5-1:30",
|
|
43
|
+
str(diagnostics_list[0].range),
|
|
44
|
+
)
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
"3:5-3:27",
|
|
47
|
+
str(diagnostics_list[1].range),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_member_access_type_inferred_server(self) -> None:
|
|
52
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
53
|
+
lsp = JacLangServer()
|
|
54
|
+
workspace_path = self.fixture_abs_path("")
|
|
55
|
+
workspace = Workspace(workspace_path, lsp)
|
|
56
|
+
lsp.lsp._workspace = workspace
|
|
57
|
+
circle_file = uris.from_fs_path(
|
|
58
|
+
self.fixture_abs_path(
|
|
59
|
+
"../../../../jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac"
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
lsp.deep_check(circle_file)
|
|
63
|
+
self.assertIn(
|
|
64
|
+
"(public variable) i: int",
|
|
65
|
+
lsp.get_hover_info(circle_file, lspt.Position(10, 2)).contents.value,
|
|
66
|
+
)
|
|
67
|
+
self.assertIn(
|
|
68
|
+
"(public variable) s: str",
|
|
69
|
+
lsp.get_hover_info(circle_file, lspt.Position(11, 2)).contents.value,
|
|
70
|
+
)
|
|
71
|
+
diagnostics_list = list(lsp.diagnostics.values())[0]
|
|
72
|
+
self.assertEqual(len(diagnostics_list), 1)
|
|
73
|
+
self.assertIn(
|
|
74
|
+
"Cannot assign",
|
|
75
|
+
diagnostics_list[0].message,
|
|
76
|
+
)
|
|
77
|
+
self.assertEqual(
|
|
78
|
+
"11:2-11:12",
|
|
79
|
+
str(diagnostics_list[0].range),
|
|
80
|
+
)
|
|
@@ -127,6 +127,9 @@ class TestJacLangServer(TestCase):
|
|
|
127
127
|
str(lsp.get_definition(circle_file, lspt.Position(20, 16))),
|
|
128
128
|
)
|
|
129
129
|
|
|
130
|
+
@pytest.mark.xfail(
|
|
131
|
+
reason="Incorrect handling of 'self' in DefUsePass.TODO: Fix chain_use_lookup."
|
|
132
|
+
)
|
|
130
133
|
def test_go_to_definition_method(self) -> None:
|
|
131
134
|
"""Test that the go to definition is correct."""
|
|
132
135
|
lsp = JacLangServer()
|
|
@@ -595,9 +598,13 @@ class TestJacLangServer(TestCase):
|
|
|
595
598
|
workspace_path = self.fixture_abs_path("")
|
|
596
599
|
workspace = Workspace(workspace_path, lsp)
|
|
597
600
|
lsp.lsp._workspace = workspace
|
|
598
|
-
guess_game_file = uris.from_fs_path(
|
|
601
|
+
guess_game_file = uris.from_fs_path(
|
|
602
|
+
self.fixture_abs_path(
|
|
603
|
+
"../../../compiler/passes/main/tests/fixtures/sym_binder.jac"
|
|
604
|
+
)
|
|
605
|
+
)
|
|
599
606
|
lsp.deep_check(guess_game_file)
|
|
600
607
|
self.assertIn(
|
|
601
608
|
"/tests/fixtures/M1.jac:0:0-0:0",
|
|
602
609
|
str(lsp.get_definition(guess_game_file, lspt.Position(29, 9))),
|
|
603
|
-
)
|
|
610
|
+
)
|
jaclang/langserve/utils.jac
CHANGED
|
@@ -31,18 +31,18 @@ glob T = TypeVar('T', bound=Callable[(P, Coroutine[(Any, Any, Any)])]);
|
|
|
31
31
|
"""Return diagnostics."""
|
|
32
32
|
def gen_diagnostics(
|
|
33
33
|
from_path: str,
|
|
34
|
-
errors:
|
|
35
|
-
warnings:
|
|
36
|
-
) ->
|
|
34
|
+
errors: list[Alert],
|
|
35
|
+
warnings: list[Alert]
|
|
36
|
+
) -> list[lspt.Diagnostic] {
|
|
37
37
|
return [ lspt.Diagnostic(
|
|
38
38
|
range=create_range(error.loc),
|
|
39
39
|
message=error.msg,
|
|
40
40
|
severity=lspt.DiagnosticSeverity.Error
|
|
41
|
-
) for error in errors if error.loc.mod_path ==
|
|
41
|
+
) for error in errors if error.loc.mod_path == from_path ] + [ lspt.Diagnostic(
|
|
42
42
|
range=create_range(warning.loc),
|
|
43
43
|
message=warning.msg,
|
|
44
44
|
severity=lspt.DiagnosticSeverity.Warning
|
|
45
|
-
) for warning in warnings if warning.loc.mod_path ==
|
|
45
|
+
) for warning in warnings if warning.loc.mod_path == from_path ];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
|
|
@@ -75,7 +75,7 @@ def debounce(<>wait: float) -> Callable[[T], Callable[(..., Awaitable[None])]] {
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
"""Iterate through symbol table."""
|
|
78
|
-
def sym_tab_list(sym_tab: UniScopeNode, file_path: str) ->
|
|
78
|
+
def sym_tab_list(sym_tab: UniScopeNode, file_path: str) -> list[UniScopeNode] {
|
|
79
79
|
sym_tabs = [sym_tab]
|
|
80
80
|
if not (isinstance(sym_tab, uni.Module) and sym_tab.loc.mod_path != file_path )
|
|
81
81
|
else []
|
|
@@ -135,7 +135,7 @@ def position_within_node(<>node: uni.UniNode, line: int, character: int) -> bool
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
"""Find index."""
|
|
138
|
-
def find_index(sem_tokens:
|
|
138
|
+
def find_index(sem_tokens: list[int], line: int, char: int) -> Optional[int] {
|
|
139
139
|
index = None;
|
|
140
140
|
token_start_list = [ get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5) ];
|
|
141
141
|
for (i, j) in enumerate(token_start_list) {
|
|
@@ -148,7 +148,7 @@ def find_index(sem_tokens: <>list[int], line: int, char: int) -> Optional[int] {
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
"""Recursively collect symbols from the AST."""
|
|
151
|
-
def get_symbols_for_outline(<>node: UniScopeNode) ->
|
|
151
|
+
def get_symbols_for_outline(<>node: UniScopeNode) -> list[lspt.DocumentSymbol] {
|
|
152
152
|
symbols = [];
|
|
153
153
|
for (key, item) in <>node.names_in_scope.items() {
|
|
154
154
|
if key in dir(builtins)
|
|
@@ -234,34 +234,30 @@ def kind_map(sub_tab: uni.UniNode) -> lspt.SymbolKind {
|
|
|
234
234
|
|
|
235
235
|
"""Map the symbol node to an lspt.CompletionItemKind."""
|
|
236
236
|
def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind {
|
|
237
|
-
return
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
;
|
|
237
|
+
return (
|
|
238
|
+
lspt.CompletionItemKind.Function
|
|
239
|
+
if sub_tab in [SymbolType.ABILITY, SymbolType.TEST]
|
|
240
|
+
else lspt.CompletionItemKind.Class
|
|
241
|
+
if sub_tab in [
|
|
242
|
+
SymbolType.OBJECT_ARCH,
|
|
243
|
+
SymbolType.NODE_ARCH,
|
|
244
|
+
SymbolType.EDGE_ARCH,
|
|
245
|
+
SymbolType.WALKER_ARCH
|
|
246
|
+
]
|
|
247
|
+
else lspt.CompletionItemKind.Module
|
|
248
|
+
if sub_tab == SymbolType.MODULE
|
|
249
|
+
else lspt.CompletionItemKind.Enum
|
|
250
|
+
if sub_tab == SymbolType.ENUM_ARCH
|
|
251
|
+
else lspt.CompletionItemKind.Field
|
|
252
|
+
if sub_tab == SymbolType.HAS_VAR
|
|
253
|
+
else lspt.CompletionItemKind.Method
|
|
254
|
+
if sub_tab == SymbolType.METHOD
|
|
255
|
+
else lspt.CompletionItemKind.EnumMember
|
|
256
|
+
if sub_tab == SymbolType.ENUM_MEMBER
|
|
257
|
+
else lspt.CompletionItemKind.Interface
|
|
258
|
+
if sub_tab == SymbolType.IMPL
|
|
259
|
+
else lspt.CompletionItemKind.Variable
|
|
260
|
+
);
|
|
265
261
|
}
|
|
266
262
|
|
|
267
263
|
|
|
@@ -269,9 +265,9 @@ def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind {
|
|
|
269
265
|
def collect_all_symbols_in_scope(
|
|
270
266
|
sym_tab: UniScopeNode,
|
|
271
267
|
up_tree: bool = True
|
|
272
|
-
) ->
|
|
268
|
+
) -> list[lspt.CompletionItem] {
|
|
273
269
|
symbols = [];
|
|
274
|
-
visited =
|
|
270
|
+
visited = set();
|
|
275
271
|
current_tab : Optional[UniScopeNode] = sym_tab;
|
|
276
272
|
while current_tab is not None and current_tab not in visited {
|
|
277
273
|
visited.add(current_tab);
|
|
@@ -295,8 +291,8 @@ def collect_all_symbols_in_scope(
|
|
|
295
291
|
|
|
296
292
|
|
|
297
293
|
"""Return all child tab's as completion items."""
|
|
298
|
-
def collect_child_tabs(sym_tab: UniScopeNode) ->
|
|
299
|
-
symbols :
|
|
294
|
+
def collect_child_tabs(sym_tab: UniScopeNode) -> list[lspt.CompletionItem] {
|
|
295
|
+
symbols : list[lspt.CompletionItem] = [];
|
|
300
296
|
for tab in sym_tab.kid_scope {
|
|
301
297
|
if tab.scope_name not in [ i.label for i in symbols ] {
|
|
302
298
|
symbols.append(
|
|
@@ -312,7 +308,7 @@ def collect_child_tabs(sym_tab: UniScopeNode) -> <>list[lspt.CompletionItem] {
|
|
|
312
308
|
|
|
313
309
|
|
|
314
310
|
"""Parse text and return a list of symbols."""
|
|
315
|
-
def parse_symbol_path(text: str, dot_position: int) ->
|
|
311
|
+
def parse_symbol_path(text: str, dot_position: int) -> list[str] {
|
|
316
312
|
text = text[ : dot_position ][ : -1 ].strip();
|
|
317
313
|
valid_character_pattern = re.compile('[a-zA-Z0-9_]');
|
|
318
314
|
reversed_text = text[ : : -1 ];
|
|
@@ -342,8 +338,8 @@ def parse_symbol_path(text: str, dot_position: int) -> <>list[str] {
|
|
|
342
338
|
"""Return the starting position of a token."""
|
|
343
339
|
def get_token_start(
|
|
344
340
|
token_index: (int | None),
|
|
345
|
-
sem_tokens:
|
|
346
|
-
) ->
|
|
341
|
+
sem_tokens: list[int]
|
|
342
|
+
) -> tuple[int, int, int] {
|
|
347
343
|
if token_index is None or token_index >= len(sem_tokens) {
|
|
348
344
|
return (0, 0, 0);
|
|
349
345
|
}
|
|
@@ -382,8 +378,8 @@ def find_surrounding_tokens(
|
|
|
382
378
|
change_start_char: int,
|
|
383
379
|
change_end_line: int,
|
|
384
380
|
change_end_char: int,
|
|
385
|
-
sem_tokens:
|
|
386
|
-
) ->
|
|
381
|
+
sem_tokens: list[int]
|
|
382
|
+
) -> tuple[(int | None), (int | None), bool] {
|
|
387
383
|
prev_token_index = None;
|
|
388
384
|
next_token_index = None;
|
|
389
385
|
inside_tok = False;
|
|
@@ -422,8 +418,8 @@ def find_surrounding_tokens(
|
|
|
422
418
|
"""Get the line of code, and the first non-space character index."""
|
|
423
419
|
def get_line_of_code(
|
|
424
420
|
line_number: int,
|
|
425
|
-
lines:
|
|
426
|
-
) -> Optional[
|
|
421
|
+
lines: list[str]
|
|
422
|
+
) -> Optional[tuple[(str, int)]] {
|
|
427
423
|
if 0 <= line_number < len(lines) {
|
|
428
424
|
line = lines[line_number].rstrip('\n');
|
|
429
425
|
first_non_space = (len(line) - len(line.lstrip()));
|
|
@@ -439,7 +435,7 @@ def get_line_of_code(
|
|
|
439
435
|
|
|
440
436
|
"""Add a new text edit to the changes dictionary if it is unique."""
|
|
441
437
|
def add_unique_text_edit(
|
|
442
|
-
changes:
|
|
438
|
+
changes: dict[(str, list[lspt.TextEdit])],
|
|
443
439
|
key: str,
|
|
444
440
|
new_edit: lspt.TextEdit
|
|
445
441
|
) -> None {
|
jaclang/runtimelib/builtin.py
CHANGED
|
@@ -7,8 +7,9 @@ from abc import abstractmethod
|
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from typing import ClassVar, Optional, override
|
|
9
9
|
|
|
10
|
-
from jaclang.runtimelib.constructs import
|
|
10
|
+
from jaclang.runtimelib.constructs import NodeArchetype
|
|
11
11
|
from jaclang.runtimelib.machine import JacMachineInterface as Jac
|
|
12
|
+
from jaclang.runtimelib.utils import collect_node_connections
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class AccessLevelEnum(Enum):
|
|
@@ -58,53 +59,39 @@ def printgraph(
|
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return Jac.get_object(id)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def grant(obj: Archetype, level: AccessLevelEnum) -> None:
|
|
72
|
-
"""Grant permission for the object."""
|
|
73
|
-
assert isinstance(level, AccessLevelEnum), f'Use {ConnectPerm} instead of "CONNECT"'
|
|
74
|
-
Jac.perm_grant(obj, level=level.value)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def revoke(obj: Archetype) -> None:
|
|
78
|
-
"""Revoke permission for the object."""
|
|
79
|
-
Jac.perm_revoke(obj)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def allroots() -> list[Jac.Root]:
|
|
83
|
-
"""Get all the roots."""
|
|
84
|
-
return Jac.get_all_root()
|
|
62
|
+
jid = Jac.object_ref
|
|
63
|
+
jobj = Jac.get_object
|
|
64
|
+
grant = Jac.perm_grant
|
|
65
|
+
revoke = Jac.perm_revoke
|
|
66
|
+
allroots = Jac.get_all_root
|
|
67
|
+
save = Jac.save
|
|
68
|
+
commit = Jac.commit
|
|
85
69
|
|
|
86
70
|
|
|
87
71
|
def _jac_graph_json(file: Optional[str] = None) -> str:
|
|
88
72
|
"""Get the graph in json string."""
|
|
89
|
-
|
|
73
|
+
visited_nodes: set = set()
|
|
74
|
+
connections: set = set()
|
|
75
|
+
edge_ids: set = set()
|
|
90
76
|
nodes: list[dict] = []
|
|
91
77
|
edges: list[dict] = []
|
|
92
|
-
working_set: list[tuple] = []
|
|
93
|
-
|
|
94
78
|
root = Jac.root()
|
|
79
|
+
|
|
80
|
+
collect_node_connections(root, visited_nodes, connections, edge_ids)
|
|
81
|
+
|
|
82
|
+
# Create nodes list from visited nodes
|
|
95
83
|
nodes.append({"id": id(root), "label": "root"})
|
|
84
|
+
for node_arch in visited_nodes:
|
|
85
|
+
if node_arch != root:
|
|
86
|
+
nodes.append({"id": id(node_arch), "label": repr(node_arch)})
|
|
87
|
+
|
|
88
|
+
# Create edges list with labels from connections
|
|
89
|
+
for _, source_node, target_node, edge_arch in connections:
|
|
90
|
+
edge_data = {"from": str(id(source_node)), "to": str(id(target_node))}
|
|
91
|
+
if repr(edge_arch) != "GenericEdge()":
|
|
92
|
+
edge_data["label"] = repr(edge_arch)
|
|
93
|
+
edges.append(edge_data)
|
|
96
94
|
|
|
97
|
-
processed.append(root)
|
|
98
|
-
working_set = [(root, ref) for ref in Jac.refs(root)]
|
|
99
|
-
|
|
100
|
-
while working_set:
|
|
101
|
-
start, end = working_set.pop(0)
|
|
102
|
-
edges.append({"from": id(start), "to": id(end)})
|
|
103
|
-
nodes.append({"id": id(end), "label": repr(end)})
|
|
104
|
-
processed.append(end)
|
|
105
|
-
for ref in Jac.refs(end):
|
|
106
|
-
if ref not in processed:
|
|
107
|
-
working_set.append((end, ref))
|
|
108
95
|
output = json.dumps(
|
|
109
96
|
{
|
|
110
97
|
"version": "1.0",
|
|
@@ -128,6 +115,8 @@ __all__ = [
|
|
|
128
115
|
"grant",
|
|
129
116
|
"revoke",
|
|
130
117
|
"allroots",
|
|
118
|
+
"save",
|
|
119
|
+
"commit",
|
|
131
120
|
"NoPerm",
|
|
132
121
|
"ReadPerm",
|
|
133
122
|
"ConnectPerm",
|
jaclang/runtimelib/importer.py
CHANGED
|
@@ -322,7 +322,7 @@ class JacImporter(Importer):
|
|
|
322
322
|
if os.path.isdir(spec.full_target):
|
|
323
323
|
module = self.handle_directory(spec.module_name, spec.full_target)
|
|
324
324
|
else:
|
|
325
|
-
spec.full_target += ".jac" if spec.language
|
|
325
|
+
spec.full_target += ".jac" if spec.language in ["jac", "jir"] else ".py"
|
|
326
326
|
module = self.create_jac_py_module(
|
|
327
327
|
module_name,
|
|
328
328
|
spec.package_path,
|
jaclang/runtimelib/machine.py
CHANGED
|
@@ -57,7 +57,6 @@ from jaclang.runtimelib.constructs import (
|
|
|
57
57
|
from jaclang.runtimelib.memory import Memory, Shelf, ShelfStorage
|
|
58
58
|
from jaclang.runtimelib.utils import (
|
|
59
59
|
all_issubclass,
|
|
60
|
-
collect_node_connections,
|
|
61
60
|
traverse_graph,
|
|
62
61
|
)
|
|
63
62
|
from jaclang.utils import infer_language
|
|
@@ -266,30 +265,6 @@ class JacAccessValidation:
|
|
|
266
265
|
class JacNode:
|
|
267
266
|
"""Jac Node Operations."""
|
|
268
267
|
|
|
269
|
-
@staticmethod
|
|
270
|
-
def node_dot(node: NodeArchetype, dot_file: Optional[str] = None) -> str:
|
|
271
|
-
"""Generate Dot file for visualizing nodes and edges."""
|
|
272
|
-
visited_nodes: set[NodeAnchor] = set()
|
|
273
|
-
connections: set[tuple[NodeArchetype, NodeArchetype, str]] = set()
|
|
274
|
-
unique_node_id_dict = {}
|
|
275
|
-
|
|
276
|
-
collect_node_connections(node.__jac__, visited_nodes, connections)
|
|
277
|
-
dot_content = 'digraph {\nnode [style="filled", shape="ellipse", fillcolor="invis", fontcolor="black"];\n'
|
|
278
|
-
for idx, i in enumerate([nodes_.archetype for nodes_ in visited_nodes]):
|
|
279
|
-
unique_node_id_dict[i] = (i.__class__.__name__, str(idx))
|
|
280
|
-
dot_content += f'{idx} [label="{i}"];\n'
|
|
281
|
-
dot_content += 'edge [color="gray", style="solid"];\n'
|
|
282
|
-
|
|
283
|
-
for pair in list(set(connections)):
|
|
284
|
-
dot_content += (
|
|
285
|
-
f"{unique_node_id_dict[pair[0]][1]} -> {unique_node_id_dict[pair[1]][1]}"
|
|
286
|
-
f' [label="{pair[2]}"];\n'
|
|
287
|
-
)
|
|
288
|
-
if dot_file:
|
|
289
|
-
with open(dot_file, "w") as f:
|
|
290
|
-
f.write(dot_content + "}")
|
|
291
|
-
return dot_content + "}"
|
|
292
|
-
|
|
293
268
|
@staticmethod
|
|
294
269
|
def get_edges(
|
|
295
270
|
origin: list[NodeArchetype], destination: DataSpatialDestination
|
|
@@ -879,6 +854,15 @@ class JacBasics:
|
|
|
879
854
|
"""Get current execution context."""
|
|
880
855
|
return JacMachine.exec_ctx
|
|
881
856
|
|
|
857
|
+
@staticmethod
|
|
858
|
+
def commit(anchor: Anchor | Archetype | None = None) -> None:
|
|
859
|
+
"""Commit all data from memory to datasource."""
|
|
860
|
+
if isinstance(anchor, Archetype):
|
|
861
|
+
anchor = anchor.__jac__
|
|
862
|
+
|
|
863
|
+
mem = JacMachineInterface.get_context().mem
|
|
864
|
+
mem.commit(anchor)
|
|
865
|
+
|
|
882
866
|
@staticmethod
|
|
883
867
|
def reset_graph(root: Optional[Root] = None) -> int:
|
|
884
868
|
"""Purge current or target graph."""
|
|
@@ -987,6 +971,7 @@ class JacBasics:
|
|
|
987
971
|
override_name: Optional[str] = None,
|
|
988
972
|
items: Optional[dict[str, Union[str, Optional[str]]]] = None,
|
|
989
973
|
reload_module: Optional[bool] = False,
|
|
974
|
+
lng: Optional[str] = None,
|
|
990
975
|
) -> tuple[types.ModuleType, ...]:
|
|
991
976
|
"""Core Import Process."""
|
|
992
977
|
from jaclang.runtimelib.importer import (
|
|
@@ -995,7 +980,8 @@ class JacBasics:
|
|
|
995
980
|
PythonImporter,
|
|
996
981
|
)
|
|
997
982
|
|
|
998
|
-
lng
|
|
983
|
+
if lng is None:
|
|
984
|
+
lng = infer_language(target, base_path)
|
|
999
985
|
|
|
1000
986
|
spec = ImportPathSpec(
|
|
1001
987
|
target,
|
|
@@ -1358,12 +1344,10 @@ class JacBasics:
|
|
|
1358
1344
|
return decorator
|
|
1359
1345
|
|
|
1360
1346
|
@staticmethod
|
|
1361
|
-
def call_llm(
|
|
1362
|
-
model: object, caller: Callable, args: dict[str | int, object]
|
|
1363
|
-
) -> Any: # noqa: ANN401
|
|
1347
|
+
def call_llm(model: object, mtir: object) -> Any: # noqa: ANN401
|
|
1364
1348
|
"""Call the LLM model."""
|
|
1365
1349
|
raise ImportError(
|
|
1366
|
-
"
|
|
1350
|
+
"byLLM is not installed. Please install it with `pip install byllm` and run `jac clean`."
|
|
1367
1351
|
)
|
|
1368
1352
|
|
|
1369
1353
|
|
|
@@ -1587,40 +1571,6 @@ class JacMachineInterface(
|
|
|
1587
1571
|
"""Jac Feature."""
|
|
1588
1572
|
|
|
1589
1573
|
|
|
1590
|
-
class JacMachine(JacMachineInterface):
|
|
1591
|
-
"""Jac Machine State."""
|
|
1592
|
-
|
|
1593
|
-
loaded_modules: dict[str, types.ModuleType] = {}
|
|
1594
|
-
base_path_dir: str = os.getcwd()
|
|
1595
|
-
program: JacProgram = JacProgram()
|
|
1596
|
-
pool: ThreadPoolExecutor = ThreadPoolExecutor()
|
|
1597
|
-
exec_ctx: ExecutionContext = ExecutionContext()
|
|
1598
|
-
|
|
1599
|
-
@staticmethod
|
|
1600
|
-
def set_base_path(base_path: str) -> None:
|
|
1601
|
-
"""Set the base path for the machine."""
|
|
1602
|
-
JacMachine.reset_machine()
|
|
1603
|
-
JacMachine.base_path_dir = (
|
|
1604
|
-
base_path if os.path.isdir(base_path) else os.path.dirname(base_path)
|
|
1605
|
-
)
|
|
1606
|
-
|
|
1607
|
-
@staticmethod
|
|
1608
|
-
def set_context(context: ExecutionContext) -> None:
|
|
1609
|
-
"""Set the context for the machine."""
|
|
1610
|
-
JacMachine.exec_ctx = context
|
|
1611
|
-
|
|
1612
|
-
@staticmethod
|
|
1613
|
-
def reset_machine() -> None:
|
|
1614
|
-
"""Reset the machine."""
|
|
1615
|
-
# for i in JacMachine.loaded_modules.values():
|
|
1616
|
-
# sys.modules.pop(i.__name__, None)
|
|
1617
|
-
JacMachine.loaded_modules.clear()
|
|
1618
|
-
JacMachine.base_path_dir = os.getcwd()
|
|
1619
|
-
JacMachine.program = JacProgram()
|
|
1620
|
-
JacMachine.pool = ThreadPoolExecutor()
|
|
1621
|
-
JacMachine.exec_ctx = ExecutionContext()
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
1574
|
def generate_plugin_helpers(
|
|
1625
1575
|
plugin_class: Type[Any],
|
|
1626
1576
|
) -> tuple[Type[Any], Type[Any], Type[Any]]:
|
|
@@ -1726,3 +1676,37 @@ def generate_plugin_helpers(
|
|
|
1726
1676
|
|
|
1727
1677
|
JacMachineSpec, JacMachineImpl, JacMachineInterface = generate_plugin_helpers(JacMachineInterface) # type: ignore[misc]
|
|
1728
1678
|
plugin_manager.add_hookspecs(JacMachineSpec)
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
class JacMachine(JacMachineInterface):
|
|
1682
|
+
"""Jac Machine State."""
|
|
1683
|
+
|
|
1684
|
+
loaded_modules: dict[str, types.ModuleType] = {}
|
|
1685
|
+
base_path_dir: str = os.getcwd()
|
|
1686
|
+
program: JacProgram = JacProgram()
|
|
1687
|
+
pool: ThreadPoolExecutor = ThreadPoolExecutor()
|
|
1688
|
+
exec_ctx: ExecutionContext = ExecutionContext()
|
|
1689
|
+
|
|
1690
|
+
@staticmethod
|
|
1691
|
+
def set_base_path(base_path: str) -> None:
|
|
1692
|
+
"""Set the base path for the machine."""
|
|
1693
|
+
JacMachine.reset_machine()
|
|
1694
|
+
JacMachine.base_path_dir = (
|
|
1695
|
+
base_path if os.path.isdir(base_path) else os.path.dirname(base_path)
|
|
1696
|
+
)
|
|
1697
|
+
|
|
1698
|
+
@staticmethod
|
|
1699
|
+
def set_context(context: ExecutionContext) -> None:
|
|
1700
|
+
"""Set the context for the machine."""
|
|
1701
|
+
JacMachine.exec_ctx = context
|
|
1702
|
+
|
|
1703
|
+
@staticmethod
|
|
1704
|
+
def reset_machine() -> None:
|
|
1705
|
+
"""Reset the machine."""
|
|
1706
|
+
# for i in JacMachine.loaded_modules.values():
|
|
1707
|
+
# sys.modules.pop(i.__name__, None)
|
|
1708
|
+
JacMachine.loaded_modules.clear()
|
|
1709
|
+
JacMachine.base_path_dir = os.getcwd()
|
|
1710
|
+
JacMachine.program = JacProgram()
|
|
1711
|
+
JacMachine.pool = ThreadPoolExecutor()
|
|
1712
|
+
JacMachine.exec_ctx = ExecutionContext()
|
jaclang/runtimelib/memory.py
CHANGED
|
@@ -82,6 +82,9 @@ class Memory(Generic[ID, TANCH]):
|
|
|
82
82
|
if anchor := self.__mem__.pop(id, None):
|
|
83
83
|
self.__gc__.add(anchor)
|
|
84
84
|
|
|
85
|
+
def commit(self, anchor: TANCH | None = None) -> None:
|
|
86
|
+
"""Commit all data from memory to datasource."""
|
|
87
|
+
|
|
85
88
|
|
|
86
89
|
@dataclass
|
|
87
90
|
class ShelfStorage(Memory[UUID, Anchor]):
|
|
@@ -94,12 +97,21 @@ class ShelfStorage(Memory[UUID, Anchor]):
|
|
|
94
97
|
super().__init__()
|
|
95
98
|
self.__shelf__ = open(session) if session else None # noqa: SIM115
|
|
96
99
|
|
|
97
|
-
def
|
|
98
|
-
"""
|
|
100
|
+
def commit(self, anchor: Anchor | None = None) -> None:
|
|
101
|
+
"""Commit all data from memory to datasource."""
|
|
99
102
|
if isinstance(self.__shelf__, Shelf):
|
|
100
|
-
|
|
101
|
-
self.
|
|
102
|
-
|
|
103
|
+
if anchor:
|
|
104
|
+
if anchor in self.__gc__:
|
|
105
|
+
self.__shelf__.pop(str(anchor.id), None)
|
|
106
|
+
self.__mem__.pop(anchor.id, None)
|
|
107
|
+
self.__gc__.remove(anchor)
|
|
108
|
+
else:
|
|
109
|
+
self.sync_mem_to_db([anchor.id])
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
for anc in self.__gc__:
|
|
113
|
+
self.__shelf__.pop(str(anc.id), None)
|
|
114
|
+
self.__mem__.pop(anc.id, None)
|
|
103
115
|
|
|
104
116
|
keys = set(self.__mem__.keys())
|
|
105
117
|
|
|
@@ -109,7 +121,13 @@ class ShelfStorage(Memory[UUID, Anchor]):
|
|
|
109
121
|
# additional after memory sync
|
|
110
122
|
self.sync_mem_to_db(set(self.__mem__.keys() - keys))
|
|
111
123
|
|
|
124
|
+
def close(self) -> None:
|
|
125
|
+
"""Close memory handler."""
|
|
126
|
+
self.commit()
|
|
127
|
+
|
|
128
|
+
if isinstance(self.__shelf__, Shelf):
|
|
112
129
|
self.__shelf__.close()
|
|
130
|
+
|
|
113
131
|
super().close()
|
|
114
132
|
|
|
115
133
|
def sync_mem_to_db(self, keys: Iterable[UUID]) -> None:
|
|
@@ -18,7 +18,7 @@ obj SavableObject {
|
|
|
18
18
|
|
|
19
19
|
walker create_custom_object {
|
|
20
20
|
can enter1 with `root entry {
|
|
21
|
-
|
|
21
|
+
self.obj = SavableObject(
|
|
22
22
|
val=0,
|
|
23
23
|
arr=[],
|
|
24
24
|
json={},
|
|
@@ -36,7 +36,15 @@ walker create_custom_object {
|
|
|
36
36
|
),
|
|
37
37
|
enum_field = Enum.A
|
|
38
38
|
);
|
|
39
|
-
|
|
39
|
+
save(self.obj);
|
|
40
|
+
|
|
41
|
+
# commit the object to db
|
|
42
|
+
commit(self.obj);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
can exit1 with `root exit {
|
|
46
|
+
# get directly from shelf
|
|
47
|
+
o = _.get_context().mem.__shelf__.get(str(self.obj.__jac__.id)).archetype;
|
|
40
48
|
print(jid(o));
|
|
41
49
|
print(o);
|
|
42
50
|
}
|