jaclang 0.8.4__py3-none-any.whl → 0.8.5__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 (53) hide show
  1. jaclang/cli/cli.py +74 -22
  2. jaclang/compiler/jac.lark +3 -3
  3. jaclang/compiler/larkparse/jac_parser.py +2 -2
  4. jaclang/compiler/parser.py +14 -21
  5. jaclang/compiler/passes/main/__init__.py +3 -1
  6. jaclang/compiler/passes/main/binder_pass.py +594 -0
  7. jaclang/compiler/passes/main/import_pass.py +8 -256
  8. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  9. jaclang/compiler/passes/main/pyast_gen_pass.py +35 -69
  10. jaclang/compiler/passes/main/pyast_load_pass.py +24 -13
  11. jaclang/compiler/passes/main/sem_def_match_pass.py +1 -1
  12. jaclang/compiler/passes/main/tests/fixtures/M1.jac +3 -0
  13. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +47 -0
  14. jaclang/compiler/passes/main/tests/test_binder_pass.py +111 -0
  15. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +13 -13
  16. jaclang/compiler/passes/main/tests/test_sem_def_match_pass.py +6 -6
  17. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +2 -0
  18. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +6 -0
  19. jaclang/compiler/program.py +15 -8
  20. jaclang/compiler/tests/test_sr_errors.py +32 -0
  21. jaclang/compiler/unitree.py +21 -15
  22. jaclang/langserve/engine.jac +23 -4
  23. jaclang/langserve/tests/test_server.py +13 -0
  24. jaclang/runtimelib/importer.py +33 -62
  25. jaclang/runtimelib/utils.py +29 -0
  26. jaclang/tests/fixtures/pyfunc_fmt.py +60 -0
  27. jaclang/tests/fixtures/pyfunc_fstr.py +25 -0
  28. jaclang/tests/fixtures/pyfunc_kwesc.py +33 -0
  29. jaclang/tests/fixtures/python_run_test.py +19 -0
  30. jaclang/tests/test_cli.py +67 -0
  31. jaclang/tests/test_language.py +96 -1
  32. jaclang/utils/lang_tools.py +3 -3
  33. jaclang/utils/module_resolver.py +90 -0
  34. jaclang/utils/symtable_test_helpers.py +125 -0
  35. jaclang/utils/test.py +3 -4
  36. jaclang/vendor/interegular/__init__.py +34 -0
  37. jaclang/vendor/interegular/comparator.py +163 -0
  38. jaclang/vendor/interegular/fsm.py +1015 -0
  39. jaclang/vendor/interegular/patterns.py +732 -0
  40. jaclang/vendor/interegular/py.typed +0 -0
  41. jaclang/vendor/interegular/utils/__init__.py +15 -0
  42. jaclang/vendor/interegular/utils/simple_parser.py +165 -0
  43. jaclang/vendor/interegular-0.3.3.dist-info/INSTALLER +1 -0
  44. jaclang/vendor/interegular-0.3.3.dist-info/LICENSE.txt +21 -0
  45. jaclang/vendor/interegular-0.3.3.dist-info/METADATA +64 -0
  46. jaclang/vendor/interegular-0.3.3.dist-info/RECORD +20 -0
  47. jaclang/vendor/interegular-0.3.3.dist-info/REQUESTED +0 -0
  48. jaclang/vendor/interegular-0.3.3.dist-info/WHEEL +5 -0
  49. jaclang/vendor/interegular-0.3.3.dist-info/top_level.txt +1 -0
  50. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/METADATA +1 -1
  51. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/RECORD +53 -29
  52. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/WHEEL +0 -0
  53. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,111 @@
