jaclang 0.8.8__py3-none-any.whl → 0.8.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (114) hide show
  1. jaclang/cli/cli.py +194 -10
  2. jaclang/cli/cmdreg.py +144 -8
  3. jaclang/compiler/__init__.py +6 -1
  4. jaclang/compiler/codeinfo.py +16 -1
  5. jaclang/compiler/constant.py +33 -8
  6. jaclang/compiler/jac.lark +154 -62
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +656 -149
  9. jaclang/compiler/passes/__init__.py +2 -1
  10. jaclang/compiler/passes/ast_gen/__init__.py +5 -0
  11. jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
  12. jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
  13. jaclang/compiler/passes/ecmascript/__init__.py +25 -0
  14. jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
  15. jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
  16. jaclang/compiler/passes/ecmascript/estree.py +972 -0
  17. jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
  18. jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
  19. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
  20. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
  21. jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
  22. jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
  23. jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
  24. jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
  25. jaclang/compiler/passes/main/__init__.py +0 -3
  26. jaclang/compiler/passes/main/annex_pass.py +23 -1
  27. jaclang/compiler/passes/main/def_use_pass.py +1 -0
  28. jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
  29. jaclang/compiler/passes/main/pyast_load_pass.py +48 -11
  30. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  31. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  32. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  34. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  35. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  36. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  37. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -3
  38. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  39. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  40. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
  41. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  42. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  43. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +219 -20
  44. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  45. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  46. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  47. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
  48. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  49. jaclang/compiler/passes/transform.py +9 -1
  50. jaclang/compiler/passes/uni_pass.py +5 -7
  51. jaclang/compiler/program.py +27 -26
  52. jaclang/compiler/tests/test_client_codegen.py +113 -0
  53. jaclang/compiler/tests/test_importer.py +12 -10
  54. jaclang/compiler/tests/test_parser.py +249 -3
  55. jaclang/compiler/type_system/type_evaluator.jac +1078 -0
  56. jaclang/compiler/type_system/type_utils.py +1 -1
  57. jaclang/compiler/type_system/types.py +6 -0
  58. jaclang/compiler/unitree.py +438 -82
  59. jaclang/langserve/engine.jac +224 -288
  60. jaclang/langserve/sem_manager.jac +12 -8
  61. jaclang/langserve/server.jac +48 -48
  62. jaclang/langserve/tests/fixtures/greet.py +17 -0
  63. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  64. jaclang/langserve/tests/fixtures/user.jac +15 -0
  65. jaclang/langserve/tests/test_server.py +66 -371
  66. jaclang/lib.py +17 -0
  67. jaclang/runtimelib/archetype.py +25 -25
  68. jaclang/runtimelib/client_bundle.py +169 -0
  69. jaclang/runtimelib/client_runtime.jac +586 -0
  70. jaclang/runtimelib/constructs.py +4 -2
  71. jaclang/runtimelib/machine.py +308 -139
  72. jaclang/runtimelib/meta_importer.py +111 -22
  73. jaclang/runtimelib/mtp.py +15 -0
  74. jaclang/runtimelib/server.py +1089 -0
  75. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  76. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  77. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  78. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  79. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  80. jaclang/runtimelib/tests/test_client_render.py +63 -0
  81. jaclang/runtimelib/tests/test_serve.py +1069 -0
  82. jaclang/settings.py +0 -3
  83. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  84. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  85. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  86. jaclang/tests/fixtures/iife_functions.jac +142 -0
  87. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  88. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  89. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  90. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  91. jaclang/tests/fixtures/py2jac_empty.py +0 -0
  92. jaclang/tests/fixtures/py_run.py +7 -5
  93. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  94. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  95. jaclang/tests/test_cli.py +134 -18
  96. jaclang/tests/test_language.py +120 -32
  97. jaclang/tests/test_reference.py +20 -3
  98. jaclang/utils/NonGPT.py +375 -0
  99. jaclang/utils/helpers.py +64 -20
  100. jaclang/utils/lang_tools.py +31 -4
  101. jaclang/utils/tests/test_lang_tools.py +5 -16
  102. jaclang/utils/treeprinter.py +8 -3
  103. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  104. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
  105. jaclang/compiler/passes/main/binder_pass.py +0 -594
  106. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  107. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  108. jaclang/compiler/type_system/type_evaluator.py +0 -844
  109. jaclang/langserve/tests/session.jac +0 -294
  110. jaclang/langserve/tests/test_dev_server.py +0 -80
  111. jaclang/runtimelib/importer.py +0 -351
  112. jaclang/tests/test_typecheck.py +0 -542
  113. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  114. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,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
@@ -94,12 +94,19 @@ def auto_generate_refs() -> str:
94
94
  os.path.split(os.path.dirname(__file__))[0], "../jaclang/compiler/jac.lark"
95
95
  )
96
96
  result = extract_headings(file_path)
97
- md_str = '# Jac Language Reference\n\n--8<-- "jac/examples/reference/introduction.md"\n\n'
97
+
98
+ # Create the reference subdirectory if it doesn't exist
99
+ docs_ref_dir = os.path.join(
100
+ os.path.split(os.path.dirname(__file__))[0], "../../docs/docs/learn/jac_ref"
101
+ )
102
+ os.makedirs(docs_ref_dir, exist_ok=True)
103
+
104
+ # Generate individual markdown files for each section
98
105
  for heading, lines in result.items():
99
106
  heading = heading.strip()
100
107
  heading_snakecase = heading_to_snake(heading)
