jaclang 0.7.2__py3-none-any.whl → 0.7.6__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.6.dist-info}/METADATA +1 -1
  81. {jaclang-0.7.2.dist-info → jaclang-0.7.6.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.6.dist-info}/WHEEL +0 -0
  92. {jaclang-0.7.2.dist-info → jaclang-0.7.6.dist-info}/entry_points.txt +0 -0
@@ -59,15 +59,15 @@ print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}
59
59
  glob expected_area = 78.53981633974483;
60
60
 
61
61
  test calc_area {
62
- check.assertAlmostEqual(calculate_area(RAD), expected_area);
62
+ check assertAlmostEqual(calculate_area(RAD), expected_area);
63
63
  }
64
64
 
65
65
  test circle_area {
66
66
  c = Circle(RAD);
67
- check.assertAlmostEqual(c.area(), expected_area);
67
+ check assertAlmostEqual(c.area(), expected_area);
68
68
  }
69
69
 
70
70
  test circle_type {
71
71
  c = Circle(RAD);
72
- check.assertEqual(c.shape_type, ShapeType.CIRCLE);
72
+ check assertEqual(c.shape_type, ShapeType.CIRCLE);
73
73
  }
@@ -0,0 +1,15 @@
1
+ glob expected_area = 78.53981633974483;
2
+
3
+ test a1 {
4
+ check assertAlmostEqual(calculate_area(RAD), expected_area);
5
+ }
6
+
7
+ test a2 {
8
+ c = Circle(RAD);
9
+ check assertAlmostEqual(c.area(), expected_area);
10
+ }
11
+
12
+ test a3 {
13
+ c = Circle(RAD);
14
+ check assertEqual(c.shape_type, ShapeType.CIRCLE);
15
+ }
@@ -0,0 +1,6 @@
1
+ import:py os;
2
+ import:py from math, sqrt as square_root;
3
+ import:py datetime as dt;
4
+ import:jac from base_module_structure, add as adsd, subtract,x,Color as clr;
5
+ import:jac base_module_structure as base_module_structure;
6
+ import:py from py_import,add1 as ss, sub1 as subtract1,apple,Orange1;
@@ -0,0 +1,26 @@
1
+ """This is a helper file."""
2
+
3
+
4
+ def add1():
5
+ return 1 + 1
6
+
7
+
8
+ def sub1():
9
+ return 1 - 1
10
+
11
+
12
+ # classes
13
+ class Orange1:
14
+ def __init__(self):
15
+ self.orange = 1
16
+
17
+ def get_orange(self):
18
+ return self.orange
19
+
20
+ def set_orange(self, orange):
21
+ self.orange = orange
22
+
23
+
24
+ # variables
25
+ orange2 = 1
26
+ apple = 1
@@ -63,10 +63,10 @@ class TestJacLangServer(TestCase):
63
63
  self.assertEqual(len(lsp.modules), 1)
64
64
  self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
65
65
  lsp.deep_check(circle_file)
66
- self.assertEqual(len(lsp.modules), 1)
66
+ # self.assertEqual(len(lsp.modules), 1)
67
67
  self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
68
68
  lsp.type_check(circle_file)
69
- self.assertEqual(len(lsp.modules), 1)
69
+ # self.assertEqual(len(lsp.modules), 1)
70
70
  self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
71
71
 
72
72
  def test_impl_stay_connected(self) -> None:
@@ -88,7 +88,7 @@ class TestJacLangServer(TestCase):
88
88
  "Circle class inherits from Shape.",
89
89
  lsp.get_hover_info(circle_file, pos).contents.value,
90
90
  )
91
- lsp.type_check(circle_impl_file, force=True)
91
+ lsp.type_check(circle_impl_file)
92
92
  pos = lspt.Position(8, 11)
93
93
  self.assertIn(
94
94
  "ability) calculate_area: float",
@@ -105,9 +105,9 @@ class TestJacLangServer(TestCase):
105
105
  circle_impl_file = uris.from_fs_path(
106
106
  self.fixture_abs_path("circle_pure.impl.jac")
107
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)
108
+ lsp.quick_check(circle_impl_file)
109
+ lsp.deep_check(circle_impl_file)
110
+ lsp.type_check(circle_impl_file)
111
111
  pos = lspt.Position(8, 11)
112
112
  self.assertIn(
113
113
  "ability) calculate_area: float",
@@ -143,16 +143,7 @@ class TestJacLangServer(TestCase):
143
143
  lsp.quick_check(circle_file)
144
144
  lsp.deep_check(circle_file)
145
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)))
146
+ self.assertEqual(8, len(lsp.get_document_symbols(circle_file)))
156
147
 
