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.

Files changed (62) hide show
  1. jaclang/compiler/absyntree.py +53 -50
  2. jaclang/compiler/compile.py +21 -0
  3. jaclang/compiler/passes/main/__init__.py +2 -2
  4. jaclang/compiler/passes/main/def_impl_match_pass.py +10 -8
  5. jaclang/compiler/passes/main/def_use_pass.py +14 -7
  6. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +20 -1
  7. jaclang/compiler/passes/main/import_pass.py +60 -11
  8. jaclang/compiler/passes/main/pyast_gen_pass.py +65 -4
  9. jaclang/compiler/passes/main/pyast_load_pass.py +2 -1
  10. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +6 -1
  11. jaclang/compiler/passes/main/pyout_pass.py +3 -1
  12. jaclang/compiler/passes/main/schedules.py +4 -3
  13. jaclang/compiler/passes/main/tests/fixtures/incautoimpl.jac +7 -0
  14. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
  15. jaclang/compiler/passes/main/tests/test_import_pass.py +21 -0
  16. jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
  17. jaclang/compiler/passes/tool/jac_formatter_pass.py +14 -2
  18. jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
  19. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
  20. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
  21. jaclang/compiler/symtable.py +21 -1
  22. jaclang/core/aott.py +107 -11
  23. jaclang/core/construct.py +171 -5
  24. jaclang/core/llms/anthropic.py +31 -2
  25. jaclang/core/llms/base.py +3 -3
  26. jaclang/core/llms/groq.py +4 -1
  27. jaclang/core/llms/huggingface.py +4 -1
  28. jaclang/core/llms/ollama.py +4 -1
  29. jaclang/core/llms/openai.py +6 -2
  30. jaclang/core/llms/togetherai.py +4 -1
  31. jaclang/langserve/engine.py +193 -121
  32. jaclang/langserve/server.py +35 -6
  33. jaclang/langserve/tests/fixtures/circle.jac +73 -0
  34. jaclang/langserve/tests/fixtures/circle_err.jac +73 -0
  35. jaclang/langserve/tests/fixtures/circle_pure.impl.jac +32 -0
  36. jaclang/langserve/tests/fixtures/circle_pure.jac +34 -0
  37. jaclang/langserve/tests/fixtures/circle_pure_err.impl.jac +32 -0
  38. jaclang/langserve/tests/fixtures/circle_pure_err.jac +34 -0
  39. jaclang/langserve/tests/test_server.py +156 -1
  40. jaclang/langserve/utils.py +127 -2
  41. jaclang/plugin/default.py +25 -83
  42. jaclang/plugin/feature.py +10 -12
  43. jaclang/plugin/tests/test_features.py +0 -33
  44. jaclang/settings.py +1 -0
  45. jaclang/tests/fixtures/byllmissue.jac +3 -0
  46. jaclang/tests/fixtures/hash_init_check.jac +17 -0
  47. jaclang/tests/fixtures/math_question.jpg +0 -0
  48. jaclang/tests/fixtures/nosigself.jac +19 -0
  49. jaclang/tests/fixtures/type_info.jac +1 -1
  50. jaclang/tests/fixtures/walker_override.jac +21 -0
  51. jaclang/tests/fixtures/with_llm_vision.jac +25 -0
  52. jaclang/tests/test_cli.py +1 -1
  53. jaclang/tests/test_language.py +61 -11
  54. jaclang/utils/helpers.py +3 -5
  55. jaclang/utils/test.py +1 -1
  56. jaclang/utils/treeprinter.py +19 -2
  57. {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/METADATA +3 -2
  58. {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/RECORD +60 -48
  59. jaclang/core/memory.py +0 -48
  60. jaclang/core/shelve_storage.py +0 -55
  61. {jaclang-0.7.0.dist-info → jaclang-0.7.2.dist-info}/WHEEL +0 -0
  62. {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": 4, "character": 0},
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
+ )
@@ -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.symtable import SymbolTable
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
+ )