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.

Files changed (114) hide show
  1. jaclang/cli/cli.py +194 -10
  2. jaclang/cli/cmdreg.py +144 -8
  3. jaclang/compiler/__init__.py +6 -1
  4. jaclang/compiler/codeinfo.py +16 -1
  5. jaclang/compiler/constant.py +33 -8
  6. jaclang/compiler/jac.lark +154 -62
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +656 -149
  9. jaclang/compiler/passes/__init__.py +2 -1
  10. jaclang/compiler/passes/ast_gen/__init__.py +5 -0
  11. jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
  12. jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
  13. jaclang/compiler/passes/ecmascript/__init__.py +25 -0
  14. jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
  15. jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
  16. jaclang/compiler/passes/ecmascript/estree.py +972 -0
  17. jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
  18. jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
  19. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
  20. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
  21. jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
  22. jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
  23. jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
  24. jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
  25. jaclang/compiler/passes/main/__init__.py +0 -3
  26. jaclang/compiler/passes/main/annex_pass.py +23 -1
  27. jaclang/compiler/passes/main/def_use_pass.py +1 -0
  28. jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
  29. jaclang/compiler/passes/main/pyast_load_pass.py +48 -11
  30. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  31. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  32. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  34. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  35. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  36. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  37. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -3
  38. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  39. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  40. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
  41. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  42. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  43. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +219 -20
  44. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  45. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  46. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  47. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
  48. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  49. jaclang/compiler/passes/transform.py +9 -1
  50. jaclang/compiler/passes/uni_pass.py +5 -7
  51. jaclang/compiler/program.py +27 -26
  52. jaclang/compiler/tests/test_client_codegen.py +113 -0
  53. jaclang/compiler/tests/test_importer.py +12 -10
  54. jaclang/compiler/tests/test_parser.py +249 -3
  55. jaclang/compiler/type_system/type_evaluator.jac +1078 -0
  56. jaclang/compiler/type_system/type_utils.py +1 -1
  57. jaclang/compiler/type_system/types.py +6 -0
  58. jaclang/compiler/unitree.py +438 -82
  59. jaclang/langserve/engine.jac +224 -288
  60. jaclang/langserve/sem_manager.jac +12 -8
  61. jaclang/langserve/server.jac +48 -48
  62. jaclang/langserve/tests/fixtures/greet.py +17 -0
  63. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  64. jaclang/langserve/tests/fixtures/user.jac +15 -0
  65. jaclang/langserve/tests/test_server.py +66 -371
  66. jaclang/lib.py +17 -0
  67. jaclang/runtimelib/archetype.py +25 -25
  68. jaclang/runtimelib/client_bundle.py +169 -0
  69. jaclang/runtimelib/client_runtime.jac +586 -0
  70. jaclang/runtimelib/constructs.py +4 -2
  71. jaclang/runtimelib/machine.py +308 -139
  72. jaclang/runtimelib/meta_importer.py +111 -22
  73. jaclang/runtimelib/mtp.py +15 -0
  74. jaclang/runtimelib/server.py +1089 -0
  75. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  76. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  77. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  78. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  79. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  80. jaclang/runtimelib/tests/test_client_render.py +63 -0
  81. jaclang/runtimelib/tests/test_serve.py +1069 -0
  82. jaclang/settings.py +0 -3
  83. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  84. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  85. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  86. jaclang/tests/fixtures/iife_functions.jac +142 -0
  87. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  88. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  89. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  90. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  91. jaclang/tests/fixtures/py2jac_empty.py +0 -0
  92. jaclang/tests/fixtures/py_run.py +7 -5
  93. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  94. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  95. jaclang/tests/test_cli.py +134 -18
  96. jaclang/tests/test_language.py +120 -32
  97. jaclang/tests/test_reference.py +20 -3
  98. jaclang/utils/NonGPT.py +375 -0
  99. jaclang/utils/helpers.py +64 -20
  100. jaclang/utils/lang_tools.py +31 -4
  101. jaclang/utils/tests/test_lang_tools.py +5 -16
  102. jaclang/utils/treeprinter.py +8 -3
  103. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  104. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
  105. jaclang/compiler/passes/main/binder_pass.py +0 -594
  106. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  107. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  108. jaclang/compiler/type_system/type_evaluator.py +0 -844
  109. jaclang/langserve/tests/session.jac +0 -294
  110. jaclang/langserve/tests/test_dev_server.py +0 -80
  111. jaclang/runtimelib/importer.py +0 -351
  112. jaclang/tests/test_typecheck.py +0 -542
  113. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  114. {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 contextlib.suppress(Exception):
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/connect_expressions.jac')}")
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("connect_expressions.dot"):
393
- os.remove("connect_expressions.dot")
394
- self.assertIn("11\n13\n15\n>>> Graph content saved to", stdout_value)
395
- self.assertIn("connect_expressions.dot\n", stdout_value)
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('../../tests/fixtures/params/test_complex_params.jac')
443
- cli.jac2py(f"{self.fixture_abs_path('../../tests/fixtures/params/test_complex_params.jac')}")
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(mode='w', suffix='.py', delete=False) as temp_file:
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 = JacProgram().compile(use_str=py_code, file_path=py_file_path).unparse()
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(mode='w', suffix='.jac', delete=False) as temp_file:
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
- os.remove(py_file_path)
460
- os.remove(jac_file_path)
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("VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[-2])
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)
@@ -133,8 +133,17 @@ class JacLanguageTests(TestCase):
133
133
 
134
134
  for edge in edges:
135
135
  label = edge["label"]
136
- self.assertIn(label, ["E(val=1)", "E(val=1)", "E(val=1)", "E(val=0)", "E(val=0)", "E(val=0)"])
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.py", "exec"))
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.assertIn("module 'pyfunc' from ", stdout_value)
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.assertIn("module 'pyfunc' from ", stdout_value)
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' ;", output)
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 {", output)
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 {", output)
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("test_kwonly_params", base_path=self.fixture_abs_path("./params"))
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("test_complex_params", base_path=self.fixture_abs_path("./params"))
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("VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5])
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 ["test_failing_posonly", "test_failing_kwonly", "test_failing_varargs"]:
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('FAILED', stdout_value)
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
- cli.run(self.fixture_abs_path("here_usage_error.jac"))
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='w', encoding='utf-8', delete=False) as f:
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(delete=False, mode="w", encoding="utf-16") as f:
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(delete=False, mode='w', encoding='utf-8-sig') as f:
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='w', encoding='utf-8', delete=False) as f:
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
+
@@ -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
- # print(f"\nJAC Output:\n{output_jac}")
83
- # print(f"\nPython Output:\n{output_py}")
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}")