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.

Files changed (103) hide show
  1. jaclang/cli/cli.py +147 -25
  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 -13
  6. jaclang/compiler/jac.lark +130 -31
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +567 -176
  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/pyast_gen_pass.py +324 -234
  28. jaclang/compiler/passes/main/pyast_load_pass.py +46 -11
  29. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  30. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  31. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  32. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  34. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  35. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  36. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -2
  37. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  38. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  39. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  40. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  41. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +115 -0
  42. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  43. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  44. jaclang/compiler/passes/transform.py +9 -1
  45. jaclang/compiler/passes/uni_pass.py +5 -7
  46. jaclang/compiler/program.py +22 -25
  47. jaclang/compiler/tests/test_client_codegen.py +113 -0
  48. jaclang/compiler/tests/test_importer.py +12 -10
  49. jaclang/compiler/tests/test_parser.py +249 -3
  50. jaclang/compiler/type_system/type_evaluator.jac +169 -50
  51. jaclang/compiler/type_system/type_utils.py +1 -1
  52. jaclang/compiler/type_system/types.py +6 -0
  53. jaclang/compiler/unitree.py +430 -84
  54. jaclang/langserve/engine.jac +224 -288
  55. jaclang/langserve/sem_manager.jac +12 -8
  56. jaclang/langserve/server.jac +48 -48
  57. jaclang/langserve/tests/fixtures/greet.py +17 -0
  58. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  59. jaclang/langserve/tests/fixtures/user.jac +15 -0
  60. jaclang/langserve/tests/test_server.py +66 -371
  61. jaclang/lib.py +1 -1
  62. jaclang/runtimelib/client_bundle.py +169 -0
  63. jaclang/runtimelib/client_runtime.jac +586 -0
  64. jaclang/runtimelib/constructs.py +2 -0
  65. jaclang/runtimelib/machine.py +259 -100
  66. jaclang/runtimelib/meta_importer.py +111 -22
  67. jaclang/runtimelib/mtp.py +15 -0
  68. jaclang/runtimelib/server.py +1089 -0
  69. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  70. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  71. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  72. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  73. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  74. jaclang/runtimelib/tests/test_client_render.py +63 -0
  75. jaclang/runtimelib/tests/test_serve.py +1069 -0
  76. jaclang/settings.py +0 -2
  77. jaclang/tests/fixtures/iife_functions.jac +142 -0
  78. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  79. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  80. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  81. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  82. jaclang/tests/fixtures/py_run.py +7 -5
  83. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  84. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  85. jaclang/tests/test_cli.py +1 -1
  86. jaclang/tests/test_language.py +10 -39
  87. jaclang/tests/test_reference.py +17 -2
  88. jaclang/utils/NonGPT.py +375 -0
  89. jaclang/utils/helpers.py +44 -16
  90. jaclang/utils/lang_tools.py +31 -4
  91. jaclang/utils/tests/test_lang_tools.py +1 -1
  92. jaclang/utils/treeprinter.py +8 -3
  93. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  94. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/RECORD +96 -66
  95. jaclang/compiler/passes/main/binder_pass.py +0 -594
  96. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  97. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  98. jaclang/langserve/tests/session.jac +0 -294
  99. jaclang/langserve/tests/test_dev_server.py +0 -80
  100. jaclang/runtimelib/importer.py +0 -351
  101. jaclang/tests/test_typecheck.py +0 -542
  102. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  103. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,7 @@
3
3
  import io
4
4
  import os
5
5
  from contextlib import redirect_stdout
6
+ import sys
6
7
  from typing import Callable, Optional
7
8
 
8
9
  import jaclang
@@ -65,6 +66,12 @@ class JacReferenceTests(TestCase):
65
66
  )
66
67
  return f.getvalue()
67
68
 
69
+ def normalize_function_addresses(text: str) -> str:
70
+ """Normalize function memory addresses in output for consistent comparison."""
71
+ import re
72
+ # Replace <function Name at 0xADDRESS> with <function Name at 0x...>
73
+ return re.sub(r'<function (\w+) at 0x[0-9a-f]+>', r'<function \1 at 0x...>', text)
74
+
68
75
  try:
