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.
- jaclang/cli/cli.py +74 -22
- jaclang/compiler/jac.lark +3 -3
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +14 -21
- jaclang/compiler/passes/main/__init__.py +3 -1
- jaclang/compiler/passes/main/binder_pass.py +594 -0
- jaclang/compiler/passes/main/import_pass.py +8 -256
- jaclang/compiler/passes/main/inheritance_pass.py +2 -2
- jaclang/compiler/passes/main/pyast_gen_pass.py +35 -69
- jaclang/compiler/passes/main/pyast_load_pass.py +24 -13
- jaclang/compiler/passes/main/sem_def_match_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/M1.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +47 -0
- jaclang/compiler/passes/main/tests/test_binder_pass.py +111 -0
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +13 -13
- jaclang/compiler/passes/main/tests/test_sem_def_match_pass.py +6 -6
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +2 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +6 -0
- jaclang/compiler/program.py +15 -8
- jaclang/compiler/tests/test_sr_errors.py +32 -0
- jaclang/compiler/unitree.py +21 -15
- jaclang/langserve/engine.jac +23 -4
- jaclang/langserve/tests/test_server.py +13 -0
- jaclang/runtimelib/importer.py +33 -62
- jaclang/runtimelib/utils.py +29 -0
- jaclang/tests/fixtures/pyfunc_fmt.py +60 -0
- jaclang/tests/fixtures/pyfunc_fstr.py +25 -0
- jaclang/tests/fixtures/pyfunc_kwesc.py +33 -0
- jaclang/tests/fixtures/python_run_test.py +19 -0
- jaclang/tests/test_cli.py +67 -0
- jaclang/tests/test_language.py +96 -1
- jaclang/utils/lang_tools.py +3 -3
- jaclang/utils/module_resolver.py +90 -0
- jaclang/utils/symtable_test_helpers.py +125 -0
- jaclang/utils/test.py +3 -4
- jaclang/vendor/interegular/__init__.py +34 -0
- jaclang/vendor/interegular/comparator.py +163 -0
- jaclang/vendor/interegular/fsm.py +1015 -0
- jaclang/vendor/interegular/patterns.py +732 -0
- jaclang/vendor/interegular/py.typed +0 -0
- jaclang/vendor/interegular/utils/__init__.py +15 -0
- jaclang/vendor/interegular/utils/simple_parser.py +165 -0
- jaclang/vendor/interegular-0.3.3.dist-info/INSTALLER +1 -0
- jaclang/vendor/interegular-0.3.3.dist-info/LICENSE.txt +21 -0
- jaclang/vendor/interegular-0.3.3.dist-info/METADATA +64 -0
- jaclang/vendor/interegular-0.3.3.dist-info/RECORD +20 -0
- jaclang/vendor/interegular-0.3.3.dist-info/REQUESTED +0 -0
- jaclang/vendor/interegular-0.3.3.dist-info/WHEEL +5 -0
- jaclang/vendor/interegular-0.3.3.dist-info/top_level.txt +1 -0
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/METADATA +1 -1
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/RECORD +53 -29
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/WHEEL +0 -0
- {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.
|
|
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.
|
|
59
|
-
self.assertEqual(sym_fn2.
|
|
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.
|
|
65
|
-
self.assertEqual(sym_fn3.
|
|
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.
|
|
71
|
-
self.assertEqual(sym_arch1.
|
|
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.
|
|
77
|
-
self.assertEqual(sym_arch2.
|
|
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.
|
|
83
|
-
self.assertEqual(sym_enum1.
|
|
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.
|
|
89
|
-
self.assertEqual(sym_enum2.
|
|
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").
|
|
19
|
-
self.assertEqual(mod.lookup("E").
|
|
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").
|
|
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.
|
|
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").
|
|
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").
|
|
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:
|
jaclang/compiler/program.py
CHANGED
|
@@ -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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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}")
|
jaclang/compiler/unitree.py
CHANGED
|
@@ -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
|
|
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] |
|
|
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,
|
|
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,
|
|
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 |
|
|
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,
|
|
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[
|
|
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
|
|
jaclang/langserve/engine.jac
CHANGED
|
@@ -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.
|
|
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.
|
|
288
|
+
if base_class_symbol.symbol_table {
|
|
288
289
|
completion_items += utils.collect_all_symbols_in_scope(
|
|
289
|
-
base_class_symbol.
|
|
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
|
|
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
|
+
)
|
jaclang/runtimelib/importer.py
CHANGED
|
@@ -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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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
|
|