jaclang 0.7.2__py3-none-any.whl → 0.7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (92) hide show
  1. jaclang/cli/cli.py +2 -2
  2. jaclang/compiler/absyntree.py +499 -294
  3. jaclang/compiler/codeloc.py +2 -2
  4. jaclang/compiler/constant.py +100 -2
  5. jaclang/compiler/jac.lark +27 -19
  6. jaclang/compiler/parser.py +119 -92
  7. jaclang/compiler/passes/main/access_modifier_pass.py +20 -12
  8. jaclang/compiler/passes/main/def_impl_match_pass.py +28 -14
  9. jaclang/compiler/passes/main/def_use_pass.py +59 -40
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +65 -43
  11. jaclang/compiler/passes/main/import_pass.py +8 -6
  12. jaclang/compiler/passes/main/pyast_gen_pass.py +97 -42
  13. jaclang/compiler/passes/main/pyast_load_pass.py +47 -12
  14. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +19 -10
  15. jaclang/compiler/passes/main/registry_pass.py +6 -6
  16. jaclang/compiler/passes/main/sub_node_tab_pass.py +0 -5
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +43 -235
  18. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +21 -4
  19. jaclang/compiler/passes/main/tests/test_def_use_pass.py +5 -10
  20. jaclang/compiler/passes/main/type_check_pass.py +2 -1
  21. jaclang/compiler/passes/tool/jac_formatter_pass.py +30 -9
  22. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +16 -0
  23. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +16 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/genai/essay_review.jac +1 -1
  25. jaclang/compiler/passes/tool/tests/fixtures/genai/expert_answer.jac +1 -1
  26. jaclang/compiler/passes/tool/tests/fixtures/genai/joke_gen.jac +1 -1
  27. jaclang/compiler/passes/tool/tests/fixtures/genai/odd_word_out.jac +1 -1
  28. jaclang/compiler/passes/tool/tests/fixtures/genai/personality_finder.jac +1 -1
  29. jaclang/compiler/passes/tool/tests/fixtures/genai/text_to_type.jac +1 -1
  30. jaclang/compiler/passes/tool/tests/fixtures/genai/translator.jac +1 -1
  31. jaclang/compiler/passes/tool/tests/fixtures/genai/wikipedia.jac +1 -1
  32. jaclang/compiler/passes/transform.py +2 -4
  33. jaclang/{core/registry.py → compiler/semtable.py} +1 -3
  34. jaclang/compiler/symtable.py +142 -101
  35. jaclang/compiler/tests/test_parser.py +2 -2
  36. jaclang/core/aott.py +15 -11
  37. jaclang/core/{construct.py → architype.py} +25 -240
  38. jaclang/core/constructs.py +44 -0
  39. jaclang/core/context.py +157 -0
  40. jaclang/core/importer.py +18 -9
  41. jaclang/core/memory.py +99 -0
  42. jaclang/core/test.py +90 -0
  43. jaclang/core/utils.py +2 -2
  44. jaclang/langserve/engine.py +127 -50
  45. jaclang/langserve/server.py +34 -61
  46. jaclang/langserve/tests/fixtures/base_module_structure.jac +28 -0
  47. jaclang/langserve/tests/fixtures/circle.jac +16 -12
  48. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  49. jaclang/langserve/tests/fixtures/circle_pure.test.jac +15 -0
  50. jaclang/langserve/tests/fixtures/import_include_statements.jac +6 -0
  51. jaclang/langserve/tests/fixtures/py_import.py +26 -0
  52. jaclang/langserve/tests/test_server.py +93 -18
  53. jaclang/langserve/utils.py +124 -10
  54. jaclang/plugin/builtin.py +1 -1
  55. jaclang/plugin/default.py +23 -9
  56. jaclang/plugin/feature.py +25 -7
  57. jaclang/plugin/spec.py +18 -20
  58. jaclang/settings.py +3 -0
  59. jaclang/tests/fixtures/abc.jac +16 -12
  60. jaclang/tests/fixtures/aott_raise.jac +1 -1
  61. jaclang/tests/fixtures/byllmissue.jac +9 -0
  62. jaclang/tests/fixtures/edgetypeissue.jac +10 -0
  63. jaclang/tests/fixtures/hello.jac +1 -1
  64. jaclang/tests/fixtures/impl_match_confused.impl.jac +1 -0
  65. jaclang/tests/fixtures/impl_match_confused.jac +5 -0
  66. jaclang/tests/fixtures/maxfail_run_test.jac +17 -5
  67. jaclang/tests/fixtures/run_test.jac +17 -5
  68. jaclang/tests/fixtures/with_llm_function.jac +1 -1
  69. jaclang/tests/fixtures/with_llm_lower.jac +1 -1
  70. jaclang/tests/fixtures/with_llm_method.jac +1 -1
  71. jaclang/tests/fixtures/with_llm_type.jac +1 -1
  72. jaclang/tests/fixtures/with_llm_vision.jac +1 -1
  73. jaclang/tests/test_bugs.py +19 -0
  74. jaclang/tests/test_cli.py +1 -1
  75. jaclang/tests/test_language.py +161 -96
  76. jaclang/tests/test_reference.py +1 -1
  77. jaclang/utils/lang_tools.py +5 -4
  78. jaclang/utils/test.py +2 -1
  79. jaclang/utils/treeprinter.py +22 -8
  80. {jaclang-0.7.2.dist-info → jaclang-0.7.7.dist-info}/METADATA +1 -1
  81. {jaclang-0.7.2.dist-info → jaclang-0.7.7.dist-info}/RECORD +83 -80
  82. jaclang/core/llms/__init__.py +0 -20
  83. jaclang/core/llms/anthropic.py +0 -90
  84. jaclang/core/llms/base.py +0 -206
  85. jaclang/core/llms/groq.py +0 -70
  86. jaclang/core/llms/huggingface.py +0 -76
  87. jaclang/core/llms/ollama.py +0 -81
  88. jaclang/core/llms/openai.py +0 -65
  89. jaclang/core/llms/togetherai.py +0 -63
  90. jaclang/core/llms/utils.py +0 -9
  91. {jaclang-0.7.2.dist-info → jaclang-0.7.7.dist-info}/WHEEL +0 -0
  92. {jaclang-0.7.2.dist-info → jaclang-0.7.7.dist-info}/entry_points.txt +0 -0