69
76
  if "tests.jac" in filename or "check_statements.jac" in filename:
70
77
  return
@@ -76,13 +83,21 @@ class JacReferenceTests(TestCase):
76
83
  )
77
84
  output_jac = execute_and_capture_output(code_content, filename=filename)
78
85
  Jac.reset_machine()
86
+ # Clear byllm modules from cache to ensure consistent behavior between JAC and Python runs
87
+ # when byllm is used
88
+ sys.modules.pop("byllm", None)
89
+ sys.modules.pop("byllm.lib", None)
79
90
  filename = filename.replace(".jac", ".py")
80
91
  with open(filename, "r") as file:
81
92
  code_content = file.read()
82
93
  output_py = execute_and_capture_output(code_content, filename=filename)
83
94
 
84
- # print(f"\nJAC Output:\n{output_jac}")
85
- # print(f"\nPython Output:\n{output_py}")
95
+ # Normalize function addresses before comparison
96
+ output_jac = normalize_function_addresses(output_jac)
97
+ output_py = normalize_function_addresses(output_py)
98
+
99
+ print(f"\nJAC Output:\n{output_jac}")
100
+ print(f"\nPython Output:\n{output_py}")
86
101
 
87
102
  self.assertGreater(len(output_py), 0)
88
103
  # doing like below for concurrent_expressions.jac and other current tests
