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
@@ -1,5 +1,5 @@
1
1
  #this file is part of formatter tests and is not meant to be run
2
- class SemTokManager {
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 and
27
- isinstance(a, int) and
28
- isinstance(a, int) and
29
- isinstance(a, int) and
30
- isinstance(a, int) and
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) and
35
- isinstance(a, int) and
36
- isinstance(a, int) and
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
- """This is a long
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
- with entry {
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
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: int,
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 and
136
- node_selected.find_parent_of_type(uni.Archetype) or
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 and
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) and
159
- node_selected.parent and
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) and
165
- node_selected.parent and
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
- print("\n".join(unified_diff(before.splitlines(), after.splitlines())))
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__(self, ir_in: T, prog: JacProgram) -> None:
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.term_signal:
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.term_signal:
132
+ if self.is_canceled():
135
133
  return node
136
134
  self.exit_node(node)
137
135
  return node
@@ -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 typing import Optional
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
- logger = logging.getLogger(__name__)
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__(self, main_mod: Optional[uni.ProgramModule] = None) -> None:
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(self, source_str: str, file_path: str) -> uni.Module:
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(mod=mod_targ, passes=type_check_sched)
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(mod=mod_targ, passes=py_code_gen)
135
- return mod_targ
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
- if current_pass == PyBytecodeGenPass:
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.assertNotIn("jac_module <jaclang.compiler.unitree.Module object", str(program.mod.hub))
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)