jaclang 0.7.0__py3-none-any.whl → 0.7.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/compiler/absyntree.py +53 -50
- jaclang/compiler/compile.py +21 -0
- jaclang/compiler/passes/main/__init__.py +2 -2
- jaclang/compiler/passes/main/def_impl_match_pass.py +10 -8
- jaclang/compiler/passes/main/def_use_pass.py +14 -7
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +20 -1
- jaclang/compiler/passes/main/import_pass.py +60 -11
- jaclang/compiler/passes/main/pyast_gen_pass.py +65 -4
- jaclang/compiler/passes/main/pyast_load_pass.py +2 -1
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +6 -1
- jaclang/compiler/passes/main/pyout_pass.py +3 -1
- jaclang/compiler/passes/main/schedules.py +4 -3
- jaclang/compiler/passes/main/tests/fixtures/incautoimpl.jac +7 -0
- jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
- jaclang/compiler/passes/main/tests/test_import_pass.py +21 -0
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
- jaclang/compiler/passes/tool/jac_formatter_pass.py +14 -2
- jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
- jaclang/compiler/symtable.py +21 -1
- jaclang/core/aott.py +107 -11
- jaclang/core/construct.py +171 -5
- jaclang/core/llms/anthropic.py +31 -2
- jaclang/core/llms/base.py +3 -3
- jaclang/core/llms/groq.py +4 -1
- jaclang/core/llms/huggingface.py +4 -1
- jaclang/core/llms/ollama.py +4 -1
- jaclang/core/llms/openai.py +6 -2
- jaclang/core/llms/togetherai.py +4 -1
- jaclang/langserve/engine.py +193 -121
- jaclang/langserve/server.py +35 -6
- jaclang/langserve/tests/fixtures/circle.jac +73 -0
- jaclang/langserve/tests/fixtures/circle_err.jac +73 -0
- jaclang/langserve/tests/fixtures/circle_pure.impl.jac +32 -0
- jaclang/langserve/tests/fixtures/circle_pure.jac +34 -0
- jaclang/langserve/tests/fixtures/circle_pure_err.impl.jac +32 -0
- jaclang/langserve/tests/fixtures/circle_pure_err.jac +34 -0
- jaclang/langserve/tests/test_server.py +156 -1
- jaclang/langserve/utils.py +127 -2
- jaclang/plugin/default.py +25 -83
- jaclang/plugin/feature.py +10 -12
- jaclang/plugin/tests/test_features.py +0 -33
- jaclang/settings.py +1 -0
- jaclang/tests/fixtures/byllmissue.jac +3 -0
- jaclang/tests/fixtures/hash_init_check.jac +17 -0
- jaclang/tests/fixtures/math_question.jpg +0 -0
- jaclang/tests/fixtures/nosigself.jac +19 -0
- jaclang/tests/fixtures/type_info.jac +1 -1
- jaclang/tests/fixtures/walker_override.jac +21 -0
- jaclang/tests/fixtures/with_llm_vision.jac +25 -0
- jaclang/tests/test_cli.py +1 -1
- jaclang/tests/test_language.py +61 -11
- jaclang/utils/helpers.py +3 -5
- jaclang/utils/test.py +1 -1
- jaclang/utils/treeprinter.py +19 -2
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/METADATA +3 -2
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/RECORD +60 -48
- jaclang/core/memory.py +0 -48
- jaclang/core/shelve_storage.py +0 -55
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/WHEEL +0 -0
- {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module demonstrates a simple circle class and a function to calculate
|
|
3
|
+
the area of a circle in all of Jac's glory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import:py math;
|
|
7
|
+
# Module-level global var
|
|
8
|
+
|
|
9
|
+
glob RAD = 5;
|
|
10
|
+
|
|
11
|
+
"""Function to calculate the area of a circle."""
|
|
12
|
+
can calculate_area(radius: float) -> float {
|
|
13
|
+
return math.pi * radius * radius;
|
|
14
|
+
}
|
|
15
|
+
#* (This is multiline comments in Jac)
|
|
16
|
+
Above we have the demonstration of a function to calculate the area of a circle.
|
|
17
|
+
Below we have the demonstration of a class to calculate the area of a circle.
|
|
18
|
+
*#
|
|
19
|
+
|
|
20
|
+
"""Enum for shape types"""
|
|
21
|
+
enum ShapeType {
|
|
22
|
+
CIRCLE="Circle",
|
|
23
|
+
UNKNOWN="Unknown"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
"""Base class for a shape."""
|
|
27
|
+
obj Shape {
|
|
28
|
+
has shape_type: ShapeType;
|
|
29
|
+
|
|
30
|
+
"""Abstract method to calculate the area of a shape."""
|
|
31
|
+
can area -> float abs;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
"""Circle class inherits from Shape."""
|
|
35
|
+
obj Circle :Shape: {
|
|
36
|
+
can init(radius: float) {
|
|
37
|
+
super.init(ShapeType.CIRCLE);
|
|
38
|
+
self.radius = radius;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
"""Overridden method to calculate the area of the circle."""
|
|
42
|
+
override can area -> float {
|
|
43
|
+
return math.pi * self.radius * self.radius;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
with entry {
|
|
48
|
+
c = Circle(RAD);
|
|
49
|
+
}
|
|
50
|
+
# Global also works here
|
|
51
|
+
|
|
52
|
+
with entry:__main__ {
|
|
53
|
+
# To run the program functionality
|
|
54
|
+
print(f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}");
|
|
55
|
+
print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}");
|
|
56
|
+
}
|
|
57
|
+
# Unit Tests!
|
|
58
|
+
|
|
59
|
+
glob expected_area = 78.53981633974483;
|
|
60
|
+
|
|
61
|
+
test calc_area {
|
|
62
|
+
check.assertAlmostEqual(calculate_area(RAD), expected_area);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
test circle_area {
|
|
66
|
+
c = Circle(RAD);
|
|
67
|
+
check.assertAlmostEqual(c.area(), expected_area);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test circle_type {
|
|
71
|
+
c = Circle(RAD);
|
|
72
|
+
check.assertEqual(c.shape_type, ShapeType.CIRCLE);
|
|
73
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module demonstrates a simple circle class and a function to calculate
|
|
3
|
+
the area of a circle in all of Jac's glory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import:py math;
|
|
7
|
+
# Module-level global var
|
|
8
|
+
|
|
9
|
+
glob RAD = 5;
|
|
10
|
+
|
|
11
|
+
"""Function to calculate the area of a circle."""
|
|
12
|
+
can calculate_area(radius: float) -> float {
|
|
13
|
+
return math.pi * radius * radius;
|
|
14
|
+
}
|
|
15
|
+
#* (This is multiline comments in Jac)
|
|
16
|
+
Above we have the demonstration of a function to calculate the area of a circle.
|
|
17
|
+
Below we have the demonstration of a class to calculate the area of a circle.
|
|
18
|
+
*#
|
|
19
|
+
|
|
20
|
+
"""Enum for shape types"""
|
|
21
|
+
enum ShapeType {
|
|
22
|
+
CIRCLE="Circle"
|
|
23
|
+
UNKNOWN="Unknown"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
"""Base class for a shape."""
|
|
27
|
+
obj Shape {
|
|
28
|
+
has shape_type: ShapeType;
|
|
29
|
+
|
|
30
|
+
"""Abstract method to calculate the area of a shape."""
|
|
31
|
+
can area -> float abs;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
"""Circle class inherits from Shape."""
|
|
35
|
+
obj Circle :Shape: {
|
|
36
|
+
can init(radius: float) {
|
|
37
|
+
super.init(ShapeType.CIRCLE);
|
|
38
|
+
self.radius = radius;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
"""Overridden method to calculate the area of the circle."""
|
|
42
|
+
override can area -> float {
|
|
43
|
+
return math.pi * self.radius * self.radius;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
with entry {
|
|
48
|
+
c = Circle(RAD);
|
|
49
|
+
}
|
|
50
|
+
# Global also works here
|
|
51
|
+
|
|
52
|
+
with entry:__main__ {
|
|
53
|
+
# To run the program functionality
|
|
54
|
+
print(f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}");
|
|
55
|
+
print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}");
|
|
56
|
+
}
|
|
57
|
+
# Unit Tests!
|
|
58
|
+
|
|
59
|
+
glob expected_area = 78.53981633974483;
|
|
60
|
+
|
|
61
|
+
test calc_area {
|
|
62
|
+
check.assertAlmostEqual(calculate_area(RAD), expected_area);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
test circle_area {
|
|
66
|
+
c = Circle(RAD);
|
|
67
|
+
check.assertAlmostEqual(c.area(), expected_area);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test circle_type {
|
|
71
|
+
c = Circle(RAD);
|
|
72
|
+
check.assertEqual(c.shape_type, ShapeType.CIRCLE);
|
|
73
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Enum for shape types"""
|
|
2
|
+
|
|
3
|
+
:enum:ShapeType {
|
|
4
|
+
CIRCLE = "Circle",
|
|
5
|
+
UNKNOWN = "Unknown"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
"""Function to calculate the area of a circle."""
|
|
9
|
+
:can:calculate_area
|
|
10
|
+
(radius: float) -> float {
|
|
11
|
+
return math.pi * radius * radius;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:obj:Circle:can:init
|
|
15
|
+
(radius: float) {
|
|
16
|
+
self.radius = radius;
|
|
17
|
+
super.init(ShapeType.CIRCLE);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
"""Overridden method to calculate the area of the circle."""
|
|
21
|
+
:obj:Circle:can:area -> float {
|
|
22
|
+
return math.pi * self.radius * self.radius;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:can:main_run {
|
|
26
|
+
print(
|
|
27
|
+
f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}"
|
|
28
|
+
);
|
|
29
|
+
print(
|
|
30
|
+
f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module demonstrates a simple circle class and a function to calculate
|
|
3
|
+
the area of a circle in all of Jac's glory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import:py math;
|
|
7
|
+
|
|
8
|
+
enum ShapeType;
|
|
9
|
+
|
|
10
|
+
can calculate_area(radius: float) -> float;
|
|
11
|
+
can main_run;
|
|
12
|
+
|
|
13
|
+
"""Base class for a shape."""
|
|
14
|
+
obj : priv Shape {
|
|
15
|
+
has shape_type: ShapeType;
|
|
16
|
+
|
|
17
|
+
can area -> float abs;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
"""Circle class inherits from Shape."""
|
|
21
|
+
obj Circle :Shape: {
|
|
22
|
+
has radius: float;
|
|
23
|
+
|
|
24
|
+
can init(radius: float);
|
|
25
|
+
override can area -> float;
|
|
26
|
+
}
|
|
27
|
+
# Radius of the demo circle
|
|
28
|
+
|
|
29
|
+
glob RAD = 5, c = Circle(radius=RAD);
|
|
30
|
+
|
|
31
|
+
"""Here we run the main program."""
|
|
32
|
+
with entry:__main__ {
|
|
33
|
+
main_run();
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Enum for shape types"""
|
|
2
|
+
|
|
3
|
+
:enum:ShapeType {
|
|
4
|
+
CIRCLE = "Circle",
|
|
5
|
+
UNKNOWN = "Unknown"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
"""Function to calculate the area of a circle."""
|
|
9
|
+
:can:calculate_area
|
|
10
|
+
(radius: float) -> float {
|
|
11
|
+
return math.pi * radius * radius;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:obj:Circle:can:init
|
|
15
|
+
(radius: float) {
|
|
16
|
+
self.radius = radius;
|
|
17
|
+
super.init(ShapeType.CIRCLE);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
"""Overridden method to calculate the area of the circle."""
|
|
21
|
+
:obj:Circle:can:area -> float {
|
|
22
|
+
return math.pi * self.radius * self.radius;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:can:main_run {
|
|
26
|
+
print(
|
|
27
|
+
f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}"
|
|
28
|
+
);
|
|
29
|
+
print(
|
|
30
|
+
f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module demonstrates a simple circle class and a function to calculate
|
|
3
|
+
the area of a circle in all of Jac's glory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import:py math;
|
|
7
|
+
|
|
8
|
+
enum ShapeType;
|
|
9
|
+
|
|
10
|
+
can calculate_area(radius: float) -> float;
|
|
11
|
+
can main_run;
|
|
12
|
+
|
|
13
|
+
"""Base class for a shape."""
|
|
14
|
+
obj Shape {
|
|
15
|
+
has shape_type: ShapeType;
|
|
16
|
+
|
|
17
|
+
can area -> float abs;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
"""Circle class inherits from Shape."""
|
|
21
|
+
obj Circle :Shape: {
|
|
22
|
+
has radius: float;
|
|
23
|
+
|
|
24
|
+
can init(radius: float);
|
|
25
|
+
can area -> float;
|
|
26
|
+
}
|
|
27
|
+
# Radius of the demo circle
|
|
28
|
+
|
|
29
|
+
glob RAD = 5, c = Circle(radius=RAD);
|
|
30
|
+
|
|
31
|
+
"""Here we run the main program."""
|
|
32
|
+
with entry:__main__ {
|
|
33
|
+
main_run();
|
|
34
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from jaclang.utils.test import TestCase
|
|
2
2
|
from jaclang.vendor.pygls import uris
|
|
3
|
+
from jaclang.vendor.pygls.workspace import Workspace
|
|
4
|
+
from jaclang.langserve.engine import JacLangServer
|
|
3
5
|
from .session import LspSession
|
|
4
6
|
|
|
7
|
+
import lsprotocol.types as lspt
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class TestJacLangServer(TestCase):
|
|
7
11
|
|
|
@@ -28,9 +32,160 @@ class TestJacLangServer(TestCase):
|
|
|
28
32
|
{
|
|
29
33
|
"range": {
|
|
30
34
|
"start": {"line": 0, "character": 0},
|
|
31
|
-
"end": {"line":
|
|
35
|
+
"end": {"line": 2, "character": 0},
|
|
32
36
|
},
|
|
33
37
|
"newText": 'with entry {\n print("Hello, World!");\n}\n',
|
|
34
38
|
}
|
|
35
39
|
],
|
|
36
40
|
)
|
|
41
|
+
|
|
42
|
+
def test_syntax_diagnostics(self) -> None:
|
|
43
|
+
"""Test diagnostics."""
|
|
44
|
+
lsp = JacLangServer()
|
|
45
|
+
# Set up the workspace path to "fixtures/"
|
|
46
|
+
workspace_path = self.fixture_abs_path("")
|
|
47
|
+
workspace = Workspace(workspace_path, lsp)
|
|
48
|
+
lsp.lsp._workspace = workspace
|
|
49
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_err.jac"))
|
|
50
|
+
lsp.quick_check(circle_file)
|
|
51
|
+
self.assertEqual(len(lsp.modules), 1)
|
|
52
|
+
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
53
|
+
|
|
54
|
+
def test_doesnt_run_if_syntax_error(self) -> None:
|
|
55
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
56
|
+
lsp = JacLangServer()
|
|
57
|
+
# Set up the workspace path to "fixtures/"
|
|
58
|
+
workspace_path = self.fixture_abs_path("")
|
|
59
|
+
workspace = Workspace(workspace_path, lsp)
|
|
60
|
+
lsp.lsp._workspace = workspace
|
|
61
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_err.jac"))
|
|
62
|
+
lsp.quick_check(circle_file)
|
|
63
|
+
self.assertEqual(len(lsp.modules), 1)
|
|
64
|
+
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
65
|
+
lsp.deep_check(circle_file)
|
|
66
|
+
self.assertEqual(len(lsp.modules), 1)
|
|
67
|
+
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
68
|
+
lsp.type_check(circle_file)
|
|
69
|
+
self.assertEqual(len(lsp.modules), 1)
|
|
70
|
+
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
71
|
+
|
|
72
|
+
def test_impl_stay_connected(self) -> None:
|
|
73
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
74
|
+
lsp = JacLangServer()
|
|
75
|
+
# Set up the workspace path to "fixtures/"
|
|
76
|
+
workspace_path = self.fixture_abs_path("")
|
|
77
|
+
workspace = Workspace(workspace_path, lsp)
|
|
78
|
+
lsp.lsp._workspace = workspace
|
|
79
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
80
|
+
circle_impl_file = uris.from_fs_path(
|
|
81
|
+
self.fixture_abs_path("circle_pure.impl.jac")
|
|
82
|
+
)
|
|
83
|
+
lsp.quick_check(circle_file)
|
|
84
|
+
lsp.deep_check(circle_file)
|
|
85
|
+
lsp.type_check(circle_file)
|
|
86
|
+
pos = lspt.Position(20, 8)
|
|
87
|
+
self.assertIn(
|
|
88
|
+
"Circle class inherits from Shape.",
|
|
89
|
+
lsp.get_hover_info(circle_file, pos).contents.value,
|
|
90
|
+
)
|
|
91
|
+
lsp.type_check(circle_impl_file, force=True)
|
|
92
|
+
pos = lspt.Position(8, 11)
|
|
93
|
+
self.assertIn(
|
|
94
|
+
"ability) calculate_area: float",
|
|
95
|
+
lsp.get_hover_info(circle_impl_file, pos).contents.value,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def test_impl_auto_discover(self) -> None:
|
|
99
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
100
|
+
lsp = JacLangServer()
|
|
101
|
+
# Set up the workspace path to "fixtures/"
|
|
102
|
+
workspace_path = self.fixture_abs_path("")
|
|
103
|
+
workspace = Workspace(workspace_path, lsp)
|
|
104
|
+
lsp.lsp._workspace = workspace
|
|
105
|
+
circle_impl_file = uris.from_fs_path(
|
|
106
|
+
self.fixture_abs_path("circle_pure.impl.jac")
|
|
107
|
+
)
|
|
108
|
+
lsp.quick_check(circle_impl_file, force=True)
|
|
109
|
+
lsp.deep_check(circle_impl_file, force=True)
|
|
110
|
+
lsp.type_check(circle_impl_file, force=True)
|
|
111
|
+
pos = lspt.Position(8, 11)
|
|
112
|
+
self.assertIn(
|
|
113
|
+
"ability) calculate_area: float",
|
|
114
|
+
lsp.get_hover_info(circle_impl_file, pos).contents.value,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def test_show_type_impl(self) -> None:
|
|
118
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
119
|
+
lsp = JacLangServer()
|
|
120
|
+
# Set up the workspace path to "fixtures/"
|
|
121
|
+
workspace_path = self.fixture_abs_path("")
|
|
122
|
+
workspace = Workspace(workspace_path, lsp)
|
|
123
|
+
lsp.lsp._workspace = workspace
|
|
124
|
+
target = uris.from_fs_path(
|
|
125
|
+
self.fixture_abs_path("../../../../examples/guess_game/guess_game4.jac")
|
|
126
|
+
)
|
|
127
|
+
lsp.quick_check(target)
|
|
128
|
+
lsp.deep_check(target)
|
|
129
|
+
lsp.type_check(target)
|
|
130
|
+
pos = lspt.Position(43, 18)
|
|
131
|
+
self.assertIn(
|
|
132
|
+
"attempts: int",
|
|
133
|
+
lsp.get_hover_info(target, pos).contents.value,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def test_outline_symbols(self) -> None:
|
|
137
|
+
"""Test that the outline symbols are correct."""
|
|
138
|
+
lsp = JacLangServer()
|
|
139
|
+
workspace_path = self.fixture_abs_path("")
|
|
140
|
+
workspace = Workspace(workspace_path, lsp)
|
|
141
|
+
lsp.lsp._workspace = workspace
|
|
142
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
143
|
+
lsp.quick_check(circle_file)
|
|
144
|
+
lsp.deep_check(circle_file)
|
|
145
|
+
lsp.type_check(circle_file)
|
|
146
|
+
expected_string = (
|
|
147
|
+
"DocumentSymbol(name='calculate_area', kind=<SymbolKind.Function: 12>, range=9:0-9:43, "
|
|
148
|
+
"selection_range=9:0-9:43, detail=None, tags=None, deprecated=None, children=["
|
|
149
|
+
"DocumentSymbol(name='radius', kind=<SymbolKind.Variable: 13>, range=9:1-9:14, "
|
|
150
|
+
"selection_range=9:1-9:14, detail=None, tags=None, deprecated=None, children=[])])"
|
|
151
|
+
)
|
|
152
|
+
self.assertEqual(
|
|
153
|
+
expected_string, str((lsp.get_document_symbols(circle_file))[6])
|
|
154
|
+
)
|
|
155
|
+
self.assertEqual(10, len(lsp.get_document_symbols(circle_file)))
|
|
156
|
+
|
|
157
|
+
def test_go_to_definition(self) -> None:
|
|
158
|
+
"""Test that the go to definition is correct."""
|
|
159
|
+
lsp = JacLangServer()
|
|
160
|
+
workspace_path = self.fixture_abs_path("")
|
|
161
|
+
workspace = Workspace(workspace_path, lsp)
|
|
162
|
+
lsp.lsp._workspace = workspace
|
|
163
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
164
|
+
lsp.quick_check(circle_file)
|
|
165
|
+
lsp.deep_check(circle_file)
|
|
166
|
+
lsp.type_check(circle_file)
|
|
167
|
+
self.assertIn(
|
|
168
|
+
"fixtures/circle_pure.impl.jac:8:0-8:19",
|
|
169
|
+
str(lsp.get_definition(circle_file, lspt.Position(9, 16))),
|
|
170
|
+
)
|
|
171
|
+
self.assertIn(
|
|
172
|
+
"fixtures/circle_pure.jac:12:0-17:1",
|
|
173
|
+
str(lsp.get_definition(circle_file, lspt.Position(20, 17))),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def test_go_to_definition_method(self) -> None:
|
|
177
|
+
"""Test that the go to definition is correct."""
|
|
178
|
+
lsp = JacLangServer()
|
|
179
|
+
workspace_path = self.fixture_abs_path("")
|
|
180
|
+
workspace = Workspace(workspace_path, lsp)
|
|
181
|
+
lsp.lsp._workspace = workspace
|
|
182
|
+
guess_game_file = uris.from_fs_path(
|
|
183
|
+
self.fixture_abs_path("../../../../examples/guess_game/guess_game4.jac")
|
|
184
|
+
)
|
|
185
|
+
lsp.quick_check(guess_game_file)
|
|
186
|
+
lsp.deep_check(guess_game_file)
|
|
187
|
+
lsp.type_check(guess_game_file)
|
|
188
|
+
self.assertIn(
|
|
189
|
+
"guess_game4.jac:27:4-27:34",
|
|
190
|
+
str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
|
|
191
|
+
)
|
jaclang/langserve/utils.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"""Utility functions for the language server."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import builtins
|
|
4
5
|
from functools import wraps
|
|
5
|
-
from typing import Any, Awaitable, Callable, Coroutine, ParamSpec, TypeVar
|
|
6
|
+
from typing import Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar
|
|
6
7
|
|
|
7
8
|
import jaclang.compiler.absyntree as ast
|
|
8
|
-
from jaclang.compiler.
|
|
9
|
+
from jaclang.compiler.codeloc import CodeLocInfo
|
|
10
|
+
from jaclang.compiler.symtable import Symbol, SymbolTable
|
|
9
11
|
|
|
12
|
+
import lsprotocol.types as lspt
|
|
10
13
|
|
|
11
14
|
T = TypeVar("T", bound=Callable[..., Coroutine[Any, Any, Any]])
|
|
12
15
|
P = ParamSpec("P")
|
|
@@ -53,3 +56,125 @@ def sym_tab_list(sym_tab: SymbolTable, file_path: str) -> list[SymbolTable]:
|
|
|
53
56
|
for i in sym_tab.kid:
|
|
54
57
|
sym_tabs += sym_tab_list(i, file_path=file_path)
|
|
55
58
|
return sym_tabs
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def find_deepest_symbol_node_at_pos(
|
|
62
|
+
node: ast.AstNode, line: int, character: int
|
|
63
|
+
) -> Optional[ast.AstSymbolNode]:
|
|
64
|
+
"""Return the deepest symbol node that contains the given position."""
|
|
65
|
+
last_symbol_node = None
|
|
66
|
+
|
|
67
|
+
if position_within_node(node, line, character):
|
|
68
|
+
if isinstance(node, ast.AstSymbolNode):
|
|
69
|
+
last_symbol_node = node
|
|
70
|
+
|
|
71
|
+
for child in node.kid:
|
|
72
|
+
if position_within_node(child, line, character):
|
|
73
|
+
deeper_node = find_deepest_symbol_node_at_pos(child, line, character)
|
|
74
|
+
if deeper_node is not None:
|
|
75
|
+
last_symbol_node = deeper_node
|
|
76
|
+
|
|
77
|
+
return last_symbol_node
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def position_within_node(node: ast.AstNode, line: int, character: int) -> bool:
|
|
81
|
+
"""Check if the position falls within the node's location."""
|
|
82
|
+
if node.loc.first_line < line + 1 < node.loc.last_line:
|
|
83
|
+
return True
|
|
84
|
+
if (
|
|
85
|
+
node.loc.first_line == line + 1
|
|
86
|
+
and node.loc.col_start <= character + 1
|
|
87
|
+
and (
|
|
88
|
+
node.loc.last_line == line + 1
|
|
89
|
+
and node.loc.col_end >= character + 1
|
|
90
|
+
or node.loc.last_line > line + 1
|
|
91
|
+
)
|
|
92
|
+
):
|
|
93
|
+
return True
|
|
94
|
+
if (
|
|
95
|
+
node.loc.last_line == line + 1
|
|
96
|
+
and node.loc.col_start <= character + 1 <= node.loc.col_end
|
|
97
|
+
):
|
|
98
|
+
return True
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
|
|
103
|
+
"""Recursively collect symbols from the AST."""
|
|
104
|
+
symbols = []
|
|
105
|
+
if node is None:
|
|
106
|
+
return symbols
|
|
107
|
+
|
|
108
|
+
for key, item in node.tab.items():
|
|
109
|
+
if key in dir(builtins):
|
|
110
|
+
continue
|
|
111
|
+
if item in [owner_sym(tab) for tab in node.kid]:
|
|
112
|
+
continue
|
|
113
|
+
else:
|
|
114
|
+
|
|
115
|
+
pos = create_range(item.defn[0].loc)
|
|
116
|
+
symbol = lspt.DocumentSymbol(
|
|
117
|
+
name=key,
|
|
118
|
+
kind=kind_map(item.defn[0]),
|
|
119
|
+
range=pos,
|
|
120
|
+
selection_range=pos,
|
|
121
|
+
children=[],
|
|
122
|
+
)
|
|
123
|
+
symbols.append(symbol)
|
|
124
|
+
|
|
125
|
+
for sub_tab in node.kid:
|
|
126
|
+
sub_symbols = collect_symbols(sub_tab)
|
|
127
|
+
|
|
128
|
+
if isinstance(
|
|
129
|
+
sub_tab.owner,
|
|
130
|
+
(ast.IfStmt, ast.ElseStmt, ast.WhileStmt, ast.IterForStmt, ast.InForStmt),
|
|
131
|
+
):
|
|
132
|
+
symbols.extend(sub_symbols)
|
|
133
|
+
else:
|
|
134
|
+
sub_pos = create_range(sub_tab.owner.loc)
|
|
135
|
+
symbol = lspt.DocumentSymbol(
|
|
136
|
+
name=sub_tab.name,
|
|
137
|
+
kind=kind_map(sub_tab.owner),
|
|
138
|
+
range=sub_pos,
|
|
139
|
+
selection_range=sub_pos,
|
|
140
|
+
children=sub_symbols,
|
|
141
|
+
)
|
|
142
|
+
symbols.append(symbol)
|
|
143
|
+
|
|
144
|
+
return symbols
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def owner_sym(table: SymbolTable) -> Optional[Symbol]:
|
|
148
|
+
"""Get owner sym."""
|
|
149
|
+
if table.has_parent() and isinstance(table.owner, ast.AstSymbolNode):
|
|
150
|
+
return table.parent.lookup(table.owner.sym_name)
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def create_range(loc: CodeLocInfo) -> lspt.Range:
|
|
155
|
+
"""Create an lspt.Range from a location object."""
|
|
156
|
+
return lspt.Range(
|
|
157
|
+
start=lspt.Position(line=loc.first_line - 1, character=loc.col_start - 1),
|
|
158
|
+
end=lspt.Position(line=loc.last_line - 1, character=loc.col_end - 1),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def kind_map(sub_tab: ast.AstNode) -> lspt.SymbolKind:
|
|
163
|
+
"""Map the symbol node to an lspt.SymbolKind."""
|
|
164
|
+
return (
|
|
165
|
+
lspt.SymbolKind.Function
|
|
166
|
+
if isinstance(sub_tab, (ast.Ability, ast.AbilityDef))
|
|
167
|
+
else (
|
|
168
|
+
lspt.SymbolKind.Class
|
|
169
|
+
if isinstance(sub_tab, (ast.Architype, ast.ArchDef))
|
|
170
|
+
else (
|
|
171
|
+
lspt.SymbolKind.Module
|
|
172
|
+
if isinstance(sub_tab, ast.Module)
|
|
173
|
+
else (
|
|
174
|
+
lspt.SymbolKind.Enum
|
|
175
|
+
if isinstance(sub_tab, (ast.Enum, ast.EnumDef))
|
|
176
|
+
else lspt.SymbolKind.Variable
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
)
|