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
@@ -0,0 +1,239 @@
1
+ """Test JavaScript code generation using consolidated Jac fixtures."""
2
+
3
+ import os
4
+ import subprocess
5
+ import tempfile
6
+ from pathlib import Path
7
+
8
+ from jaclang.compiler.passes.ecmascript import EsastGenPass
9
+ from jaclang.compiler.passes.ecmascript.es_unparse import es_to_js
10
+ from jaclang.compiler.program import JacProgram
11
+ from jaclang.utils.test import TestCase
12
+
13
+
14
+ class JavaScriptGenerationTests(TestCase):
15
+ """Validate JavaScript generation for core and advanced Jac fixtures."""
16
+
17
+ CORE_FIXTURE = "core_language_features.jac"
18
+ ADVANCED_FIXTURE = "advanced_language_features.jac"
19
+ CLIENT_FIXTURE = "client_jsx.jac"
20
+
21
+ def get_fixture_path(self, filename: str) -> str:
22
+ """Return absolute path to a fixture file."""
23
+ fixtures_dir = Path(__file__).parent / "fixtures"
24
+ return str(fixtures_dir / filename)
25
+
26
+ def compile_fixture_to_js(self, fixture_name: str) -> str:
27
+ """Compile a Jac fixture to JavaScript and return the emitted source."""
28
+ fixture_path = fixture_name
29
+ if not Path(fixture_path).exists():
30
+ fixture_path = self.get_fixture_path(fixture_name)
31
+ prog = JacProgram()
32
+ ir = prog.compile(file_path=fixture_path, no_cgen=True)
33
+
34
+ self.assertFalse(
35
+ prog.errors_had,
36
+ f"Compilation errors in {fixture_name}: {[str(e) for e in prog.errors_had]}",
37
+ )
38
+
39
+ es_pass = EsastGenPass(ir, prog)
40
+ es_ir = es_pass.ir_out
41
+
42
+ self.assertTrue(hasattr(es_ir.gen, "es_ast"), "es_ast attribute missing")
43
+ self.assertIsNotNone(es_ir.gen.es_ast, "es_ast should not be None")
44
+
45
+ return es_to_js(es_ir.gen.es_ast)
46
+
47
+ def assert_balanced_syntax(self, js_code: str, fixture_name: str) -> None:
48
+ """Ensure generated JavaScript has balanced delimiters."""
49
+ pairs = [("{", "}"), ("(", ")"), ("[", "]")]
50
+ for open_char, close_char in pairs:
51
+ self.assertEqual(
52
+ js_code.count(open_char),
53
+ js_code.count(close_char),
54
+ f"{fixture_name} produced unbalanced {open_char}{close_char} pairs",
55
+ )
56
+
57
+ def assert_no_jac_keywords(self, js_code: str, fixture_name: str) -> None:
58
+ """Verify Jac-specific keywords are absent from generated JavaScript."""
59
+ jac_keywords = [
60
+ "can ",
61
+ "has ",
62
+ "obj ",
63
+ "walker ",
64
+ "node ",
65
+ "edge ",
66
+ "visit ",
67
+ "spawn ",
68
+ "disengage ",
69
+ "here ",
70
+ "root ",
71
+ ]
72
+
73
+ for keyword in jac_keywords:
74
+ self.assertNotIn(
75
+ keyword,
76
+ js_code,
77
+ f"Jac keyword '{keyword.strip()}' leaked into JavaScript for {fixture_name}",
78
+ )
79
+
80
+ def test_core_fixture_emits_expected_constructs(self) -> None:
81
+ """Core fixture should cover fundamental language constructs."""
82
+ js_code = self.compile_fixture_to_js(self.CORE_FIXTURE)
83
+
84
+ self.assertIn("const global_counter = 0;", js_code)
85
+ self.assertIn("function add", js_code)
86
+ self.assertIn("function greet", js_code)
87
+ self.assertIn("function fibonacci", js_code)
88
+ self.assertIn("for (const i of range(limit))", js_code)
89
+ self.assertIn("while (counter > 0)", js_code)
90
+
91
+ # Operators map to JavaScript equivalents
92
+ self.assertIn("===", js_code)
93
+ self.assertIn("!==", js_code)
94
+ self.assertIn("&&", js_code)
95
+ self.assertIn("||", js_code)
96
+
97
+ # Classes and enums materialize as expected
98
+ self.assertIn("class Person", js_code)
99
+ self.assertIn("class Employee extends Person", js_code)
100
+ self.assertIn("class Calculator", js_code)
101
+ self.assertIn("class MathUtils", js_code)
102
+ self.assertIn("const Status", js_code)
103
+ self.assertIn("const Priority", js_code)
104
+
105
+ # Exception handling remains intact
106
+ self.assertIn("try", js_code)
107
+ self.assertIn("catch (err)", js_code)
108
+ self.assertIn("finally", js_code)
109
+
110
+ self.assert_balanced_syntax(js_code, self.CORE_FIXTURE)
111
+ self.assert_no_jac_keywords(js_code, self.CORE_FIXTURE)
112
+ self.assertGreater(len(js_code), 200, "Core fixture generated suspiciously small output")
113
+
114
+ def test_advanced_fixture_emits_expected_constructs(self) -> None:
115
+ """Advanced fixture should exercise higher-level Jac features."""
116
+ js_code = self.compile_fixture_to_js(self.ADVANCED_FIXTURE)
117
+
118
+ self.assertIn("function lambda_examples", js_code)
119
+ self.assertIn("async function fetch_value", js_code)
120
+ self.assertIn("await fetch_value", js_code)
121
+ self.assertIn("async function gather_async", js_code)
122
+ self.assertIn("function generator_examples", js_code)
123
+ self.assertIn("function spread_and_rest_examples", js_code)
124
+ self.assertIn("...defaults", js_code)
125
+ self.assertIn("function template_literal_examples", js_code)
126
+ self.assertIn("score >= 60 ? \"pass\" : \"fail\"", js_code)
127
+ self.assertIn("function do_while_simulation", js_code)
128
+ self.assertIn("function build_advanced_report", js_code)
129
+
130
+ # Ensure pattern matching lowered into a callable
131
+ self.assertIn("function pattern_matching_examples", js_code)
132
+
133
+ self.assert_balanced_syntax(js_code, self.ADVANCED_FIXTURE)
134
+ self.assert_no_jac_keywords(js_code, self.ADVANCED_FIXTURE)
135
+ self.assertGreater(len(js_code), 150, "Advanced fixture output unexpectedly small")
136
+
137
+ def test_client_fixture_generates_client_bundle(self) -> None:
138
+ """Client-focused fixture should emit JSX-flavoured JavaScript."""
139
+ js_code = self.compile_fixture_to_js(self.CLIENT_FIXTURE)
140
+
141
+ self.assertIn('const API_URL = "https://api.example.com";', js_code)
142
+ self.assertIn("function component()", js_code)
143
+ self.assertIn('__jacJsx("div"', js_code)
144
+ self.assertIn("class ButtonProps", js_code)
145
+ self.assertIn("constructor(props", js_code)
146
+ self.assertNotIn("server_only", js_code, "Server-only code leaked into client bundle")
147
+
148
+ self.assert_balanced_syntax(js_code, self.CLIENT_FIXTURE)
149
+
150
+ def test_iife_fixture_generates_function_expressions(self) -> None:
151
+ """IIFE-heavy fixture should lower Jac function expressions for JS runtime."""
152
+ fixture_path = self.lang_fixture_abs_path("iife_functions_client.jac")
153
+ js_code = self.compile_fixture_to_js(fixture_path)
154
+
155
+ # Ensure representative IIFE constructs are present
156
+ self.assertIn("function get_value()", js_code)
157
+ self.assertIn("function calculate(x, y)", js_code)
158
+ self.assertIn("}();", js_code, "IIFE invocation pattern missing in generated JS")
159
+ self.assertIn("function outer()", js_code)
160
+ self.assertIn("return () => {\n let count = count + 1;\n return count;\n };", js_code)
161
+ self.assertIn("All client-side IIFE tests completed!", js_code)
162
+
163
+ def test_cli_js_command_outputs_js(self) -> None:
164
+ """jac js CLI should emit JavaScript for the core fixture."""
165
+ fixture_path = self.get_fixture_path(self.CORE_FIXTURE)
166
+ env = os.environ.copy()
167
+ project_root = str(Path(__file__).resolve().parents[4])
168
+ existing = env.get("PYTHONPATH", "")
169
+ env["PYTHONPATH"] = f"{project_root}:{existing}" if existing else project_root
170
+
171
+ result = subprocess.run(
172
+ ["python3", "-m", "jaclang.cli.cli", "js", fixture_path],
173
+ capture_output=True,
174
+ text=True,
175
+ env=env,
176
+ )
177
+
178
+ self.assertEqual(result.returncode, 0, f"CLI command failed: {result.stderr}")
179
+ self.assertGreater(len(result.stdout), 0, "CLI produced no output")
180
+ self.assertIn("function add", result.stdout)
181
+
182
+ def test_empty_file_generates_minimal_js(self) -> None:
183
+ """Ensure an empty Jac file generates a minimal JavaScript stub."""
184
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".jac", delete=False) as tmp:
185
+ tmp.write('"""Empty file for smoke testing."""\n')
186
+ temp_path = tmp.name
187
+
188
+ try:
189
+ js_code = self.compile_fixture_to_js(temp_path)
190
+ self.assertLess(len(js_code), 100, "Empty file produced unexpectedly large output")
191
+ self.assert_balanced_syntax(js_code, temp_path)
192
+ finally:
193
+ os.unlink(temp_path)
194
+
195
+ def test_type_to_typeof_transformation(self) -> None:
196
+ """Test that type() calls transform to typeof operator in JavaScript."""
197
+ jac_code = '''"""Test type() to typeof conversion."""
198
+
199
+ cl def check_types() {
200
+ let x = 42;
201
+ let y = "hello";
202
+ let obj = {"key": "value"};
203
+ let arr = [1, 2, 3];
204
+
205
+ let t1 = type(x);
206
+ let t2 = type(y);
207
+ let t3 = type(obj);
208
+ let t4 = type(arr[0]);
209
+
210
+ return t1;
211
+ }
212
+ '''
213
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".jac", delete=False) as tmp:
214
+ tmp.write(jac_code)
215
+ tmp.flush()
216
+ temp_path = tmp.name
217
+
218
+ try:
219
+ js_code = self.compile_fixture_to_js(temp_path)
220
+
221
+ # Verify typeof operator is present
222
+ self.assertIn("typeof", js_code, "typeof operator should be present in output")
223
+
224
+ # Verify specific transformations
225
+ self.assertIn("typeof x", js_code, "type(x) should become typeof x")
226
+ self.assertIn("typeof y", js_code, "type(y) should become typeof y")
227
+ self.assertIn("typeof obj", js_code, "type(obj) should become typeof obj")
228
+ self.assertIn("typeof arr[0]", js_code, "type(arr[0]) should become typeof arr[0]")
229
+
230
+ # Ensure no type() function calls remain
231
+ self.assertNotIn("type(", js_code, "No type() calls should remain in JavaScript")
232
+
233
+ # Count typeof occurrences - should have exactly 4
234
+ typeof_count = js_code.count("typeof")
235
+ self.assertEqual(typeof_count, 4, f"Expected 4 typeof occurrences, found {typeof_count}")
236
+
237
+ self.assert_balanced_syntax(js_code, temp_path)
238
+ finally:
239
+ os.unlink(temp_path)
@@ -2,7 +2,6 @@
2
2
 
