jaclang 0.8.5__py3-none-any.whl → 0.8.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.
- jaclang/cli/cli.md +1 -0
- jaclang/cli/cli.py +38 -18
- jaclang/compiler/passes/main/__init__.py +2 -0
- jaclang/compiler/passes/main/cfg_build_pass.py +21 -1
- jaclang/compiler/passes/main/inheritance_pass.py +8 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +57 -8
- jaclang/compiler/passes/main/sym_tab_build_pass.py +4 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_has_var.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_if_no_else.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_return.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_imported.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_importer.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/import_symbol_type_infer.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/infer_type_assignment.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac +8 -0
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +62 -24
- jaclang/compiler/passes/main/tests/test_checker_pass.py +87 -0
- jaclang/compiler/passes/main/type_checker_pass.py +128 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +1 -4
- jaclang/compiler/program.py +17 -3
- jaclang/compiler/type_system/__init__.py +1 -0
- jaclang/compiler/type_system/type_evaluator.py +421 -0
- jaclang/compiler/type_system/type_utils.py +41 -0
- jaclang/compiler/type_system/types.py +240 -0
- jaclang/compiler/unitree.py +15 -9
- jaclang/langserve/dev_engine.jac +645 -0
- jaclang/langserve/dev_server.jac +201 -0
- jaclang/langserve/engine.jac +1 -1
- jaclang/langserve/tests/server_test/test_lang_serve.py +2 -2
- jaclang/langserve/tests/test_dev_server.py +80 -0
- jaclang/runtimelib/builtin.py +28 -39
- jaclang/runtimelib/importer.py +1 -1
- jaclang/runtimelib/machine.py +48 -64
- jaclang/runtimelib/memory.py +23 -5
- jaclang/runtimelib/tests/fixtures/savable_object.jac +10 -2
- jaclang/runtimelib/utils.py +13 -6
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/py_run.jac +8 -0
- jaclang/tests/fixtures/py_run.py +23 -0
- jaclang/tests/fixtures/pyfunc.py +2 -0
- jaclang/tests/test_cli.py +40 -0
- jaclang/tests/test_language.py +10 -4
- jaclang/utils/lang_tools.py +3 -0
- {jaclang-0.8.5.dist-info → jaclang-0.8.6.dist-info}/METADATA +2 -1
- {jaclang-0.8.5.dist-info → jaclang-0.8.6.dist-info}/RECORD +52 -31
- {jaclang-0.8.5.dist-info → jaclang-0.8.6.dist-info}/WHEEL +0 -0
- {jaclang-0.8.5.dist-info → jaclang-0.8.6.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.md
CHANGED
|
@@ -36,6 +36,7 @@ Parameters to execute the tool command:
|
|
|
36
36
|
- `sym.`: Generates a dot graph representation of the symbol table for the specified .jac file.
|
|
37
37
|
- `ast`: Displays the Abstract Syntax Tree (AST) of the specified .jac file.
|
|
38
38
|
- `ast.`: Generates a dot graph representation of the AST for the specified .jac file.
|
|
39
|
+
- `cfg.`: Generates a dot graph representatuin of the CFG(Control Flow Graph) for a specific .jac file
|
|
39
40
|
- `pyast`: Generates the Python AST for a .py file or the relevant Python AST for the generated Python code from a .jac file.
|
|
40
41
|
- `py`: Displays the relevant generated Python code for the respective Jac code in a .jac file.
|
|
41
42
|
- `file_path`: Path to the .jac or .py file.
|
jaclang/cli/cli.py
CHANGED
|
@@ -16,17 +16,12 @@ from jaclang.compiler.passes.main import PyastBuildPass
|
|
|
16
16
|
from jaclang.compiler.program import JacProgram
|
|
17
17
|
from jaclang.runtimelib.builtin import printgraph
|
|
18
18
|
from jaclang.runtimelib.constructs import WalkerArchetype
|
|
19
|
-
from jaclang.runtimelib.machine import
|
|
20
|
-
ExecutionContext,
|
|
21
|
-
JacMachine as Jac,
|
|
22
|
-
JacMachineInterface as JacInterface,
|
|
23
|
-
)
|
|
19
|
+
from jaclang.runtimelib.machine import ExecutionContext, JacMachine as Jac
|
|
24
20
|
from jaclang.runtimelib.utils import read_file_with_encoding
|
|
25
21
|
from jaclang.utils.helpers import debugger as db
|
|
26
22
|
from jaclang.utils.lang_tools import AstTool
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
JacInterface.create_cmd()
|
|
24
|
+
Jac.create_cmd()
|
|
30
25
|
Jac.setup()
|
|
31
26
|
|
|
32
27
|
|
|
@@ -140,6 +135,7 @@ def run(
|
|
|
140
135
|
# if no session specified, check if it was defined when starting the command shell
|
|
141
136
|
# otherwise default to jaclang.session
|
|
142
137
|
base, mod, mach = proc_file_sess(filename, session)
|
|
138
|
+
lng = filename.split(".")[-1]
|
|
143
139
|
Jac.set_base_path(base)
|
|
144
140
|
|
|
145
141
|
if filename.endswith((".jac", ".py")):
|
|
@@ -148,6 +144,7 @@ def run(
|
|
|
148
144
|
target=mod,
|
|
149
145
|
base_path=base,
|
|
150
146
|
override_name="__main__" if main else None,
|
|
147
|
+
lng=lng,
|
|
151
148
|
)
|
|
152
149
|
except Exception as e:
|
|
153
150
|
print(f"Error running {filename}: {e}", file=sys.stderr)
|
|
@@ -159,6 +156,7 @@ def run(
|
|
|
159
156
|
target=mod,
|
|
160
157
|
base_path=base,
|
|
161
158
|
override_name="__main__" if main else None,
|
|
159
|
+
lng=lng,
|
|
162
160
|
)
|
|
163
161
|
except Exception as e:
|
|
164
162
|
print(f"Error running {filename}: {e}", file=sys.stderr)
|
|
@@ -219,7 +217,7 @@ def get_object(filename: str, id: str, session: str = "", main: bool = True) ->
|
|
|
219
217
|
|
|
220
218
|
|
|
221
219
|
@cmd_registry.register
|
|
222
|
-
def build(filename: str) -> None:
|
|
220
|
+
def build(filename: str, typecheck: bool = False) -> None:
|
|
223
221
|
"""Build the specified .jac file.
|
|
224
222
|
|
|
225
223
|
Compiles a Jac source file into a Jac Intermediate Representation (.jir) file,
|
|
@@ -227,21 +225,25 @@ def build(filename: str) -> None:
|
|
|
227
225
|
|
|
228
226
|
Args:
|
|
229
227
|
filename: Path to the .jac file to build
|
|
230
|
-
typecheck: Perform type checking during build (default:
|
|
228
|
+
typecheck: Perform type checking during build (default: False)
|
|
231
229
|
|
|
232
230
|
Examples:
|
|
233
231
|
jac build myprogram.jac
|
|
234
|
-
jac build myprogram.jac --
|
|
232
|
+
jac build myprogram.jac --typecheck
|
|
235
233
|
"""
|
|
236
|
-
if filename.endswith(".jac"):
|
|
237
|
-
(out := JacProgram()).compile(file_path=filename)
|
|
238
|
-
errs = len(out.errors_had)
|
|
239
|
-
warnings = len(out.warnings_had)
|
|
240
|
-
print(f"Errors: {errs}, Warnings: {warnings}")
|
|
241
|
-
with open(filename[:-4] + ".jir", "wb") as f:
|
|
242
|
-
pickle.dump(out, f)
|
|
243
|
-
else:
|
|
234
|
+
if not filename.endswith(".jac"):
|
|
244
235
|
print("Not a .jac file.", file=sys.stderr)
|
|
236
|
+
exit(1)
|
|
237
|
+
(out := JacProgram()).compile(file_path=filename, type_check=typecheck)
|
|
238
|
+
errs = len(out.errors_had)
|
|
239
|
+
warnings = len(out.warnings_had)
|
|
240
|
+
print(f"Errors: {errs}, Warnings: {warnings}")
|
|
241
|
+
|
|
242
|
+
for alrt in out.errors_had + out.warnings_had:
|
|
243
|
+
print(alrt.pretty_print(), file=sys.stderr)
|
|
244
|
+
|
|
245
|
+
with open(filename[:-4] + ".jir", "wb") as f:
|
|
246
|
+
pickle.dump(out, f)
|
|
245
247
|
|
|
246
248
|
|
|
247
249
|
@cmd_registry.register
|
|
@@ -325,6 +327,24 @@ def lsp() -> None:
|
|
|
325
327
|
run_lang_server()
|
|
326
328
|
|
|
327
329
|
|
|
330
|
+
@cmd_registry.register
|
|
331
|
+
def lsp_dev() -> None:
|
|
332
|
+
"""Run Jac Language Server Protocol in Developer Mode.
|
|
333
|
+
|
|
334
|
+
Starts the experimental Jac Language Server with enhanced features
|
|
335
|
+
for development and testing. Used by editor extensions in developer mode.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
This command takes no parameters.
|
|
339
|
+
|
|
340
|
+
Examples:
|
|
341
|
+
jac lsp_dev
|
|
342
|
+
"""
|
|
343
|
+
from jaclang.langserve.dev_server import run_lang_server
|
|
344
|
+
|
|
345
|
+
run_lang_server()
|
|
346
|
+
|
|
347
|
+
|
|
328
348
|
@cmd_registry.register
|
|
329
349
|
def enter(
|
|
330
350
|
filename: str,
|
|
@@ -9,6 +9,7 @@ from .def_use_pass import DefUsePass # noqa: I100
|
|
|
9
9
|
from .sem_def_match_pass import SemDefMatchPass # noqa: I100
|
|
10
10
|
from .import_pass import JacImportDepsPass # noqa: I100
|
|
11
11
|
from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
|
|
12
|
+
from .type_checker_pass import TypeCheckPass # noqa: I100
|
|
12
13
|
from .pyast_load_pass import PyastBuildPass # type: ignore # noqa: I100
|
|
13
14
|
from .pyast_gen_pass import PyastGenPass # noqa: I100
|
|
14
15
|
from .pybc_gen_pass import PyBytecodeGenPass # noqa: I100
|
|
@@ -25,6 +26,7 @@ __all__ = [
|
|
|
25
26
|
"JacImportDepsPass",
|
|
26
27
|
"PyImportDepsPass",
|
|
27
28
|
"BinderPass",
|
|
29
|
+
"TypeCheckPass",
|
|
28
30
|
"SymTabBuildPass",
|
|
29
31
|
"SymTabLinkPass",
|
|
30
32
|
"DeclImplMatchPass",
|
|
@@ -152,7 +152,7 @@ class CFGBuildPass(UniPass):
|
|
|
152
152
|
"""Exit BasicBlockStmt nodes."""
|
|
153
153
|
if isinstance(node, uni.UniCFGNode) and not isinstance(node, uni.Semi):
|
|
154
154
|
self.first_exit = True
|
|
155
|
-
if not node.bb_out:
|
|
155
|
+
if not node.bb_out and not isinstance(node, (uni.ReturnStmt, uni.ArchHas)):
|
|
156
156
|
self.to_connect.append(node)
|
|
157
157
|
if (
|
|
158
158
|
isinstance(node, (uni.InForStmt, uni.IterForStmt))
|
|
@@ -176,6 +176,8 @@ class CFGBuildPass(UniPass):
|
|
|
176
176
|
if from_node in self.to_connect:
|
|
177
177
|
self.to_connect.remove(from_node)
|
|
178
178
|
self.ability_stack.pop()
|
|
179
|
+
elif isinstance(node, (uni.IfStmt, uni.ElseIf)) and not node.else_body:
|
|
180
|
+
self.to_connect.append(node)
|
|
179
181
|
|
|
180
182
|
def after_pass(self) -> None:
|
|
181
183
|
"""After pass."""
|
|
@@ -299,3 +301,21 @@ class CoalesceBBPass(UniPass):
|
|
|
299
301
|
|
|
300
302
|
dot += "}\n"
|
|
301
303
|
return dot
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def cfg_dot_from_file(file_name: str) -> str:
|
|
307
|
+
"""Print the control flow graph."""
|
|
308
|
+
from jaclang.compiler.program import JacProgram
|
|
309
|
+
|
|
310
|
+
with open(file_name, "r") as f:
|
|
311
|
+
file_source = f.read()
|
|
312
|
+
|
|
313
|
+
ir = (prog := JacProgram()).compile(use_str=file_source, file_path=file_name)
|
|
314
|
+
|
|
315
|
+
cfg_pass = CoalesceBBPass(
|
|
316
|
+
ir_in=ir,
|
|
317
|
+
prog=prog,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
dot = cfg_pass.printgraph_cfg()
|
|
321
|
+
return dot
|
|
@@ -62,9 +62,16 @@ class InheritancePass(Transform[uni.Module, uni.Module]):
|
|
|
62
62
|
|
|
63
63
|
base_class_symbol_table = base_class_symbol.symbol_table
|
|
64
64
|
|
|
65
|
+
# FIXME: If the base class symbol is imported from another module, the symbol table
|
|
66
|
+
# will be None. The imported symbols were ignored and introduced in the typecheck
|
|
67
|
+
# for imported module items. This needs to be investigated to ensure that even imported
|
|
68
|
+
# classes should have a symbol table (unless the module is not parsed and decided not to).
|
|
69
|
+
if base_class_symbol_table is None:
|
|
70
|
+
return
|
|
71
|
+
|
|
65
72
|
if self.is_missing_py_symbol_table(base_class_symbol, base_class_symbol_table):
|
|
66
73
|
return
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
node.sym_tab.inherit_sym_tab(base_class_symbol_table)
|
|
69
76
|
|
|
70
77
|
def inherit_from_atom_trailer(
|
|
@@ -187,6 +187,15 @@ class PyastGenPass(UniPass):
|
|
|
187
187
|
),
|
|
188
188
|
)
|
|
189
189
|
|
|
190
|
+
def needs_mtllm(self) -> None:
|
|
191
|
+
"""Ensure byLLM is imported only once."""
|
|
192
|
+
self._add_preamble_once(
|
|
193
|
+
self.needs_mtllm.__name__,
|
|
194
|
+
ast3.Import(
|
|
195
|
+
names=[self.sync(ast3.alias(name="byllm"), jac_node=self.ir_out)]
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
190
199
|
def needs_enum(self) -> None:
|
|
191
200
|
"""Ensure Enum utilities are imported only once."""
|
|
192
201
|
self._add_preamble_once(
|
|
@@ -735,17 +744,25 @@ class PyastGenPass(UniPass):
|
|
|
735
744
|
self, model: ast3.expr, caller: ast3.expr, args: ast3.Dict
|
|
736
745
|
) -> ast3.Call:
|
|
737
746
|
"""Reusable method to codegen call_llm(model, caller, args)."""
|
|
738
|
-
|
|
747
|
+
self.needs_mtllm()
|
|
748
|
+
mtir_cls_ast = self.sync(
|
|
749
|
+
ast3.Attribute(
|
|
750
|
+
value=self.sync(ast3.Name(id="byllm", ctx=ast3.Load())),
|
|
751
|
+
attr="MTIR",
|
|
752
|
+
ctx=ast3.Load(),
|
|
753
|
+
)
|
|
754
|
+
)
|
|
755
|
+
mtir_ast = self.sync(
|
|
739
756
|
ast3.Call(
|
|
740
|
-
func=self.
|
|
757
|
+
func=self.sync(
|
|
758
|
+
ast3.Attribute(
|
|
759
|
+
value=mtir_cls_ast,
|
|
760
|
+
attr="factory",
|
|
761
|
+
ctx=ast3.Load(),
|
|
762
|
+
)
|
|
763
|
+
),
|
|
741
764
|
args=[],
|
|
742
765
|
keywords=[
|
|
743
|
-
self.sync(
|
|
744
|
-
ast3.keyword(
|
|
745
|
-
arg="model",
|
|
746
|
-
value=model,
|
|
747
|
-
)
|
|
748
|
-
),
|
|
749
766
|
self.sync(
|
|
750
767
|
ast3.keyword(
|
|
751
768
|
arg="caller",
|
|
@@ -758,6 +775,38 @@ class PyastGenPass(UniPass):
|
|
|
758
775
|
value=args,
|
|
759
776
|
)
|
|
760
777
|
),
|
|
778
|
+
self.sync(
|
|
779
|
+
ast3.keyword(
|
|
780
|
+
arg="call_params",
|
|
781
|
+
value=self.sync(
|
|
782
|
+
ast3.Attribute(
|
|
783
|
+
value=model,
|
|
784
|
+
attr="call_params",
|
|
785
|
+
ctx=ast3.Load(),
|
|
786
|
+
),
|
|
787
|
+
),
|
|
788
|
+
)
|
|
789
|
+
),
|
|
790
|
+
],
|
|
791
|
+
)
|
|
792
|
+
)
|
|
793
|
+
return self.sync(
|
|
794
|
+
ast3.Call(
|
|
795
|
+
func=self.jaclib_obj("call_llm"),
|
|
796
|
+
args=[],
|
|
797
|
+
keywords=[
|
|
798
|
+
self.sync(
|
|
799
|
+
ast3.keyword(
|
|
800
|
+
arg="model",
|
|
801
|
+
value=model,
|
|
802
|
+
)
|
|
803
|
+
),
|
|
804
|
+
self.sync(
|
|
805
|
+
ast3.keyword(
|
|
806
|
+
arg="mtir",
|
|
807
|
+
value=mtir_ast,
|
|
808
|
+
)
|
|
809
|
+
),
|
|
761
810
|
],
|
|
762
811
|
)
|
|
763
812
|
)
|
|
@@ -84,6 +84,10 @@ class SymTabBuildPass(UniPass):
|
|
|
84
84
|
else:
|
|
85
85
|
pass # Need to support pythonic import symbols with dots in it
|
|
86
86
|
|
|
87
|
+
def exit_module_item(self, node: uni.ModuleItem) -> None:
|
|
88
|
+
sym_node = node.alias or node.name
|
|
89
|
+
sym_node.sym_tab.def_insert(sym_node, single_decl="import")
|
|
90
|
+
|
|
87
91
|
def enter_archetype(self, node: uni.Archetype) -> None:
|
|
88
92
|
self.push_scope_and_link(node)
|
|
89
93
|
assert node.parent_scope is not None
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import math as alias;
|
|
2
|
+
with entry {
|
|
3
|
+
|
|
4
|
+
# math module imports sys so it has the symbol
|
|
5
|
+
# we're not using math.pi since it's a Final[float]
|
|
6
|
+
# and we haven't implemented generic types yet.
|
|
7
|
+
m = alias;
|
|
8
|
+
|
|
9
|
+
i: int = m.sys.prefix; # <-- Error
|
|
10
|
+
s: str = m.sys.prefix; # <-- Ok
|
|
11
|
+
}
|
|
@@ -16,19 +16,9 @@ class TestCFGBuildPass(TestCase):
|
|
|
16
16
|
"""Test basic blocks."""
|
|
17
17
|
file_name = self.fixture_abs_path("cfg_gen.jac")
|
|
18
18
|
|
|
19
|
-
from jaclang.compiler.passes.main.cfg_build_pass import
|
|
19
|
+
from jaclang.compiler.passes.main.cfg_build_pass import cfg_dot_from_file
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
file_source = f.read()
|
|
23
|
-
|
|
24
|
-
ir = (prog := JacProgram()).compile(use_str=file_source, file_path=file_name)
|
|
25
|
-
|
|
26
|
-
cfg_pass = CoalesceBBPass(
|
|
27
|
-
ir_in=ir,
|
|
28
|
-
prog=prog,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
dot = cfg_pass.printgraph_cfg()
|
|
21
|
+
dot = cfg_dot_from_file(file_name=file_name)
|
|
32
22
|
|
|
33
23
|
expected_dot = (
|
|
34
24
|
"digraph G {\n"
|
|
@@ -62,19 +52,9 @@ class TestCFGBuildPass(TestCase):
|
|
|
62
52
|
"""Test basic blocks."""
|
|
63
53
|
file_name = self.fixture_abs_path("cfg_ability_test.jac")
|
|
64
54
|
|
|
65
|
-
from jaclang.compiler.passes.main.cfg_build_pass import
|
|
66
|
-
|
|
67
|
-
with open(file_name, "r") as f:
|
|
68
|
-
file_source = f.read()
|
|
55
|
+
from jaclang.compiler.passes.main.cfg_build_pass import cfg_dot_from_file
|
|
69
56
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
cfg_pass = CoalesceBBPass(
|
|
73
|
-
ir_in=ir,
|
|
74
|
-
prog=prog,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
dot = cfg_pass.printgraph_cfg()
|
|
57
|
+
dot = cfg_dot_from_file(file_name=file_name)
|
|
78
58
|
|
|
79
59
|
expected_dot = (
|
|
80
60
|
"digraph G {\n"
|
|
@@ -93,3 +73,61 @@ class TestCFGBuildPass(TestCase):
|
|
|
93
73
|
)
|
|
94
74
|
|
|
95
75
|
self.assertEqual(dot, expected_dot)
|
|
76
|
+
|
|
77
|
+
def test_cfg_ability_with_has(self) -> None:
|
|
78
|
+
"""Test basic blocks with ability and has."""
|
|
79
|
+
file_name = self.fixture_abs_path("cfg_has_var.jac")
|
|
80
|
+
|
|
81
|
+
from jaclang.compiler.passes.main.cfg_build_pass import cfg_dot_from_file
|
|
82
|
+
|
|
83
|
+
dot = cfg_dot_from_file(file_name=file_name)
|
|
84
|
+
|
|
85
|
+
expected_dot = (
|
|
86
|
+
"digraph G {\n"
|
|
87
|
+
' 0 [label="BB0\\nobj Rock", shape=box];\n'
|
|
88
|
+
' 1 [label="BB1\\nhas pellets : list ;", shape=box];\n'
|
|
89
|
+
' 2 [label="BB2\\ncan count_pellets( ) -> int\\nreturn self . pellets . length ( ) ;", shape=box];\n'
|
|
90
|
+
' 3 [label="BB3\\nrock = Rock ( pellets = [ 1 , 2 , 3 ] ) ;\\nprint ( \\"Number of pellets: \\" + rock . count_pellets ( ) . to_string ( ) ) ;", shape=box];\n'
|
|
91
|
+
" 0 -> 1;\n"
|
|
92
|
+
" 0 -> 2;\n"
|
|
93
|
+
"}\n"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.assertEqual(dot, expected_dot)
|
|
97
|
+
|
|
98
|
+
def test_cfg_if_no_else(self) -> None:
|
|
99
|
+
"""Test basic blocks with if without else."""
|
|
100
|
+
file_name = self.fixture_abs_path("cfg_if_no_else.jac")
|
|
101
|
+
|
|
102
|
+
from jaclang.compiler.passes.main.cfg_build_pass import cfg_dot_from_file
|
|
103
|
+
|
|
104
|
+
dot = cfg_dot_from_file(file_name=file_name)
|
|
105
|
+
|
|
106
|
+
expected_dot = (
|
|
107
|
+
"digraph G {\n"
|
|
108
|
+
' 0 [label="BB0\\ncan test_if_without_else( x : int )\\nif ( x > 0 )", shape=box];\n'
|
|
109
|
+
' 1 [label="BB1\\nprint ( \\"Positive\\" ) ;", shape=box];\n'
|
|
110
|
+
' 2 [label="BB2\\nprint ( \\"Done\\" ) ;", shape=box];\n'
|
|
111
|
+
' 3 [label="BB3\\ntest_if_without_else ( 5 ) ;\\ntest_if_without_else ( - 3 ) ;", shape=box];\n'
|
|
112
|
+
" 0 -> 1;\n"
|
|
113
|
+
" 0 -> 2;\n"
|
|
114
|
+
" 1 -> 2;\n"
|
|
115
|
+
"}\n"
|
|
116
|
+
)
|
|
117
|
+
self.assertEqual(dot, expected_dot)
|
|
118
|
+
|
|
119
|
+
def test_cfg_return_stmt(self) -> None:
|
|
120
|
+
"""Test basic blocks with return statement."""
|
|
121
|
+
file_name = self.fixture_abs_path("cfg_return.jac")
|
|
122
|
+
|
|
123
|
+
from jaclang.compiler.passes.main.cfg_build_pass import cfg_dot_from_file
|
|
124
|
+
|
|
125
|
+
dot = cfg_dot_from_file(file_name=file_name)
|
|
126
|
+
|
|
127
|
+
expected_dot = (
|
|
128
|
+
"digraph G {\n"
|
|
129
|
+
' 0 [label="BB0\\ncan test_return_direct( )\\nprint ( \\"Before return\\" ) ;\\nreturn ;\\nprint ( \\"After return\\" ) ;", shape=box];\n'
|
|
130
|
+
' 1 [label="BB1\\ntest_return_direct ( ) ;", shape=box];\n'
|
|
131
|
+
"}\n"
|
|
132
|
+
)
|
|
133
|
+
self.assertEqual(dot, expected_dot)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
|
|
2
|
+
"""Tests for typechecker pass (the pyright implementation)."""
|
|
3
|
+
|
|
4
|
+
from tempfile import NamedTemporaryFile
|
|
5
|
+
|
|
6
|
+
from jaclang.utils.test import TestCase
|
|
7
|
+
from jaclang.compiler.passes.main import TypeCheckPass
|
|
8
|
+
from jaclang.compiler.program import JacProgram
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TypeCheckerPassTests(TestCase):
|
|
12
|
+
"""Test class obviously."""
|
|
13
|
+
|
|
14
|
+
def test_explicit_type_annotation_in_assignment(self) -> None:
|
|
15
|
+
"""Test explicit type annotation in assignment."""
|
|
16
|
+
program = JacProgram()
|
|
17
|
+
program.build(
|
|
18
|
+
self.fixture_abs_path("type_annotation_assignment.jac"), type_check=True
|
|
19
|
+
)
|
|
20
|
+
self.assertEqual(len(program.errors_had), 2)
|
|
21
|
+
self._assert_error_pretty_found("""
|
|
22
|
+
glob should_fail1: int = "foo";
|
|
23
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
24
|
+
""", program.errors_had[0].pretty_print())
|
|
25
|
+
|
|
26
|
+
self._assert_error_pretty_found("""
|
|
27
|
+
glob should_fail2: str = 42;
|
|
28
|
+
^^^^^^^^^^^^^^^^^^^^^^
|
|
29
|
+
""", program.errors_had[1].pretty_print())
|
|
30
|
+
|
|
31
|
+
def test_infer_type_of_assignment(self) -> None:
|
|
32
|
+
program = JacProgram()
|
|
33
|
+
mod = program.compile(self.fixture_abs_path("infer_type_assignment.jac"))
|
|
34
|
+
TypeCheckPass(ir_in=mod, prog=program)
|
|
35
|
+
self.assertEqual(len(program.errors_had), 1)
|
|
36
|
+
|
|
37
|
+
self._assert_error_pretty_found("""
|
|
38
|
+
assigning_to_str: str = some_int_inferred;
|
|
39
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
40
|
+
""", program.errors_had[0].pretty_print())
|
|
41
|
+
|
|
42
|
+
def test_member_access_type_resolve(self) -> None:
|
|
43
|
+
program = JacProgram()
|
|
44
|
+
mod = program.compile(self.fixture_abs_path("member_access_type_resolve.jac"))
|
|
45
|
+
TypeCheckPass(ir_in=mod, prog=program)
|
|
46
|
+
self.assertEqual(len(program.errors_had), 1)
|
|
47
|
+
self._assert_error_pretty_found("""
|
|
48
|
+
s: str = f.bar.baz;
|
|
49
|
+
^^^^^^^^^^^^^^^^^^^
|
|
50
|
+
""", program.errors_had[0].pretty_print())
|
|
51
|
+
|
|
52
|
+
def test_member_access_type_infered(self) -> None:
|
|
53
|
+
program = JacProgram()
|
|
54
|
+
mod = program.compile(self.fixture_abs_path("member_access_type_inferred.jac"))
|
|
55
|
+
TypeCheckPass(ir_in=mod, prog=program)
|
|
56
|
+
self.assertEqual(len(program.errors_had), 1)
|
|
57
|
+
self._assert_error_pretty_found("""
|
|
58
|
+
s = f.bar;
|
|
59
|
+
^^^^^^^^^
|
|
60
|
+
""", program.errors_had[0].pretty_print())
|
|
61
|
+
|
|
62
|
+
def test_import_symbol_type_infer(self) -> None:
|
|
63
|
+
program = JacProgram()
|
|
64
|
+
mod = program.compile(self.fixture_abs_path("import_symbol_type_infer.jac"))
|
|
65
|
+
TypeCheckPass(ir_in=mod, prog=program)
|
|
66
|
+
self.assertEqual(len(program.errors_had), 1)
|
|
67
|
+
self._assert_error_pretty_found("""
|
|
68
|
+
i: int = m.sys.prefix;
|
|
69
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
70
|
+
""", program.errors_had[0].pretty_print())
|
|
71
|
+
|
|
72
|
+
def test_from_import(self) -> None:
|
|
73
|
+
path = self.fixture_abs_path("checker_importer.jac")
|
|
74
|
+
|
|
75
|
+
program = JacProgram()
|
|
76
|
+
mod = program.compile(path)
|
|
77
|
+
TypeCheckPass(ir_in=mod, prog=program)
|
|
78
|
+
self.assertEqual(len(program.errors_had), 1)
|
|
79
|
+
self._assert_error_pretty_found("""
|
|
80
|
+
glob s: str = alias;
|
|
81
|
+
^^^^^^^^^^^^^^
|
|
82
|
+
""", program.errors_had[0].pretty_print())
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _assert_error_pretty_found(self, needle: str, haystack: str) -> None:
|
|
86
|
+
for line in [line.strip() for line in needle.splitlines() if line.strip()]:
|
|
87
|
+
self.assertIn(line, haystack, f"Expected line '{line}' not found in:\n{haystack}")
|