@@ -0,0 +1,375 @@
1
+ # type: ignore
2
+ # flake8: noqa
3
+ from __future__ import annotations
4
+ import inspect
5
+ import random
6
+ import string
7
+ import sys
8
+ import types
9
+ from collections import deque
10
+ from dataclasses import is_dataclass, fields as dc_fields, MISSING
11
+ from datetime import date, datetime, time, timedelta
12
+ from decimal import Decimal
13
+ from functools import wraps
14
+ from typing import (
15
+ Any,
16
+ Annotated,
17
+ Callable,
18
+ Literal,
19
+ Union,
20
+ get_args,
21
+ get_origin,
22
+ get_type_hints,
23
+ TypeVar,
24
+ NewType,
25
+ )
26
+ from uuid import UUID, uuid4
27
+
28
+ # -------------------------------
29
+ # Core: random value generator
30
+ # -------------------------------
31
+
32
+ _RAND_STR_ALPH = string.ascii_letters + string.digits
33
+
34
+
35
+ def _rand_str(n: int = 12) -> str:
36
+ return "".join(random.choice(_RAND_STR_ALPH) for _ in range(n))
37
+
38
+
39
+ def _is_typed_dict(tp: Any) -> bool:
40
+ # TypedDict subclasses have these markers in CPython
41
+ return (
42
+ isinstance(tp, type)
43
+ and hasattr(tp, "__annotations__")
44
+ and hasattr(tp, "__required_keys__")
45
+ )
46
+
47
+
48
+ def _is_namedtuple(tp: Any) -> bool:
49
+ return (
50
+ isinstance(tp, type)
51
+ and issubclass(tp, tuple)
52
+ and hasattr(tp, "_fields")
53
+ and hasattr(tp, "__annotations__")
54
+ )
55
+
56
+
57
+ def _is_enum(tp: Any) -> bool:
58
+ try:
59
+ import enum
60
+
61
+ return isinstance(tp, type) and issubclass(tp, enum.Enum)
62
+ except Exception:
63
+ return False
64
+
65
+
66
+ def _safe_subclasses(tp: Any) -> list[type]:
67
+ try:
68
+ return list(tp.__subclasses__()) # pragma: no cover
69
+ except Exception:
70
+ return []
71
+
72
+
73
+ def _unwrap_annotated(tp: Any) -> Any:
74
+ if get_origin(tp) is Annotated:
75
+ return get_args(tp)[0]
76
+ return tp
77
+
78
+
79
+ def _unwrap_newtype(tp: Any) -> Any:
80
+ # NewType returns a function that has __supertype__ attr
81
+ if hasattr(tp, "__supertype__"):
82
+ return tp.__supertype__
83
+ return tp
84
+
85
+
86
+ def _choose_from(seq):
87
+ return random.choice(list(seq))
88
+
89
+
90
+ def _random_primitive(tp):
91
+ if tp is int:
92
+ return random.randint(-(10**6), 10**6)
93
+ if tp is float:
94
+ # Avoid NaN/inf to keep it JSON-safe & well-behaved
95
+ return random.uniform(-(10**6), 10**6)
96
+ if tp is bool:
97
+ return bool(random.getrandbits(1))
98
+ if tp is str:
99
+ return _rand_str()
100
+ if tp is bytes:
101
+ return _rand_str().encode()
102
+ if tp is complex:
103
+ return complex(random.uniform(-100, 100), random.uniform(-100, 100))
104
+ if tp is Decimal:
105
+ return Decimal(str(random.uniform(-1000, 1000)))
106
+ if tp is UUID:
107
+ return uuid4()
108
+ if tp is datetime:
109
+ return datetime.fromtimestamp(random.randint(0, 2_000_000_000))
110
+ if tp is date:
111
+ return date.fromordinal(random.randint(700_000, 800_000))
112
+ if tp is time:
113
+ return (
114
+ datetime.min + timedelta(seconds=random.randint(0, 24 * 3600 - 1))
115
+ ).time()
116
+ if tp is type(None):
117
+ return None
118
+ return None # sentinel
119
+
120
+
121
+ def random_value_for_type(tp: Any, *, _depth: int = 0, _max_depth: int = 10) -> Any:
122
+ """Generate a random value that (best-effort) conforms to the type 'tp'."""
123
+ random.seed(42) # for reproducibility in tests
124
+ if _depth > _max_depth:
125
+ # Depth cap to avoid infinite recursion on self-referential types
126
+ origin = get_origin(tp)
127
+ if origin in (list, set, frozenset, tuple, dict, deque):
128
+ return origin() if origin is not tuple else tuple()
129
+ prim = _random_primitive(_unwrap_newtype(_unwrap_annotated(tp)))
130
+ if prim is not None:
131
+ return prim
132
+ return None
133
+
134
+ tp = _unwrap_newtype(_unwrap_annotated(tp))
135
+
136
+ # Any / object => pick a simple JSON-friendly value
137
+ if tp in (Any, object):
138
+ return random.choice(
139
+ [_rand_str(), random.randint(0, 9999), True, None, random.uniform(0, 1)]
140
+ )
141
+
142
+ # Primitives and common stdlib scalars
143
+ prim = _random_primitive(tp)
144
+ if prim is not None:
145
+ return prim
146
+
147
+ # Literal
148
+ if get_origin(tp) is Literal:
149
+ choices = get_args(tp)
150
+ return _choose_from(choices)
151
+
152
+ # Union / Optional
153
+ if get_origin(tp) is Union:
154
+ options = list(get_args(tp))
155
+ # Bias slightly away from None, if present
156
+ if type(None) in options and len(options) > 1 and random.random() < 0.3:
157
+ return None
158
+ chosen = (
159
+ _choose_from([t for t in options if t is not type(None)])
160
+ if options
161
+ else None
162
+ )
163
+ return random_value_for_type(chosen, _depth=_depth + 1, _max_depth=_max_depth)
164
+
165
+ # Tuple (fixed-length or variadic)
166
+ if get_origin(tp) is tuple:
167
+ args = get_args(tp)
168
+ if len(args) == 2 and args[1] is Ellipsis:
169
+ # Tuple[T, ...]
170
+ n = random.randint(0, 5)
171
+ return tuple(
172
+ random_value_for_type(args[0], _depth=_depth + 1, _max_depth=_max_depth)
173
+ for _ in range(n)
174
+ )
175
+ else:
176
+ return tuple(
177
+ random_value_for_type(a, _depth=_depth + 1, _max_depth=_max_depth)
178
+ for a in args
179
+ )
180
+
181
+ # List / Set / FrozenSet / Deque / Dict
182
+ origin = get_origin(tp)
183
+ if origin in (list, set, frozenset, deque):
184
+ (elem_type,) = get_args(tp) or (Any,)
185
+ size = random.randint(0, 5)
186
+ elems = [
187
+ random_value_for_type(elem_type, _depth=_depth + 1, _max_depth=_max_depth)
188
+ for _ in range(size)
189
+ ]
190
+ if origin is list:
191
+ return elems
192
+ if origin is set:
193
+ # Attempt to hash; fallback to str
194
+ try:
195
+ return set(elems)
196
+ except TypeError:
197
+ return {str(e) for e in elems}
198
+ if origin is frozenset:
199
+ try:
200
+ return frozenset(elems)
201
+ except TypeError:
202
+ return frozenset(str(e) for e in elems)
203
+ if origin is deque:
204
+ return deque(elems)
205
+
206
+ if origin is dict:
207
+ key_t, val_t = (get_args(tp) + (Any, Any))[:2]
208
+
209
+ # Try to keep keys hashable and simple
210
+ def mk_key():
211
+ k = random_value_for_type(key_t, _depth=_depth + 1, _max_depth=_max_depth)
212
+ try:
213
+ hash(k)
214
+ return k
215
+ except TypeError:
216
+ return str(k)
217
+
218
+ size = random.randint(0, 5)
219
+ return {
220
+ mk_key(): random_value_for_type(
221
+ val_t, _depth=_depth + 1, _max_depth=_max_depth
222
+ )
223
+ for _ in range(size)
224
+ }
225
+
226
+ # TypedDict
227
+ if _is_typed_dict(tp):
228
+ # Required and optional keys are tracked by __required_keys__/__optional_keys__
229
+ req = getattr(tp, "__required_keys__", set())
230
+ opt = getattr(tp, "__optional_keys__", set())
231
+ anns = tp.__annotations__
232
+ out = {}
233
+ # required
234
+ for k in req:
235
+ out[k] = random_value_for_type(
236
+ anns[k], _depth=_depth + 1, _max_depth=_max_depth
237
+ )
238
+ # some optional
239
+ for k in opt:
240
+ if random.random() < 0.6:
241
+ out[k] = random_value_for_type(
242
+ anns[k], _depth=_depth + 1, _max_depth=_max_depth
243
+ )
244
+ return out
245
+
246
+ # NamedTuple
247
+ if _is_namedtuple(tp):
248
+ anns = get_type_hints(tp, include_extras=True)
249
+ values = [
250
+ random_value_for_type(anns[name], _depth=_depth + 1, _max_depth=_max_depth)
251
+ for name in tp._fields
252
+ ]
253
+ return tp(*values)
254
+
255
+ # Enum
256
+ if _is_enum(tp):
257
+ return _choose_from(list(tp))
258
+
259
+ # Dataclass
260
+ if is_dataclass(tp):
261
+ # Resolve string annotations to actual types
262
+ try:
263
+ type_hints = get_type_hints(tp, include_extras=True)
264
+ except Exception:
265
+ type_hints = {}
266
+
267
+ kwargs = {}
268
+ for f in dc_fields(tp):
269
+ if f.init:
270
+ if f.default is not MISSING or f.default_factory is not MISSING:
271
+ # let default/default_factory handle it sometimes
272
+ if random.random() < 0.35:
273
+ continue
274
+ # Use resolved type hints if available, otherwise use the field type
275
+ field_type = type_hints.get(f.name, f.type)
276
+ kwargs[f.name] = random_value_for_type(
277
+ field_type, _depth=_depth + 1, _max_depth=_max_depth
278
+ )
279
+ try:
280
+ return tp(**kwargs)
281
+ except TypeError:
282
+ # Fall back to constructing with defaults only
283
+ return tp(**{k: v for k, v in kwargs.items() if v is not MISSING})
284
+
285
+ # Annotated already unwrapped; NewType already unwrapped
286
+
287
+ # TypeVar: use bound or one of constraints, else Any
288
+ if isinstance(tp, TypeVar):
289
+ if tp.__constraints__:
290
+ chosen = _choose_from(tp.__constraints__)
291
+ return random_value_for_type(
292
+ chosen, _depth=_depth + 1, _max_depth=_max_depth
293
+ )
294
+ if tp.__bound__:
295
+ return random_value_for_type(
296
+ tp.__bound__, _depth=_depth + 1, _max_depth=_max_depth
297
+ )
298
+ return random_value_for_type(Any, _depth=_depth + 1, _max_depth=_max_depth)
299
+
300
+ # Callable[...] -> synthesize a dummy callable with compatible signature if possible
301
+ if get_origin(tp) is Callable:
302
+ # Return a lambda that returns a random value for the annotated return type (or Any)
303
+ args = get_args(tp)
304
+ ret_t = Any
305
+ if args:
306
+ # args is (arg_types, ret_type)
307
+ if len(args) == 2:
308
+ ret_t = args[1]
309
+
310
+ def _fn_stub(*_a, **_k):
311
+ return random_value_for_type(
312
+ ret_t, _depth=_depth + 1, _max_depth=_max_depth
313
+ )
314
+
315
+ return _fn_stub
316
+
317
+ # Plain classes:
318
+ # 1) If it’s a built-in container alias (like typing.List without params), treat as Any
319
+ if tp in (list, set, frozenset, tuple, dict, deque):
320
+ return random_value_for_type(Any, _depth=_depth + 1, _max_depth=_max_depth)
321
+
322
+ # 2) Try to instantiate using __init__ annotations.
323
+ if isinstance(tp, type):
324
+ try:
325
+ sig = inspect.signature(tp)
326
+ kwargs = {}
327
+ for name, param in sig.parameters.items():
328
+ if name == "self":
329
+ continue
330
+ ann = (
331
+ param.annotation if param.annotation is not inspect._empty else Any
332
+ )
333
+ if param.default is not inspect._empty and random.random() < 0.4:
334
+ # sometimes rely on defaults
335
+ continue
336
+ # if VAR_POSITIONAL/VAR_KEYWORD, skip
337
+ if param.kind in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
338
+ continue
339
+ kwargs[name] = random_value_for_type(
340
+ ann, _depth=_depth + 1, _max_depth=_max_depth
341
+ )
342
+ return tp(**kwargs)
343
+ except Exception:
344
+ try:
345
+ return tp()
346
+ except Exception:
347
+ # Last resort: create a simple object with random attrs
348
+ obj = types.SimpleNamespace()
349
+ return obj
350
+
351
+ # Fallback
352
+ return None
353
+
354
+
355
+ # -------------------------------
356
+ # Decorator
357
+ # -------------------------------
358
+
359
+
360
+ def returns_fake(func: Any):
361
+ """Decorator that returns a random instance of the function's return type."""
362
+ # Resolve forward refs and Annotated, NewType, etc.
363
+ try:
364
+ type_hints = get_type_hints(
365
+ func, globalns=func.__globals__, localns=None, include_extras=True
366
+ )
367
+ except Exception:
368
+ type_hints = getattr(func, "__annotations__", {})
369
+ ret_t = type_hints.get("return", Any)
370
+
371
+ @wraps(func)
372
+ def wrapper(*args, **kwargs):
373
+ return random_value_for_type(ret_t)
374
+
375
+ return wrapper
jaclang/utils/helpers.py CHANGED
@@ -156,6 +156,24 @@ def dump_traceback(e: Exception) -> str:
156
156
  def byte_offset_to_char_offset(string: str, offset: int) -> int:
157
157
  return len(string.encode("utf-8")[:offset].decode("utf-8", errors="replace"))
158
158
 
159
+ # Utility function to check if a file is a compiled Jac file and get the original .jac source
160
+ def get_jac_source_info(py_filename: str) -> tuple[str | None, str | None]:
161
+ """Return (jac_filename, jac_source) if available, else (None, None)."""
162
+ # Check if this is a generated Python file from Jac compilation
163
+ # Generated Python files are stored in __jac_gen__ directory
164
+ if "__jac_gen__" in py_filename and py_filename.endswith(".py"):
165
+ # Try to find the corresponding .jac file
166
+ # The generated .py file typically mirrors the original .jac structure
167
+ jac_filename = py_filename.replace("__jac_gen__", "").replace(".py", ".jac")
168
+ if os.path.exists(jac_filename):
169
+ try:
170
+ with open(jac_filename, "r") as f:
171
+ jac_source = f.read()
172
+ return jac_filename, jac_source
173
+ except Exception:
174
+ pass
175
+ return None, None
176
+
159
177
  tb = TracebackException(type(e), e, e.__traceback__, limit=None, compact=True)
160
178
  trace_dump += f"Error: {str(e)}\n"
161
179
 
@@ -170,6 +188,11 @@ def dump_traceback(e: Exception) -> str:
170
188
  for idx, frame in enumerate(tb.stack):
