jaclang 0.8.7__py3-none-any.whl → 0.8.9__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.py +77 -29
- jaclang/cli/cmdreg.py +44 -0
- jaclang/compiler/constant.py +6 -2
- jaclang/compiler/jac.lark +37 -47
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +356 -61
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +1 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +221 -135
- jaclang/compiler/passes/main/pyast_load_pass.py +54 -20
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +190 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +56 -0
- jaclang/compiler/passes/main/type_checker_pass.py +29 -73
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +302 -58
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +276 -10
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +19 -7
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/type_evaluator.jac +959 -0
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +102 -107
- jaclang/langserve/engine.jac +138 -159
- jaclang/langserve/server.jac +25 -1
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -309
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_server.py +21 -84
- jaclang/langserve/utils.jac +12 -15
- jaclang/lib.py +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/constructs.py +2 -2
- jaclang/runtimelib/machine.py +63 -46
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +19 -16
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
- jaclang/tests/fixtures/funccall_genexpr.py +5 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +159 -7
- jaclang/tests/test_language.py +213 -38
- jaclang/tests/test_reference.py +3 -1
- jaclang/utils/helpers.py +67 -6
- jaclang/utils/module_resolver.py +10 -0
- jaclang/utils/test.py +8 -0
- jaclang/utils/tests/test_lang_tools.py +4 -15
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -2
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/RECORD +95 -65
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/compiler/type_system/type_evaluator.py +0 -560
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- /jaclang/{langserve/tests/server_test/code_test.py → tests/fixtures/py2jac_empty.py} +0 -0
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/entry_points.txt +0 -0
|
@@ -2,21 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import tempfile
|
|
5
|
-
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from lsprotocol.types import (
|
|
9
|
+
DidOpenTextDocumentParams,
|
|
10
|
+
TextDocumentItem,
|
|
11
|
+
DidSaveTextDocumentParams,
|
|
12
|
+
DidChangeTextDocumentParams,
|
|
13
|
+
VersionedTextDocumentIdentifier,
|
|
14
|
+
)
|
|
6
15
|
from jaclang.vendor.pygls.uris import from_fs_path
|
|
7
16
|
from jaclang.vendor.pygls.workspace import Workspace
|
|
8
17
|
|
|
9
|
-
from textwrap import dedent
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def get_jac_file_path():
|
|
13
|
-
"""Return the absolute path to the sample Jac file used for testing."""
|
|
14
|
-
return os.path.abspath(
|
|
15
|
-
os.path.join(
|
|
16
|
-
os.path.dirname(__file__), "../../../../examples/manual_code/circle.jac"
|
|
17
|
-
)
|
|
18
|
-
)
|
|
19
|
-
|
|
20
18
|
|
|
21
19
|
def create_temp_jac_file(initial_content: str = "") -> str:
|
|
22
20
|
"""Create a temporary Jac file with optional initial content and return its path."""
|
|
@@ -28,110 +26,11 @@ def create_temp_jac_file(initial_content: str = "") -> str:
|
|
|
28
26
|
return temp.name
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
def
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
f
|
|
35
|
-
""
|
|
36
|
-
This module demonstrates a simple circle class and a function to calculate
|
|
37
|
-
the area of a circle in all of Jac's glory.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
import math;
|
|
41
|
-
|
|
42
|
-
# Module-level global variable
|
|
43
|
-
glob RAD = 5;
|
|
44
|
-
|
|
45
|
-
"""Function to calculate the area of a circle."""
|
|
46
|
-
def calculate_area(radius: float) -> float {{
|
|
47
|
-
return math.pi * radius * radius;
|
|
48
|
-
}}
|
|
49
|
-
|
|
50
|
-
#* (This is a multiline comment in Jac)
|
|
51
|
-
Above we have the demonstration of a function to calculate the area of a circle.
|
|
52
|
-
Below we have the demonstration of a class to calculate the area of a circle.
|
|
53
|
-
*#
|
|
54
|
-
|
|
55
|
-
"""Enum for shape types"""
|
|
56
|
-
enum ShapeType {{
|
|
57
|
-
CIRCLE = "Circle",
|
|
58
|
-
UNKNOWN = "Unknown"
|
|
59
|
-
}}
|
|
60
|
-
|
|
61
|
-
"""Base class for a shape."""
|
|
62
|
-
obj Shape {{
|
|
63
|
-
has shape_type: ShapeType;
|
|
64
|
-
|
|
65
|
-
"""Abstract method to calculate the area of a shape."""
|
|
66
|
-
def area -> float abs;
|
|
67
|
-
}}
|
|
68
|
-
|
|
69
|
-
"""Circle class inherits from Shape."""
|
|
70
|
-
obj Circle(Shape) {{
|
|
71
|
-
def init(radius: float) {{
|
|
72
|
-
super.init(ShapeType.CIRCLE);
|
|
73
|
-
self.radius = radius;
|
|
74
|
-
}}
|
|
75
|
-
|
|
76
|
-
"""Overridden method to calculate the area of the circle."""
|
|
77
|
-
override def area -> float {{
|
|
78
|
-
return math.pi * self.radius * self.radius;
|
|
79
|
-
}}
|
|
80
|
-
}}
|
|
81
|
-
|
|
82
|
-
with entry {{
|
|
83
|
-
c = Circle(RAD);
|
|
84
|
-
}}
|
|
85
|
-
|
|
86
|
-
# Global also works here
|
|
87
|
-
|
|
88
|
-
with entry:__main__ {{
|
|
89
|
-
# To run the program functionality
|
|
90
|
-
print(
|
|
91
|
-
f"Area of a circle with radius {{RAD}} using function: {{calculate_area(RAD)}}"
|
|
92
|
-
);
|
|
93
|
-
print(
|
|
94
|
-
f"Area of a {{c.shape_type.value}} with radius {{RAD}} using class: {{c.area()}}"
|
|
95
|
-
);
|
|
96
|
-
}}
|
|
97
|
-
|
|
98
|
-
# Unit Tests!
|
|
99
|
-
glob expected_area = 78.53981633974483;
|
|
100
|
-
{code}
|
|
101
|
-
|
|
102
|
-
test calc_area {{
|
|
103
|
-
check almostEqual(calculate_area(RAD), expected_area);
|
|
104
|
-
}}
|
|
105
|
-
|
|
106
|
-
test circle_area {{
|
|
107
|
-
c = Circle(RAD);
|
|
108
|
-
check almostEqual(c.area(), expected_area);
|
|
109
|
-
}}
|
|
110
|
-
|
|
111
|
-
test circle_type {{
|
|
112
|
-
c = Circle(RAD);
|
|
113
|
-
check c.shape_type == ShapeType.CIRCLE;
|
|
114
|
-
}}
|
|
115
|
-
'''
|
|
116
|
-
)
|
|
117
|
-
return jac_code
|
|
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
|
|
29
|
+
def load_jac_template(template_file: str, code: str = "") -> str:
|
|
30
|
+
"""Load a Jac template file and inject code into placeholder."""
|
|
31
|
+
with open(template_file, "r") as f:
|
|
32
|
+
jac_template = f.read()
|
|
33
|
+
return jac_template.replace("#{{INJECT_CODE}}", code)
|
|
135
34
|
|
|
136
35
|
|
|
137
36
|
def create_ls_with_workspace(file_path: str):
|
|
@@ -142,3 +41,141 @@ def create_ls_with_workspace(file_path: str):
|
|
|
142
41
|
uri = from_fs_path(file_path)
|
|
143
42
|
ls.lsp._workspace = Workspace(os.path.dirname(file_path), ls)
|
|
144
43
|
return uri, ls
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class TestFile:
|
|
48
|
+
"""Encapsulates test file information and operations."""
|
|
49
|
+
|
|
50
|
+
path: str
|
|
51
|
+
uri: str
|
|
52
|
+
code: str
|
|
53
|
+
version: int = 1
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_template(cls, template_name: str, content: str = "") -> "TestFile":
|
|
57
|
+
"""Create a test file from a template."""
|
|
58
|
+
code = load_jac_template(cls._get_template_path(template_name), content)
|
|
59
|
+
temp_path = create_temp_jac_file(code)
|
|
60
|
+
uri = from_fs_path(temp_path)
|
|
61
|
+
return cls(
|
|
62
|
+
path=temp_path,
|
|
63
|
+
uri=uri or "",
|
|
64
|
+
code=code,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _get_template_path(file_name: str) -> str:
|
|
69
|
+
"""Get absolute path to test template file."""
|
|
70
|
+
return os.path.abspath(
|
|
71
|
+
os.path.join(os.path.dirname(__file__), file_name)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def cleanup(self):
|
|
75
|
+
"""Remove temporary test file."""
|
|
76
|
+
if os.path.exists(self.path):
|
|
77
|
+
os.remove(self.path)
|
|
78
|
+
|
|
79
|
+
def increment_version(self) -> int:
|
|
80
|
+
"""Increment and return the version number."""
|
|
81
|
+
self.version += 1
|
|
82
|
+
return self.version
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class LanguageServerTestHelper:
|
|
86
|
+
"""Helper class for language server testing operations."""
|
|
87
|
+
|
|
88
|
+
def __init__(self, ls, test_file: TestFile):
|
|
89
|
+
self.ls = ls
|
|
90
|
+
self.test_file = test_file
|
|
91
|
+
|
|
92
|
+
async def open_document(self) -> None:
|
|
93
|
+
"""Open a document in the language server."""
|
|
94
|
+
from jaclang.langserve.server import did_open
|
|
95
|
+
|
|
96
|
+
params = DidOpenTextDocumentParams(
|
|
97
|
+
text_document=TextDocumentItem(
|
|
98
|
+
uri=self.test_file.uri,
|
|
99
|
+
language_id="jac",
|
|
100
|
+
version=self.test_file.version,
|
|
101
|
+
text=self.test_file.code,
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
await did_open(self.ls, params)
|
|
105
|
+
|
|
106
|
+
async def save_document(self, code: Optional[str] = None) -> None:
|
|
107
|
+
"""Save a document in the language server."""
|
|
108
|
+
from jaclang.langserve.server import did_save
|
|
109
|
+
|
|
110
|
+
content = code if code is not None else self.test_file.code
|
|
111
|
+
version = self.test_file.increment_version()
|
|
112
|
+
|
|
113
|
+
if code:
|
|
114
|
+
self._update_workspace(code, version)
|
|
115
|
+
|
|
116
|
+
from lsprotocol.types import TextDocumentIdentifier
|
|
117
|
+
|
|
118
|
+
params = DidSaveTextDocumentParams(
|
|
119
|
+
text_document=TextDocumentIdentifier(uri=self.test_file.uri),
|
|
120
|
+
text=content
|
|
121
|
+
)
|
|
122
|
+
await did_save(self.ls, params)
|
|
123
|
+
|
|
124
|
+
async def change_document(self, code: str) -> None:
|
|
125
|
+
"""Change document content in the language server."""
|
|
126
|
+
from jaclang.langserve.server import did_change
|
|
127
|
+
|
|
128
|
+
version = self.test_file.increment_version()
|
|
129
|
+
self._update_workspace(code, version)
|
|
130
|
+
|
|
131
|
+
params = DidChangeTextDocumentParams(
|
|
132
|
+
text_document=VersionedTextDocumentIdentifier(
|
|
133
|
+
uri=self.test_file.uri,
|
|
134
|
+
version=version
|
|
135
|
+
),
|
|
136
|
+
content_changes=[{"text": code}], # type: ignore
|
|
137
|
+
)
|
|
138
|
+
await did_change(self.ls, params)
|
|
139
|
+
|
|
140
|
+
def _update_workspace(self, code: str, version: int) -> None:
|
|
141
|
+
"""Update workspace with new document content."""
|
|
142
|
+
self.ls.workspace.put_text_document(
|
|
143
|
+
TextDocumentItem(
|
|
144
|
+
uri=self.test_file.uri,
|
|
145
|
+
language_id="jac",
|
|
146
|
+
version=version,
|
|
147
|
+
text=code,
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def get_diagnostics(self) -> list:
|
|
152
|
+
"""Get diagnostics for the current document."""
|
|
153
|
+
return self.ls.diagnostics.get(self.test_file.uri, [])
|
|
154
|
+
|
|
155
|
+
def get_semantic_tokens(self):
|
|
156
|
+
"""Get semantic tokens for the current document."""
|
|
157
|
+
return self.ls.get_semantic_tokens(self.test_file.uri)
|
|
158
|
+
|
|
159
|
+
def assert_no_diagnostics(self) -> None:
|
|
160
|
+
"""Assert that there are no diagnostics."""
|
|
161
|
+
diagnostics = self.get_diagnostics()
|
|
162
|
+
assert isinstance(diagnostics, list)
|
|
163
|
+
assert len(diagnostics) == 0, f"Expected no diagnostics, found {len(diagnostics)}"
|
|
164
|
+
|
|
165
|
+
def assert_has_diagnostics(self, count: int = 1, message_contains: Optional[str] = None) -> None:
|
|
166
|
+
"""Assert that diagnostics exist with optional message validation."""
|
|
167
|
+
diagnostics = self.get_diagnostics()
|
|
168
|
+
assert isinstance(diagnostics, list)
|
|
169
|
+
assert len(diagnostics) == count, f"Expected {count} diagnostic(s), found {len(diagnostics)}"
|
|
170
|
+
|
|
171
|
+
if message_contains:
|
|
172
|
+
assert message_contains in diagnostics[0].message, \
|
|
173
|
+
f"Expected '{message_contains}' in diagnostic message"
|
|
174
|
+
|
|
175
|
+
def assert_semantic_tokens_count(self, expected_count: int) -> None:
|
|
176
|
+
"""Assert semantic tokens data has expected count."""
|
|
177
|
+
tokens = self.get_semantic_tokens()
|
|
178
|
+
assert hasattr(tokens, "data")
|
|
179
|
+
assert isinstance(tokens.data, list)
|
|
180
|
+
assert len(tokens.data) == expected_count, \
|
|
181
|
+
f"Expected {expected_count} tokens, found {len(tokens.data)}"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
1
2
|
from jaclang.utils.test import TestCase
|
|
2
3
|
from jaclang.vendor.pygls import uris
|
|
3
4
|
from jaclang.vendor.pygls.workspace import Workspace
|
|
@@ -310,7 +311,6 @@ class TestJacLangServer(TestCase):
|
|
|
310
311
|
for token_type, expected_count in expected_counts:
|
|
311
312
|
self.assertEqual(str(sem_list).count(token_type), expected_count)
|
|
312
313
|
|
|
313
|
-
@pytest.mark.xfail(reason="TODO: Fix when we have the type checker")
|
|
314
314
|
def test_completion(self) -> None:
|
|
315
315
|
"""Test that the completions are correct."""
|
|
316
316
|
lsp = JacLangServer()
|
|
@@ -318,95 +318,28 @@ class TestJacLangServer(TestCase):
|
|
|
318
318
|
workspace = Workspace(workspace_path, lsp)
|
|
319
319
|
lsp.lsp._workspace = workspace
|
|
320
320
|
base_module_file = uris.from_fs_path(
|
|
321
|
-
self.fixture_abs_path("
|
|
321
|
+
self.fixture_abs_path("completion_test_err.jac")
|
|
322
322
|
)
|
|
323
323
|
lsp.deep_check(base_module_file)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
(
|
|
333
|
-
lspt.Position(
|
|
334
|
-
["
|
|
335
|
-
3,
|
|
336
|
-
),
|
|
337
|
-
(
|
|
338
|
-
lspt.Position(65, 13),
|
|
339
|
-
[
|
|
340
|
-
"get_color1",
|
|
341
|
-
"color1",
|
|
342
|
-
"point1",
|
|
343
|
-
"base_colorred",
|
|
344
|
-
"pointred",
|
|
345
|
-
"inner_red",
|
|
346
|
-
"doubleinner",
|
|
347
|
-
"apply_red",
|
|
348
|
-
],
|
|
349
|
-
11,
|
|
350
|
-
),
|
|
351
|
-
(
|
|
352
|
-
lspt.Position(65, 23),
|
|
353
|
-
["color22", "doublepoint22", "point22", "apply_inner_red", "enum_red"],
|
|
354
|
-
5,
|
|
355
|
-
),
|
|
356
|
-
(
|
|
357
|
-
lspt.Position(65, 31),
|
|
358
|
-
["RED22", "GREEN22", "BLUE22"],
|
|
359
|
-
3,
|
|
360
|
-
),
|
|
361
|
-
(
|
|
362
|
-
lspt.Position(35, 28),
|
|
363
|
-
[],
|
|
364
|
-
0,
|
|
365
|
-
),
|
|
366
|
-
(
|
|
367
|
-
lspt.Position(72, 12),
|
|
368
|
-
[
|
|
369
|
-
"get_color1",
|
|
370
|
-
"color1",
|
|
371
|
-
"point1",
|
|
372
|
-
"base_colorred",
|
|
373
|
-
"pointred",
|
|
374
|
-
"inner_red",
|
|
375
|
-
"doubleinner",
|
|
376
|
-
"apply_red",
|
|
377
|
-
],
|
|
378
|
-
11,
|
|
379
|
-
),
|
|
380
|
-
(
|
|
381
|
-
lspt.Position(73, 22),
|
|
382
|
-
["color22", "doublepoint22", "apply_inner_red", "point22", "enum_red"],
|
|
383
|
-
5,
|
|
384
|
-
),
|
|
385
|
-
(
|
|
386
|
-
lspt.Position(37, 12),
|
|
387
|
-
["self", "add", "subtract", "x", "Colorenum", "Colour1", "red", "r"],
|
|
388
|
-
153,
|
|
389
|
-
None,
|
|
324
|
+
|
|
325
|
+
@dataclass
|
|
326
|
+
class Case:
|
|
327
|
+
pos: lspt.Position
|
|
328
|
+
expected: list[str]
|
|
329
|
+
trigger: str = "."
|
|
330
|
+
|
|
331
|
+
test_cases: list[Case] = [
|
|
332
|
+
Case(
|
|
333
|
+
lspt.Position(8, 8),
|
|
334
|
+
["bar", "baz"],
|
|
390
335
|
),
|
|
391
336
|
]
|
|
392
|
-
default_trigger = "."
|
|
393
337
|
for case in test_cases:
|
|
394
|
-
position, expected_completions, expected_length = case[:3]
|
|
395
|
-
completion_trigger = case[3] if len(case) > 3 else default_trigger
|
|
396
338
|
completions = lsp.get_completion(
|
|
397
|
-
base_module_file,
|
|
339
|
+
base_module_file, case.pos, completion_trigger=case.trigger
|
|
398
340
|
).items
|
|
399
|
-
for completion in
|
|
341
|
+
for completion in case.expected:
|
|
400
342
|
self.assertIn(completion, str(completions))
|
|
401
|
-
self.assertEqual(expected_length, len(completions))
|
|
402
|
-
|
|
403
|
-
if position == lspt.Position(73, 12):
|
|
404
|
-
self.assertEqual(
|
|
405
|
-
2, str(completions).count("kind=<CompletionItemKind.Function: 3>")
|
|
406
|
-
)
|
|
407
|
-
self.assertEqual(
|
|
408
|
-
4, str(completions).count("kind=<CompletionItemKind.Field: 5>")
|
|
409
|
-
)
|
|
410
343
|
|
|
411
344
|
def test_go_to_reference(self) -> None:
|
|
412
345
|
"""Test that the go to reference is correct."""
|
|
@@ -419,8 +352,8 @@ class TestJacLangServer(TestCase):
|
|
|
419
352
|
lsp.deep_check(circle_file)
|
|
420
353
|
test_cases = [
|
|
421
354
|
(47, 12, ["circle.jac:47:8-47:14", "69:8-69:14", "74:8-74:14"]),
|
|
422
|
-
(54, 66, ["54:62-54:76", "65:
|
|
423
|
-
(62, 14, ["65:
|
|
355
|
+
(54, 66, ["54:62-54:76", "65:23-65:37"]),
|
|
356
|
+
(62, 14, ["65:44-65:57", "70:33-70:46"]),
|
|
424
357
|
]
|
|
425
358
|
for line, char, expected_refs in test_cases:
|
|
426
359
|
references = str(lsp.get_references(circle_file, lspt.Position(line, char)))
|
|
@@ -608,3 +541,7 @@ class TestJacLangServer(TestCase):
|
|
|
608
541
|
"/tests/fixtures/M1.jac:0:0-0:0",
|
|
609
542
|
str(lsp.get_definition(guess_game_file, lspt.Position(29, 9))),
|
|
610
543
|
)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
TestJacLangServer().test_completion()
|
jaclang/langserve/utils.jac
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Utility functions for the language server."""
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
import asyncio;
|
|
4
5
|
import builtins;
|
|
5
6
|
import re;
|
|
@@ -112,25 +113,21 @@ def find_deepest_symbol_node_at_pos(
|
|
|
112
113
|
|
|
113
114
|
|
|
114
115
|
"""Check if the position falls within the node's location."""
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
# All lines and columns are 1-Based
|
|
117
|
+
def position_within_node(n: uni.UniNode, line: int, character: int) -> bool {
|
|
118
|
+
if n.loc.first_line < line < n.loc.last_line {
|
|
117
119
|
return True;
|
|
118
120
|
}
|
|
119
|
-
if
|
|
120
|
-
|
|
121
|
-
and <>node.loc.last_line == (line + 1)
|
|
122
|
-
and <>node.loc.col_end >= (character + 1)
|
|
123
|
-
|
|
124
|
-
or <>node.loc.last_line > (line + 1)
|
|
125
|
-
{
|
|
126
|
-
return True;
|
|
121
|
+
if line < n.loc.first_line or line > n.loc.last_line {
|
|
122
|
+
return False;
|
|
127
123
|
}
|
|
128
|
-
if
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
if line == n.loc.first_line and character < n.loc.col_start {
|
|
125
|
+
return False;
|
|
126
|
+
}
|
|
127
|
+
if line == n.loc.last_line and character >= n.loc.col_end {
|
|
128
|
+
return False;
|
|
132
129
|
}
|
|
133
|
-
return
|
|
130
|
+
return True;
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
|
jaclang/lib.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Jac Library - User-friendly interface for library mode."""
|
|
2
|
+
|
|
3
|
+
from jaclang.runtimelib.machine import JacMachineInterface
|
|
4
|
+
|
|
5
|
+
# Automatically expose all public attributes from JacMachineInterface
|
|
6
|
+
# This includes archetype classes (Obj, Node, Edge, Walker, Root, Path) and all methods
|
|
7
|
+
_jac_interface_attrs = {
|
|
8
|
+
name: getattr(JacMachineInterface, name)
|
|
9
|
+
for name in dir(JacMachineInterface)
|
|
10
|
+
if not name.startswith("_")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Add to module globals
|
|
14
|
+
globals().update(_jac_interface_attrs)
|
|
15
|
+
|
|
16
|
+
# Build __all__ with all JacMachineInterface exports
|
|
17
|
+
__all__ = sorted(_jac_interface_attrs.keys())
|
jaclang/runtimelib/archetype.py
CHANGED
|
@@ -69,13 +69,13 @@ class AnchorReport:
|
|
|
69
69
|
context: dict[str, Any]
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
ObjectSpatialFilter: TypeAlias = (
|
|
73
73
|
Callable[["Archetype"], bool] | "Archetype" | list["Archetype"] | None
|
|
74
74
|
)
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
@dataclass(eq=False, repr=False)
|
|
78
|
-
class
|
|
78
|
+
class ObjectSpatialDestination:
|
|
79
79
|
"""Object-Spatial Destination."""
|
|
80
80
|
|
|
81
81
|
direction: EdgeDir
|
|
@@ -92,18 +92,18 @@ class DataSpatialDestination:
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
@dataclass(eq=False, repr=False)
|
|
95
|
-
class
|
|
95
|
+
class ObjectSpatialPath:
|
|
96
96
|
"""Object-Spatial Path."""
|
|
97
97
|
|
|
98
98
|
origin: list[NodeArchetype]
|
|
99
|
-
destinations: list[
|
|
99
|
+
destinations: list[ObjectSpatialDestination]
|
|
100
100
|
edge_only: bool
|
|
101
101
|
from_visit: bool
|
|
102
102
|
|
|
103
103
|
def __init__(
|
|
104
104
|
self,
|
|
105
105
|
origin: NodeArchetype | list[NodeArchetype],
|
|
106
|
-
destinations: list[
|
|
106
|
+
destinations: list[ObjectSpatialDestination] | None = None,
|
|
107
107
|
) -> None:
|
|
108
108
|
"""Override Init."""
|
|
109
109
|
if not isinstance(origin, list):
|
|
@@ -115,7 +115,7 @@ class DataSpatialPath:
|
|
|
115
115
|
|
|
116
116
|
def convert(
|
|
117
117
|
self,
|
|
118
|
-
filter:
|
|
118
|
+
filter: ObjectSpatialFilter,
|
|
119
119
|
) -> Callable[["Archetype"], bool] | None:
|
|
120
120
|
"""Convert filter."""
|
|
121
121
|
if not filter:
|
|
@@ -129,44 +129,44 @@ class DataSpatialPath:
|
|
|
129
129
|
def append(
|
|
130
130
|
self,
|
|
131
131
|
direction: EdgeDir,
|
|
132
|
-
edge:
|
|
133
|
-
node:
|
|
134
|
-
) ->
|
|
132
|
+
edge: ObjectSpatialFilter,
|
|
133
|
+
node: ObjectSpatialFilter,
|
|
134
|
+
) -> ObjectSpatialPath:
|
|
135
135
|
"""Append destination."""
|
|
136
136
|
self.destinations.append(
|
|
137
|
-
|
|
137
|
+
ObjectSpatialDestination(direction, self.convert(edge), self.convert(node))
|
|
138
138
|
)
|
|
139
139
|
return self
|
|
140
140
|
|
|
141
|
-
def
|
|
142
|
-
self, edge:
|
|
143
|
-
) ->
|
|
141
|
+
def edge_out(
|
|
142
|
+
self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
|
|
143
|
+
) -> ObjectSpatialPath:
|
|
144
144
|
"""Override greater than function."""
|
|
145
145
|
return self.append(EdgeDir.OUT, edge, node)
|
|
146
146
|
|
|
147
|
-
def
|
|
148
|
-
self, edge:
|
|
149
|
-
) ->
|
|
147
|
+
def edge_in(
|
|
148
|
+
self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
|
|
149
|
+
) -> ObjectSpatialPath:
|
|
150
150
|
"""Override greater than function."""
|
|
151
151
|
return self.append(EdgeDir.IN, edge, node)
|
|
152
152
|
|
|
153
|
-
def
|
|
154
|
-
self, edge:
|
|
155
|
-
) ->
|
|
153
|
+
def edge_any(
|
|
154
|
+
self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
|
|
155
|
+
) -> ObjectSpatialPath:
|
|
156
156
|
"""Override greater than function."""
|
|
157
157
|
return self.append(EdgeDir.ANY, edge, node)
|
|
158
158
|
|
|
159
|
-
def edge(self) ->
|
|
159
|
+
def edge(self) -> ObjectSpatialPath:
|
|
160
160
|
"""Set edge only."""
|
|
161
161
|
self.edge_only = True
|
|
162
162
|
return self
|
|
163
163
|
|
|
164
|
-
def visit(self) ->
|
|
164
|
+
def visit(self) -> ObjectSpatialPath:
|
|
165
165
|
"""Set from visit."""
|
|
166
166
|
self.from_visit = True
|
|
167
167
|
return self
|
|
168
168
|
|
|
169
|
-
def repr_builder(self, repr: str, dest:
|
|
169
|
+
def repr_builder(self, repr: str, dest: ObjectSpatialDestination, mark: str) -> str:
|
|
170
170
|
"""Repr builder."""
|
|
171
171
|
repr += mark
|
|
172
172
|
repr += f' (edge{" filter" if dest.edge else ""}) '
|
|
@@ -361,8 +361,8 @@ class ObjectAnchor(Anchor):
|
|
|
361
361
|
class Archetype:
|
|
362
362
|
"""Archetype Protocol."""
|
|
363
363
|
|
|
364
|
-
_jac_entry_funcs_: ClassVar[list[
|
|
365
|
-
_jac_exit_funcs_: ClassVar[list[
|
|
364
|
+
_jac_entry_funcs_: ClassVar[list[ObjectSpatialFunction]] = []
|
|
365
|
+
_jac_exit_funcs_: ClassVar[list[ObjectSpatialFunction]] = []
|
|
366
366
|
|
|
367
367
|
@cached_property
|
|
368
368
|
def __jac__(self) -> Anchor:
|
|
@@ -454,7 +454,7 @@ class Root(NodeArchetype):
|
|
|
454
454
|
|
|
455
455
|
|
|
456
456
|
@dataclass(eq=False)
|
|
457
|
-
class
|
|
457
|
+
class ObjectSpatialFunction:
|
|
458
458
|
"""Object-Spatial Function."""
|
|
459
459
|
|
|
460
460
|
name: str
|
jaclang/runtimelib/constructs.py
CHANGED
|
@@ -7,12 +7,12 @@ from .archetype import (
|
|
|
7
7
|
AccessLevel,
|
|
8
8
|
Anchor,
|
|
9
9
|
Archetype,
|
|
10
|
-
DataSpatialFunction,
|
|
11
10
|
EdgeAnchor,
|
|
12
11
|
EdgeArchetype,
|
|
13
12
|
GenericEdge,
|
|
14
13
|
NodeAnchor,
|
|
15
14
|
NodeArchetype,
|
|
15
|
+
ObjectSpatialFunction,
|
|
16
16
|
Root,
|
|
17
17
|
WalkerAnchor,
|
|
18
18
|
WalkerArchetype,
|
|
@@ -32,7 +32,7 @@ __all__ = [
|
|
|
32
32
|
"WalkerArchetype",
|
|
33
33
|
"GenericEdge",
|
|
34
34
|
"Root",
|
|
35
|
-
"
|
|
35
|
+
"ObjectSpatialFunction",
|
|
36
36
|
"Memory",
|
|
37
37
|
"ShelfStorage",
|
|
38
38
|
"JacTestResult",
|