1
+ """Test Binder pass."""
2
+
3
+ from jaclang.compiler.program import JacProgram
4
+ from jaclang.utils.symtable_test_helpers import SymTableTestMixin
5
+
6
+
7
+ class BinderPassTests( SymTableTestMixin):
8
+ """Test pass module."""
9
+
10
+ def setUp(self) -> None:
11
+ """Set up test."""
12
+ return super().setUp()
13
+
14
+ def test_glob_sym_build(self) -> None:
15
+ """Test symbol table construction for symbol_binding_test.jac fixture."""
16
+ mod_targ = JacProgram().bind(self.fixture_abs_path("sym_binder.jac"))
17
+ sym_table = mod_targ.sym_tab
18
+
19
+ #currenlty 'aa' is not in the main table, need fix #TODO
20
+ # defns=[(9, 6), (16, 5), (27, 9), (32, 5)],
21
+ # uses=[(33, 11), (37, 11)]
22
+ # Test global variable 'aa'
23
+ self.assert_symbol_complete(
24
+ sym_table, "aa", "variable",
25
+ decl=(9, 6),
26
+ defns=[(9, 6), (16, 5), ],
27
+ uses=[ (37, 11)]
28
+ )
29
+
30
+ # Test global variable 'n'
31
+ self.assert_symbol_complete(
32
+ sym_table, "n", "variable",
33
+ decl=(14, 5),
34
+ defns=[(14, 5)]
35
+ )
36
+
37
+ # Test imported module 'M1'
38
+ self.assert_symbol_complete(
39
+ sym_table, "M1", "module",
40
+ decl=(1, 8),
41
+ defns=[(1, 8)]
42
+ )
43
+
44
+ # Test global variable 'z'
45
+ self.assert_symbol_complete(
46
+ sym_table, "z", "variable",
47
+ decl=(15, 5),
48
+ defns=[(15, 5)]
49
+ )
50
+
51
+ # Test global variable 'Y'
52
+ self.assert_symbol_complete(
53
+ sym_table, "Y", "variable",
54
+ decl=(11, 5),
55
+ defns=[(11, 5), (12, 5), (19, 5)],
56
+ uses=[(15, 9)]
57
+ )
58
+
59
+ # Test ability 'ccc'
60
+ self.assert_symbol_complete(
61
+ sym_table, "ccc", "ability",
62
+ decl=(22, 5),
63
+ defns=[(22, 5)],
64
+ uses=[(36, 5)]
65
+ )
66
+
67
+ #TODO: Fix the following test, 'bb' is not in the main table
68
+ # # Test global variable 'bb'
69
+ # self.assert_symbol_complete(
70
+ # sym_table, "bb", "variable",
71
+ # decl=(26, 17),
72
+ # defns=[(26, 17), (28, 9)]
73
+ # )
74
+
75
+ # Test sub-table for ability 'ccc'
76
+ ccc_table = self.assert_sub_table_exists(sym_table, "ccc",'ability')
77
+
78
+ # Test sub-table for if statement inside 'ccc'
79
+ if_table = self.assert_sub_table_exists(ccc_table, "IfStmt",'variable')
80
+
81
+ # Test local variable 'p' inside if statement
82
+ self.assert_symbol_complete(
83
+ if_table, "p", "variable",
84
+ decl=(29, 9),
85
+ defns=[(29, 9)]
86
+ )
87
+
88
+ def test_symbol_table_structure(self) -> None:
89
+ """Test the overall structure of the symbol table."""
90
+ mod_targ = JacProgram().build(self.fixture_abs_path("sym_binder.jac"))
91
+ sym_table = mod_targ.sym_tab
92
+
93
+ # Verify main module table exists
94
+ self.assertIn("sym_binder", str(sym_table))
95
+
96
+ # Verify expected number of symbols in main table
97
+ main_symbols = ["aa", "n", "M1", "z", "Y", "ccc"]
98
+ # 'bb' is not here:need fix #TODO
99
+ for symbol_name in main_symbols:
100
+ self.assert_symbol_exists(sym_table, symbol_name)
101
+
102
+ # Verify sub-tables exist
103
+ sub_tables = sym_table.kid_scope
104
+ self.assertTrue(len(sub_tables) > 0, "No sub-tables found")
105
+
106
+ # Verify ability sub-table has nested if-statement table
107
+ ccc_table = self.assert_sub_table_exists(sym_table, "ccc", 'ability')
108
+ if_table = self.assert_sub_table_exists(ccc_table, "IfStmt", 'variable')
109
+
110
+ # Verify if-statement table contains local variable
111
+ self.assert_symbol_exists(if_table, "p")
@@ -50,43 +50,43 @@ class PyastGenPassTests(TestCaseMicroSuite, AstSyncTestMixin):
50
50
  # Function (full).
51
51
  sym_fn1 = code_gen.lookup("fn1")
52
52
  self.assertEqual(sym_fn1.semstr, "A function that takes two integers and returns nothing.")
53
- self.assertEqual(sym_fn1.fetch_sym_tab.lookup("bar").semstr, "The first integer parameter.")
53
+ self.assertEqual(sym_fn1.symbol_table.lookup("bar").semstr, "The first integer parameter.")
54
54
 
55
55
  # Function (Missing baz)
56
56
  sym_fn2 = code_gen.lookup("fn2")
57
57
  self.assertEqual(sym_fn2.semstr, "A function that takes one integer and returns nothing.")
58
- self.assertEqual(sym_fn2.fetch_sym_tab.lookup("bar").semstr, "The first integer parameter.")
59
- self.assertEqual(sym_fn2.fetch_sym_tab.lookup("baz").semstr, "")
58
+ self.assertEqual(sym_fn2.symbol_table.lookup("bar").semstr, "The first integer parameter.")
59
+ self.assertEqual(sym_fn2.symbol_table.lookup("baz").semstr, "")
60
60
 