101
108
  content = (
102
- f'## {heading}\n**Code Example**\n!!! example "Runnable Example in Jac and JacLib"\n'
109
+ f'# {heading}\n\n**Code Example**\n!!! example "Runnable Example in Jac and JacLib"\n'
103
110
  ' === "Try it!"\n <div class="code-block">\n'
104
111
  " ```jac\n"
105
112
  f' --8<-- "jac/examples/reference/{heading_snakecase}.jac"\n'
@@ -112,12 +119,21 @@ def auto_generate_refs() -> str:
112
119
  ' --8<-- "jac/examples/reference/'
113
120
  f'{heading_snakecase}.py"\n ```\n'
114
121
  f'??? info "Jac Grammar Snippet"\n ```yaml linenums="{lines[0]}"\n --8<-- '
115
- f'"jac/jaclang/compiler/jac.lark:{lines[0]}:{lines[1]}"\n ```\n'
122
+ f'"jac/jaclang/compiler/jac.lark:{lines[0]}:{lines[1]}"\n ```\n\n'
116
123
  "**Description**\n\n--8<-- "
117
124
  f'"jac/examples/reference/'
118
125
  f'{heading_snakecase}.md"\n'
119
126
  )
120
- md_str += f"{content}\n"
127
+
128
+ # Write individual file
129
+ output_file = os.path.join(docs_ref_dir, f"{heading_snakecase}.md")
130
+ with open(output_file, "w") as f:
131
+ f.write(content)
132
+
133
+ # Return just the introduction for the main jac_ref.md file
134
+ md_str = (
135
+ '# Jac Language Reference\n\n--8<-- "jac/examples/reference/introduction.md"\n'
136
+ )
121
137
  return md_str
122
138
 
123
139
 
@@ -140,6 +156,24 @@ def dump_traceback(e: Exception) -> str:
140
156
  def byte_offset_to_char_offset(string: str, offset: int) -> int:
141
157
  return len(string.encode("utf-8")[:offset].decode("utf-8", errors="replace"))
142
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
+
143
177
  tb = TracebackException(type(e), e, e.__traceback__, limit=None, compact=True)
144
178
  trace_dump += f"Error: {str(e)}\n"
145
179
 
@@ -154,6 +188,11 @@ def dump_traceback(e: Exception) -> str:
154
188
  for idx, frame in enumerate(tb.stack):
155
189
  func_signature = frame.name + ("()" if frame.name.isidentifier() else "")
156
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
+
157
196
  # Pretty print the most recent call's location.
158
197
  if idx == 0 and (
159
198
  (frame.lineno is not None) and frame.line and frame.line.strip() != ""
@@ -172,22 +211,27 @@ def dump_traceback(e: Exception) -> str:
172
211
  off_start = byte_offset_to_char_offset(line_o, frame.colno) - 1
173
212
  off_end = byte_offset_to_char_offset(line_o, frame.end_colno) - 1
174
213
 
175
- # Get the source.
176
- file_source = None
177
- with open(frame.filename, "r") as file:
178
- file_source = file.read()
179
-
180
- # Get the source offset.
181
- lines = file_source.split("\n")
182
- for i in range(frame.lineno - 1):
183
- off_start += len(lines[i]) + 1
184
- off_end += len(lines[i]) + 1
185
-
186
- trace_dump += pretty_print_source_location(
187
- frame.filename, file_source, frame.lineno, off_start, off_end
188
- )
189
-
190
- 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}'
191
235
 
192
236
  return trace_dump
193
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"
@@ -112,7 +112,7 @@ class JacAstToolTests(TestCase):
112
112
  """Testing for sym, sym. AstTool."""
113
113
  jac_file = os.path.normpath(
114
114
  os.path.join(
115
- os.path.dirname(jaclang.__file__), "../examples/reference/atom.jac"
115
+ os.path.dirname(jaclang.__file__), "../examples/reference/while_statements.jac"
116
116
  )
117
117
  )
118
118
  out = self.tool.ir(["sym", jac_file])
@@ -122,30 +122,19 @@ class JacAstToolTests(TestCase):
122
122
  )
123
123
  check_list = [
124
124
  "########",
125
- "# atom #",
125
+ "# while_statements #",
126
126
  "########",
127
- "SymTable::Module(atom)",
128
- "| +-- list1",
129
- "| +-- x",
130
- "| +-- impl.x",
131
- "| +-- c",
132
- "| +-- d",
133
- "| +-- a",
134
- "| +-- b",
135
- "+-- SymTable::ImplDef(impl.x)",
136
- " SymTable::Enum(x)",
137
- "+-- line 19, col 13",
127
+ "SymTable::Module(while_statements)",
138
128
  ]
139
129
  for i in check_list:
140
130
  self.assertIn(i, out)
141
131
  out = self.tool.ir(["sym.", jac_file])
142
- self.assertIn('[label="impl.x"];', out)
143
- self.assertNotIn('[label="str"];', out)
132
+ self.assertIn('[label="', out)
144
133
 
145
134
  def test_uninode_doc(self) -> None:
146
135
  """Testing for Autodoc for Uninodes."""
147
136
  auto_uni = self.tool.autodoc_uninode()
148
137
  self.assertIn(
149
- "## LambdaExpr\n```mermaid\nflowchart LR\nLambdaExpr -->|Expr| body",
138
+ "## LambdaExpr\n```mermaid\nflowchart LR\nLambdaExpr -->|Expr, CodeBlockStmt| body",
150
139
  auto_uni,
151
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.8
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