157
148
  def test_go_to_definition(self) -> None:
158
149
  """Test that the go to definition is correct."""
@@ -169,7 +160,7 @@ class TestJacLangServer(TestCase):
169
160
  str(lsp.get_definition(circle_file, lspt.Position(9, 16))),
170
161
  )
171
162
  self.assertIn(
172
- "fixtures/circle_pure.jac:12:0-17:1",
163
+ "fixtures/circle_pure.jac:13:11-13:16",
173
164
  str(lsp.get_definition(circle_file, lspt.Position(20, 17))),
174
165
  )
175
166
 
@@ -186,6 +177,90 @@ class TestJacLangServer(TestCase):
186
177
  lsp.deep_check(guess_game_file)
187
178
  lsp.type_check(guess_game_file)
188
179
  self.assertIn(
189
- "guess_game4.jac:27:4-27:34",
180
+ "guess_game4.jac:27:8-27:21",
190
181
  str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
191
182
  )
183
+
184
+ def test_test_annex(self) -> None:
185
+ """Test that the server doesn't run if there is a syntax error."""
186
+ lsp = JacLangServer()
187
+ # Set up the workspace path to "fixtures/"
188
+ workspace_path = self.fixture_abs_path("")
189
+ workspace = Workspace(workspace_path, lsp)
190
+ lsp.lsp._workspace = workspace
191
+ circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.test.jac"))
192
+ lsp.quick_check(circle_file)
193
+ lsp.deep_check(circle_file)
194
+ lsp.type_check(circle_file)
195
+ pos = lspt.Position(13, 29)
196
+ self.assertIn(
197
+ "shape_type: circle_pure.ShapeType",
198
+ lsp.get_hover_info(circle_file, pos).contents.value,
199
+ )
200
+
201
+ def test_go_to_defintion_import(self) -> None:
202
+ """Test that the go to definition is correct."""
203
+ lsp = JacLangServer()
204
+ workspace_path = self.fixture_abs_path("")
205
+ workspace = Workspace(workspace_path, lsp)
206
+ lsp.lsp._workspace = workspace
207
+ import_file = uris.from_fs_path(
208
+ self.fixture_abs_path("import_include_statements.jac")
209
+ )
210
+ lsp.quick_check(import_file)
211
+ lsp.deep_check(import_file)
212
+ lsp.type_check(import_file)
213
+ positions = [
214
+ (2, 16, "datetime.py:0:0-0:0"),
215
+ (3, 17, "base_module_structure.jac:0:0-0:0"),
216
+ (3, 74, "base_module_structure.jac:23:0-23:5"),
217
+ (5, 65, "py_import.py:12:0-20:5"),
218
+ (5, 35, "py_import.py:3:0-4:5"),
219
+ ]
220
+
221
+ for line, char, expected in positions:
222
+ with self.subTest(line=line, char=char):
223
+ self.assertIn(
224
+ expected,
225
+ str(lsp.get_definition(import_file, lspt.Position(line, char))),
226
+ )
227
+
228
+ def test_sem_tokens(self) -> None:
229
+ """Test that the Semantic Tokens are generated correctly."""
230
+ lsp = JacLangServer()
231
+ workspace_path = self.fixture_abs_path("")
232
+ workspace = Workspace(workspace_path, lsp)
233
+ lsp.lsp._workspace = workspace
234
+ circle_file = uris.from_fs_path(self.fixture_abs_path("circle.jac"))
235
+ lsp.quick_check(circle_file)
236
+ lsp.deep_check(circle_file)
237
+ lsp.type_check(circle_file)
238
+ sem_list = lsp.get_semantic_tokens(circle_file).data
239
+ expected_counts = [
240
+ ("<JacSemTokenType.VARIABLE: 8>, <JacSemTokenModifier.READONLY: 4>", 206),
241
+ (
242
+ "<JacSemTokenType.PROPERTY: 9>, <JacSemTokenModifier.DEFINITION: 2>,",
243
+ 112,
244
+ ),
245
+ (
246
+ "<JacSemTokenType.PARAMETER: 7>, <JacSemTokenModifier.DECLARATION: 1>,",
247
+ 56,
248
+ ),
249
+ (
250
+ "<JacSemTokenType.FUNCTION: 12>, <JacSemTokenModifier.DECLARATION: 1>,",
251
+ 25,
252
+ ),
253
+ ("<JacSemTokenType.METHOD: 13>, <JacSemTokenModifier.DECLARATION: 1>", 12),
254
+ ("<JacSemTokenType.ENUM: 3>, <JacSemTokenModifier.DECLARATION: 1>,", 37),
255
+ ("<JacSemTokenType.CLASS: 2>, <JacSemTokenModifier.DECLARATION: ", 162),
256
+ (
257
+ "<JacSemTokenType.NAMESPACE: 0>, <JacSemTokenModifier.DEFINITION: 2>,",
258
+ 10,
259
+ ),
260
+ ("0, 0, 4,", 22),
261
+ ("0, 0, 3,", 192),
262
+ ("0, 0, 6, ", 65),
263
+ (" 0, 7, 3,", 3),
264
+ ]
265
+ for token_type, expected_count in expected_counts:
266
+ self.assertEqual(str(sem_list).count(token_type), expected_count)
@@ -2,12 +2,16 @@
2
2
 
