jaclang 0.8.8__py3-none-any.whl → 0.8.10__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 +194 -10
- jaclang/cli/cmdreg.py +144 -8
- jaclang/compiler/__init__.py +6 -1
- jaclang/compiler/codeinfo.py +16 -1
- jaclang/compiler/constant.py +33 -8
- jaclang/compiler/jac.lark +154 -62
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +656 -149
- jaclang/compiler/passes/__init__.py +2 -1
- jaclang/compiler/passes/ast_gen/__init__.py +5 -0
- jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
- jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
- jaclang/compiler/passes/ecmascript/__init__.py +25 -0
- jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
- jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
- jaclang/compiler/passes/ecmascript/estree.py +972 -0
- jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
- jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
- jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
- jaclang/compiler/passes/main/__init__.py +0 -3
- jaclang/compiler/passes/main/annex_pass.py +23 -1
- jaclang/compiler/passes/main/def_use_pass.py +1 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
- jaclang/compiler/passes/main/pyast_load_pass.py +48 -11
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
- jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
- jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -3
- jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
- jaclang/compiler/passes/main/type_checker_pass.py +7 -0
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +219 -20
- jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
- jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
- jaclang/compiler/passes/transform.py +9 -1
- jaclang/compiler/passes/uni_pass.py +5 -7
- jaclang/compiler/program.py +27 -26
- jaclang/compiler/tests/test_client_codegen.py +113 -0
- jaclang/compiler/tests/test_importer.py +12 -10
- jaclang/compiler/tests/test_parser.py +249 -3
- jaclang/compiler/type_system/type_evaluator.jac +1078 -0
- jaclang/compiler/type_system/type_utils.py +1 -1
- jaclang/compiler/type_system/types.py +6 -0
- jaclang/compiler/unitree.py +438 -82
- jaclang/langserve/engine.jac +224 -288
- jaclang/langserve/sem_manager.jac +12 -8
- jaclang/langserve/server.jac +48 -48
- jaclang/langserve/tests/fixtures/greet.py +17 -0
- jaclang/langserve/tests/fixtures/md_path.jac +22 -0
- jaclang/langserve/tests/fixtures/user.jac +15 -0
- jaclang/langserve/tests/test_server.py +66 -371
- jaclang/lib.py +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/client_bundle.py +169 -0
- jaclang/runtimelib/client_runtime.jac +586 -0
- jaclang/runtimelib/constructs.py +4 -2
- jaclang/runtimelib/machine.py +308 -139
- jaclang/runtimelib/meta_importer.py +111 -22
- jaclang/runtimelib/mtp.py +15 -0
- jaclang/runtimelib/server.py +1089 -0
- jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
- jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
- jaclang/runtimelib/tests/test_client_bundle.py +55 -0
- jaclang/runtimelib/tests/test_client_render.py +63 -0
- jaclang/runtimelib/tests/test_serve.py +1069 -0
- jaclang/settings.py +0 -3
- jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
- jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
- jaclang/tests/fixtures/funccall_genexpr.py +5 -0
- jaclang/tests/fixtures/iife_functions.jac +142 -0
- jaclang/tests/fixtures/iife_functions_client.jac +143 -0
- jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
- jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
- jaclang/tests/fixtures/needs_import_dup.jac +6 -4
- jaclang/tests/fixtures/py2jac_empty.py +0 -0
- jaclang/tests/fixtures/py_run.py +7 -5
- jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
- jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
- jaclang/tests/test_cli.py +134 -18
- jaclang/tests/test_language.py +120 -32
- jaclang/tests/test_reference.py +20 -3
- jaclang/utils/NonGPT.py +375 -0
- jaclang/utils/helpers.py +64 -20
- jaclang/utils/lang_tools.py +31 -4
- jaclang/utils/tests/test_lang_tools.py +5 -16
- jaclang/utils/treeprinter.py +8 -3
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
- jaclang/compiler/passes/main/binder_pass.py +0 -594
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
- jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
- jaclang/compiler/type_system/type_evaluator.py +0 -844
- jaclang/langserve/tests/session.jac +0 -294
- jaclang/langserve/tests/test_dev_server.py +0 -80
- jaclang/runtimelib/importer.py +0 -351
- jaclang/tests/test_typecheck.py +0 -542
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
jaclang/tests/test_cli.py
CHANGED
|
@@ -128,12 +128,15 @@ class JacCliTests(TestCase):
|
|
|
128
128
|
sys.stdout = captured_output
|
|
129
129
|
sys.stderr = captured_output
|
|
130
130
|
|
|
131
|
-
with
|
|
131
|
+
with self.assertRaises(SystemExit) as cm:
|
|
132
132
|
cli.run(self.fixture_abs_path("err_runtime.jac"))
|
|
133
133
|
|
|
134
134
|
sys.stdout = sys.__stdout__
|
|
135
135
|
sys.stderr = sys.__stderr__
|
|
136
136
|
|
|
137
|
+
# Verify exit code is 1
|
|
138
|
+
self.assertEqual(cm.exception.code, 1)
|
|
139
|
+
|
|
137
140
|
expected_stdout_values = (
|
|
138
141
|
"Error: list index out of range",
|
|
139
142
|
" print(some_list[invalid_index]);",
|
|
@@ -386,13 +389,13 @@ class JacCliTests(TestCase):
|
|
|
386
389
|
"""Test for graph CLI cmd."""
|
|
387
390
|
captured_output = io.StringIO()
|
|
388
391
|
sys.stdout = captured_output
|
|
389
|
-
cli.dot(f"{self.examples_abs_path('reference/
|
|
392
|
+
cli.dot(f"{self.examples_abs_path('reference/connect_expressions_(osp).jac')}")
|
|
390
393
|
sys.stdout = sys.__stdout__
|
|
391
394
|
stdout_value = captured_output.getvalue()
|
|
392
|
-
if os.path.exists("
|
|
393
|
-
os.remove("
|
|
394
|
-
self.assertIn("
|
|
395
|
-
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)
|
|
396
399
|
|
|
397
400
|
def test_py_to_jac(self) -> None:
|
|
398
401
|
"""Test for graph CLI cmd."""
|
|
@@ -438,27 +441,37 @@ class JacCliTests(TestCase):
|
|
|
438
441
|
captured_output = io.StringIO()
|
|
439
442
|
sys.stdout = captured_output
|
|
440
443
|
from jaclang.compiler.program import JacProgram
|
|
441
|
-
|
|
442
|
-
filename = self.fixture_abs_path(
|
|
443
|
-
|
|
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
|
+
)
|
|
444
451
|
py_code = JacProgram().compile(file_path=filename).gen.py
|
|
445
|
-
|
|
452
|
+
|
|
446
453
|
# Create temporary Python file
|
|
447
|
-
with tempfile.NamedTemporaryFile(
|
|
454
|
+
with tempfile.NamedTemporaryFile(
|
|
455
|
+
mode="w", suffix=".py", delete=False
|
|
456
|
+
) as temp_file:
|
|
448
457
|
temp_file.write(py_code)
|
|
449
458
|
py_file_path = temp_file.name
|
|
450
|
-
|
|
459
|
+
|
|
451
460
|
try:
|
|
452
|
-
jac_code =
|
|
461
|
+
jac_code = (
|
|
462
|
+
JacProgram().compile(use_str=py_code, file_path=py_file_path).unparse()
|
|
463
|
+
)
|
|
453
464
|
# Create temporary Jac file
|
|
454
|
-
with tempfile.NamedTemporaryFile(
|
|
465
|
+
with tempfile.NamedTemporaryFile(
|
|
466
|
+
mode="w", suffix=".jac", delete=False
|
|
467
|
+
) as temp_file:
|
|
455
468
|
temp_file.write(jac_code)
|
|
456
469
|
jac_file_path = temp_file.name
|
|
457
470
|
cli.run(jac_file_path)
|
|
458
471
|
finally:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
472
|
+
os.remove(py_file_path)
|
|
473
|
+
os.remove(jac_file_path)
|
|
474
|
+
|
|
462
475
|
sys.stdout = sys.__stdout__
|
|
463
476
|
stdout_value = captured_output.getvalue().split("\n")
|
|
464
477
|
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[-7])
|
|
@@ -466,7 +479,9 @@ class JacCliTests(TestCase):
|
|
|
466
479
|
self.assertEqual("SEPARATORS: 42", stdout_value[-5])
|
|
467
480
|
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[-4])
|
|
468
481
|
self.assertEqual("RECURSIVE: 7 11", stdout_value[-3])
|
|
469
|
-
self.assertEqual(
|
|
482
|
+
self.assertEqual(
|
|
483
|
+
"VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[-2]
|
|
484
|
+
)
|
|
470
485
|
|
|
471
486
|
def test_caching_issue(self) -> None:
|
|
472
487
|
"""Test for Caching Issue."""
|
|
@@ -645,3 +660,104 @@ class JacCliTests(TestCase):
|
|
|
645
660
|
)
|
|
646
661
|
stdout, stderr = process.communicate()
|
|
647
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", 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
|
@@ -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()
|
|
@@ -334,7 +346,10 @@ class JacLanguageTests(TestCase):
|
|
|
334
346
|
sys.stdout = sys.__stdout__
|
|
335
347
|
stdout_value = captured_output.getvalue()
|
|
336
348
|
self.assertIn("one level deeperslHello World!", stdout_value)
|
|
337
|
-
self.
|
|
349
|
+
self.assertTrue(
|
|
350
|
+
"module 'pyfunc' from " in stdout_value
|
|
351
|
+
or "module 'jaclang.tests.fixtures.pyfunc' from " in stdout_value
|
|
352
|
+
)
|
|
338
353
|
|
|
339
354
|
def test_deep_outer_imports_from_loc(self) -> None:
|
|
340
355
|
"""Parse micro jac file."""
|
|
@@ -345,7 +360,10 @@ class JacLanguageTests(TestCase):
|
|
|
345
360
|
sys.stdout = sys.__stdout__
|
|
346
361
|
stdout_value = captured_output.getvalue()
|
|
347
362
|
self.assertIn("one level deeperslHello World!", stdout_value)
|
|
348
|
-
self.
|
|
363
|
+
self.assertTrue(
|
|
364
|
+
"module 'jaclang.tests.fixtures.pyfunc' from " in stdout_value
|
|
365
|
+
or "module 'pyfunc' from " in stdout_value
|
|
366
|
+
)
|
|
349
367
|
|
|
350
368
|
def test_has_lambda_goodness(self) -> None:
|
|
351
369
|
"""Test has lambda_goodness."""
|
|
@@ -516,7 +534,7 @@ class JacLanguageTests(TestCase):
|
|
|
516
534
|
).ir_out.unparse()
|
|
517
535
|
self.assertIn("def greet2( **kwargs: Any) {", output)
|
|
518
536
|
self.assertEqual(output.count("with entry {"), 14)
|
|
519
|
-
self.assertIn("assert (x == 5) , 'x should be equal to 5'
|
|
537
|
+
self.assertIn("assert (x == 5) , 'x should be equal to 5';", output)
|
|
520
538
|
self.assertIn("if not (x == y) {", output)
|
|
521
539
|
self.assertIn("squares_dict = {x : (x ** 2) for x in numbers};", output)
|
|
522
540
|
self.assertIn(
|
|
@@ -539,7 +557,7 @@ class JacLanguageTests(TestCase):
|
|
|
539
557
|
),
|
|
540
558
|
prog=JacProgram(),
|
|
541
559
|
).ir_out.unparse()
|
|
542
|
-
self.assertIn("class X {\n with entry {\n a_b = 67;", output)
|
|
560
|
+
self.assertIn("class X {\n with entry {\n let a_b = 67;", output)
|
|
543
561
|
self.assertIn("br = b'Hello\\\\\\\\nWorld'", output)
|
|
544
562
|
self.assertIn(
|
|
545
563
|
"class Circle {\n def init(self: Circle, radius: float", output
|
|
@@ -605,9 +623,34 @@ class JacLanguageTests(TestCase):
|
|
|
605
623
|
prog=None,
|
|
606
624
|
).ir_out.unparse()
|
|
607
625
|
self.assertIn(
|
|
608
|
-
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {",
|
|
626
|
+
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {",
|
|
627
|
+
output,
|
|
628
|
+
)
|
|
609
629
|
self.assertIn(
|
|
610
|
-
"def len(<>obj: Sized, astt: Any, /, z: int, j: str, a: Any = 90) -> int {",
|
|
630
|
+
"def len(<>obj: Sized, astt: Any, /, z: int, j: str, a: Any = 90) -> int {",
|
|
631
|
+
output,
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
def test_py2jac_empty_file(self) -> None:
|
|
635
|
+
"""Test py ast to Jac ast conversion."""
|
|
636
|
+
from jaclang.compiler.passes.main import PyastBuildPass
|
|
637
|
+
import jaclang.compiler.unitree as ast
|
|
638
|
+
import ast as py_ast
|
|
639
|
+
|
|
640
|
+
py_out_path = os.path.join(self.fixture_abs_path("./"), "py2jac_empty.py")
|
|
641
|
+
with open(py_out_path) as f:
|
|
642
|
+
file_source = f.read()
|
|
643
|
+
converted_ast = PyastBuildPass(
|
|
644
|
+
ir_in=ast.PythonModuleAst(
|
|
645
|
+
py_ast.parse(file_source),
|
|
646
|
+
orig_src=ast.Source(file_source, py_out_path),
|
|
647
|
+
),
|
|
648
|
+
prog=None,
|
|
649
|
+
).ir_out
|
|
650
|
+
self.assertIsInstance(
|
|
651
|
+
converted_ast,
|
|
652
|
+
ast.Module
|
|
653
|
+
)
|
|
611
654
|
|
|
612
655
|
def test_refs_target(self) -> None:
|
|
613
656
|
"""Test py ast to Jac ast conversion output."""
|
|
@@ -771,7 +814,9 @@ class JacLanguageTests(TestCase):
|
|
|
771
814
|
"""Test importing python."""
|
|
772
815
|
captured_output = io.StringIO()
|
|
773
816
|
sys.stdout = captured_output
|
|
774
|
-
Jac.jac_import(
|
|
817
|
+
Jac.jac_import(
|
|
818
|
+
"test_kwonly_params", base_path=self.fixture_abs_path("./params")
|
|
819
|
+
)
|
|
775
820
|
sys.stdout = sys.__stdout__
|
|
776
821
|
stdout_value = captured_output.getvalue().split("\n")
|
|
777
822
|
self.assertEqual("KW_SIMPLE: 42", stdout_value[0])
|
|
@@ -784,7 +829,9 @@ class JacLanguageTests(TestCase):
|
|
|
784
829
|
"""Test importing python."""
|
|
785
830
|
captured_output = io.StringIO()
|
|
786
831
|
sys.stdout = captured_output
|
|
787
|
-
Jac.jac_import(
|
|
832
|
+
Jac.jac_import(
|
|
833
|
+
"test_complex_params", base_path=self.fixture_abs_path("./params")
|
|
834
|
+
)
|
|
788
835
|
sys.stdout = sys.__stdout__
|
|
789
836
|
stdout_value = captured_output.getvalue().split("\n")
|
|
790
837
|
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[0])
|
|
@@ -792,17 +839,23 @@ class JacLanguageTests(TestCase):
|
|
|
792
839
|
self.assertEqual("SEPARATORS: 42", stdout_value[2])
|
|
793
840
|
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[3])
|
|
794
841
|
self.assertEqual("RECURSIVE: 7 11", stdout_value[4])
|
|
795
|
-
self.assertEqual(
|
|
842
|
+
self.assertEqual(
|
|
843
|
+
"VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5]
|
|
844
|
+
)
|
|
796
845
|
|
|
797
846
|
def test_param_failing(self) -> None:
|
|
798
847
|
"""Test importing python."""
|
|
799
848
|
captured_output = io.StringIO()
|
|
800
849
|
sys.stdout = captured_output
|
|
801
|
-
for i in [
|
|
850
|
+
for i in [
|
|
851
|
+
"test_failing_posonly",
|
|
852
|
+
"test_failing_kwonly",
|
|
853
|
+
"test_failing_varargs",
|
|
854
|
+
]:
|
|
802
855
|
Jac.jac_import(i, base_path=self.fixture_abs_path("./params"))
|
|
803
856
|
sys.stdout = sys.__stdout__
|
|
804
857
|
stdout_value = captured_output.getvalue()
|
|
805
|
-
self.assertNotIn(
|
|
858
|
+
self.assertNotIn("FAILED", stdout_value)
|
|
806
859
|
|
|
807
860
|
def test_double_import_exec(self) -> None:
|
|
808
861
|
"""Test importing python."""
|
|
@@ -1315,9 +1368,11 @@ class JacLanguageTests(TestCase):
|
|
|
1315
1368
|
captured_output = io.StringIO()
|
|
1316
1369
|
sys.stdout = captured_output
|
|
1317
1370
|
sys.stderr = captured_output
|
|
1318
|
-
|
|
1371
|
+
with self.assertRaises(SystemExit) as cm:
|
|
1372
|
+
cli.run(self.fixture_abs_path("here_usage_error.jac"))
|
|
1319
1373
|
sys.stdout = sys.__stdout__
|
|
1320
1374
|
sys.stderr = sys.__stderr__
|
|
1375
|
+
self.assertEqual(cm.exception.code, 1)
|
|
1321
1376
|
stdout_value = captured_output.getvalue()
|
|
1322
1377
|
self.assertIn("'here' is not defined", stdout_value)
|
|
1323
1378
|
|
|
@@ -1432,7 +1487,7 @@ class JacLanguageTests(TestCase):
|
|
|
1432
1487
|
|
|
1433
1488
|
def test_read_file_with_encoding_utf8(self) -> None:
|
|
1434
1489
|
"""Test reading UTF-8 encoded file."""
|
|
1435
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1490
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1436
1491
|
test_content = "Hello, 世界! 🌍 Testing UTF-8 encoding."
|
|
1437
1492
|
f.write(test_content)
|
|
1438
1493
|
temp_path = f.name
|
|
@@ -1445,7 +1500,9 @@ class JacLanguageTests(TestCase):
|
|
|
1445
1500
|
|
|
1446
1501
|
def test_read_file_with_encoding_utf16(self) -> None:
|
|
1447
1502
|
"""Test reading UTF-16 encoded file when UTF-8 fails."""
|
|
1448
|
-
with tempfile.NamedTemporaryFile(
|
|
1503
|
+
with tempfile.NamedTemporaryFile(
|
|
1504
|
+
delete=False, mode="w", encoding="utf-16"
|
|
1505
|
+
) as f:
|
|
1449
1506
|
test_content = "Hello, 世界! UTF-16 encoding test."
|
|
1450
1507
|
f.write(test_content)
|
|
1451
1508
|
temp_path = f.name
|
|
@@ -1458,7 +1515,9 @@ class JacLanguageTests(TestCase):
|
|
|
1458
1515
|
|
|
1459
1516
|
def test_read_file_with_encoding_utf8_bom(self) -> None:
|
|
1460
1517
|
"""Test reading UTF-8 with BOM encoded file."""
|
|
1461
|
-
with tempfile.NamedTemporaryFile(
|
|
1518
|
+
with tempfile.NamedTemporaryFile(
|
|
1519
|
+
delete=False, mode="w", encoding="utf-8-sig"
|
|
1520
|
+
) as f:
|
|
1462
1521
|
test_content = "Hello, UTF-8 BOM test! 🚀"
|
|
1463
1522
|
f.write(test_content)
|
|
1464
1523
|
temp_path = f.name
|
|
@@ -1501,7 +1560,7 @@ class JacLanguageTests(TestCase):
|
|
|
1501
1560
|
|
|
1502
1561
|
def test_read_file_with_encoding_special_characters(self) -> None:
|
|
1503
1562
|
"""Test reading file with various special characters."""
|
|
1504
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1563
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1505
1564
|
test_content = (
|
|
1506
1565
|
"Special chars: åäö ñ ü ç é\n"
|
|
1507
1566
|
"Symbols: ©®™ §¶†‡•\n"
|
|
@@ -1522,4 +1581,33 @@ class JacLanguageTests(TestCase):
|
|
|
1522
1581
|
self.assertIn("∑∏∫", result)
|
|
1523
1582
|
self.assertIn("😀😍", result)
|
|
1524
1583
|
finally:
|
|
1525
|
-
os.unlink(temp_path)
|
|
1584
|
+
os.unlink(temp_path)
|
|
1585
|
+
|
|
1586
|
+
def test_funccall_genexpr(self) -> None:
|
|
1587
|
+
"""Test function call with generator expression in both Jac and py2jac."""
|
|
1588
|
+
# Test language support
|
|
1589
|
+
captured_output = io.StringIO()
|
|
1590
|
+
sys.stdout = captured_output
|
|
1591
|
+
Jac.jac_import("funccall_genexpr", base_path=self.fixture_abs_path("./"))
|
|
1592
|
+
sys.stdout = sys.__stdout__
|
|
1593
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1594
|
+
self.assertIn("Result: 30", stdout_value[0])
|
|
1595
|
+
|
|
1596
|
+
# Test py2jac conversion
|
|
1597
|
+
py_file_path = f"{self.fixture_abs_path('../../tests/fixtures/funccall_genexpr.py')}"
|
|
1598
|
+
captured_output = io.StringIO()
|
|
1599
|
+
sys.stdout = captured_output
|
|
1600
|
+
cli.py2jac(py_file_path)
|
|
1601
|
+
sys.stdout = sys.__stdout__
|
|
1602
|
+
stdout_value = captured_output.getvalue()
|
|
1603
|
+
self.assertIn("result = total((x * x) for x in range(5));", stdout_value)
|
|
1604
|
+
|
|
1605
|
+
def test_attr_pattern_case(self) -> None:
|
|
1606
|
+
"""Test attribute pattern matching."""
|
|
1607
|
+
captured_output = io.StringIO()
|
|
1608
|
+
sys.stdout = captured_output
|
|
1609
|
+
Jac.jac_import("attr_pattern_case", base_path=self.fixture_abs_path("./"))
|
|
1610
|
+
sys.stdout = sys.__stdout__
|
|
1611
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1612
|
+
self.assertIn("Matched a.b.c Hello Jaseci!", stdout_value[0])
|
|
1613
|
+
|
jaclang/tests/test_reference.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
5
|
from contextlib import redirect_stdout
|
|
6
|
+
import sys
|
|
6
7
|
from typing import Callable, Optional
|
|
7
8
|
|
|
8
9
|
import jaclang
|
|
@@ -50,6 +51,8 @@ class JacReferenceTests(TestCase):
|
|
|
50
51
|
|
|
51
52
|
def micro_suite_test(self, filename: str) -> None:
|
|
52
53
|
"""Test file."""
|
|
54
|
+
# Reset machine at the start of each test to ensure clean state
|
|
55
|
+
Jac.reset_machine()
|
|
53
56
|
|
|
54
57
|
def execute_and_capture_output(code: str | bytes, filename: str = "") -> str:
|
|
55
58
|
f = io.StringIO()
|
|
@@ -63,6 +66,12 @@ class JacReferenceTests(TestCase):
|
|
|
63
66
|
)
|
|
64
67
|
return f.getvalue()
|
|
65
68
|
|
|
69
|
+
def normalize_function_addresses(text: str) -> str:
|
|
70
|
+
"""Normalize function memory addresses in output for consistent comparison."""
|
|
71
|
+
import re
|
|
72
|
+
# Replace <function Name at 0xADDRESS> with <function Name at 0x...>
|
|
73
|
+
return re.sub(r'<function (\w+) at 0x[0-9a-f]+>', r'<function \1 at 0x...>', text)
|
|
74
|
+
|
|
66
75
|
try:
|
|
67
76
|
if "tests.jac" in filename or "check_statements.jac" in filename:
|
|
68
77
|
return
|
|
@@ -74,21 +83,29 @@ class JacReferenceTests(TestCase):
|
|
|
74
83
|
)
|
|
75
84
|
output_jac = execute_and_capture_output(code_content, filename=filename)
|
|
76
85
|
Jac.reset_machine()
|
|
86
|
+
# Clear byllm modules from cache to ensure consistent behavior between JAC and Python runs
|
|
87
|
+
# when byllm is used
|
|
88
|
+
sys.modules.pop("byllm", None)
|
|
89
|
+
sys.modules.pop("byllm.lib", None)
|
|
77
90
|
filename = filename.replace(".jac", ".py")
|
|
78
91
|
with open(filename, "r") as file:
|
|
79
92
|
code_content = file.read()
|
|
80
93
|
output_py = execute_and_capture_output(code_content, filename=filename)
|
|
81
94
|
|
|
82
|
-
#
|
|
83
|
-
|
|
95
|
+
# Normalize function addresses before comparison
|
|
96
|
+
output_jac = normalize_function_addresses(output_jac)
|
|
97
|
+
output_py = normalize_function_addresses(output_py)
|
|
98
|
+
|
|
99
|
+
print(f"\nJAC Output:\n{output_jac}")
|
|
100
|
+
print(f"\nPython Output:\n{output_py}")
|
|
84
101
|
|
|
85
102
|
self.assertGreater(len(output_py), 0)
|
|
86
|
-
self.assertEqual(len(output_jac.split("\n")), len(output_py.split("\n")))
|
|
87
103
|
# doing like below for concurrent_expressions.jac and other current tests
|
|
88
104
|
for i in output_py.split("\n"):
|
|
89
105
|
self.assertIn(i, output_jac)
|
|
90
106
|
for i in output_jac.split("\n"):
|
|
91
107
|
self.assertIn(i, output_py)
|
|
108
|
+
self.assertEqual(len(output_jac.split("\n")), len(output_py.split("\n")))
|
|
92
109
|
# self.assertEqual(output_py, output_jac)
|
|
93
110
|
except Exception as e:
|
|
94
111
|
# print(f"\nJAC Output:\n{output_jac}")
|