jaclang/core/test.py ADDED
@@ -0,0 +1,90 @@
1
+ """Core constructs for Jac Language."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import unittest
6
+ from typing import Callable, Optional
7
+
8
+
9
+ class JacTestResult(unittest.TextTestResult):
10
+ """Jac test result class."""
11
+
12
+ def __init__(
13
+ self,
14
+ stream, # noqa
15
+ descriptions, # noqa
16
+ verbosity: int,
17
+ max_failures: Optional[int] = None,
18
+ ) -> None:
19
+ """Initialize FailFastTestResult object."""
20
+ super().__init__(stream, descriptions, verbosity) # noqa
21
+ self.failures_count = JacTestCheck.failcount
22
+ self.max_failures = max_failures
23
+
24
+ def addFailure(self, test, err) -> None: # noqa
25
+ """Count failures and stop."""
26
+ super().addFailure(test, err)
27
+ self.failures_count += 1
28
+ if self.max_failures is not None and self.failures_count >= self.max_failures:
29
+ self.stop()
30
+
31
+ def stop(self) -> None:
32
+ """Stop the test execution."""
33
+ self.shouldStop = True
34
+
35
+
36
+ class JacTextTestRunner(unittest.TextTestRunner):
37
+ """Jac test runner class."""
38
+
39
+ def __init__(self, max_failures: Optional[int] = None, **kwargs) -> None: # noqa
40
+ """Initialize JacTextTestRunner object."""
41
+ self.max_failures = max_failures
42
+ super().__init__(**kwargs)
43
+
44
+ def _makeResult(self) -> JacTestResult: # noqa
45
+ """Override the method to return an instance of JacTestResult."""
46
+ return JacTestResult(
47
+ self.stream,
48
+ self.descriptions,
49
+ self.verbosity,
50
+ max_failures=self.max_failures,
51
+ )
52
+
53
+
54
+ class JacTestCheck:
55
+ """Jac Testing and Checking."""
56
+
57
+ test_case = unittest.TestCase()
58
+ test_suite = unittest.TestSuite()
59
+ breaker = False
60
+ failcount = 0
61
+
62
+ @staticmethod
63
+ def reset() -> None:
64
+ """Clear the test suite."""
65
+ JacTestCheck.test_case = unittest.TestCase()
66
+ JacTestCheck.test_suite = unittest.TestSuite()
67
+
68
+ @staticmethod
69
+ def run_test(xit: bool, maxfail: int | None, verbose: bool) -> None:
70
+ """Run the test suite."""
71
+ verb = 2 if verbose else 1
72
+ runner = JacTextTestRunner(max_failures=maxfail, failfast=xit, verbosity=verb)
73
+ result = runner.run(JacTestCheck.test_suite)
74
+ if result.wasSuccessful():
75
+ print("Passed successfully.")
76
+ else:
77
+ fails = len(result.failures)
78
+ JacTestCheck.failcount += fails
79
+ JacTestCheck.breaker = (
80
+ (JacTestCheck.failcount >= maxfail) if maxfail else True
81
+ )
82
+
83
+ @staticmethod
84
+ def add_test(test_fun: Callable) -> None:
85
+ """Create a new test."""
86
+ JacTestCheck.test_suite.addTest(unittest.FunctionTestCase(test_fun))
87
+
88
+ def __getattr__(self, name: str) -> object:
89
+ """Make convenient check.Equal(...) etc."""
90
+ return getattr(JacTestCheck.test_case, name)
jaclang/core/utils.py CHANGED
@@ -8,10 +8,10 @@ from contextlib import contextmanager
8
8
  from typing import Callable, Iterator, TYPE_CHECKING
9
9
 
10
10
  import jaclang.compiler.absyntree as ast
11
- from jaclang.core.registry import SemScope
11
+ from jaclang.compiler.semtable import SemScope
12
12
 
13
13
  if TYPE_CHECKING:
14
- from jaclang.core.construct import NodeAnchor, NodeArchitype
14
+ from jaclang.core.constructs import NodeAnchor, NodeArchitype
15
15
 
16
16
 
17
17
  @contextmanager
@@ -4,8 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  from enum import IntEnum
7
- from hashlib import md5
8
- from typing import Optional, Sequence
7
+ from typing import Optional
9
8
 
10
9
 
11
10
  import jaclang.compiler.absyntree as ast
@@ -19,6 +18,8 @@ from jaclang.langserve.utils import (
19
18
  collect_symbols,
20
19
  create_range,
21
20
  find_deepest_symbol_node_at_pos,
21
+ get_item_path,
22
+ get_mod_path,
22
23
  )
23
24
  from jaclang.vendor.pygls import uris
24
25
  from jaclang.vendor.pygls.server import LanguageServer
@@ -27,7 +28,7 @@ import lsprotocol.types as lspt
27
28
 
28
29
 
29
30
  class ALev(IntEnum):
30
- """Analysis Level."""
31
+ """Analysis Level successfully completed."""
31
32
 
32
33
  QUICK = 1
33
34
  DEEP = 2
@@ -40,8 +41,8 @@ class ModuleInfo:
40
41
  def __init__(
41
42
  self,
42
43
  ir: ast.Module,
43
- errors: Sequence[Alert],
44
- warnings: Sequence[Alert],
44
+ errors: list[Alert],
45
+ warnings: list[Alert],
45
46
  alev: ALev,
46
47
  parent: Optional[ModuleInfo] = None,
47
48
  ) -> None:
@@ -52,6 +53,7 @@ class ModuleInfo:
52
53
  self.alev = alev
53
54
  self.parent: Optional[ModuleInfo] = parent
54
55
  self.diagnostics = self.gen_diagnostics()
56
+ self.sem_tokens: list[int] = self.gen_sem_tokens()
55
57
 
56
58
  @property
57
59
  def uri(self) -> str:
@@ -63,6 +65,20 @@ class ModuleInfo:
63
65
  """Return if there are syntax errors."""
64
66
  return len(self.errors) > 0 and self.alev == ALev.QUICK
65
67
 
68
+ def update_with(self, new_info: ModuleInfo, refresh: bool = False) -> None:
69
+ """Update module info."""
70
+ self.ir = new_info.ir
71
+ if refresh:
72
+ self.errors = new_info.errors
73
+ self.warnings = new_info.warnings
74
+ else:
75
+ self.errors += [i for i in new_info.errors if i not in self.errors]
76
+ self.warnings += [i for i in new_info.warnings if i not in self.warnings]
77
+ self.alev = new_info.alev
78
+ self.diagnostics = self.gen_diagnostics()
79
+ if self.alev == ALev.TYPE:
80
+ self.sem_tokens = self.gen_sem_tokens()
81
+
66
82
  def gen_diagnostics(self) -> list[lspt.Diagnostic]:
67
83
  """Return diagnostics."""
68
84
  return [
@@ -81,6 +97,27 @@ class ModuleInfo:
81
97
  for warning in self.warnings
82
98
  ]
83
99
 
100
+ def gen_sem_tokens(self) -> list[int]:
101
+ """Return semantic tokens."""
102
+ tokens = []
103
+ prev_line, prev_col = 0, 0
104
+ for node in self.ir._in_mod_nodes:
105
+ if isinstance(node, ast.NameAtom) and node.sem_token:
106
+ line, col_start, col_end = (
107
+ node.loc.first_line - 1,
108
+ node.loc.col_start - 1,
109
+ node.loc.col_end - 1,
110
+ )
111
+ length = col_end - col_start
112
+ tokens += [
113
+ line - prev_line,
114
+ col_start if line != prev_line else col_start - prev_col,
115
+ length,
116
+ *node.sem_token,
117
+ ]
118
+ prev_line, prev_col = line, col_start
119
+ return tokens
120
+
84
121
 
85
122
  class JacLangServer(LanguageServer):
86
123
  """Class for managing workspace."""
@@ -90,19 +127,6 @@ class JacLangServer(LanguageServer):
90
127
  super().__init__("jac-lsp", "v0.1")
91
128
  self.modules: dict[str, ModuleInfo] = {}
92
129
 
93
- def module_not_diff(self, uri: str, alev: ALev) -> bool:
94
- """Check if module was changed."""
95
- doc = self.workspace.get_text_document(uri)
96
- return (
97
- doc.uri in self.modules
98
- and self.modules[doc.uri].ir.source.hash
99
- == md5(doc.source.encode()).hexdigest()
100
- and (
101
- self.modules[doc.uri].alev >= alev
102
- or self.modules[doc.uri].has_syntax_error
103
- )
104
- )
105
-
106
130
  def push_diagnostics(self, file_path: str) -> None:
107
131
  """Push diagnostics for a file."""
108
132
  if file_path in self.modules:
@@ -124,15 +148,14 @@ class JacLangServer(LanguageServer):
124
148
  self.quick_check(file_path)
125
149
  return file_path
126
150
 
127
- def update_modules(self, file_path: str, build: Pass, alev: ALev) -> None:
151
+ def update_modules(
152
+ self, file_path: str, build: Pass, alev: ALev, refresh: bool = False
153
+ ) -> None:
128
154
  """Update modules."""
129
155
  if not isinstance(build.ir, ast.Module):
130
156
  self.log_error("Error with module build.")
131
157
  return
132
- save_parent = (
133
- self.modules[file_path].parent if file_path in self.modules else None
134
- )
135
- self.modules[file_path] = ModuleInfo(
158
+ new_mod = ModuleInfo(
136
159
  ir=build.ir,
137
160
  errors=[
138
161
  i
@@ -146,23 +169,28 @@ class JacLangServer(LanguageServer):
146
169
  ],
147
170
  alev=alev,
148
171
  )
149
- self.modules[file_path].parent = save_parent
172
+ if file_path in self.modules:
173
+ self.modules[file_path].update_with(new_mod, refresh=refresh)
174
+ else:
175
+ self.modules[file_path] = new_mod
150
176
  for p in build.ir.mod_deps.keys():
151
177
  uri = uris.from_fs_path(p)
152
- self.modules[uri] = ModuleInfo(
178
+ new_mod = ModuleInfo(
153
179
  ir=build.ir.mod_deps[p],
154
180
  errors=[i for i in build.errors_had if i.loc.mod_path == p],
155
181
  warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
156
182
  alev=alev,
157
183
  )
184
+ if not refresh and uri in self.modules:
185
+ self.modules[uri].update_with(new_mod)
186
+ else:
187
+ self.modules[uri] = new_mod
158
188
  self.modules[uri].parent = (
159
189
  self.modules[file_path] if file_path != uri else None
160
190
  )
161
191
 
162
- def quick_check(self, file_path: str, force: bool = False) -> None:
192
+ def quick_check(self, file_path: str) -> bool:
163
193
  """Rebuild a file."""
164
- if not force and self.module_not_diff(file_path, ALev.QUICK):
165
- return
166
194
  try:
167
195
  document = self.workspace.get_text_document(file_path)
168
196
  build = jac_str_to_pass(
@@ -170,27 +198,27 @@ class JacLangServer(LanguageServer):
170
198
  )
171
199
  except Exception as e:
172
200
  self.log_error(f"Error during syntax check: {e}")
173
- self.update_modules(file_path, build, ALev.QUICK)
201
+ return False
202
+ self.update_modules(file_path, build, ALev.QUICK, refresh=True)
203
+ return len(self.modules[file_path].errors) == 0
174
204
 
175
- def deep_check(self, file_path: str, force: bool = False) -> None:
205
+ def deep_check(self, file_path: str) -> bool:
176
206
  """Rebuild a file and its dependencies."""
177
- if file_path in self.modules:
178
- self.quick_check(file_path, force=force)
179
- if not force and self.module_not_diff(file_path, ALev.DEEP):
180
- return
207
+ if file_path not in self.modules:
208
+ self.quick_check(file_path)
181
209
  try:
182
210
  file_path = self.unwind_to_parent(file_path)
183
211
  build = jac_ir_to_pass(ir=self.modules[file_path].ir)
184
212
  except Exception as e:
185
213
  self.log_error(f"Error during syntax check: {e}")
214
+ return False
186
215
  self.update_modules(file_path, build, ALev.DEEP)
216
+ return len(self.modules[file_path].errors) == 0
187
217
 
188
- def type_check(self, file_path: str, force: bool = False) -> None:
218
+ def type_check(self, file_path: str) -> bool:
189
219
  """Rebuild a file and its dependencies."""
190
220
  if file_path not in self.modules:
191
- self.deep_check(file_path, force=force)
192
- if not force and self.module_not_diff(file_path, ALev.TYPE):
193
- return
221
+ self.deep_check(file_path)
194
222
  try:
195
223
  file_path = self.unwind_to_parent(file_path)
196
224
  build = jac_ir_to_pass(
@@ -198,7 +226,21 @@ class JacLangServer(LanguageServer):
198
226
  )
199
227
  except Exception as e:
200
228
  self.log_error(f"Error during type check: {e}")
229
+ return False
201
230
  self.update_modules(file_path, build, ALev.TYPE)
231
+ return len(self.modules[file_path].errors) == 0
232
+
233
+ def analyze_and_publish(self, uri: str, level: int = 2) -> None:
234
+ """Analyze and publish diagnostics."""
235
+ self.log_py(f"Analyzing {uri}...")
236
+ success = self.quick_check(uri)
237
+ self.push_diagnostics(uri)
238
+ if success and level > 0:
239
+ success = self.deep_check(uri)
240
+ self.push_diagnostics(uri)
241
+ if level > 1:
242
+ self.type_check(uri)
243
+ self.push_diagnostics(uri)
202
244
 
203
245
  def get_completion(
204
246
  self, file_path: str, position: lspt.Position
@@ -275,21 +317,20 @@ class JacLangServer(LanguageServer):
275
317
  def get_node_info(self, node: ast.AstSymbolNode) -> Optional[str]:
276
318
  """Extract meaningful information from the AST node."""
277
319
  try:
278
- if isinstance(node, ast.NameSpec):
320
+ if isinstance(node, ast.NameAtom):
279
321
  node = node.name_of
280
- access = node.sym_link.access.value + " " if node.sym_link else None
322
+ access = node.sym.access.value + " " if node.sym else None
281
323
  node_info = (
282
- f"({access if access else ''}{node.sym_type.value}) {node.sym_name}"
324
+ f"({access if access else ''}{node.sym_category.value}) {node.sym_name}"
283
325
  )
284
- if node.sym_info.clean_type:
285
- node_info += f": {node.sym_info.clean_type}"
326
+ if node.name_spec.clean_type:
327
+ node_info += f": {node.name_spec.clean_type}"
286
328
  if isinstance(node, ast.AstSemStrNode) and node.semstr:
287
329
  node_info += f"\n{node.semstr.value}"
288
330
  if isinstance(node, ast.AstDocNode) and node.doc:
289
331
  node_info += f"\n{node.doc.value}"
290
332
  if isinstance(node, ast.Ability) and node.signature:
291
333
  node_info += f"\n{node.signature.unparse()}"
292
- self.log_py(node.pp())
293
334
  self.log_py(f"mypy_node: {node.gen.mypy_ast}")
294
335
  except AttributeError as e:
295
336
  self.log_warning(f"Attribute error when accessing node attributes: {e}")
@@ -297,8 +338,7 @@ class JacLangServer(LanguageServer):
297
338
 
298
339
  def get_document_symbols(self, file_path: str) -> list[lspt.DocumentSymbol]:
299
340
  """Return document symbols for a file."""
300
- root_node = self.modules[file_path].ir.sym_tab
301
- if root_node:
341
+ if root_node := self.modules[file_path].ir._sym_tab:
302
342
  return collect_symbols(root_node)
303
343
  return []
304
344
 
@@ -310,7 +350,39 @@ class JacLangServer(LanguageServer):
310
350
  self.modules[file_path].ir, position.line, position.character
311
351
  )
312
352
  if node_selected:
313
- if isinstance(node_selected, (ast.ElementStmt, ast.BuiltinType)):
353
+ if (
354
+ isinstance(node_selected, ast.Name)
355
+ and node_selected.parent
356
+ and isinstance(node_selected.parent, ast.ModulePath)
357
+ ):
358
+ spec = get_mod_path(node_selected.parent, node_selected)
359
+ if spec:
360
+ return lspt.Location(
361
+ uri=uris.from_fs_path(spec),
362
+ range=lspt.Range(
363
+ start=lspt.Position(line=0, character=0),
364
+ end=lspt.Position(line=0, character=0),
365
+ ),
366
+ )
367
+ else:
368
+ return None
369
+ elif node_selected.parent and isinstance(
370
+ node_selected.parent, ast.ModuleItem
371
+ ):
372
+ path_range = get_item_path(node_selected.parent)
373
+ if path_range:
374
+ path, range = path_range
375
+ if path and range:
376
+ return lspt.Location(
377
+ uri=uris.from_fs_path(path),
378
+ range=lspt.Range(
379
+ start=lspt.Position(line=range[0], character=0),
380
+ end=lspt.Position(line=range[1], character=5),
381
+ ),
382
+ )
383
+ else:
384
+ return None
385
+ elif isinstance(node_selected, (ast.ElementStmt, ast.BuiltinType)):
314
386
  return None
315
387
  decl_node = (
316
388
  node_selected.parent.body.target
@@ -318,8 +390,8 @@ class JacLangServer(LanguageServer):
318
390
  and isinstance(node_selected.parent, ast.AstImplNeedingNode)
319
391
  and isinstance(node_selected.parent.body, ast.AstImplOnlyNode)
320
392
  else (
321
- node_selected.sym_link.decl
322
- if (node_selected.sym_link and node_selected.sym_link.decl)
393
+ node_selected.sym.decl
394
+ if (node_selected.sym and node_selected.sym.decl)
323
395
  else node_selected
324
396
  )
325
397
  )
@@ -336,9 +408,14 @@ class JacLangServer(LanguageServer):
336
408
 
337
409
  return decl_location
338
410
  else:
339
- self.log_info("No declaration found for the selected node.")
340
411
  return None
341
412
 
413
+ def get_semantic_tokens(self, file_path: str) -> lspt.SemanticTokens:
414
+ """Return semantic tokens for a file."""
415
+ if file_path not in self.modules:
416
+ return lspt.SemanticTokens(data=[])
417
+ return lspt.SemanticTokens(data=self.modules[file_path].sem_tokens)
418
+
342
419
  def log_error(self, message: str) -> None:
343
420
  """Log an error message."""
344
421
  self.show_message_log(message, lspt.MessageType.Error)
@@ -2,68 +2,37 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import threading
6
5
  from typing import Optional
7
6
 
7
+ from jaclang.compiler.constant import (
8
+ JacSemTokenModifier as SemTokMod,
9
+ JacSemTokenType as SemTokType,
10
+ )
8
11
  from jaclang.langserve.engine import JacLangServer
9
12
  from jaclang.langserve.utils import debounce
10
13
 
11
14
  import lsprotocol.types as lspt
12
15
 
13
16
  server = JacLangServer()
14
- analysis_thread: Optional[threading.Thread] = None
15
- analysis_stop_event = threading.Event()
16
-
17
-
18
- def analyze_and_publish(ls: JacLangServer, uri: str, level: int = 2) -> None:
19
- """Analyze and publish diagnostics."""
20
- global analysis_thread, analysis_stop_event
21
-
22
- def run_analysis() -> None:
23
- ls.quick_check(uri)
24
- ls.push_diagnostics(uri)
25
- if not analysis_stop_event.is_set() and level > 0:
26
- ls.deep_check(uri)
27
- ls.push_diagnostics(uri)
28
- if not analysis_stop_event.is_set() and level > 1:
29
- ls.type_check(uri)
30
- ls.push_diagnostics(uri)
31
-
32
- analysis_thread = threading.Thread(target=run_analysis)
33
- analysis_thread.start()
34
-
35
-
36
- def stop_analysis() -> None:
37
- """Stop analysis."""
38
- global analysis_thread, analysis_stop_event
39
- if analysis_thread is not None:
40
- analysis_stop_event.set()
41
- analysis_thread.join()
42
- analysis_stop_event.clear()
43
17
 
44
18
 
45
19
  @server.feature(lspt.TEXT_DOCUMENT_DID_OPEN)
46
- async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
20
+ @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
21
+ def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
47
22
  """Check syntax on change."""
48
- stop_analysis()
49
- analyze_and_publish(ls, params.text_document.uri)
23
+ ls.analyze_and_publish(params.text_document.uri)
24
+ # token_params = lspt.SemanticTokensParams(
25
+ # text_document=lspt.TextDocumentIdentifier(uri=params.text_document.uri)
26
+ # )
27
+ # tokens = semantic_tokens_full(ls, token_params)
28
+ # ls.send_notification("textDocument/publishSemanticTokens", tokens)
50
29
 
51
30
 
52
31
  @server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
53
32
  @debounce(0.1)
54
- async def did_change(
55
- ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
56
- ) -> None:
33
+ async def did_change(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
57
34
  """Check syntax on change."""
58
- stop_analysis()
59
- analyze_and_publish(ls, params.text_document.uri)
60
-
61
-
62
- @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
63
- async def did_save(ls: JacLangServer, params: lspt.DidSaveTextDocumentParams) -> None:
64
- """Check syntax on save."""
65
- stop_analysis()
66
- analyze_and_publish(ls, params.text_document.uri)
35
+ ls.analyze_and_publish(params.text_document.uri, level=0)
67
36
 
68
37
 
69
38
  @server.feature(
@@ -74,11 +43,8 @@ async def did_save(ls: JacLangServer, params: lspt.DidSaveTextDocumentParams) ->
74
43
  ]
75
44
  ),
76
45
  )
77
- async def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) -> None:
46
+ def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) -> None:
78
47
  """Check syntax on file creation."""
79
- for file in params.files:
80
- ls.quick_check(file.uri)
81
- ls.push_diagnostics(file.uri)
82
48
 
83
49
 
84
50
  @server.feature(
@@ -89,13 +55,12 @@ async def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) ->
89
55
  ]
90
56
  ),
91
57
  )
92
- async def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) -> None:
58
+ def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) -> None:
93
59
  """Check syntax on file rename."""
94
60
  new_uris = [file.new_uri for file in params.files]
95
61
  old_uris = [file.old_uri for file in params.files]
96
62
  for i in range(len(new_uris)):
97
63
  ls.rename_module(old_uris[i], new_uris[i])
98
- ls.quick_check(new_uris[i])
99
64
 
100
65
 
101
66
  @server.feature(
@@ -106,7 +71,7 @@ async def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) ->
106
71
  ]
107
72
  ),
108
73
  )
109
- async def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
74
+ def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
110
75
  """Check syntax on file delete."""
111
76
  for file in params.files:
112
77
  ls.delete_module(file.uri)
@@ -116,9 +81,7 @@ async def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) ->
116
81
  lspt.TEXT_DOCUMENT_COMPLETION,
117
82
  lspt.CompletionOptions(trigger_characters=[".", ":", ""]),
118
83
  )
119
- async def completion(
120
- ls: JacLangServer, params: lspt.CompletionParams
121
- ) -> lspt.CompletionList:
84
+ def completion(ls: JacLangServer, params: lspt.CompletionParams) -> lspt.CompletionList:
122
85
  """Provide completion."""
123
86
  return ls.get_completion(params.text_document.uri, params.position)
124
87
 
@@ -140,25 +103,35 @@ def hover(
140
103
 
141
104
 
142
105
  @server.feature(lspt.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
143
- async def document_symbol(
106
+ def document_symbol(
144
107
  ls: JacLangServer, params: lspt.DocumentSymbolParams
145
108
  ) -> list[lspt.DocumentSymbol]:
146
109
  """Provide document symbols."""
147
- stop_analysis()
148
- analyze_and_publish(ls, params.text_document.uri)
149
110
  return ls.get_document_symbols(params.text_document.uri)
150
111
 
151
112
 
152
113
  @server.feature(lspt.TEXT_DOCUMENT_DEFINITION)
153
- async def definition(
114
+ def definition(
154
115
  ls: JacLangServer, params: lspt.TextDocumentPositionParams
155
116
  ) -> Optional[lspt.Location]:
156
117
  """Provide definition."""
157
- stop_analysis()
158
- analyze_and_publish(ls, params.text_document.uri, level=1)
159
118
  return ls.get_definition(params.text_document.uri, params.position)
160
119
 
161
120
 
121
+ @server.feature(
122
+ lspt.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
123
+ lspt.SemanticTokensLegend(
124
+ token_types=SemTokType.as_str_list(),
125
+ token_modifiers=SemTokMod.as_str_list(),
126
+ ),
127
+ )
128
+ def semantic_tokens_full(
129
+ ls: JacLangServer, params: lspt.SemanticTokensParams
130
+ ) -> lspt.SemanticTokens:
131
+ """Provide semantic tokens."""
132
+ return ls.get_semantic_tokens(params.text_document.uri)
133
+
134
+
162
135
  def run_lang_server() -> None:
163
136
  """Run the language server."""
164
137
  server.start_io()
@@ -0,0 +1,28 @@
1
+ """A Docstring can be added the head of any module.
2
+
3
+ Any element in the module can also have a docstring.
4
+ If there is only one docstring before the first element,
5
+ it is assumed to be a module docstring.
6
+ """
7
+
8
+ """A docstring for add function"""
9
+ can add(a: int, b: int) -> int {
10
+ return a + b;
11
+ }
12
+ # No docstring for subtract function
13
+
14
+ can subtract(a: int, b: int) -> int {
15
+ return a - b;
16
+ }
17
+
18
+ with entry:__main__ {
19
+ print(add(1, subtract(3, 1)));
20
+ }
21
+
22
+ glob x: int = 10;
23
+
24
+ enum Color {
25
+ RED,
26
+ GREEN,
27
+ BLUE
28
+ }
@@ -18,9 +18,9 @@ Below we have the demonstration of a class to calculate the area of a circle.
18
18
  *#
19
19
 
20
20
  """Enum for shape types"""
21
- enum ShapeType {
22
- CIRCLE="Circle",
23
- UNKNOWN="Unknown"
21
+ enum ShapeType {
22
+ CIRCLE = "Circle",
23
+ UNKNOWN = "Unknown"
24
24
  }
25
25
 
26
26
  """Base class for a shape."""
@@ -50,24 +50,28 @@ with entry {
50
50
  # Global also works here
51
51
 
52
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()}");
53
+ # To run the program functionality
54
+ print(
55
+ f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}"
56
+ );
57
+ print(
58
+ f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}"
59
+ );
56
60
  }
57
61
  # Unit Tests!
58
62
 
59
63
  glob expected_area = 78.53981633974483;
60
64
 
61
- test calc_area {
62
- check.assertAlmostEqual(calculate_area(RAD), expected_area);
65
+ test calc_area {
66
+ check assertAlmostEqual(calculate_area(RAD), expected_area);
63
67
  }
64
68
 
65
- test circle_area {
69
+ test circle_area {
66
70
  c = Circle(RAD);
67
- check.assertAlmostEqual(c.area(), expected_area);
71
+ check assertAlmostEqual(c.area(), expected_area);
68
72
  }
69
73
 
70
- test circle_type {
74
+ test circle_type {
71
75
  c = Circle(RAD);
72
- check.assertEqual(c.shape_type, ShapeType.CIRCLE);
76
+ check assertEqual(c.shape_type, ShapeType.CIRCLE);
73
77
  }