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.

Files changed (36) hide show
  1. jaclang/cli/cli.py +64 -2
  2. jaclang/compiler/constant.py +6 -1
  3. jaclang/compiler/jac.lark +34 -41
  4. jaclang/compiler/larkparse/jac_parser.py +2 -2
  5. jaclang/compiler/parser.py +143 -27
  6. jaclang/compiler/passes/main/def_use_pass.py +1 -0
  7. jaclang/compiler/passes/main/pyast_gen_pass.py +151 -83
  8. jaclang/compiler/passes/main/pyast_load_pass.py +2 -0
  9. jaclang/compiler/passes/main/tests/test_checker_pass.py +0 -1
  10. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
  11. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +104 -20
  12. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  13. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  14. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
  15. jaclang/compiler/program.py +6 -2
  16. jaclang/compiler/type_system/type_evaluator.jac +959 -0
  17. jaclang/compiler/unitree.py +23 -13
  18. jaclang/lib.py +17 -0
  19. jaclang/runtimelib/archetype.py +25 -25
  20. jaclang/runtimelib/constructs.py +2 -2
  21. jaclang/runtimelib/machine.py +57 -47
  22. jaclang/settings.py +1 -2
  23. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  24. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  25. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  26. jaclang/tests/fixtures/py2jac_empty.py +0 -0
  27. jaclang/tests/test_cli.py +134 -18
  28. jaclang/tests/test_language.py +146 -29
  29. jaclang/tests/test_reference.py +3 -1
  30. jaclang/utils/helpers.py +20 -4
  31. jaclang/utils/tests/test_lang_tools.py +4 -15
  32. {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -1
  33. {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/RECORD +35 -30
  34. jaclang/compiler/type_system/type_evaluator.py +0 -844
  35. {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/WHEEL +0 -0
  36. {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 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 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)
@@ -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()
@@ -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' ;", output)
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 {", output)
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 {", output)
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("test_kwonly_params", base_path=self.fixture_abs_path("./params"))
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("test_complex_params", base_path=self.fixture_abs_path("./params"))
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("VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5])
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 ["test_failing_posonly", "test_failing_kwonly", "test_failing_varargs"]:
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('FAILED', stdout_value)
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
- cli.run(self.fixture_abs_path("here_usage_error.jac"))
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='w', encoding='utf-8', delete=False) as f:
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(delete=False, mode="w", encoding="utf-16") as f:
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(delete=False, mode='w', encoding='utf-8-sig') as f:
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='w', encoding='utf-8', delete=False) as f:
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
+ )
@@ -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
- md_str = '# Jac Language Reference\n\n--8<-- "jac/examples/reference/introduction.md"\n\n'
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'## {heading}\n**Code Example**\n!!! example "Runnable Example in Jac and JacLib"\n'
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
- md_str += f"{content}\n"
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/atom.jac"
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
- "# atom #",
125
+ "# while_statements #",
126
126
  "########",
127
- "SymTable::Module(atom)",
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="impl.x"];', out)
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.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