3
3
  import asyncio
4
4
  import builtins
5
+ import importlib.util
6
+ import os
7
+ import sys
5
8
  from functools import wraps
6
9
  from typing import Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar
7
10
 
8
11
  import jaclang.compiler.absyntree as ast
9
12
  from jaclang.compiler.codeloc import CodeLocInfo
10
13
  from jaclang.compiler.symtable import Symbol, SymbolTable
14
+ from jaclang.utils.helpers import import_target_to_relative_path
11
15
 
12
16
  import lsprotocol.types as lspt
13
17
 
@@ -68,7 +72,7 @@ def find_deepest_symbol_node_at_pos(
68
72
  if isinstance(node, ast.AstSymbolNode):
69
73
  last_symbol_node = node
70
74
 
71
- for child in node.kid:
75
+ for child in [i for i in node.kid if i.loc.mod_path == node.loc.mod_path]:
72
76
  if position_within_node(child, line, character):
73
77
  deeper_node = find_deepest_symbol_node_at_pos(child, line, character)
74
78
  if deeper_node is not None:
@@ -106,23 +110,27 @@ def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
106
110
  return symbols
107
111
 
108
112
  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]:
113
+ if (
114
+ key in dir(builtins)
115
+ or item in [owner_sym(tab) for tab in node.kid]
116
+ or item.decl.loc.mod_path != node.owner.loc.mod_path
117
+ ):
112
118
  continue
113
119
  else:
114
120
 
115
- pos = create_range(item.defn[0].loc)
121
+ pos = create_range(item.decl.loc)
116
122
  symbol = lspt.DocumentSymbol(
117
123
  name=key,
118
- kind=kind_map(item.defn[0]),
124
+ kind=kind_map(item.decl),
119
125
  range=pos,
120
126
  selection_range=pos,
121
127
  children=[],
122
128
  )
123
129
  symbols.append(symbol)
124
130
 
125
- for sub_tab in node.kid:
131
+ for sub_tab in [
132
+ i for i in node.kid if i.owner.loc.mod_path == node.owner.loc.mod_path
133
+ ]:
126
134
  sub_symbols = collect_symbols(sub_tab)
127
135
 
128
136
  if isinstance(
@@ -146,7 +154,7 @@ def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
146
154
 
147
155
  def owner_sym(table: SymbolTable) -> Optional[Symbol]:
148
156
  """Get owner sym."""
149
- if table.has_parent() and isinstance(table.owner, ast.AstSymbolNode):
157
+ if table.parent and isinstance(table.owner, ast.AstSymbolNode):
150
158
  return table.parent.lookup(table.owner.sym_name)
151
159
  return None
152
160
 
@@ -154,8 +162,14 @@ def owner_sym(table: SymbolTable) -> Optional[Symbol]:
154
162
  def create_range(loc: CodeLocInfo) -> lspt.Range:
155
163
  """Create an lspt.Range from a location object."""
156
164
  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),
