jaclang 0.8.1__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.
- jaclang/__init__.py +6 -0
- jaclang/cli/cli.py +21 -50
- jaclang/compiler/codeinfo.py +0 -1
- jaclang/compiler/jac.lark +12 -10
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +18 -10
- jaclang/compiler/passes/main/__init__.py +0 -14
- jaclang/compiler/passes/main/annex_pass.py +2 -8
- jaclang/compiler/passes/main/cfg_build_pass.py +38 -12
- jaclang/compiler/passes/main/import_pass.py +3 -11
- jaclang/compiler/passes/main/pyast_gen_pass.py +243 -592
- jaclang/compiler/passes/main/sym_tab_link_pass.py +2 -5
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +2 -8
- jaclang/compiler/passes/main/tests/test_decl_impl_match_pass.py +7 -8
- jaclang/compiler/passes/main/tests/test_import_pass.py +5 -18
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -6
- jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
- jaclang/compiler/passes/main/tests/test_sym_tab_link_pass.py +20 -17
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +237 -105
- jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -0
- jaclang/compiler/passes/tool/tests/fixtures/archetype_frmt.jac +14 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +5 -4
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +6 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +9 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +18 -3
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +2 -2
- jaclang/compiler/program.py +21 -60
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +2 -8
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +1 -5
- jaclang/compiler/tests/test_importer.py +10 -13
- jaclang/compiler/unitree.py +32 -16
- jaclang/langserve/__init__.jac +1 -1
- jaclang/langserve/engine.jac +113 -108
- jaclang/langserve/server.jac +17 -2
- jaclang/langserve/tests/server_test/test_lang_serve.py +138 -46
- jaclang/langserve/tests/server_test/utils.py +35 -9
- jaclang/langserve/tests/test_sem_tokens.py +1 -1
- jaclang/langserve/tests/test_server.py +3 -7
- jaclang/runtimelib/archetype.py +127 -5
- jaclang/runtimelib/importer.py +51 -94
- jaclang/runtimelib/machine.py +391 -268
- jaclang/runtimelib/meta_importer.py +86 -0
- jaclang/runtimelib/tests/fixtures/graph_purger.jac +24 -26
- jaclang/runtimelib/tests/fixtures/other_root_access.jac +25 -16
- jaclang/runtimelib/tests/test_jaseci.py +3 -1
- jaclang/tests/fixtures/arch_rel_import_creation.jac +23 -23
- jaclang/tests/fixtures/async_ability.jac +43 -10
- jaclang/tests/fixtures/async_function.jac +18 -0
- jaclang/tests/fixtures/async_walker.jac +17 -12
- jaclang/tests/fixtures/create_dynamic_archetype.jac +25 -28
- jaclang/tests/fixtures/deep/deeper/deep_outer_import.jac +7 -4
- jaclang/tests/fixtures/deep/deeper/snd_lev.jac +2 -2
- jaclang/tests/fixtures/deep/deeper/snd_lev_dup.jac +6 -0
- jaclang/tests/fixtures/deep/one_lev.jac +2 -2
- jaclang/tests/fixtures/deep/one_lev_dup.jac +4 -3
- jaclang/tests/fixtures/dynamic_archetype.jac +19 -12
- jaclang/tests/fixtures/foo.jac +14 -22
- jaclang/tests/fixtures/jac_from_py.py +1 -1
- jaclang/tests/fixtures/jp_importer.jac +6 -6
- jaclang/tests/fixtures/jp_importer_auto.jac +5 -3
- jaclang/tests/fixtures/unicode_strings.jac +24 -0
- jaclang/tests/fixtures/walker_update.jac +5 -7
- jaclang/tests/test_language.py +138 -140
- jaclang/tests/test_reference.py +9 -4
- jaclang/tests/test_typecheck.py +13 -26
- jaclang/utils/lang_tools.py +7 -5
- jaclang/utils/module_resolver.py +23 -0
- {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/METADATA +1 -1
- {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/RECORD +72 -70
- jaclang/compiler/passes/main/tests/fixtures/main_err.jac +0 -6
- jaclang/compiler/passes/main/tests/fixtures/second_err.jac +0 -4
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +0 -644
- jaclang/compiler/passes/tool/tests/test_doc_ir_gen_pass.py +0 -29
- jaclang/tests/fixtures/deep/deeper/__init__.jac +0 -1
- {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/WHEEL +0 -0
- {jaclang-0.8.1.dist-info → jaclang-0.8.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Test for Jac language server[VSCE] features"""
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
import pytest
|
|
4
5
|
|
|
@@ -16,19 +17,18 @@ from jaclang.langserve.tests.server_test.utils import (
|
|
|
16
17
|
create_temp_jac_file,
|
|
17
18
|
get_code,
|
|
18
19
|
get_jac_file_path,
|
|
20
|
+
get_simple_code,
|
|
19
21
|
create_ls_with_workspace, # new helper
|
|
20
22
|
)
|
|
21
23
|
from jaclang.vendor.pygls.uris import from_fs_path
|
|
22
24
|
from jaclang.vendor.pygls.workspace import Workspace
|
|
23
25
|
from jaclang import JacMachineInterface as _
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"....langserve.engine", __file__, items={"JacLangServer": None}
|
|
27
|
-
)[0]
|
|
28
|
-
(did_open, did_save, did_change, formatting) = _.py_jac_import( "....langserve.server", __file__, items={"did_open": None, "did_save": None, "did_change": None, "formatting": None})
|
|
26
|
+
from jaclang.langserve.engine import JacLangServer
|
|
27
|
+
from jaclang.langserve.server import did_open, did_save, did_change, formatting
|
|
29
28
|
|
|
30
29
|
JAC_FILE = get_jac_file_path()
|
|
31
30
|
|
|
31
|
+
|
|
32
32
|
class TestLangServe:
|
|
33
33
|
"""Test class for Jac language server features."""
|
|
34
34
|
|
|
@@ -95,7 +95,7 @@ class TestLangServe:
|
|
|
95
95
|
temp_file_path = create_temp_jac_file(code)
|
|
96
96
|
uri = from_fs_path(temp_file_path)
|
|
97
97
|
ls.lsp._workspace = Workspace(os.path.dirname(temp_file_path), ls)
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
params = DidOpenTextDocumentParams(
|
|
100
100
|
text_document=TextDocumentItem(
|
|
101
101
|
uri=uri,
|
|
@@ -130,7 +130,9 @@ class TestLangServe:
|
|
|
130
130
|
ls.shutdown()
|
|
131
131
|
assert hasattr(sem_tokens, "data")
|
|
132
132
|
assert isinstance(sem_tokens.data, list)
|
|
133
|
-
assert
|
|
133
|
+
assert (
|
|
134
|
+
len(sem_tokens.data) == 0
|
|
135
|
+
) # TODO: we should retain the sem tokens, will be fixed in next PR
|
|
134
136
|
|
|
135
137
|
os.remove(temp_file_path)
|
|
136
138
|
|
|
@@ -141,14 +143,17 @@ class TestLangServe:
|
|
|
141
143
|
temp_file_path = create_temp_jac_file(code)
|
|
142
144
|
uri, ls = create_ls_with_workspace(temp_file_path)
|
|
143
145
|
|
|
144
|
-
await did_open(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
await did_open(
|
|
147
|
+
ls,
|
|
148
|
+
DidOpenTextDocumentParams(
|
|
149
|
+
text_document=TextDocumentItem(
|
|
150
|
+
uri=uri,
|
|
151
|
+
language_id="jac",
|
|
152
|
+
version=1,
|
|
153
|
+
text=code,
|
|
154
|
+
)
|
|
155
|
+
),
|
|
156
|
+
)
|
|
152
157
|
|
|
153
158
|
params = DidSaveTextDocumentParams(
|
|
154
159
|
text_document=TextDocumentItem(
|
|
@@ -166,12 +171,14 @@ class TestLangServe:
|
|
|
166
171
|
|
|
167
172
|
# Now simulate a syntax error by updating the workspace and saving
|
|
168
173
|
broken_code = get_code("error")
|
|
169
|
-
ls.workspace.put_text_document(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
ls.workspace.put_text_document(
|
|
175
|
+
TextDocumentItem(
|
|
176
|
+
uri=uri,
|
|
177
|
+
language_id="jac",
|
|
178
|
+
version=3,
|
|
179
|
+
text=broken_code,
|
|
180
|
+
)
|
|
181
|
+
)
|
|
175
182
|
params = DidSaveTextDocumentParams(
|
|
176
183
|
text_document=TextDocumentItem(
|
|
177
184
|
uri=uri,
|
|
@@ -181,6 +188,9 @@ class TestLangServe:
|
|
|
181
188
|
)
|
|
182
189
|
)
|
|
183
190
|
await did_save(ls, params)
|
|
191
|
+
sem_tokens = ls.get_semantic_tokens(uri)
|
|
192
|
+
# semantic tokens should still be present even if there is a syntax error
|
|
193
|
+
assert len(sem_tokens.data) == 300
|
|
184
194
|
diagnostics = ls.diagnostics.get(uri, [])
|
|
185
195
|
assert isinstance(diagnostics, list)
|
|
186
196
|
assert len(diagnostics) == 1
|
|
@@ -196,26 +206,31 @@ class TestLangServe:
|
|
|
196
206
|
temp_file_path = create_temp_jac_file(code)
|
|
197
207
|
uri, ls = create_ls_with_workspace(temp_file_path)
|
|
198
208
|
|
|
199
|
-
await did_open(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
await did_open(
|
|
210
|
+
ls,
|
|
211
|
+
DidOpenTextDocumentParams(
|
|
212
|
+
text_document=TextDocumentItem(
|
|
213
|
+
uri=uri,
|
|
214
|
+
language_id="jac",
|
|
215
|
+
version=1,
|
|
216
|
+
text=code,
|
|
217
|
+
)
|
|
218
|
+
),
|
|
219
|
+
)
|
|
207
220
|
|
|
208
221
|
# No error, should be no diagnostics
|
|
209
222
|
params = DidChangeTextDocumentParams(
|
|
210
223
|
text_document=VersionedTextDocumentIdentifier(uri=uri, version=2),
|
|
211
|
-
content_changes=[{"text": "\n" + code}]
|
|
224
|
+
content_changes=[{"text": "\n" + code}],
|
|
225
|
+
)
|
|
226
|
+
ls.workspace.put_text_document(
|
|
227
|
+
TextDocumentItem(
|
|
228
|
+
uri=uri,
|
|
229
|
+
language_id="jac",
|
|
230
|
+
version=2,
|
|
231
|
+
text="\n" + code,
|
|
232
|
+
)
|
|
212
233
|
)
|
|
213
|
-
ls.workspace.put_text_document(TextDocumentItem(
|
|
214
|
-
uri=uri,
|
|
215
|
-
language_id="jac",
|
|
216
|
-
version=2,
|
|
217
|
-
text="\n" + code,
|
|
218
|
-
))
|
|
219
234
|
await did_change(ls, params)
|
|
220
235
|
diagnostics = ls.diagnostics.get(uri, [])
|
|
221
236
|
assert isinstance(diagnostics, list)
|
|
@@ -226,15 +241,20 @@ class TestLangServe:
|
|
|
226
241
|
error_code = "\nerror"
|
|
227
242
|
params = DidChangeTextDocumentParams(
|
|
228
243
|
text_document=VersionedTextDocumentIdentifier(uri=uri, version=3),
|
|
229
|
-
content_changes=[{"text": error_code + code}]
|
|
244
|
+
content_changes=[{"text": error_code + code}],
|
|
245
|
+
)
|
|
246
|
+
ls.workspace.put_text_document(
|
|
247
|
+
TextDocumentItem(
|
|
248
|
+
uri=uri,
|
|
249
|
+
language_id="jac",
|
|
250
|
+
version=3,
|
|
251
|
+
text=error_code + code,
|
|
252
|
+
)
|
|
230
253
|
)
|
|
231
|
-
ls.workspace.put_text_document(TextDocumentItem(
|
|
232
|
-
uri=uri,
|
|
233
|
-
language_id="jac",
|
|
234
|
-
version=3,
|
|
235
|
-
text=error_code + code,
|
|
236
|
-
))
|
|
237
254
|
await did_change(ls, params)
|
|
255
|
+
sem_tokens = ls.get_semantic_tokens(uri)
|
|
256
|
+
# semantic tokens should still be present even if there is a syntax error
|
|
257
|
+
assert len(sem_tokens.data) == 300
|
|
238
258
|
diagnostics = ls.diagnostics.get(uri, [])
|
|
239
259
|
assert isinstance(diagnostics, list)
|
|
240
260
|
assert len(diagnostics) == 1
|
|
@@ -250,12 +270,84 @@ class TestLangServe:
|
|
|
250
270
|
uri, ls = create_ls_with_workspace(temp_file_path)
|
|
251
271
|
params = DocumentFormattingParams(
|
|
252
272
|
text_document=TextDocumentIdentifier(uri=uri),
|
|
253
|
-
options={"tabSize": 4, "insertSpaces": True}
|
|
273
|
+
options={"tabSize": 4, "insertSpaces": True},
|
|
254
274
|
)
|
|
255
275
|
edits = formatting(ls, params)
|
|
256
276
|
assert isinstance(edits, list)
|
|
257
277
|
assert isinstance(edits[0], TextEdit)
|
|
258
|
-
assert
|
|
278
|
+
assert (
|
|
279
|
+
len(edits[0].new_text) > 100
|
|
280
|
+
) # it is a random number to check if the text is changed
|
|
259
281
|
print(edits[0].new_text)
|
|
260
282
|
ls.shutdown()
|
|
261
|
-
os.remove(temp_file_path)
|
|
283
|
+
os.remove(temp_file_path)
|
|
284
|
+
|
|
285
|
+
@pytest.mark.asyncio
|
|
286
|
+
async def test_multifile_workspace(self):
|
|
287
|
+
"""Test opening multiple Jac files in a workspace."""
|
|
288
|
+
code1 = get_simple_code("")
|
|
289
|
+
code2 = get_simple_code("error")
|
|
290
|
+
temp_file_path1 = create_temp_jac_file(code1)
|
|
291
|
+
temp_file_path2 = create_temp_jac_file(code2)
|
|
292
|
+
|
|
293
|
+
uri1, ls = create_ls_with_workspace(temp_file_path1)
|
|
294
|
+
uri2 = from_fs_path(temp_file_path2)
|
|
295
|
+
|
|
296
|
+
await did_open(
|
|
297
|
+
ls,
|
|
298
|
+
DidOpenTextDocumentParams(
|
|
299
|
+
text_document=TextDocumentItem(
|
|
300
|
+
uri=uri1,
|
|
301
|
+
language_id="jac",
|
|
302
|
+
version=1,
|
|
303
|
+
text=code1,
|
|
304
|
+
)
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
await did_open(
|
|
308
|
+
ls,
|
|
309
|
+
DidOpenTextDocumentParams(
|
|
310
|
+
text_document=TextDocumentItem(
|
|
311
|
+
uri=uri2,
|
|
312
|
+
language_id="jac",
|
|
313
|
+
version=1,
|
|
314
|
+
text=code2,
|
|
315
|
+
)
|
|
316
|
+
),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
diagnostics1 = ls.diagnostics.get(uri1, [])
|
|
320
|
+
diagnostics2 = ls.diagnostics.get(uri2, [])
|
|
321
|
+
assert len(diagnostics1) == 0
|
|
322
|
+
assert len(diagnostics2) == 1
|
|
323
|
+
assert diagnostics2[0].message == "Syntax Error"
|
|
324
|
+
|
|
325
|
+
before_sem_tokens_1 = ls.get_semantic_tokens(uri1)
|
|
326
|
+
before_sem_tokens_2 = ls.get_semantic_tokens(uri2)
|
|
327
|
+
assert len(before_sem_tokens_1.data) == 15
|
|
328
|
+
assert len(before_sem_tokens_2.data) == 0
|
|
329
|
+
|
|
330
|
+
changed_code = get_simple_code("glob x = 90;")
|
|
331
|
+
ls.workspace.put_text_document(
|
|
332
|
+
TextDocumentItem(
|
|
333
|
+
uri=uri1,
|
|
334
|
+
language_id="jac",
|
|
335
|
+
version=2,
|
|
336
|
+
text=changed_code,
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
params = DidChangeTextDocumentParams(
|
|
340
|
+
text_document=VersionedTextDocumentIdentifier(uri=uri1, version=2),
|
|
341
|
+
content_changes=[{"text": changed_code}],
|
|
342
|
+
)
|
|
343
|
+
await did_change(ls, params)
|
|
344
|
+
|
|
345
|
+
after_sem_tokens_1 = ls.get_semantic_tokens(uri1)
|
|
346
|
+
after_sem_tokens_2 = ls.get_semantic_tokens(uri2)
|
|
347
|
+
|
|
348
|
+
assert len(after_sem_tokens_1.data) == 20
|
|
349
|
+
assert len(after_sem_tokens_2.data) == 0
|
|
350
|
+
|
|
351
|
+
ls.shutdown()
|
|
352
|
+
os.remove(temp_file_path1)
|
|
353
|
+
os.remove(temp_file_path2)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Unit test utilities for JacLangServer."""
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
import tempfile
|
|
4
5
|
|
|
@@ -6,27 +7,31 @@ from jaclang.vendor.pygls.uris import from_fs_path
|
|
|
6
7
|
from jaclang.vendor.pygls.workspace import Workspace
|
|
7
8
|
|
|
8
9
|
from textwrap import dedent
|
|
9
|
-
|
|
10
|
-
JacLangServer = _.py_jac_import(
|
|
11
|
-
"....langserve.engine", __file__, items={"JacLangServer": None}
|
|
12
|
-
)[0]
|
|
10
|
+
|
|
13
11
|
|
|
14
12
|
def get_jac_file_path():
|
|
15
13
|
"""Return the absolute path to the sample Jac file used for testing."""
|
|
16
14
|
return os.path.abspath(
|
|
17
|
-
os.path.join(
|
|
15
|
+
os.path.join(
|
|
16
|
+
os.path.dirname(__file__), "../../../../examples/manual_code/circle.jac"
|
|
17
|
+
)
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
def create_temp_jac_file(initial_content: str = "") -> str:
|
|
21
22
|
"""Create a temporary Jac file with optional initial content and return its path."""
|
|
22
|
-
temp = tempfile.NamedTemporaryFile(
|
|
23
|
+
temp = tempfile.NamedTemporaryFile(
|
|
24
|
+
delete=False, suffix=".jac", mode="w", encoding="utf-8"
|
|
25
|
+
)
|
|
23
26
|
temp.write(initial_content)
|
|
24
27
|
temp.close()
|
|
25
28
|
return temp.name
|
|
26
29
|
|
|
30
|
+
|
|
27
31
|
def get_code(code: str) -> str:
|
|
28
32
|
"""Generate a sample Jac code snippet with optional test code injected."""
|
|
29
|
-
jac_code = dedent(
|
|
33
|
+
jac_code = dedent(
|
|
34
|
+
f'''
|
|
30
35
|
"""
|
|
31
36
|
This module demonstrates a simple circle class and a function to calculate
|
|
32
37
|
the area of a circle in all of Jac's glory.
|
|
@@ -107,12 +112,33 @@ def get_code(code: str) -> str:
|
|
|
107
112
|
c = Circle(RAD);
|
|
108
113
|
check c.shape_type == ShapeType.CIRCLE;
|
|
109
114
|
}}
|
|
110
|
-
'''
|
|
115
|
+
'''
|
|
116
|
+
)
|
|
111
117
|
return jac_code
|
|
112
118
|
|
|
119
|
+
|
|
120
|
+
def get_simple_code(code: str) -> str:
|
|
121
|
+
"""Generate a sample Jac code snippet with optional test code injected."""
|
|
122
|
+
jac_code = dedent(
|
|
123
|
+
f"""
|
|
124
|
+
|
|
125
|
+
# Unit Tests!
|
|
126
|
+
glob expected_area0 = 78.53981633974483;
|
|
127
|
+
glob expected_area1 = 78.53981633974483;
|
|
128
|
+
{code}
|
|
129
|
+
glob expected_area2 = 78.53981633974483;
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
)
|
|
134
|
+
return jac_code
|
|
135
|
+
|
|
136
|
+
|
|
113
137
|
def create_ls_with_workspace(file_path: str):
|
|
114
138
|
"""Create JacLangServer and workspace for a given file path, return (uri, ls)."""
|
|
139
|
+
from jaclang.langserve.engine import JacLangServer
|
|
140
|
+
|
|
115
141
|
ls = JacLangServer()
|
|
116
142
|
uri = from_fs_path(file_path)
|
|
117
143
|
ls.lsp._workspace = Workspace(os.path.dirname(file_path), ls)
|
|
118
|
-
return uri, ls
|
|
144
|
+
return uri, ls
|
|
@@ -4,10 +4,10 @@ import copy
|
|
|
4
4
|
import lsprotocol.types as lspt
|
|
5
5
|
from jaclang import JacMachineInterface as _
|
|
6
6
|
from jaclang.utils.test import TestCase
|
|
7
|
+
from jaclang.langserve.sem_manager import SemTokManager
|
|
7
8
|
|
|
8
9
|
from typing import Tuple
|
|
9
10
|
|
|
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"""
|
|
@@ -5,13 +5,9 @@ from jaclang.vendor.pygls.workspace import Workspace
|
|
|
5
5
|
import lsprotocol.types as lspt
|
|
6
6
|
import pytest
|
|
7
7
|
from jaclang import JacMachineInterface as _
|
|
8
|
+
from jaclang.langserve.engine import JacLangServer
|
|
9
|
+
from .session import LspSession
|
|
8
10
|
|
|
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]
|
|
15
11
|
|
|
16
12
|
class TestJacLangServer(TestCase):
|
|
17
13
|
|
|
@@ -143,7 +139,7 @@ class TestJacLangServer(TestCase):
|
|
|
143
139
|
lsp.deep_check(guess_game_file)
|
|
144
140
|
self.assertIn(
|
|
145
141
|
"guess_game4.jac:16:8-16:21",
|
|
146
|
-
str(lsp.get_definition(guess_game_file, lspt.Position(
|
|
142
|
+
str(lsp.get_definition(guess_game_file, lspt.Position(15, 45))),
|
|
147
143
|
)
|
|
148
144
|
|
|
149
145
|
def test_go_to_definition_method_manual_impl(self) -> None:
|
jaclang/runtimelib/archetype.py
CHANGED
|
@@ -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."""
|
|
@@ -329,7 +451,7 @@ class Root(NodeArchetype):
|
|
|
329
451
|
|
|
330
452
|
@dataclass(eq=False)
|
|
331
453
|
class DataSpatialFunction:
|
|
332
|
-
"""
|
|
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 =
|
|
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 !=
|
|
466
|
+
return ty if ty != _empty else None
|
|
345
467
|
return None
|