3
3
  from ..transform import Alert, Transform # noqa: I100
4
4
  from .annex_pass import JacAnnexPass # noqa: I100
5
- from .binder_pass import BinderPass # noqa: I100
6
5
  from .sym_tab_build_pass import SymTabBuildPass, UniPass # noqa: I100
7
6
  from .def_use_pass import DefUsePass # noqa: I100
8
7
  from .sem_def_match_pass import SemDefMatchPass # noqa: I100
@@ -23,8 +22,6 @@ __all__ = [
23
22
  "UniPass",
24
23
  "JacAnnexPass",
25
24
  "JacImportDepsPass",
26
- "PyImportDepsPass",
27
- "BinderPass",
28
25
  "TypeCheckPass",
29
26
  "SymTabBuildPass",
30
27
  "DeclImplMatchPass",
@@ -34,6 +34,7 @@ class JacAnnexPass(Transform[uni.Module, uni.Module]):
34
34
  self.base_path = self.mod_path[:-4]
35
35
  self.impl_folder = self.base_path + ".impl"
36
36
  self.test_folder = self.base_path + ".test"
37
+ self.cl_folder = self.base_path + ".cl"
37
38
  self.directory = os.path.dirname(self.mod_path) or os.getcwd()
38
39
  self.load_annexes(jac_program=self.prog, node=ir_in)
39
40
  return ir_in
@@ -41,7 +42,7 @@ class JacAnnexPass(Transform[uni.Module, uni.Module]):
41
42
  def find_annex_paths(self) -> list[str]:
42
43
  """Return list of .impl.jac and .test.jac files related to base module."""
43
44
  paths = [os.path.join(self.directory, f) for f in os.listdir(self.directory)]
44
- for folder in [self.impl_folder, self.test_folder]:
45
+ for folder in [self.impl_folder, self.test_folder, self.cl_folder]:
45
46
  if os.path.exists(folder):
46
47
  paths += [os.path.join(folder, f) for f in os.listdir(folder)]
47
48
  return paths
@@ -66,6 +67,15 @@ class JacAnnexPass(Transform[uni.Module, uni.Module]):
66
67
  if mod:
67
68
  node.impl_mod.append(mod)
68
69
 
70
+ elif path.endswith(".cl.jac") and (
71
+ path.startswith(f"{self.base_path}.")
72
+ or os.path.dirname(path) == self.cl_folder
73
+ ):
74
+ mod = jac_program.compile(file_path=path, no_cgen=True)
75
+ if mod:
76
+ self._mark_client_declarations(mod)
77
+ node.impl_mod.append(mod)
78
+
69
79
  elif (
70
80
  path.endswith(".test.jac")
71
81
  and not settings.ignore_test_annex
@@ -77,3 +87,15 @@ class JacAnnexPass(Transform[uni.Module, uni.Module]):
77
87
  mod = jac_program.compile(file_path=path, no_cgen=True)
78
88
  if mod:
79
89
  node.test_mod.append(mod)
90
+
91
+ def _mark_client_declarations(self, module: uni.Module) -> None:
92
+ """Recursively mark client-facing nodes in the module as client declarations."""
93
+
94
+ def _mark(node: uni.UniNode) -> None:
95
+ if isinstance(node, uni.ClientFacingNode):
96
+ node.is_client_decl = True
97
+ for child in getattr(node, "kid", []):
98
+ if child:
99
+ _mark(child)
100
+
101
+ _mark(module)
@@ -37,6 +37,7 @@ class DefUsePass(UniPass):
37
37
  + node.get_all_sub_nodes(uni.DisengageStmt)
38
38
  + node.get_all_sub_nodes(uni.EdgeOpRef)
39
39
  + node.get_all_sub_nodes(uni.EventSignature)
40
+ + node.get_all_sub_nodes(uni.TypedCtxBlock)
40
41
  ):
41
42
  i.from_walker = True
42
43