jaclang 0.8.7__py3-none-any.whl → 0.8.9__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 +77 -29
- jaclang/cli/cmdreg.py +44 -0
- jaclang/compiler/constant.py +6 -2
- jaclang/compiler/jac.lark +37 -47
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +356 -61
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +1 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +221 -135
- jaclang/compiler/passes/main/pyast_load_pass.py +54 -20
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +190 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +56 -0
- jaclang/compiler/passes/main/type_checker_pass.py +29 -73
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +302 -58
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +276 -10
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +19 -7
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/type_evaluator.jac +959 -0
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +102 -107
- jaclang/langserve/engine.jac +138 -159
- jaclang/langserve/server.jac +25 -1
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -309
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_server.py +21 -84
- jaclang/langserve/utils.jac +12 -15
- jaclang/lib.py +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/constructs.py +2 -2
- jaclang/runtimelib/machine.py +63 -46
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +19 -16
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
- jaclang/tests/fixtures/funccall_genexpr.py +5 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +159 -7
- jaclang/tests/test_language.py +213 -38
- jaclang/tests/test_reference.py +3 -1
- jaclang/utils/helpers.py +67 -6
- jaclang/utils/module_resolver.py +10 -0
- jaclang/utils/test.py +8 -0
- jaclang/utils/tests/test_lang_tools.py +4 -15
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -2
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/RECORD +95 -65
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/compiler/type_system/type_evaluator.py +0 -560
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- /jaclang/{langserve/tests/server_test/code_test.py → tests/fixtures/py2jac_empty.py} +0 -0
- {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/entry_points.txt +0 -0
jaclang/tests/test_cli.py
CHANGED
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
|
+
import tempfile
|
|
10
11
|
import traceback
|
|
11
12
|
import unittest
|
|
12
13
|
from jaclang.cli import cli
|
|
@@ -127,12 +128,15 @@ class JacCliTests(TestCase):
|
|
|
127
128
|
sys.stdout = captured_output
|
|
128
129
|
sys.stderr = captured_output
|
|
129
130
|
|
|
130
|
-
with
|
|
131
|
+
with self.assertRaises(SystemExit) as cm:
|
|
131
132
|
cli.run(self.fixture_abs_path("err_runtime.jac"))
|
|
132
133
|
|
|
133
134
|
sys.stdout = sys.__stdout__
|
|
134
135
|
sys.stderr = sys.__stderr__
|
|
135
136
|
|
|
137
|
+
# Verify exit code is 1
|
|
138
|
+
self.assertEqual(cm.exception.code, 1)
|
|
139
|
+
|
|
136
140
|
expected_stdout_values = (
|
|
137
141
|
"Error: list index out of range",
|
|
138
142
|
" print(some_list[invalid_index]);",
|
|
@@ -385,13 +389,13 @@ class JacCliTests(TestCase):
|
|
|
385
389
|
"""Test for graph CLI cmd."""
|
|
386
390
|
captured_output = io.StringIO()
|
|
387
391
|
sys.stdout = captured_output
|
|
388
|
-
cli.dot(f"{self.examples_abs_path('reference/
|
|
392
|
+
cli.dot(f"{self.examples_abs_path('reference/connect_expressions_(osp).jac')}")
|
|
389
393
|
sys.stdout = sys.__stdout__
|
|
390
394
|
stdout_value = captured_output.getvalue()
|
|
391
|
-
if os.path.exists("
|
|
392
|
-
os.remove("
|
|
393
|
-
self.assertIn("
|
|
394
|
-
self.assertIn("
|
|
395
|
+
if os.path.exists("connect_expressions_(osp).dot"):
|
|
396
|
+
os.remove("connect_expressions_(osp).dot")
|
|
397
|
+
self.assertIn(">>> Graph content saved to", stdout_value)
|
|
398
|
+
self.assertIn("connect_expressions_(osp).dot\n", stdout_value)
|
|
395
399
|
|
|
396
400
|
def test_py_to_jac(self) -> None:
|
|
397
401
|
"""Test for graph CLI cmd."""
|
|
@@ -432,6 +436,53 @@ class JacCliTests(TestCase):
|
|
|
432
436
|
"sorted(users, key=lambda x: x['email'], reverse=True)", stdout_value
|
|
433
437
|
)
|
|
434
438
|
|
|
439
|
+
def test_param_arg(self) -> None:
|
|
440
|
+
"""Test for lambda argument annotation."""
|
|
441
|
+
captured_output = io.StringIO()
|
|
442
|
+
sys.stdout = captured_output
|
|
443
|
+
from jaclang.compiler.program import JacProgram
|
|
444
|
+
|
|
445
|
+
filename = self.fixture_abs_path(
|
|
446
|
+
"../../tests/fixtures/params/test_complex_params.jac"
|
|
447
|
+
)
|
|
448
|
+
cli.jac2py(
|
|
449
|
+
f"{self.fixture_abs_path('../../tests/fixtures/params/test_complex_params.jac')}"
|
|
450
|
+
)
|
|
451
|
+
py_code = JacProgram().compile(file_path=filename).gen.py
|
|
452
|
+
|
|
453
|
+
# Create temporary Python file
|
|
454
|
+
with tempfile.NamedTemporaryFile(
|
|
455
|
+
mode="w", suffix=".py", delete=False
|
|
456
|
+
) as temp_file:
|
|
457
|
+
temp_file.write(py_code)
|
|
458
|
+
py_file_path = temp_file.name
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
jac_code = (
|
|
462
|
+
JacProgram().compile(use_str=py_code, file_path=py_file_path).unparse()
|
|
463
|
+
)
|
|
464
|
+
# Create temporary Jac file
|
|
465
|
+
with tempfile.NamedTemporaryFile(
|
|
466
|
+
mode="w", suffix=".jac", delete=False
|
|
467
|
+
) as temp_file:
|
|
468
|
+
temp_file.write(jac_code)
|
|
469
|
+
jac_file_path = temp_file.name
|
|
470
|
+
cli.run(jac_file_path)
|
|
471
|
+
finally:
|
|
472
|
+
os.remove(py_file_path)
|
|
473
|
+
os.remove(jac_file_path)
|
|
474
|
+
|
|
475
|
+
sys.stdout = sys.__stdout__
|
|
476
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
477
|
+
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[-7])
|
|
478
|
+
self.assertEqual("ULTIMATE_FULL: 1|custom|3.14|3|req|200|1", stdout_value[-6])
|
|
479
|
+
self.assertEqual("SEPARATORS: 42", stdout_value[-5])
|
|
480
|
+
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[-4])
|
|
481
|
+
self.assertEqual("RECURSIVE: 7 11", stdout_value[-3])
|
|
482
|
+
self.assertEqual(
|
|
483
|
+
"VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[-2]
|
|
484
|
+
)
|
|
485
|
+
|
|
435
486
|
def test_caching_issue(self) -> None:
|
|
436
487
|
"""Test for Caching Issue."""
|
|
437
488
|
test_file = self.fixture_abs_path("test_caching_issue.jac")
|
|
@@ -441,7 +492,7 @@ class JacCliTests(TestCase):
|
|
|
441
492
|
f.write(
|
|
442
493
|
f"""
|
|
443
494
|
test mytest{{
|
|
444
|
-
|
|
495
|
+
assert 10 == {x};
|
|
445
496
|
}}
|
|
446
497
|
"""
|
|
447
498
|
)
|
|
@@ -609,3 +660,104 @@ class JacCliTests(TestCase):
|
|
|
609
660
|
)
|
|
610
661
|
stdout, stderr = process.communicate()
|
|
611
662
|
self.assertIn("Hello World!", stdout)
|
|
663
|
+
|
|
664
|
+
def test_cli_error_exit_codes(self) -> None:
|
|
665
|
+
"""Test that CLI commands return non-zero exit codes on errors."""
|
|
666
|
+
# Test run command with syntax error
|
|
667
|
+
process = subprocess.Popen(
|
|
668
|
+
["jac", "run", self.fixture_abs_path("err2.jac")],
|
|
669
|
+
stdout=subprocess.PIPE,
|
|
670
|
+
stderr=subprocess.PIPE,
|
|
671
|
+
text=True,
|
|
672
|
+
)
|
|
673
|
+
stdout, stderr = process.communicate()
|
|
674
|
+
self.assertEqual(
|
|
675
|
+
process.returncode, 1, "run command should exit with code 1 on syntax error"
|
|
676
|
+
)
|
|
677
|
+
self.assertIn("Error running", stderr)
|
|
678
|
+
|
|
679
|
+
# Test build command with syntax error
|
|
680
|
+
process = subprocess.Popen(
|
|
681
|
+
["jac", "build", self.fixture_abs_path("err2.jac")],
|
|
682
|
+
stdout=subprocess.PIPE,
|
|
683
|
+
stderr=subprocess.PIPE,
|
|
684
|
+
text=True,
|
|
685
|
+
)
|
|
686
|
+
stdout, stderr = process.communicate()
|
|
687
|
+
self.assertEqual(
|
|
688
|
+
process.returncode,
|
|
689
|
+
1,
|
|
690
|
+
"build command should exit with code 1 on compilation error",
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# Test check command with syntax error
|
|
694
|
+
process = subprocess.Popen(
|
|
695
|
+
["jac", "check", self.fixture_abs_path("err2.jac")],
|
|
696
|
+
stdout=subprocess.PIPE,
|
|
697
|
+
stderr=subprocess.PIPE,
|
|
698
|
+
text=True,
|
|
699
|
+
)
|
|
700
|
+
stdout, stderr = process.communicate()
|
|
701
|
+
self.assertEqual(
|
|
702
|
+
process.returncode,
|
|
703
|
+
1,
|
|
704
|
+
"check command should exit with code 1 on type check error",
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Test format command with non-existent file
|
|
708
|
+
process = subprocess.Popen(
|
|
709
|
+
["jac", "format", "/nonexistent.jac"],
|
|
710
|
+
stdout=subprocess.PIPE,
|
|
711
|
+
stderr=subprocess.PIPE,
|
|
712
|
+
text=True,
|
|
713
|
+
)
|
|
714
|
+
stdout, stderr = process.communicate()
|
|
715
|
+
self.assertEqual(
|
|
716
|
+
process.returncode,
|
|
717
|
+
1,
|
|
718
|
+
"format command should exit with code 1 on missing file",
|
|
719
|
+
)
|
|
720
|
+
self.assertIn("does not exist", stderr)
|
|
721
|
+
|
|
722
|
+
# Test check command with invalid file type
|
|
723
|
+
process = subprocess.Popen(
|
|
724
|
+
["jac", "check", "/nonexistent.txt"],
|
|
725
|
+
stdout=subprocess.PIPE,
|
|
726
|
+
stderr=subprocess.PIPE,
|
|
727
|
+
text=True,
|
|
728
|
+
)
|
|
729
|
+
stdout, stderr = process.communicate()
|
|
730
|
+
self.assertEqual(
|
|
731
|
+
process.returncode,
|
|
732
|
+
1,
|
|
733
|
+
"check command should exit with code 1 on invalid file type",
|
|
734
|
+
)
|
|
735
|
+
self.assertIn("Not a .jac file", stderr)
|
|
736
|
+
|
|
737
|
+
# Test tool command with non-existent tool
|
|
738
|
+
process = subprocess.Popen(
|
|
739
|
+
["jac", "tool", "nonexistent_tool"],
|
|
740
|
+
stdout=subprocess.PIPE,
|
|
741
|
+
stderr=subprocess.PIPE,
|
|
742
|
+
text=True,
|
|
743
|
+
)
|
|
744
|
+
stdout, stderr = process.communicate()
|
|
745
|
+
self.assertEqual(
|
|
746
|
+
process.returncode,
|
|
747
|
+
1,
|
|
748
|
+
"tool command should exit with code 1 on non-existent tool",
|
|
749
|
+
)
|
|
750
|
+
self.assertIn("not found", stderr)
|
|
751
|
+
|
|
752
|
+
# Test successful run returns exit code 0
|
|
753
|
+
process = subprocess.Popen(
|
|
754
|
+
["jac", "run", self.fixture_abs_path("hello.jac")],
|
|
755
|
+
stdout=subprocess.PIPE,
|
|
756
|
+
stderr=subprocess.PIPE,
|
|
757
|
+
text=True,
|
|
758
|
+
)
|
|
759
|
+
stdout, stderr = process.communicate()
|
|
760
|
+
self.assertEqual(
|
|
761
|
+
process.returncode, 0, "run command should exit with code 0 on success"
|
|
762
|
+
)
|
|
763
|
+
self.assertIn("Hello World!", stdout)
|
jaclang/tests/test_language.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
-
import
|
|
6
|
+
import pytest
|
|
7
7
|
import tempfile
|
|
8
8
|
import subprocess
|
|
9
9
|
from pathlib import Path
|
|
@@ -133,8 +133,17 @@ class JacLanguageTests(TestCase):
|
|
|
133
133
|
|
|
134
134
|
for edge in edges:
|
|
135
135
|
label = edge["label"]
|
|
136
|
-
self.assertIn(
|
|
137
|
-
|
|
136
|
+
self.assertIn(
|
|
137
|
+
label,
|
|
138
|
+
[
|
|
139
|
+
"E(val=1)",
|
|
140
|
+
"E(val=1)",
|
|
141
|
+
"E(val=1)",
|
|
142
|
+
"E(val=0)",
|
|
143
|
+
"E(val=0)",
|
|
144
|
+
"E(val=0)",
|
|
145
|
+
],
|
|
146
|
+
)
|
|
138
147
|
|
|
139
148
|
def test_printgraph_mermaid(self) -> None:
|
|
140
149
|
"""Test the mermaid gen of builtin function."""
|
|
@@ -201,11 +210,26 @@ class JacLanguageTests(TestCase):
|
|
|
201
210
|
)
|
|
202
211
|
captured_output = io.StringIO()
|
|
203
212
|
sys.stdout = captured_output
|
|
204
|
-
exec(compile(prog.gen.py_ast[0], "test.
|
|
213
|
+
exec(compile(prog.gen.py_ast[0], "test.jac", "exec"))
|
|
205
214
|
sys.stdout = sys.__stdout__
|
|
206
215
|
stdout_value = captured_output.getvalue()
|
|
207
216
|
self.assertEqual(stdout_value, "-5\n")
|
|
208
217
|
|
|
218
|
+
def test_assignment_list_no_infinite_loop(self) -> None:
|
|
219
|
+
"""Test that assignment list parsing doesn't cause infinite loop."""
|
|
220
|
+
# This syntax previously caused an infinite loop in two places:
|
|
221
|
+
# 1. Grammar: assignment_list: (assignment_list COMMA)? (assignment | named_ref)
|
|
222
|
+
# Fixed by: assignment_list: (assignment | named_ref) (COMMA (assignment | named_ref))* COMMA?
|
|
223
|
+
# 2. Error recovery: feed_current_token() had unbounded while loop
|
|
224
|
+
# Fixed by: adding max_attempts limit in parser.py
|
|
225
|
+
code = "with entry { p1, p2 = (10, 20); }"
|
|
226
|
+
# Compilation should complete quickly (even though syntax is invalid)
|
|
227
|
+
jac_prog = JacProgram()
|
|
228
|
+
result = jac_prog.compile(use_str=code, file_path="test.jac")
|
|
229
|
+
# Should have errors (invalid syntax) but not hang
|
|
230
|
+
self.assertIsNotNone(result) # Returns a Module object
|
|
231
|
+
self.assertGreater(len(jac_prog.errors_had), 0) # Check errors on program
|
|
232
|
+
|
|
209
233
|
def test_need_import(self) -> None:
|
|
210
234
|
"""Test importing python."""
|
|
211
235
|
captured_output = io.StringIO()
|
|
@@ -215,18 +239,6 @@ class JacLanguageTests(TestCase):
|
|
|
215
239
|
stdout_value = captured_output.getvalue()
|
|
216
240
|
self.assertIn("<module 'pyfunc' from", stdout_value)
|
|
217
241
|
|
|
218
|
-
def test_filter_compr(self) -> None:
|
|
219
|
-
"""Testing filter comprehension."""
|
|
220
|
-
captured_output = io.StringIO()
|
|
221
|
-
sys.stdout = captured_output
|
|
222
|
-
Jac.jac_import(
|
|
223
|
-
"reference.special_comprehensions",
|
|
224
|
-
base_path=self.examples_abs_path(""),
|
|
225
|
-
)
|
|
226
|
-
sys.stdout = sys.__stdout__
|
|
227
|
-
stdout_value = captured_output.getvalue()
|
|
228
|
-
self.assertIn("True", stdout_value)
|
|
229
|
-
|
|
230
242
|
def test_gen_dot_bubble(self) -> None:
|
|
231
243
|
"""Test the dot gen of nodes and edges of bubblesort."""
|
|
232
244
|
captured_output = io.StringIO()
|
|
@@ -514,14 +526,13 @@ class JacLanguageTests(TestCase):
|
|
|
514
526
|
),
|
|
515
527
|
prog=JacProgram(),
|
|
516
528
|
).ir_out.unparse()
|
|
517
|
-
self.assertIn("def greet2(**kwargs: Any)", output)
|
|
529
|
+
self.assertIn("def greet2( **kwargs: Any) {", output)
|
|
518
530
|
self.assertEqual(output.count("with entry {"), 14)
|
|
519
|
-
self.assertIn("assert (x == 5) , 'x should be equal to 5'
|
|
531
|
+
self.assertIn("assert (x == 5) , 'x should be equal to 5';", output)
|
|
520
532
|
self.assertIn("if not (x == y) {", output)
|
|
521
|
-
self.assertIn("
|
|
522
|
-
self.assertIn("squares_dict = { x : (x ** 2) for x in numbers };", output)
|
|
533
|
+
self.assertIn("squares_dict = {x : (x ** 2) for x in numbers};", output)
|
|
523
534
|
self.assertIn(
|
|
524
|
-
'\n\n"""Say hello"""\n@
|
|
535
|
+
'\n\n"""Say hello"""\n@my_decorator\n\n def say_hello() {', output
|
|
525
536
|
)
|
|
526
537
|
|
|
527
538
|
def test_pyfunc_2(self) -> None:
|
|
@@ -589,6 +600,52 @@ class JacLanguageTests(TestCase):
|
|
|
589
600
|
self.assertIn("case Container(inner = Inner(x = a, y = b)):\n", output)
|
|
590
601
|
self.assertIn("case _:\n", output)
|
|
591
602
|
|
|
603
|
+
def test_py2jac_params(self) -> None:
|
|
604
|
+
"""Test py ast to Jac ast conversion."""
|
|
605
|
+
from jaclang.compiler.passes.main import PyastBuildPass
|
|
606
|
+
import jaclang.compiler.unitree as ast
|
|
607
|
+
import ast as py_ast
|
|
608
|
+
|
|
609
|
+
py_out_path = os.path.join(self.fixture_abs_path("./"), "py2jac_params.py")
|
|
610
|
+
with open(py_out_path) as f:
|
|
611
|
+
file_source = f.read()
|
|
612
|
+
output = PyastBuildPass(
|
|
613
|
+
ir_in=ast.PythonModuleAst(
|
|
614
|
+
py_ast.parse(file_source),
|
|
615
|
+
orig_src=ast.Source(file_source, py_out_path),
|
|
616
|
+
),
|
|
617
|
+
prog=None,
|
|
618
|
+
).ir_out.unparse()
|
|
619
|
+
self.assertIn(
|
|
620
|
+
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {",
|
|
621
|
+
output,
|
|
622
|
+
)
|
|
623
|
+
self.assertIn(
|
|
624
|
+
"def len(<>obj: Sized, astt: Any, /, z: int, j: str, a: Any = 90) -> int {",
|
|
625
|
+
output,
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
def test_py2jac_empty_file(self) -> None:
|
|
629
|
+
"""Test py ast to Jac ast conversion."""
|
|
630
|
+
from jaclang.compiler.passes.main import PyastBuildPass
|
|
631
|
+
import jaclang.compiler.unitree as ast
|
|
632
|
+
import ast as py_ast
|
|
633
|
+
|
|
634
|
+
py_out_path = os.path.join(self.fixture_abs_path("./"), "py2jac_empty.py")
|
|
635
|
+
with open(py_out_path) as f:
|
|
636
|
+
file_source = f.read()
|
|
637
|
+
converted_ast = PyastBuildPass(
|
|
638
|
+
ir_in=ast.PythonModuleAst(
|
|
639
|
+
py_ast.parse(file_source),
|
|
640
|
+
orig_src=ast.Source(file_source, py_out_path),
|
|
641
|
+
),
|
|
642
|
+
prog=None,
|
|
643
|
+
).ir_out
|
|
644
|
+
self.assertIsInstance(
|
|
645
|
+
converted_ast,
|
|
646
|
+
ast.Module
|
|
647
|
+
)
|
|
648
|
+
|
|
592
649
|
def test_refs_target(self) -> None:
|
|
593
650
|
"""Test py ast to Jac ast conversion output."""
|
|
594
651
|
captured_output = io.StringIO()
|
|
@@ -712,6 +769,7 @@ class JacLanguageTests(TestCase):
|
|
|
712
769
|
mypass = JacProgram().compile(self.fixture_abs_path("byllmissue.jac"))
|
|
713
770
|
self.assertIn("2:5 - 4:8", mypass.pp())
|
|
714
771
|
|
|
772
|
+
@pytest.mark.xfail(reason="TODO: Support symtable for inheritance")
|
|
715
773
|
def test_inherit_baseclass_sym(self) -> None:
|
|
716
774
|
"""Basic test for symtable support for inheritance."""
|
|
717
775
|
mypass = JacProgram().compile(
|
|
@@ -746,6 +804,53 @@ class JacLanguageTests(TestCase):
|
|
|
746
804
|
stdout_value = captured_output.getvalue()
|
|
747
805
|
self.assertIn("i work", stdout_value)
|
|
748
806
|
|
|
807
|
+
def test_kwonly_params(self) -> None:
|
|
808
|
+
"""Test importing python."""
|
|
809
|
+
captured_output = io.StringIO()
|
|
810
|
+
sys.stdout = captured_output
|
|
811
|
+
Jac.jac_import(
|
|
812
|
+
"test_kwonly_params", base_path=self.fixture_abs_path("./params")
|
|
813
|
+
)
|
|
814
|
+
sys.stdout = sys.__stdout__
|
|
815
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
816
|
+
self.assertEqual("KW_SIMPLE: 42", stdout_value[0])
|
|
817
|
+
self.assertEqual("KW_DEF: 10-def 20-def", stdout_value[1])
|
|
818
|
+
self.assertEqual("REG_KW: 10|test", stdout_value[2])
|
|
819
|
+
self.assertEqual("MIXED_KW: 1-def-2.5-True 2-custom-3.5-False", stdout_value[3])
|
|
820
|
+
self.assertEqual("ALL_KW: 100:test:1.0 200:hi:9.9", stdout_value[4])
|
|
821
|
+
|
|
822
|
+
def test_complex_params(self) -> None:
|
|
823
|
+
"""Test importing python."""
|
|
824
|
+
captured_output = io.StringIO()
|
|
825
|
+
sys.stdout = captured_output
|
|
826
|
+
Jac.jac_import(
|
|
827
|
+
"test_complex_params", base_path=self.fixture_abs_path("./params")
|
|
828
|
+
)
|
|
829
|
+
sys.stdout = sys.__stdout__
|
|
830
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
831
|
+
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[0])
|
|
832
|
+
self.assertEqual("ULTIMATE_FULL: 1|custom|3.14|3|req|200|1", stdout_value[1])
|
|
833
|
+
self.assertEqual("SEPARATORS: 42", stdout_value[2])
|
|
834
|
+
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[3])
|
|
835
|
+
self.assertEqual("RECURSIVE: 7 11", stdout_value[4])
|
|
836
|
+
self.assertEqual(
|
|
837
|
+
"VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5]
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
def test_param_failing(self) -> None:
|
|
841
|
+
"""Test importing python."""
|
|
842
|
+
captured_output = io.StringIO()
|
|
843
|
+
sys.stdout = captured_output
|
|
844
|
+
for i in [
|
|
845
|
+
"test_failing_posonly",
|
|
846
|
+
"test_failing_kwonly",
|
|
847
|
+
"test_failing_varargs",
|
|
848
|
+
]:
|
|
849
|
+
Jac.jac_import(i, base_path=self.fixture_abs_path("./params"))
|
|
850
|
+
sys.stdout = sys.__stdout__
|
|
851
|
+
stdout_value = captured_output.getvalue()
|
|
852
|
+
self.assertNotIn("FAILED", stdout_value)
|
|
853
|
+
|
|
749
854
|
def test_double_import_exec(self) -> None:
|
|
750
855
|
"""Test importing python."""
|
|
751
856
|
captured_output = io.StringIO()
|
|
@@ -1238,9 +1343,9 @@ class JacLanguageTests(TestCase):
|
|
|
1238
1343
|
).ir_out.unparse()
|
|
1239
1344
|
self.assertIn("(prev_token_index is None)", output)
|
|
1240
1345
|
self.assertIn("(next_token_index is None)", output)
|
|
1241
|
-
self.assertIn("(tok[
|
|
1242
|
-
self.assertIn("(tok[
|
|
1243
|
-
self.assertIn("(tok[
|
|
1346
|
+
self.assertIn("(tok[0] > change_end_line)", output)
|
|
1347
|
+
self.assertIn("(tok[0] == change_end_line)", output)
|
|
1348
|
+
self.assertIn("(tok[1] > change_end_char)", output)
|
|
1244
1349
|
|
|
1245
1350
|
def test_here_visitor_usage(self) -> None:
|
|
1246
1351
|
"""Test visitor, here keyword usage in jaclang."""
|
|
@@ -1257,9 +1362,11 @@ class JacLanguageTests(TestCase):
|
|
|
1257
1362
|
captured_output = io.StringIO()
|
|
1258
1363
|
sys.stdout = captured_output
|
|
1259
1364
|
sys.stderr = captured_output
|
|
1260
|
-
|
|
1365
|
+
with self.assertRaises(SystemExit) as cm:
|
|
1366
|
+
cli.run(self.fixture_abs_path("here_usage_error.jac"))
|
|
1261
1367
|
sys.stdout = sys.__stdout__
|
|
1262
1368
|
sys.stderr = sys.__stderr__
|
|
1369
|
+
self.assertEqual(cm.exception.code, 1)
|
|
1263
1370
|
stdout_value = captured_output.getvalue()
|
|
1264
1371
|
self.assertIn("'here' is not defined", stdout_value)
|
|
1265
1372
|
|
|
@@ -1374,11 +1481,11 @@ class JacLanguageTests(TestCase):
|
|
|
1374
1481
|
|
|
1375
1482
|
def test_read_file_with_encoding_utf8(self) -> None:
|
|
1376
1483
|
"""Test reading UTF-8 encoded file."""
|
|
1377
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1484
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1378
1485
|
test_content = "Hello, 世界! 🌍 Testing UTF-8 encoding."
|
|
1379
1486
|
f.write(test_content)
|
|
1380
1487
|
temp_path = f.name
|
|
1381
|
-
|
|
1488
|
+
|
|
1382
1489
|
try:
|
|
1383
1490
|
result = read_file_with_encoding(temp_path)
|
|
1384
1491
|
self.assertEqual(result, test_content)
|
|
@@ -1387,11 +1494,13 @@ class JacLanguageTests(TestCase):
|
|
|
1387
1494
|
|
|
1388
1495
|
def test_read_file_with_encoding_utf16(self) -> None:
|
|
1389
1496
|
"""Test reading UTF-16 encoded file when UTF-8 fails."""
|
|
1390
|
-
with tempfile.NamedTemporaryFile(
|
|
1497
|
+
with tempfile.NamedTemporaryFile(
|
|
1498
|
+
delete=False, mode="w", encoding="utf-16"
|
|
1499
|
+
) as f:
|
|
1391
1500
|
test_content = "Hello, 世界! UTF-16 encoding test."
|
|
1392
1501
|
f.write(test_content)
|
|
1393
1502
|
temp_path = f.name
|
|
1394
|
-
|
|
1503
|
+
|
|
1395
1504
|
try:
|
|
1396
1505
|
result = read_file_with_encoding(temp_path)
|
|
1397
1506
|
self.assertEqual(result, test_content)
|
|
@@ -1400,11 +1509,13 @@ class JacLanguageTests(TestCase):
|
|
|
1400
1509
|
|
|
1401
1510
|
def test_read_file_with_encoding_utf8_bom(self) -> None:
|
|
1402
1511
|
"""Test reading UTF-8 with BOM encoded file."""
|
|
1403
|
-
with tempfile.NamedTemporaryFile(
|
|
1512
|
+
with tempfile.NamedTemporaryFile(
|
|
1513
|
+
delete=False, mode="w", encoding="utf-8-sig"
|
|
1514
|
+
) as f:
|
|
1404
1515
|
test_content = "Hello, UTF-8 BOM test! 🚀"
|
|
1405
1516
|
f.write(test_content)
|
|
1406
1517
|
temp_path = f.name
|
|
1407
|
-
|
|
1518
|
+
|
|
1408
1519
|
try:
|
|
1409
1520
|
result = read_file_with_encoding(temp_path)
|
|
1410
1521
|
self.assertEqual(result, test_content)
|
|
@@ -1431,9 +1542,9 @@ class JacLanguageTests(TestCase):
|
|
|
1431
1542
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
|
1432
1543
|
binary_data = bytes([0xFF, 0xFE, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
1433
1544
|
f.write(binary_data)
|
|
1434
|
-
f.flush()
|
|
1545
|
+
f.flush()
|
|
1435
1546
|
temp_path = f.name
|
|
1436
|
-
|
|
1547
|
+
|
|
1437
1548
|
try:
|
|
1438
1549
|
result = read_file_with_encoding(temp_path)
|
|
1439
1550
|
self.assertIsInstance(result, str)
|
|
@@ -1443,18 +1554,18 @@ class JacLanguageTests(TestCase):
|
|
|
1443
1554
|
|
|
1444
1555
|
def test_read_file_with_encoding_special_characters(self) -> None:
|
|
1445
1556
|
"""Test reading file with various special characters."""
|
|
1446
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1557
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1447
1558
|
test_content = (
|
|
1448
1559
|
"Special chars: åäö ñ ü ç é\n"
|
|
1449
|
-
"Symbols: ©®™ §¶†‡•\n"
|
|
1560
|
+
"Symbols: ©®™ §¶†‡•\n"
|
|
1450
1561
|
"Math: ∑∏∫√±≤≥≠\n"
|
|
1451
1562
|
"Arrows: ←→↑↓↔\n"
|
|
1452
1563
|
"Emoji: 😀😍🎉🔥💯\n"
|
|
1453
1564
|
)
|
|
1454
1565
|
f.write(test_content)
|
|
1455
|
-
f.flush()
|
|
1566
|
+
f.flush()
|
|
1456
1567
|
temp_path = f.name
|
|
1457
|
-
|
|
1568
|
+
|
|
1458
1569
|
try:
|
|
1459
1570
|
result = read_file_with_encoding(temp_path)
|
|
1460
1571
|
|
|
@@ -1464,4 +1575,68 @@ class JacLanguageTests(TestCase):
|
|
|
1464
1575
|
self.assertIn("∑∏∫", result)
|
|
1465
1576
|
self.assertIn("😀😍", result)
|
|
1466
1577
|
finally:
|
|
1467
|
-
os.unlink(temp_path)
|
|
1578
|
+
os.unlink(temp_path)
|
|
1579
|
+
|
|
1580
|
+
def test_funccall_genexpr(self) -> None:
|
|
1581
|
+
"""Test function call with generator expression in both Jac and py2jac."""
|
|
1582
|
+
# Test language support
|
|
1583
|
+
captured_output = io.StringIO()
|
|
1584
|
+
sys.stdout = captured_output
|
|
1585
|
+
Jac.jac_import("funccall_genexpr", base_path=self.fixture_abs_path("./"))
|
|
1586
|
+
sys.stdout = sys.__stdout__
|
|
1587
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1588
|
+
self.assertIn("Result: 30", stdout_value[0])
|
|
1589
|
+
|
|
1590
|
+
# Test py2jac conversion
|
|
1591
|
+
py_file_path = f"{self.fixture_abs_path('../../tests/fixtures/funccall_genexpr.py')}"
|
|
1592
|
+
captured_output = io.StringIO()
|
|
1593
|
+
sys.stdout = captured_output
|
|
1594
|
+
cli.py2jac(py_file_path)
|
|
1595
|
+
sys.stdout = sys.__stdout__
|
|
1596
|
+
stdout_value = captured_output.getvalue()
|
|
1597
|
+
self.assertIn("result = total((x * x) for x in range(5));", stdout_value)
|
|
1598
|
+
|
|
1599
|
+
def test_attr_pattern_case(self) -> None:
|
|
1600
|
+
"""Test attribute pattern matching."""
|
|
1601
|
+
captured_output = io.StringIO()
|
|
1602
|
+
sys.stdout = captured_output
|
|
1603
|
+
Jac.jac_import("attr_pattern_case", base_path=self.fixture_abs_path("./"))
|
|
1604
|
+
sys.stdout = sys.__stdout__
|
|
1605
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1606
|
+
self.assertIn("Matched a.b.c Hello Jaseci!", stdout_value[0])
|
|
1607
|
+
|
|
1608
|
+
def test_known_builtins_matches_actual(self) -> None:
|
|
1609
|
+
"""Test that KNOWN_BUILTINS in PyastGenPass matches actual builtins."""
|
|
1610
|
+
from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
|
|
1611
|
+
import jaclang.runtimelib.builtin as builtin_module
|
|
1612
|
+
|
|
1613
|
+
# Get actual builtins from the module (excluding private members)
|
|
1614
|
+
actual_builtins = {
|
|
1615
|
+
name for name in builtin_module.__all__
|
|
1616
|
+
if not name.startswith('_')
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
# Exceptions: builtins that are imported from other sources
|
|
1620
|
+
# - Enum: imported via needs_enum() from standard library's enum module
|
|
1621
|
+
exceptions = {'Enum'}
|
|
1622
|
+
actual_builtins_to_check = actual_builtins - exceptions
|
|
1623
|
+
|
|
1624
|
+
# Get the KNOWN_BUILTINS set from PyastGenPass
|
|
1625
|
+
known_builtins = PyastGenPass.KNOWN_BUILTINS
|
|
1626
|
+
|
|
1627
|
+
# Find missing and extra builtins
|
|
1628
|
+
missing = actual_builtins_to_check - known_builtins
|
|
1629
|
+
extra = known_builtins - actual_builtins_to_check
|
|
1630
|
+
|
|
1631
|
+
# Build error message
|
|
1632
|
+
error_msg = []
|
|
1633
|
+
if missing:
|
|
1634
|
+
error_msg.append(f"Missing from KNOWN_BUILTINS: {sorted(missing)}")
|
|
1635
|
+
if extra:
|
|
1636
|
+
error_msg.append(f"Extra in KNOWN_BUILTINS (not in builtin module): {sorted(extra)}")
|
|
1637
|
+
|
|
1638
|
+
# Assert they match
|
|
1639
|
+
self.assertEqual(
|
|
1640
|
+
known_builtins, actual_builtins_to_check,
|
|
1641
|
+
"\n".join(error_msg) if error_msg else ""
|
|
1642
|
+
)
|
jaclang/tests/test_reference.py
CHANGED
|
@@ -50,6 +50,8 @@ class JacReferenceTests(TestCase):
|
|
|
50
50
|
|
|
51
51
|
def micro_suite_test(self, filename: str) -> None:
|
|
52
52
|
"""Test file."""
|
|
53
|
+
# Reset machine at the start of each test to ensure clean state
|
|
54
|
+
Jac.reset_machine()
|
|
53
55
|
|
|
54
56
|
def execute_and_capture_output(code: str | bytes, filename: str = "") -> str:
|
|
55
57
|
f = io.StringIO()
|
|
@@ -83,12 +85,12 @@ class JacReferenceTests(TestCase):
|
|
|
83
85
|
# print(f"\nPython Output:\n{output_py}")
|
|
84
86
|
|
|
85
87
|
self.assertGreater(len(output_py), 0)
|
|
86
|
-
self.assertEqual(len(output_jac.split("\n")), len(output_py.split("\n")))
|
|
87
88
|
# doing like below for concurrent_expressions.jac and other current tests
|
|
88
89
|
for i in output_py.split("\n"):
|
|
89
90
|
self.assertIn(i, output_jac)
|
|
90
91
|
for i in output_jac.split("\n"):
|
|
91
92
|
self.assertIn(i, output_py)
|
|
93
|
+
self.assertEqual(len(output_jac.split("\n")), len(output_py.split("\n")))
|
|
92
94
|
# self.assertEqual(output_py, output_jac)
|
|
93
95
|
except Exception as e:
|
|
94
96
|
# print(f"\nJAC Output:\n{output_jac}")
|