171
189
  func_signature = frame.name + ("()" if frame.name.isidentifier() else "")
172
190
 
191
+ # Check if we can map this to a .jac file
192
+ jac_filename, jac_source = get_jac_source_info(frame.filename)
193
+ display_filename = jac_filename if jac_filename else frame.filename
194
+ display_source = jac_source if jac_source else None
195
+
173
196
  # Pretty print the most recent call's location.
174
197
  if idx == 0 and (
175
198
  (frame.lineno is not None) and frame.line and frame.line.strip() != ""
@@ -188,22 +211,27 @@ def dump_traceback(e: Exception) -> str:
188
211
  off_start = byte_offset_to_char_offset(line_o, frame.colno) - 1
189
212
  off_end = byte_offset_to_char_offset(line_o, frame.end_colno) - 1
190
213
 
191
- # Get the source.
192
- file_source = None
193
- with open(frame.filename, "r") as file:
194
- file_source = file.read()
195
-
196
- # Get the source offset.
197
- lines = file_source.split("\n")
198
- for i in range(frame.lineno - 1):
199
- off_start += len(lines[i]) + 1
200
- off_end += len(lines[i]) + 1
201
-
202
- trace_dump += pretty_print_source_location(
203
- frame.filename, file_source, frame.lineno, off_start, off_end
204
- )
205
-
206
- trace_dump += f'\n{" " * dump_tab_width}at {func_signature} {frame.filename}:{frame.lineno}'
214
+ # Get the source - prefer .jac source if available, otherwise use .py
215
+ file_source = display_source
216
+ if file_source is None:
217
+ try:
218
+ with open(frame.filename, "r") as file:
219
+ file_source = file.read()
220
+ except Exception:
221
+ file_source = ""
222
+
223
+ if file_source:
224
+ # Get the source offset.
225
+ lines = file_source.split("\n")
226
+ for i in range(frame.lineno - 1):
227
+ off_start += len(lines[i]) + 1
228
+ off_end += len(lines[i]) + 1
229
+
230
+ trace_dump += pretty_print_source_location(
231
+ display_filename, file_source, frame.lineno, off_start, off_end
232
+ )
233
+
234
+ trace_dump += f'\n{" " * dump_tab_width}at {func_signature} {display_filename}:{frame.lineno}'
207
235
 
208
236
  return trace_dump
209
237
 
@@ -181,7 +181,7 @@ class AstTool:
181
181
  """Generate a AST, SymbolTable tree for .jac file, or Python AST for .py file."""
182
182
  error = (
183
183
  "Usage: ir <choose one of (sym / sym. / ast / ast. / docir / "
184
- "pyast / py / unparse)> <.py or .jac file_path>"
184
+ "pyast / py / unparse / esast / es)> <.py or .jac file_path>"
185
185
  )
186
186
  if len(args) != 2:
187
187
  return error
@@ -263,6 +263,29 @@ class AstTool:
263
263
  if isinstance(ir.gen.py[0], str)
264
264
  else "Compile failed."
265
265
  )
