jaclang 0.8.9__py3-none-any.whl → 0.8.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jaclang might be problematic. Click here for more details.
- jaclang/cli/cli.py +147 -25
- jaclang/cli/cmdreg.py +144 -8
- jaclang/compiler/__init__.py +6 -1
- jaclang/compiler/codeinfo.py +16 -1
- jaclang/compiler/constant.py +33 -13
- jaclang/compiler/jac.lark +130 -31
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +567 -176
- jaclang/compiler/passes/__init__.py +2 -1
- jaclang/compiler/passes/ast_gen/__init__.py +5 -0
- jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
- jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
- jaclang/compiler/passes/ecmascript/__init__.py +25 -0
- jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
- jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
- jaclang/compiler/passes/ecmascript/estree.py +972 -0
- jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
- jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
- jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
- jaclang/compiler/passes/main/__init__.py +0 -3
- jaclang/compiler/passes/main/annex_pass.py +23 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +324 -234
- jaclang/compiler/passes/main/pyast_load_pass.py +46 -11
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
- jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
- jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -2
- jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
- jaclang/compiler/passes/main/type_checker_pass.py +7 -0
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +115 -0
- jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
- jaclang/compiler/passes/transform.py +9 -1
- jaclang/compiler/passes/uni_pass.py +5 -7
- jaclang/compiler/program.py +22 -25
- jaclang/compiler/tests/test_client_codegen.py +113 -0
- jaclang/compiler/tests/test_importer.py +12 -10
- jaclang/compiler/tests/test_parser.py +249 -3
- jaclang/compiler/type_system/type_evaluator.jac +169 -50
- jaclang/compiler/type_system/type_utils.py +1 -1
- jaclang/compiler/type_system/types.py +6 -0
- jaclang/compiler/unitree.py +430 -84
- jaclang/langserve/engine.jac +224 -288
- jaclang/langserve/sem_manager.jac +12 -8
- jaclang/langserve/server.jac +48 -48
- jaclang/langserve/tests/fixtures/greet.py +17 -0
- jaclang/langserve/tests/fixtures/md_path.jac +22 -0
- jaclang/langserve/tests/fixtures/user.jac +15 -0
- jaclang/langserve/tests/test_server.py +66 -371
- jaclang/lib.py +1 -1
- jaclang/runtimelib/client_bundle.py +169 -0
- jaclang/runtimelib/client_runtime.jac +586 -0
- jaclang/runtimelib/constructs.py +2 -0
- jaclang/runtimelib/machine.py +259 -100
- jaclang/runtimelib/meta_importer.py +111 -22
- jaclang/runtimelib/mtp.py +15 -0
- jaclang/runtimelib/server.py +1089 -0
- jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
- jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
- jaclang/runtimelib/tests/test_client_bundle.py +55 -0
- jaclang/runtimelib/tests/test_client_render.py +63 -0
- jaclang/runtimelib/tests/test_serve.py +1069 -0
- jaclang/settings.py +0 -2
- jaclang/tests/fixtures/iife_functions.jac +142 -0
- jaclang/tests/fixtures/iife_functions_client.jac +143 -0
- jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
- jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
- jaclang/tests/fixtures/needs_import_dup.jac +6 -4
- jaclang/tests/fixtures/py_run.py +7 -5
- jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
- jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
- jaclang/tests/test_cli.py +1 -1
- jaclang/tests/test_language.py +10 -39
- jaclang/tests/test_reference.py +17 -2
- jaclang/utils/NonGPT.py +375 -0
- jaclang/utils/helpers.py +44 -16
- jaclang/utils/lang_tools.py +31 -4
- jaclang/utils/tests/test_lang_tools.py +1 -1
- jaclang/utils/treeprinter.py +8 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/RECORD +96 -66
- jaclang/compiler/passes/main/binder_pass.py +0 -594
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
- jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
- jaclang/langserve/tests/session.jac +0 -294
- jaclang/langserve/tests/test_dev_server.py +0 -80
- jaclang/runtimelib/importer.py +0 -351
- jaclang/tests/test_typecheck.py +0 -542
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
- {jaclang-0.8.9.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)
|