165
+ start=lspt.Position(
166
+ line=loc.first_line - 1 if loc.first_line > 0 else 0,
167
+ character=loc.col_start - 1 if loc.col_start > 0 else 0,
168
+ ),
169
+ end=lspt.Position(
170
+ line=loc.last_line - 1 if loc.last_line > 0 else 0,
171
+ character=loc.col_end - 1 if loc.col_end > 0 else 0,
172
+ ),
159
173
  )
160
174
 
161
175
 
@@ -178,3 +192,103 @@ def kind_map(sub_tab: ast.AstNode) -> lspt.SymbolKind:
178
192
  )
179
193
  )
180
194
  )
195
+
196
+
197
+ def get_mod_path(mod_path: ast.ModulePath, name_node: ast.Name) -> str | None:
198
+ """Get path for a module import name."""
199
+ ret_target = None
200
+ module_location_path = mod_path.loc.mod_path
201
+ if mod_path.parent and (
202
+ (
203
+ isinstance(mod_path.parent.parent, ast.Import)
204
+ and mod_path.parent.parent.hint.tag.value == "py"
205
+ )
206
+ or (
207
+ isinstance(mod_path.parent, ast.Import)
208
+ and mod_path.parent.from_loc
209
+ and mod_path.parent.hint.tag.value == "py"
210
+ )
211
+ ):
212
+ if mod_path.path and name_node in mod_path.path:
213
+ temporary_path_str = ("." * mod_path.level) + ".".join(
214
+ [p.value for p in mod_path.path[: mod_path.path.index(name_node) + 1]]
215
+ if mod_path.path
216
+ else ""
217
+ )
218
+ else:
219
+ temporary_path_str = mod_path.path_str
220
+ sys.path.append(os.path.dirname(module_location_path))
221
+ spec = importlib.util.find_spec(temporary_path_str)
222
+ sys.path.remove(os.path.dirname(module_location_path))
223
+ if spec and spec.origin and spec.origin.endswith(".py"):
224
+ ret_target = spec.origin
225
+ elif mod_path.parent and (
226
+ (
227
+ isinstance(mod_path.parent.parent, ast.Import)
228
+ and mod_path.parent.parent.hint.tag.value == "jac"
229
+ )
230
+ or (
231
+ isinstance(mod_path.parent, ast.Import)
232
+ and mod_path.parent.from_loc
233
+ and mod_path.parent.hint.tag.value == "jac"
234
+ )
235
+ ):
236
+ ret_target = import_target_to_relative_path(
237
+ level=mod_path.level,
238
+ target=mod_path.path_str,
239
+ base_path=os.path.dirname(module_location_path),
240
+ )
241
+ return ret_target
242
+
243
+
244
+ def get_item_path(mod_item: ast.ModuleItem) -> tuple[str, tuple[int, int]] | None:
245
+ """Get path."""
246
+ item_name = mod_item.name.value
247
+ if mod_item.from_parent.hint.tag.value == "py" and mod_item.from_parent.from_loc:
248
+ path = get_mod_path(mod_item.from_parent.from_loc, mod_item.name)
249
+ if path:
250
+ return get_definition_range(path, item_name)
251
+ elif mod_item.from_parent.hint.tag.value == "jac":
252
+ mod_node = mod_item.from_mod_path
253
+ if mod_node.sub_module and mod_node.sub_module._sym_tab:
254
+ for symbol_name, symbol in mod_node.sub_module._sym_tab.tab.items():
255
+ if symbol_name == item_name:
256
+ return symbol.decl.loc.mod_path, (
257
+ symbol.decl.loc.first_line - 1,
258
+ symbol.decl.loc.last_line - 1,
259
+ )
260
+ return None
261
+
262
+
263
+ def get_definition_range(
264
+ filename: str, name: str
265
+ ) -> tuple[str, tuple[int, int]] | None:
266
+ """Get the start and end line of a function or class definition in a file."""
267
+ import ast
268
+
269
+ with open(filename, "r") as file:
270
+ source = file.read()
271
+
272
+ tree = ast.parse(source)
273
+
274
+ for node in ast.walk(tree):
275
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef)) and node.name == name:
276
+ start_line = node.lineno
277
+ end_line = (
278
+ node.body[-1].end_lineno
279
+ if hasattr(node.body[-1], "end_lineno")
280
+ else node.body[-1].lineno
281
+ )
282
+ if start_line and end_line:
283
+ return filename, (start_line - 1, end_line - 1)
284
+ elif isinstance(node, ast.Assign):
285
+ for target in node.targets:
286
+ if isinstance(target, ast.Name) and target.id == name:
287
+ start_line = node.lineno
288
+ end_line = (
289
+ node.end_lineno if hasattr(node, "end_lineno") else node.lineno
290
+ )
291
+ if start_line and end_line:
292
+ return filename, (start_line - 1, end_line - 1)
293
+
294
+ return None
jaclang/plugin/builtin.py CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from typing import Optional
9
- from jaclang.core.construct import NodeArchitype
9
+ from jaclang.core.constructs import NodeArchitype
10
10
 
