jaclang 0.8.8__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 +64 -2
- jaclang/compiler/constant.py +6 -1
- jaclang/compiler/jac.lark +34 -41
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +143 -27
- jaclang/compiler/passes/main/def_use_pass.py +1 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +151 -83
- jaclang/compiler/passes/main/pyast_load_pass.py +2 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +0 -1
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +104 -20
- 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/program.py +6 -2
- jaclang/compiler/type_system/type_evaluator.jac +959 -0
- jaclang/compiler/unitree.py +23 -13
- jaclang/lib.py +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/constructs.py +2 -2
- jaclang/runtimelib/machine.py +57 -47
- jaclang/settings.py +1 -2
- 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/py2jac_empty.py +0 -0
- jaclang/tests/test_cli.py +134 -18
- jaclang/tests/test_language.py +146 -29
- jaclang/tests/test_reference.py +3 -1
- jaclang/utils/helpers.py +20 -4
- jaclang/utils/tests/test_lang_tools.py +4 -15
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -1
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/RECORD +35 -30
- jaclang/compiler/type_system/type_evaluator.py +0 -844
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/WHEEL +0 -0
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.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 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
|
@@ -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()
|
|
@@ -516,7 +528,7 @@ class JacLanguageTests(TestCase):
|
|
|
516
528
|
).ir_out.unparse()
|
|
517
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
533
|
self.assertIn("squares_dict = {x : (x ** 2) for x in numbers};", output)
|
|
522
534
|
self.assertIn(
|
|
@@ -605,9 +617,34 @@ class JacLanguageTests(TestCase):
|
|
|
605
617
|
prog=None,
|
|
606
618
|
).ir_out.unparse()
|
|
607
619
|
self.assertIn(
|
|
608
|
-
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {",
|
|
620
|
+
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {",
|
|
621
|
+
output,
|
|
622
|
+
)
|
|
609
623
|
self.assertIn(
|
|
610
|
-
"def len(<>obj: Sized, astt: Any, /, z: int, j: str, a: Any = 90) -> int {",
|
|
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
|
+
)
|
|
611
648
|
|
|
612
649
|
def test_refs_target(self) -> None:
|
|
613
650
|
"""Test py ast to Jac ast conversion output."""
|
|
@@ -771,7 +808,9 @@ class JacLanguageTests(TestCase):
|
|
|
771
808
|
"""Test importing python."""
|
|
772
809
|
captured_output = io.StringIO()
|
|
773
810
|
sys.stdout = captured_output
|
|
774
|
-
Jac.jac_import(
|
|
811
|
+
Jac.jac_import(
|
|
812
|
+
"test_kwonly_params", base_path=self.fixture_abs_path("./params")
|
|
813
|
+
)
|
|
775
814
|
sys.stdout = sys.__stdout__
|
|
776
815
|
stdout_value = captured_output.getvalue().split("\n")
|
|
777
816
|
self.assertEqual("KW_SIMPLE: 42", stdout_value[0])
|
|
@@ -784,7 +823,9 @@ class JacLanguageTests(TestCase):
|
|
|
784
823
|
"""Test importing python."""
|
|
785
824
|
captured_output = io.StringIO()
|
|
786
825
|
sys.stdout = captured_output
|
|
787
|
-
Jac.jac_import(
|
|
826
|
+
Jac.jac_import(
|
|
827
|
+
"test_complex_params", base_path=self.fixture_abs_path("./params")
|
|
828
|
+
)
|
|
788
829
|
sys.stdout = sys.__stdout__
|
|
789
830
|
stdout_value = captured_output.getvalue().split("\n")
|
|
790
831
|
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[0])
|
|
@@ -792,17 +833,23 @@ class JacLanguageTests(TestCase):
|
|
|
792
833
|
self.assertEqual("SEPARATORS: 42", stdout_value[2])
|
|
793
834
|
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[3])
|
|
794
835
|
self.assertEqual("RECURSIVE: 7 11", stdout_value[4])
|
|
795
|
-
self.assertEqual(
|
|
836
|
+
self.assertEqual(
|
|
837
|
+
"VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5]
|
|
838
|
+
)
|
|
796
839
|
|
|
797
840
|
def test_param_failing(self) -> None:
|
|
798
841
|
"""Test importing python."""
|
|
799
842
|
captured_output = io.StringIO()
|
|
800
843
|
sys.stdout = captured_output
|
|
801
|
-
for i in [
|
|
844
|
+
for i in [
|
|
845
|
+
"test_failing_posonly",
|
|
846
|
+
"test_failing_kwonly",
|
|
847
|
+
"test_failing_varargs",
|
|
848
|
+
]:
|
|
802
849
|
Jac.jac_import(i, base_path=self.fixture_abs_path("./params"))
|
|
803
850
|
sys.stdout = sys.__stdout__
|
|
804
851
|
stdout_value = captured_output.getvalue()
|
|
805
|
-
self.assertNotIn(
|
|
852
|
+
self.assertNotIn("FAILED", stdout_value)
|
|
806
853
|
|
|
807
854
|
def test_double_import_exec(self) -> None:
|
|
808
855
|
"""Test importing python."""
|
|
@@ -1315,9 +1362,11 @@ class JacLanguageTests(TestCase):
|
|
|
1315
1362
|
captured_output = io.StringIO()
|
|
1316
1363
|
sys.stdout = captured_output
|
|
1317
1364
|
sys.stderr = captured_output
|
|
1318
|
-
|
|
1365
|
+
with self.assertRaises(SystemExit) as cm:
|
|
1366
|
+
cli.run(self.fixture_abs_path("here_usage_error.jac"))
|
|
1319
1367
|
sys.stdout = sys.__stdout__
|
|
1320
1368
|
sys.stderr = sys.__stderr__
|
|
1369
|
+
self.assertEqual(cm.exception.code, 1)
|
|
1321
1370
|
stdout_value = captured_output.getvalue()
|
|
1322
1371
|
self.assertIn("'here' is not defined", stdout_value)
|
|
1323
1372
|
|
|
@@ -1432,7 +1481,7 @@ class JacLanguageTests(TestCase):
|
|
|
1432
1481
|
|
|
1433
1482
|
def test_read_file_with_encoding_utf8(self) -> None:
|
|
1434
1483
|
"""Test reading UTF-8 encoded file."""
|
|
1435
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1484
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1436
1485
|
test_content = "Hello, 世界! 🌍 Testing UTF-8 encoding."
|
|
1437
1486
|
f.write(test_content)
|
|
1438
1487
|
temp_path = f.name
|
|
@@ -1445,7 +1494,9 @@ class JacLanguageTests(TestCase):
|
|
|
1445
1494
|
|
|
1446
1495
|
def test_read_file_with_encoding_utf16(self) -> None:
|
|
1447
1496
|
"""Test reading UTF-16 encoded file when UTF-8 fails."""
|
|
1448
|
-
with tempfile.NamedTemporaryFile(
|
|
1497
|
+
with tempfile.NamedTemporaryFile(
|
|
1498
|
+
delete=False, mode="w", encoding="utf-16"
|
|
1499
|
+
) as f:
|
|
1449
1500
|
test_content = "Hello, 世界! UTF-16 encoding test."
|
|
1450
1501
|
f.write(test_content)
|
|
1451
1502
|
temp_path = f.name
|
|
@@ -1458,7 +1509,9 @@ class JacLanguageTests(TestCase):
|
|
|
1458
1509
|
|
|
1459
1510
|
def test_read_file_with_encoding_utf8_bom(self) -> None:
|
|
1460
1511
|
"""Test reading UTF-8 with BOM encoded file."""
|
|
1461
|
-
with tempfile.NamedTemporaryFile(
|
|
1512
|
+
with tempfile.NamedTemporaryFile(
|
|
1513
|
+
delete=False, mode="w", encoding="utf-8-sig"
|
|
1514
|
+
) as f:
|
|
1462
1515
|
test_content = "Hello, UTF-8 BOM test! 🚀"
|
|
1463
1516
|
f.write(test_content)
|
|
1464
1517
|
temp_path = f.name
|
|
@@ -1501,7 +1554,7 @@ class JacLanguageTests(TestCase):
|
|
|
1501
1554
|
|
|
1502
1555
|
def test_read_file_with_encoding_special_characters(self) -> None:
|
|
1503
1556
|
"""Test reading file with various special characters."""
|
|
1504
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
1557
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
|
|
1505
1558
|
test_content = (
|
|
1506
1559
|
"Special chars: åäö ñ ü ç é\n"
|
|
1507
1560
|
"Symbols: ©®™ §¶†‡•\n"
|
|
@@ -1522,4 +1575,68 @@ class JacLanguageTests(TestCase):
|
|
|
1522
1575
|
self.assertIn("∑∏∫", result)
|
|
1523
1576
|
self.assertIn("😀😍", result)
|
|
1524
1577
|
finally:
|
|
1525
|
-
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}")
|
jaclang/utils/helpers.py
CHANGED
|
@@ -94,12 +94,19 @@ def auto_generate_refs() -> str:
|
|
|
94
94
|
os.path.split(os.path.dirname(__file__))[0], "../jaclang/compiler/jac.lark"
|
|
95
95
|
)
|
|
96
96
|
result = extract_headings(file_path)
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
# Create the reference subdirectory if it doesn't exist
|
|
99
|
+
docs_ref_dir = os.path.join(
|
|
100
|
+
os.path.split(os.path.dirname(__file__))[0], "../../docs/docs/learn/jac_ref"
|
|
101
|
+
)
|
|
102
|
+
os.makedirs(docs_ref_dir, exist_ok=True)
|
|
103
|
+
|
|
104
|
+
# Generate individual markdown files for each section
|
|
98
105
|
for heading, lines in result.items():
|
|
99
106
|
heading = heading.strip()
|
|
100
107
|
heading_snakecase = heading_to_snake(heading)
|
|
101
108
|
content = (
|
|
102
|
-
f'
|
|
109
|
+
f'# {heading}\n\n**Code Example**\n!!! example "Runnable Example in Jac and JacLib"\n'
|
|
103
110
|
' === "Try it!"\n <div class="code-block">\n'
|
|
104
111
|
" ```jac\n"
|
|
105
112
|
f' --8<-- "jac/examples/reference/{heading_snakecase}.jac"\n'
|
|
@@ -112,12 +119,21 @@ def auto_generate_refs() -> str:
|
|
|
112
119
|
' --8<-- "jac/examples/reference/'
|
|
113
120
|
f'{heading_snakecase}.py"\n ```\n'
|
|
114
121
|
f'??? info "Jac Grammar Snippet"\n ```yaml linenums="{lines[0]}"\n --8<-- '
|
|
115
|
-
f'"jac/jaclang/compiler/jac.lark:{lines[0]}:{lines[1]}"\n ```\n'
|
|
122
|
+
f'"jac/jaclang/compiler/jac.lark:{lines[0]}:{lines[1]}"\n ```\n\n'
|
|
116
123
|
"**Description**\n\n--8<-- "
|
|
117
124
|
f'"jac/examples/reference/'
|
|
118
125
|
f'{heading_snakecase}.md"\n'
|
|
119
126
|
)
|
|
120
|
-
|
|
127
|
+
|
|
128
|
+
# Write individual file
|
|
129
|
+
output_file = os.path.join(docs_ref_dir, f"{heading_snakecase}.md")
|
|
130
|
+
with open(output_file, "w") as f:
|
|
131
|
+
f.write(content)
|
|
132
|
+
|
|
133
|
+
# Return just the introduction for the main jac_ref.md file
|
|
134
|
+
md_str = (
|
|
135
|
+
'# Jac Language Reference\n\n--8<-- "jac/examples/reference/introduction.md"\n'
|
|
136
|
+
)
|
|
121
137
|
return md_str
|
|
122
138
|
|
|
123
139
|
|
|
@@ -112,7 +112,7 @@ class JacAstToolTests(TestCase):
|
|
|
112
112
|
"""Testing for sym, sym. AstTool."""
|
|
113
113
|
jac_file = os.path.normpath(
|
|
114
114
|
os.path.join(
|
|
115
|
-
os.path.dirname(jaclang.__file__), "../examples/reference/
|
|
115
|
+
os.path.dirname(jaclang.__file__), "../examples/reference/while_statements.jac"
|
|
116
116
|
)
|
|
117
117
|
)
|
|
118
118
|
out = self.tool.ir(["sym", jac_file])
|
|
@@ -122,25 +122,14 @@ class JacAstToolTests(TestCase):
|
|
|
122
122
|
)
|
|
123
123
|
check_list = [
|
|
124
124
|
"########",
|
|
125
|
-
"#
|
|
125
|
+
"# while_statements #",
|
|
126
126
|
"########",
|
|
127
|
-
"SymTable::Module(
|
|
128
|
-
"| +-- list1",
|
|
129
|
-
"| +-- x",
|
|
130
|
-
"| +-- impl.x",
|
|
131
|
-
"| +-- c",
|
|
132
|
-
"| +-- d",
|
|
133
|
-
"| +-- a",
|
|
134
|
-
"| +-- b",
|
|
135
|
-
"+-- SymTable::ImplDef(impl.x)",
|
|
136
|
-
" SymTable::Enum(x)",
|
|
137
|
-
"+-- line 19, col 13",
|
|
127
|
+
"SymTable::Module(while_statements)",
|
|
138
128
|
]
|
|
139
129
|
for i in check_list:
|
|
140
130
|
self.assertIn(i, out)
|
|
141
131
|
out = self.tool.ir(["sym.", jac_file])
|
|
142
|
-
self.assertIn('[label="
|
|
143
|
-
self.assertNotIn('[label="str"];', out)
|
|
132
|
+
self.assertIn('[label="', out)
|
|
144
133
|
|
|
145
134
|
def test_uninode_doc(self) -> None:
|
|
146
135
|
"""Testing for Autodoc for Uninodes."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jaclang
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.9
|
|
4
4
|
Summary: Jac is a unique and powerful programming language that runs on top of Python, offering an unprecedented level of intelligence and intuitive understanding.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: jac,jaclang,jaseci,python,programming-language,machine-learning,artificial-intelligence
|