61
61
  # Function (Without sem at all)
62
62
  sym_fn3 = code_gen.lookup("fn3")
63
63
  self.assertTrue(sym_fn3.semstr == "")
64
- self.assertEqual(sym_fn3.fetch_sym_tab.lookup("bar").semstr, "")
65
- self.assertEqual(sym_fn3.fetch_sym_tab.lookup("baz").semstr, "")
64
+ self.assertEqual(sym_fn3.symbol_table.lookup("bar").semstr, "")
65
+ self.assertEqual(sym_fn3.symbol_table.lookup("baz").semstr, "")
66
66
 
67
67
  # Architype (with body).
68
68
  sym_arch1 = code_gen.lookup("Arch1")
69
69
  self.assertEqual(sym_arch1.semstr, "An object that contains two integer properties.")
70
- self.assertEqual(sym_arch1.fetch_sym_tab.lookup("bar").semstr, "The first integer property.")
71
- self.assertEqual(sym_arch1.fetch_sym_tab.lookup("baz").semstr, "The second integer property.")
70
+ self.assertEqual(sym_arch1.symbol_table.lookup("bar").semstr, "The first integer property.")
71
+ self.assertEqual(sym_arch1.symbol_table.lookup("baz").semstr, "The second integer property.")
72
72
 
73
73
  # Architype (without body).
74
74
  sym_arch2 = code_gen.lookup("Arch2")
75
75
  self.assertEqual(sym_arch2.semstr, "An object that contains two integer properties.")
76
- self.assertEqual(sym_arch2.fetch_sym_tab.lookup("bar").semstr, "The first integer property.")
77
- self.assertEqual(sym_arch2.fetch_sym_tab.lookup("baz").semstr, "The second integer property.")
76
+ self.assertEqual(sym_arch2.symbol_table.lookup("bar").semstr, "The first integer property.")
77
+ self.assertEqual(sym_arch2.symbol_table.lookup("baz").semstr, "The second integer property.")
78
78
 
79
79
  # Enum (with body).
80
80
  sym_enum1 = code_gen.lookup("Enum1")
81
81
  self.assertEqual(sym_enum1.semstr, "An enumeration that defines two values: Bar and Baz.")
82
- self.assertEqual(sym_enum1.fetch_sym_tab.lookup("Bar").semstr, "The Bar value of the Enum1 enumeration.")
83
- self.assertEqual(sym_enum1.fetch_sym_tab.lookup("Baz").semstr, "The Baz value of the Enum1 enumeration.")
82
+ self.assertEqual(sym_enum1.symbol_table.lookup("Bar").semstr, "The Bar value of the Enum1 enumeration.")
83
+ self.assertEqual(sym_enum1.symbol_table.lookup("Baz").semstr, "The Baz value of the Enum1 enumeration.")
84
84
 
85
85
  # Enum (without body).
86
86
  sym_enum2 = code_gen.lookup("Enum2")
87
87
  self.assertEqual(sym_enum2.semstr, "An enumeration that defines two values: Bar and Baz.")
88
- self.assertEqual(sym_enum2.fetch_sym_tab.lookup("Bar").semstr, "The Bar value of the Enum2 enumeration.")
89
- self.assertEqual(sym_enum2.fetch_sym_tab.lookup("Baz").semstr, "The Baz value of the Enum2 enumeration.")
88
+ self.assertEqual(sym_enum2.symbol_table.lookup("Bar").semstr, "The Bar value of the Enum2 enumeration.")
89
+ self.assertEqual(sym_enum2.symbol_table.lookup("Baz").semstr, "The Baz value of the Enum2 enumeration.")
90
90
 
91
91
  if code_gen.gen.py_ast and isinstance(code_gen.gen.py_ast[0], ast3.Module):
92
92
  prog = compile(code_gen.gen.py_ast[0], filename="<ast>", mode="exec")
@@ -15,24 +15,24 @@ class SemDefMatchPassTests(TestCase):
15
15
  mod = out.mod.hub[self.fixture_abs_path("sem_def_match.jac")]
16
16
 
17
17
  self.assertEqual(mod.lookup("E").decl.name_of.semstr, "An enum representing some values.") # type: ignore
18
- self.assertEqual(mod.lookup("E").fetch_sym_tab.lookup("A").decl.name_of.semstr, "The first value of the enum E.") # type: ignore
19
- self.assertEqual(mod.lookup("E").fetch_sym_tab.lookup("B").decl.name_of.semstr, "The second value of the enum E.") # type: ignore
18
+ self.assertEqual(mod.lookup("E").symbol_table.lookup("A").decl.name_of.semstr, "The first value of the enum E.") # type: ignore
19
+ self.assertEqual(mod.lookup("E").symbol_table.lookup("B").decl.name_of.semstr, "The second value of the enum E.") # type: ignore
20
20
 