266
+ case "esast":
267
+ from jaclang.compiler.passes.ecmascript import (
268
+ EsastGenPass,
269
+ es_node_to_dict,
270
+ )
271
+ import json
272
+
273
+ esast_pass = EsastGenPass(ir, prog)
274
+ es_ir = esast_pass.ir_out
275
+ if hasattr(es_ir.gen, "es_ast") and es_ir.gen.es_ast:
276
+ return f"\n{json.dumps(es_node_to_dict(es_ir.gen.es_ast), indent=2)}"
277
+ else:
278
+ return "ECMAScript AST generation failed."
279
+ case "es":
280
+ from jaclang.compiler.passes.ecmascript import EsastGenPass
281
+ from jaclang.compiler.passes.ecmascript.es_unparse import es_to_js
282
+
283
+ esast_pass = EsastGenPass(ir, prog)
284
+ es_ir = esast_pass.ir_out
285
+ if hasattr(es_ir.gen, "es_ast") and es_ir.gen.es_ast:
286
+ return f"\n{es_to_js(es_ir.gen.es_ast)}"
287
+ else:
288
+ return "ECMAScript code generation failed."
266
289
  case _:
267
290
  return f"Invalid key: {error}"
268
291
  else:
@@ -279,14 +302,18 @@ class AstTool:
279
302
  for kid in cls.kids:
