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
jaclang/utils/NonGPT.py
ADDED
|
@@ -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
|
-
|
|
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'
|
|
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
|
-
|
|
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 =
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
jaclang/utils/lang_tools.py
CHANGED
|
@@ -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
|
-
|
|
305
|
+
typ_str = str(kid.typ)
|
|
306
|
+
arrow = "-.->" if "Optional" in typ_str else "-->"
|
|
283
307
|
typ = (
|
|
284
|
-
|
|
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/
|
|
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
|
-
"#
|
|
125
|
+
"# while_statements #",
|
|
126
126
|
"########",
|
|
127
|
-
"SymTable::Module(
|
|
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="
|
|
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
|
)
|
jaclang/utils/treeprinter.py
CHANGED
|
@@ -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,
|
|
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.
|
|
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@
|
|
8
|
+
Author-email: jason@mars.ninja
|
|
9
9
|
Maintainer: Jason Mars
|
|
10
|
-
Maintainer-email: jason@
|
|
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
|