21
21
  self.assertEqual(mod.lookup("Person").decl.name_of.semstr, "A class representing a person.") # type: ignore
22
22
 
23
- person_scope = mod.lookup("Person").fetch_sym_tab # type: ignore
23
+ person_scope = mod.lookup("Person").symbol_table # type: ignore
24
24
  self.assertEqual(person_scope.lookup("name").decl.name_of.semstr, "The name of the person.") # type: ignore
25
25
  self.assertEqual(person_scope.lookup("yob").decl.name_of.semstr, "The year of birth of the person.") # type: ignore
26
26
 
27
27
  sym_calc_age = person_scope.lookup("calc_age") # type: ignore
28
28
  self.assertEqual(sym_calc_age.decl.name_of.semstr, "Calculate the age of the person.") # type: ignore
29
29
 
30
- calc_age_scope = sym_calc_age.fetch_sym_tab # type: ignore
30
+ calc_age_scope = sym_calc_age.symbol_table # type: ignore
31
31
  self.assertEqual(calc_age_scope.lookup("year").decl.name_of.semstr, "The year to calculate the age against.") # type: ignore
32
32
 
33
33
  self.assertEqual(mod.lookup("OuterClass").decl.name_of.semstr, "A class containing an inner class.") # type: ignore
34
- outer_scope = mod.lookup("OuterClass").fetch_sym_tab # type: ignore
34
+ outer_scope = mod.lookup("OuterClass").symbol_table # type: ignore
35
35
  self.assertEqual(outer_scope.lookup("InnerClass").decl.name_of.semstr, "An inner class within OuterClass.") # type: ignore
36
- inner_scope = outer_scope.lookup("InnerClass").fetch_sym_tab # type: ignore
36
+ inner_scope = outer_scope.lookup("InnerClass").symbol_table # type: ignore
37
37
  self.assertEqual(inner_scope.lookup("inner_value").decl.name_of.semstr, "A value specific to the inner class.") # type: ignore
38
38
 
@@ -484,6 +484,8 @@ class DocIRGenPass(UniPass):
484
484
  indent_parts.append(i.gen.doc_ir)
485
485
  else:
486
486
  parts.append(i.gen.doc_ir)
487
+ if isinstance(i, uni.Token) and i.name == Tok.KW_BY:
488
+ parts.append(self.space())
487
489
  node.gen.doc_ir = self.group(self.concat(parts))
488
490
 
489
491
  def exit_atom_trailer(self, node: uni.AtomTrailer) -> None:
@@ -51,3 +51,9 @@ def test_run {
51
51
  with entry {
52
52
  test_run();
53
53
  }
54
+
55
+
56
+ def generate_joke -> dict[str, str] by llm(
57
+ incl_info={"jokes_example" : self.jokes },
58
+ temperature=0.0
59
+ );
@@ -11,6 +11,7 @@ import jaclang.compiler.unitree as uni
11
11
  from jaclang.compiler.parser import JacParser
12
12
  from jaclang.compiler.passes.main import (
13
13
  Alert,
14
+ BinderPass,
14
15
  CFGBuildPass,
15
16
  DeclImplMatchPass,
16
17
  DefUsePass,
@@ -31,6 +32,7 @@ from jaclang.compiler.passes.tool import (
31
32
  FuseCommentsPass,
32
33
  JacFormatPass,
33
34
  )
35
+ from jaclang.runtimelib.utils import read_file_with_encoding
34
36
  from jaclang.utils.log import logging
35
37
 
36
38
 
@@ -73,7 +75,7 @@ class JacProgram:
73
75
  def parse_str(self, source_str: str, file_path: str) -> uni.Module:
74
76
  """Convert a Jac file to an AST."""
75
77
  had_error = False
76
- if file_path.endswith(".py"):
78
+ if file_path.endswith(".py") or file_path.endswith(".pyi"):
77
79
  parsed_ast = py_ast.parse(source_str)
78
80
  py_ast_ret = PyastBuildPass(
79
81
  ir_in=uni.PythonModuleAst(
@@ -103,15 +105,20 @@ class JacProgram:
103
105
  self, file_path: str, use_str: str | None = None, no_cgen: bool = False
104
106
  ) -> uni.Module:
105
107
  """Convert a Jac file to an AST."""
106
- if not use_str:
107
- with open(file_path, "r", encoding="utf-8") as file:
108
- use_str = file.read()
109
- mod_targ = self.parse_str(use_str, file_path)
108
+ keep_str = use_str or read_file_with_encoding(file_path)
109
+ mod_targ = self.parse_str(keep_str, file_path)
110
110
  self.run_schedule(mod=mod_targ, passes=ir_gen_sched)
111
111
  if not no_cgen:
112
112
  self.run_schedule(mod=mod_targ, passes=py_code_gen)
113
113
  return mod_targ
114
114
 
115
+ def bind(self, file_path: str, use_str: str | None = None) -> uni.Module:
116
+ """Bind the Jac module."""
117
+ keep_str = use_str or read_file_with_encoding(file_path)
118
+ mod_targ = self.parse_str(keep_str, file_path)
119
+ BinderPass(ir_in=mod_targ, prog=self)
120
+ return mod_targ
121
+
115
122
  def build(self, file_path: str, use_str: str | None = None) -> uni.Module:
116
123
  """Convert a Jac file to an AST."""
117
124
  mod_targ = self.compile(file_path, use_str)
@@ -141,9 +148,9 @@ class JacProgram:
141
148
  def jac_file_formatter(file_path: str) -> str:
142
149
  """Convert a Jac file to an AST."""
143
150
  prog = JacProgram()
144
- with open(file_path) as file:
145
- source = uni.Source(file.read(), mod_path=file_path)
146
- prse: Transform = JacParser(root_ir=source, prog=prog)
151
+ source_str = read_file_with_encoding(file_path)
152
+ source = uni.Source(source_str, mod_path=file_path)
153
+ prse: Transform = JacParser(root_ir=source, prog=prog)
147
154
  for i in format_sched:
148
155
  prse = i(ir_in=prse.ir_out, prog=prog)
149
156
  prse.errors_had = prog.errors_had
@@ -0,0 +1,32 @@
1
+ """Test jac.lark grammar for Shift/Reduce errors."""
2
+
3
+ import os
4
+
5
+ import jaclang
6
+ from jaclang.utils.test import TestCase
7
+
8
+ try:
9
+ from lark import Lark
10
+ from lark.exceptions import GrammarError
11
+ except ImportError: # pragma: no cover - lark should be installed for tests
12
+ Lark = None # type: ignore
13
+ GrammarError = Exception
14
+
15
+
16
+ class TestJacGrammar(TestCase):
17
+ """Check that jac.lark has no shift/reduce conflicts."""
18
+
19
+ def test_no_shift_reduce_errors(self) -> None:
20
+ """Ensure jac.lark parses with strict mode."""
21
+ if Lark is None:
22
+ self.fail("lark library not available")
23
+
24
+ lark_path = os.path.join(os.path.dirname(jaclang.__file__), "compiler/jac.lark")
25
+ with open(lark_path, "r", encoding="utf-8") as f:
26
+ grammar = f.read()
27
+
28
+ # Lark's strict mode raises GrammarError on conflicts
29
+ try:
30
+ Lark(grammar, parser="lalr", start="start", strict=True)
31
+ except GrammarError as e: # pragma: no cover - fail if conflicts
32
+ self.fail(f"Shift/reduce conflicts detected: {e}")
@@ -209,7 +209,7 @@ class UniNode:
209
209
 
210
210
  def flatten(self) -> list[UniNode]:
211
211
  """Flatten ast."""
212
- ret = [self]
212
+ ret: list[UniNode] = [self]
213
213
  for k in self.kid:
214
214
  ret += k.flatten()
215
215
  return ret
@@ -235,10 +235,12 @@ class Symbol:
235
235
  defn: NameAtom,
236
236
  access: SymbolAccess,
237
237
  parent_tab: UniScopeNode,
238
+ imported: bool = False,
238
239
  ) -> None:
239
240
  """Initialize."""
240
241
  self.defn: list[NameAtom] = [defn]
241
242
  self.uses: list[NameAtom] = []
243
+ self.imported: bool = imported
242
244
  defn.sym = self
243
245
  self.access: SymbolAccess = access
244
246
  self.parent_tab = parent_tab
@@ -271,7 +273,7 @@ class Symbol:
271
273
  return ".".join(out)
272
274
 
273
275
  @property
274
- def fetch_sym_tab(self) -> Optional[UniScopeNode]:
276
+ def symbol_table(self) -> Optional[UniScopeNode]:
275
277
  """Get symbol table."""
276
278
  return self.parent_tab.find_scope(self.sym_name)
277
279
 
@@ -333,6 +335,7 @@ class UniScopeNode(UniNode):
333
335
  access_spec: Optional[AstAccessNode] | SymbolAccess = None,
334
336
  single: bool = False,
335
337
  force_overwrite: bool = False,
338
+ imported: bool = False,
336
339
  ) -> Optional[UniNode]:
337
340
  """Set a variable in the symbol table.
338
341
 
@@ -353,6 +356,7 @@ class UniScopeNode(UniNode):
353
356
  else access_spec.access_type if access_spec else SymbolAccess.PUBLIC
354
357
  ),
355
358
  parent_tab=self,
359
+ imported=imported,
356
360
  )
357
361
  else:
358
362
  self.names_in_scope[node.sym_name].add_defn(node.name_spec)
@@ -386,6 +390,7 @@ class UniScopeNode(UniNode):
386
390
  access_spec: Optional[AstAccessNode] | SymbolAccess = None,
387
391
  single_decl: Optional[str] = None,
388
392
  force_overwrite: bool = False,
393
+ imported: bool = False,
389
394
  ) -> Optional[Symbol]:
390
395
  """Insert into symbol table."""
391
396
  if node.sym and self == node.sym.parent_tab:
@@ -395,6 +400,7 @@ class UniScopeNode(UniNode):
395
400
  single=single_decl is not None,
396
401
  access_spec=access_spec,
397
402
  force_overwrite=force_overwrite,
403
+ imported=imported,
398
404
  )
399
405
  self.update_py_ctx_for_def(node)
400
406
  return node.sym
@@ -967,8 +973,10 @@ class Module(AstDocNode, UniScopeNode):
967
973
  prog=JacProgram(),
968
974
  ).ir_out.gen.jac
969
975
 
970
- def unparse(self) -> str:
971
- super().unparse()
976
+ def unparse(self, requires_format: bool = True) -> str:
977
+ unparsed = super().unparse()
978
+ if not requires_format:
979
+ return unparsed
972
980
  return self.format()
973
981
 
974
982
  @staticmethod
@@ -1529,7 +1537,7 @@ class ImplDef(CodeBlockStmt, ElementStmt, ArchBlockStmt, AstSymbolNode, UniScope
1529
1537
  decorators: Optional[Sequence[Expr]],
1530
1538
  target: Sequence[NameAtom],
1531
1539
  spec: Sequence[Expr] | FuncSignature | EventSignature | None,
1532
- body: Sequence[CodeBlockStmt] | Sequence[EnumBlockStmt] | FuncCall,
1540
+ body: Sequence[CodeBlockStmt] | Sequence[EnumBlockStmt] | Expr,
1533
1541
  kid: Sequence[UniNode],
1534
1542
  doc: Optional[String] = None,
1535
1543
  decl_link: Optional[UniNode] = None,
@@ -1575,7 +1583,7 @@ class ImplDef(CodeBlockStmt, ElementStmt, ArchBlockStmt, AstSymbolNode, UniScope
1575
1583
  res = res and sp.normalize(deep)
1576
1584
  else:
1577
1585
  res = res and self.spec.normalize(deep) if self.spec else res
1578
- if isinstance(self.body, FuncCall):
1586
+ if isinstance(self.body, Expr):
1579
1587
  res = res and self.body.normalize(deep)
1580
1588
  else:
1581
1589
  for stmt in self.body:
@@ -1608,7 +1616,7 @@ class ImplDef(CodeBlockStmt, ElementStmt, ArchBlockStmt, AstSymbolNode, UniScope
1608
1616
  new_kid.append(self.gen_token(Tok.RPAREN))
1609
1617
  else:
1610
1618
  new_kid.append(self.spec)
1611
- if isinstance(self.body, FuncCall):
1619
+ if isinstance(self.body, Expr):
1612
1620
  new_kid.append(self.body)
1613
1621
  else:
1614
1622
  new_kid.append(self.gen_token(Tok.LBRACE))
@@ -1781,7 +1789,7 @@ class Ability(
1781
1789
  is_abstract: bool,
1782
1790
  access: Optional[SubTag[Token]],
1783
1791
  signature: FuncSignature | EventSignature | None,
1784
- body: Sequence[CodeBlockStmt] | ImplDef | FuncCall | None,
1792
+ body: Sequence[CodeBlockStmt] | ImplDef | Expr | None,
1785
1793
  kid: Sequence[UniNode],
1786
1794
  doc: Optional[String] = None,
1787
1795
  decorators: Sequence[Expr] | None = None,
@@ -1832,7 +1840,7 @@ class Ability(
1832
1840
 
1833
1841
  @property
1834
1842
  def is_genai_ability(self) -> bool:
1835
- return isinstance(self.body, FuncCall)
1843
+ return isinstance(self.body, Expr)
1836
1844
 
1837
1845
  def get_pos_argc_range(self) -> tuple[int, int]:
1838
1846
  """Get the range of positional arguments for this ability.
@@ -2285,8 +2293,7 @@ class ExprStmt(CodeBlockStmt):
2285
2293
  CodeBlockStmt.__init__(self)
2286
2294
 
2287
2295
  def normalize(self, deep: bool = True) -> bool:
2288
- if deep:
2289
- res = self.expr.normalize(deep)
2296
+ res = self.expr.normalize(deep) if deep else False
2290
2297
  new_kid: list[UniNode] = []
2291
2298
  if self.in_fstring:
2292
2299
  new_kid.append(self.expr)
@@ -3488,7 +3495,7 @@ class InnerCompr(AstAsyncNode, UniScopeNode):
3488
3495
  return res
3489
3496
 
3490
3497
 
3491
- class ListCompr(AtomExpr):
3498
+ class ListCompr(AtomExpr, UniScopeNode):
3492
3499
  """ListCompr node type for Jac Ast."""
3493
3500
 
3494
3501
  def __init__(
@@ -3502,6 +3509,7 @@ class ListCompr(AtomExpr):
3502
3509
  UniNode.__init__(self, kid=kid)
3503
3510
  Expr.__init__(self)
3504
3511
  AstSymbolStubNode.__init__(self, sym_type=SymbolType.SEQUENCE)
3512
+ UniScopeNode.__init__(self, name=f"{self.__class__.__name__}")
3505
3513
 
3506
3514
  def normalize(self, deep: bool = False) -> bool:
3507
3515
  res = True
@@ -3702,14 +3710,12 @@ class FuncCall(Expr):
3702
3710
  self,
3703
3711
  target: Expr,
3704
3712
  params: Sequence[Expr | KWPair] | None,
3705
- genai_call: Optional[FuncCall],
3713
+ genai_call: Optional[Expr],
3706
3714
  kid: Sequence[UniNode],
3707
- body_genai_call: Optional[FuncCall] = None,
3708
3715
  ) -> None:
3709
3716
  self.target = target
3710
3717
  self.params = list(params) if params else []
3711
3718
  self.genai_call = genai_call
3712
- self.body_genai_call = body_genai_call
3713
3719
  UniNode.__init__(self, kid=kid)
3714
3720
  Expr.__init__(self)
3715
3721
 
@@ -8,6 +8,7 @@ import from typing { Callable, Optional }
8
8
 
9
9
  import jaclang.compiler.unitree as uni;
10
10
  import from jaclang { JacMachineInterface as Jac }
11
+ import from jaclang.compiler.constant {SymbolType}
11
12
  import from jaclang.compiler.program { JacProgram }
12
13
  import from jaclang.compiler.unitree { UniScopeNode }
13
14
  import from sem_manager { SemTokManager }
@@ -259,7 +260,7 @@ class JacLangServer(JacProgram , LanguageServer) {
259
260
  }
260
261
  symb = temp_tab.lookup(symbol);
261
262
  if symb {
262
- fetc_tab = symb.fetch_sym_tab;
263
+ fetc_tab = symb.symbol_table;
263
264
  if fetc_tab {
264
265
  temp_tab = fetc_tab;
265
266
  } else {
@@ -284,9 +285,9 @@ class JacLangServer(JacProgram , LanguageServer) {
284
285
  }
285
286
  }
286
287
  for base_class_symbol in base {
287
- if base_class_symbol.fetch_sym_tab {
288
+ if base_class_symbol.symbol_table {
288
289
  completion_items += utils.collect_all_symbols_in_scope(
289
- base_class_symbol.fetch_sym_tab,
290
+ base_class_symbol.symbol_table,
290
291
  up_tree=False
291
292
  );
292
293
  }
@@ -446,7 +447,25 @@ class JacLangServer(JacProgram , LanguageServer) {
446
447
  }
447
448
  node_selected = sem_mgr.static_sem_tokens[token_index][3];
448
449
  if node_selected {
449
- if isinstance(node_selected, uni.Name)
450
+ if (node_selected.sym.sym_type == SymbolType.MODULE) {
451
+ spec = node_selected.sym.decl.parent.resolve_relative_path();
452
+ if spec {
453
+ spec = spec[ 5 : ] if spec.startswith('File:') else spec;
454
+ return lspt.Location(
455
+ uri=uris.from_fs_path(spec),
456
+ range=lspt.Range(
457
+ start=lspt.Position(line=0, character=0),
458
+ end=lspt.Position(line=0, character=0)
459
+ )
460
+ );
461
+ } else {
462
+ return None;
463
+ }
464
+ }
465
+ if isinstance(node_selected.sym, uni.NameAtom) {
466
+ node_selected = node_selected.name_of;
467
+ }
468
+ elif isinstance(node_selected, uni.Name)
450
469
  and node_selected.parent
451
470
  and isinstance(node_selected.parent, uni.ModulePath)
452
471
  {
@@ -588,3 +588,16 @@ class TestJacLangServer(TestCase):
588
588
  )
589
589
  for expected in expected_refs:
590
590
  self.assertIn(expected, references)
591
+
592
+ def test_binder_go_to_module(self) -> None:
593
+ """Test that the go to definition is correct."""
594
+ lsp = JacLangServer()
595
+ workspace_path = self.fixture_abs_path("")
596
+ workspace = Workspace(workspace_path, lsp)
597
+ lsp.lsp._workspace = workspace
598
+ guess_game_file = uris.from_fs_path(self.fixture_abs_path('../../../compiler/passes/main/tests/fixtures/sym_binder.jac'))
599
+ lsp.deep_check(guess_game_file)
600
+ self.assertIn(
601
+ "/tests/fixtures/M1.jac:0:0-0:0",
602
+ str(lsp.get_definition(guess_game_file, lspt.Position(29, 9))),
603
+ )
@@ -2,10 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import importlib
6
- import importlib.util
7
5
  import os
8
- import sys
9
6
  import types
10
7
  from os import getcwd, path
11
8
  from typing import Optional, Union
@@ -146,69 +143,43 @@ class Importer:
146
143
 
147
144
 
148
145
  class PythonImporter(Importer):
149
- """Importer for Python modules."""
146
+ """Importer for Python modules using Jac AST conversion."""
147
+
148
+ def __init__(self) -> None:
149
+ """Initialize the Python importer."""
150
+ super().__init__()
151
+ from jaclang.utils.module_resolver import PythonModuleResolver
152
+
153
+ self.resolver = PythonModuleResolver()
154
+
155
+ def load_and_execute(self, file_path: str) -> types.ModuleType:
156
+ """Convert Python file to Jac AST and create module."""
157
+ module_name = os.path.splitext(os.path.basename(file_path))[0]
158
+ module = types.ModuleType(module_name)
159
+ module.__file__ = file_path
160
+ module.__name__ = "__main__"
161
+
162
+ from jaclang.runtimelib.machine import JacMachine
163
+
164
+ codeobj = JacMachine.program.get_bytecode(full_target=file_path)
165
+ if codeobj:
166
+ exec(codeobj, module.__dict__)
167
+ else:
168
+ raise ImportError(f"Failed to generate bytecode for {file_path}")
169
+
170
+ return module
150
171
 
151
172
  def run_import(self, spec: ImportPathSpec) -> ImportReturn:
152
- """Run the import process for Python modules."""
173
+ """Run the import process for Python modules using Jac AST."""
153
174
  try:
154
- loaded_items: list = []
155
- if spec.target.startswith("."):
156
- spec.target = spec.target.lstrip(".")
157
- if len(spec.target.split(".")) > 1:
158
- spec.target = spec.target.split(".")[-1]
159
- full_target = path.normpath(path.join(spec.caller_dir, spec.target))
160
- imp_spec = importlib.util.spec_from_file_location(
161
- spec.target, full_target + ".py"
162
- )
163
- if imp_spec and imp_spec.loader:
164
- imported_module = importlib.util.module_from_spec(imp_spec)
165
- sys.modules[imp_spec.name] = imported_module
166
- imp_spec.loader.exec_module(imported_module)
167
- else:
168
- raise ImportError(
169
- f"Cannot find module {spec.target} at {full_target}"
170
- )
171
- else:
172
- imported_module = importlib.import_module(name=spec.target)
173
-
174
- main_module = __import__("__main__")
175
- if spec.absorb:
176
- for name in dir(imported_module):
177
- if not name.startswith("_"):
178
- setattr(main_module, name, getattr(imported_module, name))
179
-
180
- elif spec.items:
181
- for name, alias in spec.items.items():
182
- if isinstance(alias, bool):
183
- alias = name
184
- try:
185
- item = getattr(imported_module, name)
186
- if item not in loaded_items:
187
- setattr(
188
- main_module,
189
- alias if isinstance(alias, str) else name,
190
- item,
191
- )
192
- loaded_items.append(item)
193
- except AttributeError as e:
194
- if hasattr(imported_module, "__path__"):
195
- item = importlib.import_module(f"{spec.target}.{name}")
196
- if item not in loaded_items:
197
- setattr(
198
- main_module,
199
- alias if isinstance(alias, str) else name,
200
- item,
201
- )
202
- loaded_items.append(item)
203
- else:
204
- raise e
175
+ python_file_path = self.resolver.resolve_module_path(
176
+ target=spec.target,
177
+ base_path=spec.base_path,
178
+ )
179
+ imported_module = self.load_and_execute(python_file_path)
180
+ # JacMachineInterface.load_module(imported_module.__name__, imported_module)
205
181
 
206
- else:
207
- setattr(
208
- __import__("__main__"),
209
- spec.mdl_alias if isinstance(spec.mdl_alias, str) else spec.target,
210
- imported_module,
211
- )
182
+ loaded_items: list = []
212
183
  self.result = ImportReturn(imported_module, loaded_items, self)
213
184
  return self.result
214
185