11
11
 
12
12
  def dotgen(
jaclang/plugin/default.py CHANGED
@@ -14,6 +14,7 @@ from typing import Any, Callable, Optional, Type, Union
14
14
 
15
15
  from jaclang.compiler.absyntree import Module
16
16
  from jaclang.compiler.constant import EdgeDir, colors
17
+ from jaclang.compiler.semtable import SemInfo, SemRegistry, SemScope
17
18
  from jaclang.core.aott import (
18
19
  aott_raise,
19
20
  extract_non_primary_type,
@@ -21,7 +22,7 @@ from jaclang.core.aott import (
21
22
  get_info_types,
22
23
  get_input_information,
23
24
  )
24
- from jaclang.core.construct import (
25
+ from jaclang.core.constructs import (
25
26
  Architype,
26
27
  DSFunc,
27
28
  EdgeAnchor,
@@ -39,7 +40,6 @@ from jaclang.core.construct import (
39
40
  exec_context,
40
41
  )
41
42
  from jaclang.core.importer import jac_importer
42
- from jaclang.core.registry import SemInfo, SemRegistry, SemScope
43
43
  from jaclang.core.utils import traverse_graph
44
44
  from jaclang.plugin.feature import JacFeature as Jac
45
45
  from jaclang.plugin.spec import T
@@ -110,7 +110,9 @@ class JacFeatureDefaults:
110
110
  """Create a new architype."""
111
111
  for i in on_entry + on_exit:
112
112
  i.resolve(cls)
113
- if not issubclass(cls, arch_base):
113
+ if not hasattr(cls, "_jac_entry_funcs_") or not hasattr(
114
+ cls, "_jac_exit_funcs_"
115
+ ):
114
116
  # Saving the module path and reassign it after creating cls
115
117
  # So the jac modules are part of the correct module
116
118
  cur_module = cls.__module__
@@ -216,7 +218,7 @@ class JacFeatureDefaults:
216
218
  cachable: bool,
217
219
  mdl_alias: Optional[str],
218
220
  override_name: Optional[str],
219
- mod_bundle: Optional[Module],
221
+ mod_bundle: Optional[Module | str],
220
222
  lng: Optional[str],
221
223
  items: Optional[dict[str, Union[str, bool]]],
222
224
  ) -> Optional[types.ModuleType]:
@@ -335,7 +337,13 @@ class JacFeatureDefaults:
335
337
  @hookimpl
336
338
  def ignore(
337
339
  walker: WalkerArchitype,
338
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
340
+ expr: (
341
+ list[NodeArchitype | EdgeArchitype]
342
+ | list[NodeArchitype]
343
+ | list[EdgeArchitype]
344
+ | NodeArchitype
345
+ | EdgeArchitype
346
+ ),
339
347
  ) -> bool:
340
348
  """Jac's ignore stmt feature."""
341
349
  return walker._jac_.ignore_node(expr)
@@ -344,7 +352,13 @@ class JacFeatureDefaults:
344
352
  @hookimpl
345
353
  def visit_node(
346
354
  walker: WalkerArchitype,
347
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
355
+ expr: (
356
+ list[NodeArchitype | EdgeArchitype]
357
+ | list[NodeArchitype]
358
+ | list[EdgeArchitype]
359
+ | NodeArchitype
360
+ | EdgeArchitype
361
+ ),
348
362
  ) -> bool:
349
363
  """Jac's visit stmt feature."""
350
364
  if isinstance(walker, WalkerArchitype):
@@ -481,14 +495,14 @@ class JacFeatureDefaults:
481
495
  @hookimpl
482
496
  def build_edge(
483
497
  is_undirected: bool,
484
- conn_type: Optional[Type[EdgeArchitype]],
498
+ conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
485
499
  conn_assign: Optional[tuple[tuple, tuple]],
486
500
  ) -> Callable[[], EdgeArchitype]:
487
501
  """Jac's root getter."""
488
502
  conn_type = conn_type if conn_type else GenericEdge
489
503
 
490
504
  def builder() -> EdgeArchitype:
491
- edge = conn_type()
505
+ edge = conn_type() if isinstance(conn_type, type) else conn_type
492
506
  edge._jac_.is_undirected = is_undirected
493
507
  if conn_assign:
494
508
  for fld, val in zip(conn_assign[0], conn_assign[1]):
@@ -649,7 +663,7 @@ class JacFeatureDefaults:
649
663
 
650
664
  inputs_information = get_input_information(inputs, type_collector)
651
665
 
652
- output_information = f"{outputs[0]} ({outputs[1]})"
666
+ output_information = f"{outputs[0]} ({outputs[1]})".strip()
653
667
  type_collector.extend(extract_non_primary_type(outputs[1]))
654
668
  output_type_explanations = "\n".join(
655
669
  list(
jaclang/plugin/feature.py CHANGED
@@ -3,10 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import types
6
- from typing import Any, Callable, Optional, Type, Union
6
+ from typing import Any, Callable, Optional, Type, TypeAlias, Union
7
7
 
8
8
  from jaclang.compiler.absyntree import Module
9
- from jaclang.core.construct import (
9
+ from jaclang.core.constructs import (
10
10
  Architype,
11
11
  EdgeArchitype,
12
12
  Memory,
@@ -31,7 +31,13 @@ class JacFeature:
31
31
 
32
32
  import abc
33
33
  from jaclang.compiler.constant import EdgeDir
34
- from jaclang.plugin.spec import DSFunc
34
+ from jaclang.core.constructs import DSFunc
35
+
36
+ RootType: TypeAlias = Root
37
+ Obj: TypeAlias = Architype
38
+ Node: TypeAlias = NodeArchitype
39
+ Edge: TypeAlias = EdgeArchitype
40
+ Walker: TypeAlias = WalkerArchitype
35
41
 
36
42
  @staticmethod
37
43
  def context(session: str = "") -> ExecutionContext:
@@ -96,7 +102,7 @@ class JacFeature:
96
102
  cachable: bool = True,
97
103
  mdl_alias: Optional[str] = None,
98
104
  override_name: Optional[str] = None,
99
- mod_bundle: Optional[Module] = None,
105
+ mod_bundle: Optional[Module | str] = None,
100
106
  lng: Optional[str] = "jac",
101
107
  items: Optional[dict[str, Union[str, bool]]] = None,
102
108
  ) -> Optional[types.ModuleType]:
@@ -160,7 +166,13 @@ class JacFeature:
160
166
  @staticmethod
161
167
  def ignore(
162
168
  walker: WalkerArchitype,
163
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
169
+ expr: (
170
+ list[NodeArchitype | EdgeArchitype]
171
+ | list[NodeArchitype]
172
+ | list[EdgeArchitype]
173
+ | NodeArchitype
174
+ | EdgeArchitype
175
+ ),
164
176
  ) -> bool: # noqa: ANN401
165
177
  """Jac's ignore stmt feature."""
166
178
  return pm.hook.ignore(walker=walker, expr=expr)
@@ -168,7 +180,13 @@ class JacFeature:
168
180
  @staticmethod
169
181
  def visit_node(
170
182
  walker: WalkerArchitype,
171
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
183
+ expr: (
184
+ list[NodeArchitype | EdgeArchitype]
185
+ | list[NodeArchitype]
186
+ | list[EdgeArchitype]
187
+ | NodeArchitype
188
+ | EdgeArchitype
189
+ ),
172
190
  ) -> bool: # noqa: ANN401
173
191
  """Jac's visit stmt feature."""
174
192
  return pm.hook.visit_node(walker=walker, expr=expr)
@@ -245,7 +263,7 @@ class JacFeature:
245
263
  @staticmethod
246
264
  def build_edge(
247
265
  is_undirected: bool,
248
- conn_type: Optional[Type[EdgeArchitype]],
266
+ conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
249
267
  conn_assign: Optional[tuple[tuple, tuple]],
250
268
  ) -> Callable[[], EdgeArchitype]:
251
269
  """Jac's root getter."""
jaclang/plugin/spec.py CHANGED
@@ -3,19 +3,19 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import types
6
- from dataclasses import dataclass
7
6
  from typing import Any, Callable, Optional, TYPE_CHECKING, Type, TypeVar, Union
8
7
 
9
8
  from jaclang.compiler.absyntree import Module
10
9
 
11
10
  if TYPE_CHECKING:
12
- from jaclang.core.construct import EdgeArchitype, NodeArchitype
11
+ from jaclang.core.constructs import EdgeArchitype, NodeArchitype
13
12
  from jaclang.plugin.default import (
14
13
  Architype,
15
14
  EdgeDir,
16
15
  ExecutionContext,
17
16
  WalkerArchitype,
18
17
  Root,
18
+ DSFunc,
19
19
  )
20
20
  from jaclang.core.memory import Memory
21
21
 
@@ -26,20 +26,6 @@ hookspec = pluggy.HookspecMarker("jac")
26
26
  T = TypeVar("T")
27
27
 
28
28
 
29
- # TODO: DSFunc should be moved into jaclang/core
30
- @dataclass(eq=False)
31
- class DSFunc:
32
- """Data Spatial Function."""
33
-
34
- name: str
35
- trigger: type | types.UnionType | tuple[type | types.UnionType, ...] | None
36
- func: Callable[[Any, Any], Any] | None = None
37
-
38
- def resolve(self, cls: type) -> None:
39
- """Resolve the function."""
40
- self.func = getattr(cls, self.name)
41
-
42
-
43
29
  class JacFeatureSpec:
44
30
  """Jac Feature."""
45
31
 
@@ -113,7 +99,7 @@ class JacFeatureSpec:
113
99
  cachable: bool,
114
100
  mdl_alias: Optional[str],
115
101
  override_name: Optional[str],
116
- mod_bundle: Optional[Module],
102
+ mod_bundle: Optional[Module | str],
117
103
  lng: Optional[str],
118
104
  items: Optional[dict[str, Union[str, bool]]],
119
105
  ) -> Optional[types.ModuleType]:
@@ -167,7 +153,13 @@ class JacFeatureSpec:
167
153
  @hookspec(firstresult=True)
168
154
  def ignore(
169
155
  walker: WalkerArchitype,
170
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
156
+ expr: (
157
+ list[NodeArchitype | EdgeArchitype]
158
+ | list[NodeArchitype]
159
+ | list[EdgeArchitype]
160
+ | NodeArchitype
161
+ | EdgeArchitype
162
+ ),
171
163
  ) -> bool:
172
164
  """Jac's ignore stmt feature."""
173
165
  raise NotImplementedError
@@ -176,7 +168,13 @@ class JacFeatureSpec:
176
168
  @hookspec(firstresult=True)
177
169
  def visit_node(
178
170
  walker: WalkerArchitype,
179
- expr: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
171
+ expr: (
172
+ list[NodeArchitype | EdgeArchitype]
173
+ | list[NodeArchitype]
174
+ | list[EdgeArchitype]
175
+ | NodeArchitype
176
+ | EdgeArchitype
177
+ ),
180
178
  ) -> bool: # noqa: ANN401
181
179
  """Jac's visit stmt feature."""
182
180
  raise NotImplementedError
@@ -248,7 +246,7 @@ class JacFeatureSpec:
248
246
  @hookspec(firstresult=True)
249
247
  def build_edge(
250
248
  is_undirected: bool,
251
- conn_type: Optional[Type[EdgeArchitype]],
249
+ conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
252
250
  conn_assign: Optional[tuple[tuple, tuple]],
253
251
  ) -> Callable[[], EdgeArchitype]:
254
252
  """Jac's root getter."""