280
303
  if "_end" in kid.name:
281
304
  kid.name = kid.name.replace("_end", "_end_")
282
- arrow = "-.->" if "Optional" in kid.typ else "-->"
305
+ typ_str = str(kid.typ)
306
+ arrow = "-.->" if "Optional" in typ_str else "-->"
283
307
  typ = (
284
- kid.typ.replace("Optional[", "")
308
+ typ_str.replace("typing.", "")
309
+ .replace("jaclang.compiler.unitree.", "")
310
+ .replace("Optional[", "")
311
+ .replace("Union[", "")
285
312
  .replace("SubTag[", "")
286
313
  .replace("Sequence[", "")
314
+ .replace("list[", "list - ")
287
315
  .replace("]", "")
288
316
  .replace("|", ",")
289
- .replace("list[", "list - ")
290
317
  )
291
318
  output += f"{cls.name} {arrow}|{typ}| {kid.name}\n"
292
319
  output += "```\n\n"
@@ -135,6 +135,6 @@ class JacAstToolTests(TestCase):
135
135
  """Testing for Autodoc for Uninodes."""
136
136
  auto_uni = self.tool.autodoc_uninode()
137
137
  self.assertIn(
138
- "## LambdaExpr\n```mermaid\nflowchart LR\nLambdaExpr -->|Expr| body",
138
+ "## LambdaExpr\n```mermaid\nflowchart LR\nLambdaExpr -->|Expr, CodeBlockStmt| body",
139
139
  auto_uni,
140
140
  )
@@ -234,12 +234,17 @@ def print_ast_tree(
234
234
  and not print_py_raise
235
235
  ):
236
236
  return f"{node.__class__.__name__} - PythonModuleRaised: {node.name}"
237
- elif isinstance(node, (uni.ModuleItem, uni.ModulePath)):
237
+ elif isinstance(node, uni.ModuleItem):
238
238
  out = (
239
- f"{node.__class__.__name__} - {node.sym_name} - "
239
+ f"{node.__class__.__name__} - {node.name.sym_name} - "
240
+ f"abs_path: {node.abs_path}"
241
+ )
242
+ return out
243
+ elif isinstance(node, uni.ModulePath):
244
+ out = (
245
+ f"{node.__class__.__name__} - {'.'.join([n.value for n in node.path] if node.path else [])} - "
240
246
  f"abs_path: {node.abs_path}"
241
247
  )
242
-
243
248
  return out
244
249
  elif isinstance(node, AstSymbolNode):
245
250
  out = (
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaclang
3
- Version: 0.8.9
3
+ Version: 0.8.10
4
4
  Summary: Jac is a unique and powerful programming language that runs on top of Python, offering an unprecedented level of intelligence and intuitive understanding.
5
5
  License: MIT
6
6
  Keywords: jac,jaclang,jaseci,python,programming-language,machine-learning,artificial-intelligence
7
7
  Author: Jason Mars
8
- Author-email: jason@jaseci.org
8
+ Author-email: jason@mars.ninja
9
9
  Maintainer: Jason Mars
10
- Maintainer-email: jason@jaseci.org
10
+ Maintainer-email: jason@mars.ninja
11
11
  Requires-Python: >=3.11.0,<4.0.0
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Programming Language :: Python :: 3