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.
- jaclang/cli/cli.py +194 -10
- jaclang/cli/cmdreg.py +144 -8
- jaclang/compiler/__init__.py +6 -1
- jaclang/compiler/codeinfo.py +16 -1
- jaclang/compiler/constant.py +33 -8
- jaclang/compiler/jac.lark +154 -62
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +656 -149
- 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/def_use_pass.py +1 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
- jaclang/compiler/passes/main/pyast_load_pass.py +48 -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 -3
- 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_predynamo_pass.py +13 -14
- 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 +219 -20
- jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
- jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
- jaclang/compiler/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 +27 -26
- 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 +1078 -0
- jaclang/compiler/type_system/type_utils.py +1 -1
- jaclang/compiler/type_system/types.py +6 -0
- jaclang/compiler/unitree.py +438 -82
- 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 +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/client_bundle.py +169 -0
- jaclang/runtimelib/client_runtime.jac +586 -0
- jaclang/runtimelib/constructs.py +4 -2
- jaclang/runtimelib/machine.py +308 -139
- 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 -3
- jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
- jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
- jaclang/tests/fixtures/funccall_genexpr.py +5 -0
- jaclang/tests/fixtures/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/py2jac_empty.py +0 -0
- 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 +134 -18
- jaclang/tests/test_language.py +120 -32
- jaclang/tests/test_reference.py +20 -3
- jaclang/utils/NonGPT.py +375 -0
- jaclang/utils/helpers.py +64 -20
- jaclang/utils/lang_tools.py +31 -4
- jaclang/utils/tests/test_lang_tools.py +5 -16
- jaclang/utils/treeprinter.py +8 -3
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
- 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/compiler/type_system/type_evaluator.py +0 -844
- 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.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
- {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#this file is part of formatter tests and is not meant to be run
|
|
2
|
-
|
|
2
|
+
class SemTokManager {
|
|
3
3
|
"""Initialize semantic token manager."""
|
|
4
4
|
def init(self: SemTokManager, ir: uni.Module) -> None {
|
|
5
5
|
self.aaaaastatic_sem_tokens:
|
|
@@ -23,25 +23,23 @@ def walrus_example() {
|
|
|
23
23
|
with entry {
|
|
24
24
|
c = (
|
|
25
25
|
a()
|
|
26
|
-
if 1
|
|
27
|
-
isinstance(a, int)
|
|
28
|
-
isinstance(a, int)
|
|
29
|
-
isinstance(a, int)
|
|
30
|
-
isinstance(a, int)
|
|
31
|
-
isinstance(a, int)
|
|
26
|
+
if 1
|
|
27
|
+
and isinstance(a, int)
|
|
28
|
+
and isinstance(a, int)
|
|
29
|
+
and isinstance(a, int)
|
|
30
|
+
and isinstance(a, int)
|
|
31
|
+
and isinstance(a, int)
|
|
32
32
|
else (
|
|
33
33
|
999
|
|
34
|
-
if isinstance(a, int)
|
|
35
|
-
isinstance(a, int)
|
|
36
|
-
isinstance(a, int)
|
|
37
|
-
isinstance(4, bool)
|
|
34
|
+
if isinstance(a, int)
|
|
35
|
+
and isinstance(a, int)
|
|
36
|
+
and isinstance(a, int)
|
|
37
|
+
and isinstance(4, bool)
|
|
38
38
|
else 7
|
|
39
39
|
)
|
|
40
40
|
);
|
|
41
|
-
print(
|
|
42
|
-
"""
|
|
43
|
-
line of code."""
|
|
44
|
-
);
|
|
41
|
+
print("""This is a long
|
|
42
|
+
line of code.""");
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
|
|
@@ -95,7 +93,7 @@ class ModuleManager {
|
|
|
95
93
|
|
|
96
94
|
|
|
97
95
|
## expr as item extra space issue
|
|
98
|
-
|
|
96
|
+
with entry {
|
|
99
97
|
with open(f"Apple{apple}.txt") as f {
|
|
100
98
|
# Fix syntax highlighting
|
|
101
99
|
print(
|
|
@@ -106,7 +104,7 @@ class ModuleManager {
|
|
|
106
104
|
|
|
107
105
|
|
|
108
106
|
def func_with_very_long_params(
|
|
109
|
-
|
|
107
|
+
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: int,
|
|
110
108
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: int,
|
|
111
109
|
) -> None {
|
|
112
110
|
print(
|
|
@@ -132,9 +130,9 @@ def func_with_short_params(a: int, b: int) -> None {
|
|
|
132
130
|
|
|
133
131
|
with entry {
|
|
134
132
|
if (
|
|
135
|
-
node_selected
|
|
136
|
-
node_selected.find_parent_of_type(uni.Archetype)
|
|
137
|
-
node_selected.find_parent_of_type(uni.ImplDef)
|
|
133
|
+
node_selected
|
|
134
|
+
and node_selected.find_parent_of_type(uni.Archetype)
|
|
135
|
+
or node_selected.find_parent_of_type(uni.ImplDef)
|
|
138
136
|
) {
|
|
139
137
|
self_symbol = [
|
|
140
138
|
lspt.CompletionItem(label='self', kind=lspt.CompletionItemKind.Variable)
|
|
@@ -143,27 +141,35 @@ with entry {
|
|
|
143
141
|
self_symbol = [];
|
|
144
142
|
}
|
|
145
143
|
x = (
|
|
146
|
-
1222222222
|
|
147
|
-
2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
|
|
144
|
+
1222222222
|
|
145
|
+
and 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
|
|
148
146
|
);
|
|
149
147
|
a = 4 if True else 4;
|
|
150
148
|
}
|
|
151
149
|
|
|
152
150
|
|
|
151
|
+
import from typing { Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar }
|
|
152
|
+
|
|
153
|
+
import from jaclang.compiler.constant {
|
|
154
|
+
JacSemTokenModifier as SemTokMod,
|
|
155
|
+
JacSemTokenType as SemTokType,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
153
159
|
"""Return diagnostics."""
|
|
154
160
|
def gen_diagnostics(
|
|
155
161
|
from_path: str, errors: list[Alert], warnings: list[Alert]
|
|
156
162
|
) -> list[lspt.Diagnostic] {
|
|
157
163
|
if (
|
|
158
|
-
isinstance(node_selected, uni.Name)
|
|
159
|
-
node_selected.parent
|
|
160
|
-
isinstance(node_selected.parent, uni.ModulePath)
|
|
164
|
+
isinstance(node_selected, uni.Name)
|
|
165
|
+
and node_selected.parent
|
|
166
|
+
and isinstance(node_selected.parent, uni.ModulePath)
|
|
161
167
|
) {
|
|
162
168
|
node_selected = node_selected.name_of;
|
|
163
169
|
} elif (
|
|
164
|
-
isinstance(node_selected, uni.Name)
|
|
165
|
-
node_selected.parent
|
|
166
|
-
isinstance(node_selected.parent, uni.ModulePath)
|
|
170
|
+
isinstance(node_selected, uni.Name)
|
|
171
|
+
and node_selected.parent
|
|
172
|
+
and isinstance(node_selected.parent, uni.ModulePath)
|
|
167
173
|
) {
|
|
168
174
|
spec = node_selected.parent.parent.abs_path;
|
|
169
175
|
}
|
|
@@ -171,8 +177,108 @@ def gen_diagnostics(
|
|
|
171
177
|
|
|
172
178
|
|
|
173
179
|
@decorator
|
|
174
|
-
def x() {
|
|
180
|
+
def x() {
|
|
181
|
+
assert gg == 7;
|
|
182
|
+
}
|
|
175
183
|
|
|
176
184
|
|
|
177
185
|
@decorator()
|
|
178
186
|
class KK {}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# kkk
|
|
190
|
+
import pygame, random, math;
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# fff
|
|
194
|
+
with entry {
|
|
195
|
+
# gg
|
|
196
|
+
a = 9;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# kkk
|
|
201
|
+
class X {}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# comment
|
|
205
|
+
@decorator
|
|
206
|
+
def xxx() { }
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ddd
|
|
210
|
+
glob a = 7;
|
|
211
|
+
def get_node_info(self: JacLangServer, sym_node: uni.AstSymbolNode) -> Optional[str] {
|
|
212
|
+
try {
|
|
213
|
+
if isinstance(sym_node, uni.NameAtom) {
|
|
214
|
+
sym_node = sym_node.name_of;
|
|
215
|
+
}
|
|
216
|
+
access = (sym_node.sym.access.value + ' ') if sym_node.sym else None;
|
|
217
|
+
node_info = f"'('{(access if access else '')}{sym_node.sym_category.value}') '{sym_node.sym_name}";
|
|
218
|
+
if sym_node.name_spec.clean_type {
|
|
219
|
+
node_info += f"': '{sym_node.name_spec.clean_type}";
|
|
220
|
+
}
|
|
221
|
+
if (
|
|
222
|
+
isinstance(sym_node, uni.AstSymbolNode)
|
|
223
|
+
and isinstance(sym_node.name_spec.type, ClassType)
|
|
224
|
+
) {
|
|
225
|
+
node_info += f"': '{sym_node.name_spec.type.shared.class_name}";
|
|
226
|
+
}
|
|
227
|
+
if (isinstance(sym_node, uni.AstDocNode) and sym_node.doc) {
|
|
228
|
+
node_info += f"'\n'{sym_node.doc.value}";
|
|
229
|
+
}
|
|
230
|
+
if (isinstance(sym_node, uni.Ability) and sym_node.signature) {
|
|
231
|
+
node_info += f"'\n'{sym_node.signature.unparse()}";
|
|
232
|
+
}
|
|
233
|
+
} except AttributeError as e {
|
|
234
|
+
self.log_warning(f"'Attribute error when accessing node attributes: '{e}");
|
|
235
|
+
}
|
|
236
|
+
return node_info.strip();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class foo {
|
|
241
|
+
"""Get information about a node."""
|
|
242
|
+
def get_node_info(
|
|
243
|
+
self: JacLangServer, sym_node: uni.AstSymbolNode
|
|
244
|
+
) -> Optional[str] {
|
|
245
|
+
if isinstance(sym_node, uni.AstVarNode) {
|
|
246
|
+
var_name = sym_node.name;
|
|
247
|
+
var_type = sym_node.var_type if sym_node.var_type else 'unknown';
|
|
248
|
+
return f" 'Variable: ' {var_name} ', Type: ' {var_type} ";
|
|
249
|
+
} elif isinstance(sym_node, uni.AstFuncNode) {
|
|
250
|
+
func_name = sym_node.name;
|
|
251
|
+
params = ', '.join(
|
|
252
|
+
[f" {p.name} ': ' {p.param_type} " for p in sym_node.params]
|
|
253
|
+
);
|
|
254
|
+
return f" 'Function: ' {func_name} '(' {params} ')' ";
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def x3() {
|
|
261
|
+
if dest_type.is_class_instance() and src_type.is_class_instance() {
|
|
262
|
+
print("x");
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
dest_type.is_class_instance()
|
|
266
|
+
and src_type.is_class_instance()
|
|
267
|
+
and src_type.is_class_instance()
|
|
268
|
+
) {
|
|
269
|
+
print("x");
|
|
270
|
+
}
|
|
271
|
+
if (dest_type.is_class_instance() and src_type.is_class_instance()) {
|
|
272
|
+
print("x");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class HH {
|
|
278
|
+
def test() -> Optional[str] {
|
|
279
|
+
a = """!
|
|
280
|
+
hello
|
|
281
|
+
hru
|
|
282
|
+
""";
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -133,6 +133,8 @@ class JacFormatPassTests(TestCaseMicroSuite):
|
|
|
133
133
|
self.assertEqual(tokens[i + 1], "{")
|
|
134
134
|
self.assertEqual(num_test, 3)
|
|
135
135
|
return
|
|
136
|
+
before = ""
|
|
137
|
+
after = ""
|
|
136
138
|
try:
|
|
137
139
|
before = ast3.dump(code_gen_pure.gen.py_ast[0], indent=2)
|
|
138
140
|
after = ast3.dump(code_gen_jac.gen.py_ast[0], indent=2)
|
|
@@ -151,7 +153,8 @@ class JacFormatPassTests(TestCaseMicroSuite):
|
|
|
151
153
|
print("\n+++++++++++++++++++++++++++++++++++++++\n")
|
|
152
154
|
print(add_line_numbers(code_gen_format))
|
|
153
155
|
print("\n+++++++++++++++++++++++++++++++++++++++\n")
|
|
154
|
-
|
|
156
|
+
if before and after:
|
|
157
|
+
print("\n".join(unified_diff(before.splitlines(), after.splitlines())))
|
|
155
158
|
raise e
|
|
156
159
|
|
|
157
160
|
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
+
from threading import Event
|
|
7
8
|
from typing import Generic, Optional, TYPE_CHECKING, Type, TypeVar
|
|
8
9
|
|
|
9
10
|
from jaclang.compiler.codeinfo import CodeLocInfo
|
|
@@ -70,7 +71,9 @@ class Alert:
|
|
|
70
71
|
class Transform(ABC, Generic[T, R]):
|
|
71
72
|
"""Abstract class for IR passes."""
|
|
72
73
|
|
|
73
|
-
def __init__(
|
|
74
|
+
def __init__(
|
|
75
|
+
self, ir_in: T, prog: JacProgram, cancel_token: Event | None = None
|
|
76
|
+
) -> None:
|
|
74
77
|
"""Initialize pass."""
|
|
75
78
|
self.logger = logging.getLogger(self.__class__.__name__)
|
|
76
79
|
self.errors_had: list[Alert] = []
|
|
@@ -79,6 +82,7 @@ class Transform(ABC, Generic[T, R]):
|
|
|
79
82
|
self.prog = prog
|
|
80
83
|
self.time_taken = 0.0
|
|
81
84
|
self.ir_in: T = ir_in
|
|
85
|
+
self.cancel_token = cancel_token
|
|
82
86
|
self.pre_transform()
|
|
83
87
|
self.ir_out: R = self.timed_transform(ir_in=ir_in)
|
|
84
88
|
self.post_transform()
|
|
@@ -136,6 +140,10 @@ class Transform(ABC, Generic[T, R]):
|
|
|
136
140
|
"""Log info."""
|
|
137
141
|
self.logger.info(msg)
|
|
138
142
|
|
|
143
|
+
def is_canceled(self) -> bool:
|
|
144
|
+
"""Check if the pass has been canceled."""
|
|
145
|
+
return self.cancel_token is not None and self.cancel_token.is_set()
|
|
146
|
+
|
|
139
147
|
def ice(self, msg: str = "Something went horribly wrong!") -> RuntimeError:
|
|
140
148
|
"""Pass Error."""
|
|
141
149
|
self.log_error(f"ICE: Pass {self.__class__.__name__} - {msg}")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from threading import Event
|
|
5
6
|
from typing import Optional, TYPE_CHECKING, Type, TypeVar
|
|
6
7
|
|
|
7
8
|
import jaclang.compiler.unitree as uni
|
|
@@ -21,11 +22,12 @@ class UniPass(Transform[uni.Module, uni.Module]):
|
|
|
21
22
|
self,
|
|
22
23
|
ir_in: uni.Module,
|
|
23
24
|
prog: JacProgram,
|
|
25
|
+
cancel_token: Event | None = None,
|
|
24
26
|
) -> None:
|
|
25
27
|
"""Initialize parser."""
|
|
26
28
|
self.term_signal = False
|
|
27
29
|
self.prune_signal = False
|
|
28
|
-
Transform.__init__(self, ir_in, prog)
|
|
30
|
+
Transform.__init__(self, ir_in, prog, cancel_token=cancel_token)
|
|
29
31
|
|
|
30
32
|
def before_pass(self) -> None:
|
|
31
33
|
"""Run once before pass."""
|
|
@@ -43,10 +45,6 @@ class UniPass(Transform[uni.Module, uni.Module]):
|
|
|
43
45
|
if hasattr(self, f"exit_{pascal_to_snake(type(node).__name__)}"):
|
|
44
46
|
getattr(self, f"exit_{pascal_to_snake(type(node).__name__)}")(node)
|
|
45
47
|
|
|
46
|
-
def terminate(self) -> None:
|
|
47
|
-
"""Terminate traversal."""
|
|
48
|
-
self.term_signal = True
|
|
49
|
-
|
|
50
48
|
def prune(self) -> None:
|
|
51
49
|
"""Prune traversal."""
|
|
52
50
|
self.prune_signal = True
|
|
@@ -120,7 +118,7 @@ class UniPass(Transform[uni.Module, uni.Module]):
|
|
|
120
118
|
|
|
121
119
|
def traverse(self, node: uni.UniNode) -> uni.UniNode:
|
|
122
120
|
"""Traverse tree."""
|
|
123
|
-
if self.
|
|
121
|
+
if self.is_canceled():
|
|
124
122
|
return node
|
|
125
123
|
self.cur_node = node
|
|
126
124
|
self.enter_node(node)
|
|
@@ -131,7 +129,7 @@ class UniPass(Transform[uni.Module, uni.Module]):
|
|
|
131
129
|
else:
|
|
132
130
|
self.prune_signal = False
|
|
133
131
|
self.cur_node = node
|
|
134
|
-
if self.
|
|
132
|
+
if self.is_canceled():
|
|
135
133
|
return node
|
|
136
134
|
self.exit_node(node)
|
|
137
135
|
return node
|
jaclang/compiler/program.py
CHANGED
|
@@ -5,13 +5,14 @@ from __future__ import annotations
|
|
|
5
5
|
import ast as py_ast
|
|
6
6
|
import marshal
|
|
7
7
|
import types
|
|
8
|
-
from
|
|
8
|
+
from threading import Event
|
|
9
|
+
from typing import Optional, TYPE_CHECKING
|
|
9
10
|
|
|
10
11
|
import jaclang.compiler.unitree as uni
|
|
11
12
|
from jaclang.compiler.parser import JacParser
|
|
13
|
+
from jaclang.compiler.passes.ecmascript import EsastGenPass
|
|
12
14
|
from jaclang.compiler.passes.main import (
|
|
13
15
|
Alert,
|
|
14
|
-
BinderPass,
|
|
15
16
|
CFGBuildPass,
|
|
16
17
|
DeclImplMatchPass,
|
|
17
18
|
DefUsePass,
|
|
@@ -32,13 +33,11 @@ from jaclang.compiler.passes.tool import (
|
|
|
32
33
|
FuseCommentsPass,
|
|
33
34
|
JacFormatPass,
|
|
34
35
|
)
|
|
35
|
-
from jaclang.compiler.type_system.type_evaluator import TypeEvaluator
|
|
36
36
|
from jaclang.runtimelib.utils import read_file_with_encoding
|
|
37
37
|
from jaclang.settings import settings
|
|
38
|
-
from jaclang.utils.log import logging
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from jaclang.compiler.type_system.type_evaluator import TypeEvaluator
|
|
42
41
|
|
|
43
42
|
ir_gen_sched = [
|
|
44
43
|
SymTabBuildPass,
|
|
@@ -51,6 +50,7 @@ type_check_sched: list = [
|
|
|
51
50
|
TypeCheckPass,
|
|
52
51
|
]
|
|
53
52
|
py_code_gen = [
|
|
53
|
+
EsastGenPass,
|
|
54
54
|
PyastGenPass,
|
|
55
55
|
PyJacAstLinkPass,
|
|
56
56
|
PyBytecodeGenPass,
|
|
@@ -61,7 +61,10 @@ format_sched = [FuseCommentsPass, DocIRGenPass, JacFormatPass]
|
|
|
61
61
|
class JacProgram:
|
|
62
62
|
"""JacProgram to handle the Jac program-related functionalities."""
|
|
63
63
|
|
|
64
|
-
def __init__(
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
main_mod: Optional[uni.ProgramModule] = None,
|
|
67
|
+
) -> None:
|
|
65
68
|
"""Initialize the JacProgram object."""
|
|
66
69
|
self.mod: uni.ProgramModule = main_mod if main_mod else uni.ProgramModule()
|
|
67
70
|
self.py_raise_map: dict[str, str] = {}
|
|
@@ -71,6 +74,8 @@ class JacProgram:
|
|
|
71
74
|
|
|
72
75
|
def get_type_evaluator(self) -> TypeEvaluator:
|
|
73
76
|
"""Return the type evaluator."""
|
|
77
|
+
from jaclang.compiler.type_system.type_evaluator import TypeEvaluator
|
|
78
|
+
|
|
74
79
|
if not self.type_evaluator:
|
|
75
80
|
self.type_evaluator = TypeEvaluator(program=self)
|
|
76
81
|
return self.type_evaluator
|
|
@@ -83,7 +88,9 @@ class JacProgram:
|
|
|
83
88
|
result = self.compile(file_path=full_target)
|
|
84
89
|
return marshal.loads(result.gen.py_bytecode) if result.gen.py_bytecode else None
|
|
85
90
|
|
|
86
|
-
def parse_str(
|
|
91
|
+
def parse_str(
|
|
92
|
+
self, source_str: str, file_path: str, cancel_token: Event | None = None
|
|
93
|
+
) -> uni.Module:
|
|
87
94
|
"""Convert a Jac file to an AST."""
|
|
88
95
|
had_error = False
|
|
89
96
|
if file_path.endswith(".py") or file_path.endswith(".pyi"):
|
|
@@ -94,6 +101,7 @@ class JacProgram:
|
|
|
94
101
|
orig_src=uni.Source(source_str, mod_path=file_path),
|
|
95
102
|
),
|
|
96
103
|
prog=self,
|
|
104
|
+
cancel_token=cancel_token,
|
|
97
105
|
)
|
|
98
106
|
had_error = len(py_ast_ret.errors_had) > 0
|
|
99
107
|
mod = py_ast_ret.ir_out
|
|
@@ -120,25 +128,23 @@ class JacProgram:
|
|
|
120
128
|
# options in it.
|
|
121
129
|
no_cgen: bool = False,
|
|
122
130
|
type_check: bool = False,
|
|
131
|
+
cancel_token: Event | None = None,
|
|
123
132
|
) -> uni.Module:
|
|
124
133
|
"""Convert a Jac file to an AST."""
|
|
125
134
|
keep_str = use_str or read_file_with_encoding(file_path)
|
|
126
|
-
mod_targ = self.parse_str(keep_str, file_path)
|
|
127
|
-
self.run_schedule(mod=mod_targ, passes=ir_gen_sched)
|
|
135
|
+
mod_targ = self.parse_str(keep_str, file_path, cancel_token=cancel_token)
|
|
136
|
+
self.run_schedule(mod=mod_targ, passes=ir_gen_sched, cancel_token=cancel_token)
|
|
128
137
|
if type_check:
|
|
129
|
-
self.run_schedule(
|
|
138
|
+
self.run_schedule(
|
|
139
|
+
mod=mod_targ, passes=type_check_sched, cancel_token=cancel_token
|
|
140
|
+
)
|
|
130
141
|
# If the module has syntax errors, we skip code generation.
|
|
131
142
|
if (not mod_targ.has_syntax_errors) and (not no_cgen):
|
|
132
143
|
if settings.predynamo_pass and PreDynamoPass not in py_code_gen:
|
|
133
144
|
py_code_gen.insert(0, PreDynamoPass)
|
|
134
|
-
self.run_schedule(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def bind(self, file_path: str, use_str: str | None = None) -> uni.Module:
|
|
138
|
-
"""Bind the Jac module."""
|
|
139
|
-
keep_str = use_str or read_file_with_encoding(file_path)
|
|
140
|
-
mod_targ = self.parse_str(keep_str, file_path)
|
|
141
|
-
BinderPass(ir_in=mod_targ, prog=self)
|
|
145
|
+
self.run_schedule(
|
|
146
|
+
mod=mod_targ, passes=py_code_gen, cancel_token=cancel_token
|
|
147
|
+
)
|
|
142
148
|
return mod_targ
|
|
143
149
|
|
|
144
150
|
def build(
|
|
@@ -155,16 +161,11 @@ class JacProgram:
|
|
|
155
161
|
self,
|
|
156
162
|
mod: uni.Module,
|
|
157
163
|
passes: list[type[Transform[uni.Module, uni.Module]]],
|
|
164
|
+
cancel_token: Event | None = None,
|
|
158
165
|
) -> None:
|
|
159
166
|
"""Run the passes on the module."""
|
|
160
|
-
final_pass: Optional[type[Transform[uni.Module, uni.Module]]] = None
|
|
161
167
|
for current_pass in passes:
|
|
162
|
-
|
|
163
|
-
final_pass = current_pass
|
|
164
|
-
break
|
|
165
|
-
current_pass(ir_in=mod, prog=self) # type: ignore
|
|
166
|
-
if final_pass:
|
|
167
|
-
final_pass(mod, prog=self)
|
|
168
|
+
current_pass(ir_in=mod, prog=self, cancel_token=cancel_token) # type: ignore
|
|
168
169
|
|
|
169
170
|
@staticmethod
|
|
170
171
|
def jac_file_formatter(file_path: str) -> str:
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from jaclang.compiler.program import JacProgram
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
FIXTURE_DIR = Path(__file__).resolve().parent.parent / "passes" / "ecmascript" / "tests" / "fixtures"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_js_codegen_generates_js_and_manifest() -> None:
|
|
12
|
+
fixture = FIXTURE_DIR / "client_jsx.jac"
|
|
13
|
+
prog = JacProgram()
|
|
14
|
+
module = prog.compile(str(fixture))
|
|
15
|
+
|
|
16
|
+
assert module.gen.js.strip(), "Expected JavaScript output for client declarations"
|
|
17
|
+
assert "function component" in module.gen.js
|
|
18
|
+
assert "__jacJsx(" in module.gen.js
|
|
19
|
+
|
|
20
|
+
# Client Python code should be omitted in js_only mode
|
|
21
|
+
assert "def component" not in module.gen.py
|
|
22
|
+
|
|
23
|
+
# Metadata should be stored in module.gen.client_manifest
|
|
24
|
+
assert "__jac_client_manifest__" not in module.gen.py
|
|
25
|
+
manifest = module.gen.client_manifest
|
|
26
|
+
assert manifest, "Client manifest should be available in module.gen"
|
|
27
|
+
assert "component" in manifest.exports
|
|
28
|
+
assert "ButtonProps" in manifest.exports
|
|
29
|
+
assert "API_URL" in manifest.globals
|
|
30
|
+
|
|
31
|
+
# Module.gen.client_manifest should have the metadata
|
|
32
|
+
assert "component" in module.gen.client_manifest.exports
|
|
33
|
+
assert "ButtonProps" in module.gen.client_manifest.exports
|
|
34
|
+
assert "API_URL" in module.gen.client_manifest.globals
|
|
35
|
+
assert module.gen.client_manifest.params.get("component", []) == []
|
|
36
|
+
assert "ButtonProps" not in module.gen.client_manifest.params
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_compilation_skips_python_stubs() -> None:
|
|
40
|
+
fixture = FIXTURE_DIR / "client_jsx.jac"
|
|
41
|
+
prog = JacProgram()
|
|
42
|
+
module = prog.compile(str(fixture))
|
|
43
|
+
|
|
44
|
+
assert module.gen.js.strip(), "Expected JavaScript output when emitting both"
|
|
45
|
+
assert "function component" in module.gen.js
|
|
46
|
+
assert "__jacJsx(" in module.gen.js
|
|
47
|
+
|
|
48
|
+
# Client Python definitions are intentionally omitted
|
|
49
|
+
assert "def component" not in module.gen.py
|
|
50
|
+
assert "__jac_client__" not in module.gen.py
|
|
51
|
+
assert "class ButtonProps" not in module.gen.py
|
|
52
|
+
|
|
53
|
+
# Manifest data should be in module.gen.client_manifest
|
|
54
|
+
assert "__jac_client_manifest__" not in module.gen.py
|
|
55
|
+
manifest = module.gen.client_manifest
|
|
56
|
+
assert manifest, "Client manifest should be available in module.gen"
|
|
57
|
+
assert "component" in manifest.exports
|
|
58
|
+
assert "ButtonProps" in manifest.exports
|
|
59
|
+
assert "API_URL" in manifest.globals
|
|
60
|
+
|
|
61
|
+
# Module.gen.client_manifest should have the metadata
|
|
62
|
+
assert "component" in module.gen.client_manifest.exports
|
|
63
|
+
assert "ButtonProps" in module.gen.client_manifest.exports
|
|
64
|
+
assert "API_URL" in module.gen.client_manifest.globals
|
|
65
|
+
assert module.gen.client_manifest.params.get("component", []) == []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_type_to_typeof_conversion() -> None:
|
|
69
|
+
"""Test that type() calls are converted to typeof in JavaScript."""
|
|
70
|
+
from tempfile import NamedTemporaryFile
|
|
71
|
+
|
|
72
|
+
# Create a temporary test file
|
|
73
|
+
test_code = '''"""Test type() to typeof conversion."""
|
|
74
|
+
|
|
75
|
+
cl def check_types() {
|
|
76
|
+
let x = 42;
|
|
77
|
+
let y = "hello";
|
|
78
|
+
let z = True;
|
|
79
|
+
|
|
80
|
+
let t1 = type(x);
|
|
81
|
+
let t2 = type(y);
|
|
82
|
+
let t3 = type(z);
|
|
83
|
+
let t4 = type("world");
|
|
84
|
+
|
|
85
|
+
return t1;
|
|
86
|
+
}
|
|
87
|
+
'''
|
|
88
|
+
|
|
89
|
+
with NamedTemporaryFile(mode='w', suffix='.jac', delete=False) as f:
|
|
90
|
+
f.write(test_code)
|
|
91
|
+
f.flush()
|
|
92
|
+
|
|
93
|
+
prog = JacProgram()
|
|
94
|
+
module = prog.compile(f.name)
|
|
95
|
+
|
|
96
|
+
assert module.gen.js.strip(), "Expected JavaScript output for client code"
|
|
97
|
+
|
|
98
|
+
# Verify type() was converted to typeof
|
|
99
|
+
assert "typeof" in module.gen.js, "type() should be converted to typeof"
|
|
100
|
+
assert module.gen.js.count("typeof") == 4, "Should have 4 typeof expressions"
|
|
101
|
+
|
|
102
|
+
# Verify no type() calls remain
|
|
103
|
+
assert "type(" not in module.gen.js, "No type() calls should remain in JavaScript"
|
|
104
|
+
|
|
105
|
+
# Verify the typeof expressions are correctly formed
|
|
106
|
+
assert "typeof x" in module.gen.js
|
|
107
|
+
assert "typeof y" in module.gen.js
|
|
108
|
+
assert "typeof z" in module.gen.js
|
|
109
|
+
assert 'typeof "world"' in module.gen.js
|
|
110
|
+
|
|
111
|
+
# Clean up
|
|
112
|
+
import os
|
|
113
|
+
os.unlink(f.name)
|
|
@@ -17,6 +17,9 @@ class TestLoader(TestCase):
|
|
|
17
17
|
|
|
18
18
|
def test_import_basic_python(self) -> None:
|
|
19
19
|
"""Test basic self loading."""
|
|
20
|
+
Jac.reset_machine()
|
|
21
|
+
sys.modules.pop("fixtures", None)
|
|
22
|
+
sys.modules.pop("fixtures.hello_world", None)
|
|
20
23
|
Jac.set_base_path(self.fixture_abs_path(__file__))
|
|
21
24
|
JacMachineInterface.attach_program(
|
|
22
25
|
JacProgram(),
|
|
@@ -26,6 +29,9 @@ class TestLoader(TestCase):
|
|
|
26
29
|
|
|
27
30
|
def test_modules_correct(self) -> None:
|
|
28
31
|
"""Test basic self loading."""
|
|
32
|
+
Jac.reset_machine()
|
|
33
|
+
sys.modules.pop("fixtures", None)
|
|
34
|
+
sys.modules.pop("fixtures.hello_world", None)
|
|
29
35
|
Jac.set_base_path(self.fixture_abs_path(__file__))
|
|
30
36
|
JacMachineInterface.attach_program(
|
|
31
37
|
JacProgram(),
|
|
@@ -141,29 +147,25 @@ class TestLoader(TestCase):
|
|
|
141
147
|
try:
|
|
142
148
|
os.chdir(os.path.dirname(self.fixture_abs_path("jac_import_py_files.py")))
|
|
143
149
|
Jac.set_base_path(self.fixture_abs_path("jac_import_py_files.py"))
|
|
144
|
-
JacMachineInterface.attach_program(
|
|
145
|
-
program:=JacProgram(),
|
|
146
|
-
)
|
|
150
|
+
JacMachineInterface.attach_program(JacProgram())
|
|
147
151
|
Jac.jac_import("jac_import_py_files", base_path=self.fixture_abs_path("jac_import_py_files.py"), lng="py")
|
|
148
152
|
cli.run(self.fixture_abs_path("jac_import_py_files.py"))
|
|
149
153
|
sys.stdout = sys.__stdout__
|
|
150
154
|
stdout_value = captured_output.getvalue()
|
|
151
155
|
self.assertIn("This is main test file for jac import of python files", stdout_value)
|
|
152
|
-
self.assertIn("python_module <jaclang.compiler.unitree.Module object", str(program.mod.hub))
|
|
153
|
-
self.assertIn("jac_module <jaclang.compiler.unitree.Module object", str(program.mod.hub))
|
|
156
|
+
self.assertIn("python_module <jaclang.compiler.unitree.Module object", str(Jac.program.mod.hub))
|
|
157
|
+
self.assertIn("jac_module <jaclang.compiler.unitree.Module object", str(Jac.program.mod.hub))
|
|
154
158
|
os.environ["JAC_PYFILE_RAISE"] = "false"
|
|
155
159
|
settings.load_env_vars()
|
|
156
160
|
os.chdir(os.path.dirname(self.fixture_abs_path("jac_import_py_files.py")))
|
|
157
161
|
Jac.set_base_path(self.fixture_abs_path("jac_import_py_files.py"))
|
|
158
|
-
JacMachineInterface.attach_program(
|
|
159
|
-
program:=JacProgram(),
|
|
160
|
-
)
|
|
162
|
+
JacMachineInterface.attach_program(JacProgram())
|
|
161
163
|
Jac.jac_import("jac_import_py_files", base_path=self.fixture_abs_path("jac_import_py_files.py"), lng="py")
|
|
162
164
|
cli.run(self.fixture_abs_path("jac_import_py_files.py"))
|
|
163
165
|
sys.stdout = sys.__stdout__
|
|
164
166
|
stdout_value = captured_output.getvalue()
|
|
165
167
|
self.assertIn("This is main test file for jac import of python files", stdout_value)
|
|
166
|
-
self.assertNotIn("python_module <jaclang.compiler.unitree.Module object", str(program.mod.hub))
|
|
167
|
-
self.
|
|
168
|
+
self.assertNotIn("python_module <jaclang.compiler.unitree.Module object", str(Jac.program.mod.hub))
|
|
169
|
+
self.assertIn("jac_module <jaclang.compiler.unitree.Module object", str(Jac.program.mod.hub))
|
|
168
170
|
finally:
|
|
169
171
|
os.chdir(original_cwd)
|