jaclang 0.8.7__py3-none-any.whl → 0.8.8__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 +13 -27
- jaclang/cli/cmdreg.py +44 -0
- jaclang/compiler/constant.py +0 -1
- jaclang/compiler/jac.lark +3 -6
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +213 -34
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +0 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +70 -52
- jaclang/compiler/passes/main/pyast_load_pass.py +52 -20
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +191 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +57 -0
- jaclang/compiler/passes/main/type_checker_pass.py +29 -73
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +204 -44
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +171 -11
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +14 -6
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/type_evaluator.py +351 -67
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +79 -94
- jaclang/langserve/engine.jac +138 -159
- jaclang/langserve/server.jac +25 -1
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -309
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_server.py +21 -84
- jaclang/langserve/utils.jac +12 -15
- jaclang/runtimelib/machine.py +7 -0
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +18 -14
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +37 -1
- jaclang/tests/test_language.py +74 -16
- jaclang/utils/helpers.py +47 -2
- jaclang/utils/module_resolver.py +10 -0
- jaclang/utils/test.py +8 -0
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.7.dist-info → jaclang-0.8.8.dist-info}/METADATA +1 -2
- {jaclang-0.8.7.dist-info → jaclang-0.8.8.dist-info}/RECORD +85 -60
- {jaclang-0.8.7.dist-info → jaclang-0.8.8.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- jaclang/langserve/tests/server_test/code_test.py +0 -0
- {jaclang-0.8.7.dist-info → jaclang-0.8.8.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Test failing variable arguments cases (*args, **kwargs)
|
|
2
|
+
|
|
3
|
+
def simple_args_only(*args: int) -> int {
|
|
4
|
+
return sum(args);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
def simple_kwargs_only(**kwargs: str) -> int {
|
|
8
|
+
return len(kwargs);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def args_with_required(req: str, *args: int) -> str {
|
|
12
|
+
return f"{req}: {sum(args)}";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def kwargs_with_required(req: int, **kwargs: str) -> dict {
|
|
16
|
+
return {"req": req, "kwargs": kwargs};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def full_varargs_signature(
|
|
20
|
+
pos: int,
|
|
21
|
+
/,
|
|
22
|
+
reg: str,
|
|
23
|
+
def_param: float = 1.0,
|
|
24
|
+
*args: int,
|
|
25
|
+
kw_req: bool,
|
|
26
|
+
kw_opt: str = "default",
|
|
27
|
+
**kwargs: any
|
|
28
|
+
) -> dict {
|
|
29
|
+
return {
|
|
30
|
+
"pos": pos, "reg": reg, "def_param": def_param,
|
|
31
|
+
"args": list(args), "kw_req": kw_req, "kw_opt": kw_opt, "kwargs": kwargs
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def conflicting_names(name: str, *args: str, name_kw: str, **kwargs: str) -> dict {
|
|
36
|
+
return {"name": name, "args": list(args), "name_kw": name_kw, "kwargs": kwargs};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def type_sensitive_varargs(*args: int, **kwargs: int) -> dict {
|
|
40
|
+
return {"args_sum": sum(args), "kwargs_sum": sum(kwargs.values())};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def edge_case_empty_varargs(required: str, *args: float, **kwargs: bool) -> str {
|
|
44
|
+
return f"{required}: args={len(args)}, kwargs={len(kwargs)}";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def double_star_issues(**kwargs: dict) -> int {
|
|
48
|
+
total = 0;
|
|
49
|
+
for (k, v) in kwargs.items() {
|
|
50
|
+
if isinstance(v, dict) {
|
|
51
|
+
total += len(v);
|
|
52
|
+
} else {
|
|
53
|
+
total += 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return total;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def additional_varargs_edge_cases() -> None {
|
|
60
|
+
print("\n=== ADDITIONAL VARARGS EDGE CASES ===");
|
|
61
|
+
|
|
62
|
+
# Test 16: Empty containers passed to varargs
|
|
63
|
+
def test_empty_containers(*args: list, **kwargs: dict) -> dict {
|
|
64
|
+
return {"args_count": len(args), "kwargs_count": len(kwargs)};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
result = test_empty_containers([], {}, third_arg=[]);
|
|
69
|
+
print("✅ PASS: Empty containers:", result);
|
|
70
|
+
} except Exception as e {
|
|
71
|
+
print("❌ FAIL: Empty containers failed");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Test 17: Nested data structures
|
|
75
|
+
def complex_data_varargs(*args: dict, **kwargs: list) -> dict {
|
|
76
|
+
return {"total_keys": sum(args), "total_items": len(kwargs)};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
result = complex_data_varargs(
|
|
81
|
+
12,
|
|
82
|
+
16,
|
|
83
|
+
list1=[1, 2, 3],
|
|
84
|
+
list2=[4, 5]
|
|
85
|
+
);
|
|
86
|
+
print("✅ PASS: Complex data structures:", result);
|
|
87
|
+
} except Exception as e {
|
|
88
|
+
print("❌ FAIL: Complex data structures failed");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Test 18: Special character handling in kwargs keys
|
|
92
|
+
def special_chars_kwargs(**kwargs: str) -> dict {
|
|
93
|
+
return {"keys": list(kwargs.keys()), "count": len(kwargs)};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
# Note: Most of these won't work as valid identifiers, but let's test what we can
|
|
98
|
+
result = special_chars_kwargs(normal="value", with_underscore="test");
|
|
99
|
+
print("✅ PASS: Special chars in kwargs:", result);
|
|
100
|
+
} except Exception as e {
|
|
101
|
+
print("❌ FAIL: Special chars failed");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Test 19: Very large argument counts
|
|
105
|
+
def stress_test_args(*args: int) -> dict {
|
|
106
|
+
return {"count": len(args), "sum": sum(args), "avg": sum(args)/len(args) if args else 0};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
large_args = list(range(100)); # 100 arguments
|
|
111
|
+
result = stress_test_args(*large_args);
|
|
112
|
+
print("✅ PASS: Large arg count:", f"count={result['count']}, avg={result['avg']}");
|
|
113
|
+
} except Exception as e {
|
|
114
|
+
print("❌ FAIL: Large arg count failed");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Test 20: Mixing unpacking with explicit args
|
|
118
|
+
def mixed_unpacking_test(first: str, *args: int, last: str, **kwargs: bool) -> dict {
|
|
119
|
+
_sum = sum(args);
|
|
120
|
+
_kwargs_true_count = 0;
|
|
121
|
+
for v in kwargs.values() {
|
|
122
|
+
if v {
|
|
123
|
+
_kwargs_true_count += 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
"first": first,
|
|
128
|
+
"args_sum": _sum,
|
|
129
|
+
"last": last,
|
|
130
|
+
"kwargs_true_count": _kwargs_true_count
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
numbers = [10, 20, 30];
|
|
136
|
+
flags = {"debug": True, "verbose": False, "strict": True};
|
|
137
|
+
result = mixed_unpacking_test("start", *numbers, last="end", **flags);
|
|
138
|
+
print("✅ PASS: Mixed unpacking:", result);
|
|
139
|
+
} except Exception as e {
|
|
140
|
+
print("❌ FAIL: Mixed unpacking failed");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Test 21: Recursive function with varargs
|
|
144
|
+
def recursive_varargs(depth: int, *args: str, **kwargs: int) -> dict {
|
|
145
|
+
if depth <= 0 or not args {
|
|
146
|
+
return {"depth": depth, "args": list(args), "kwargs": kwargs};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
new_args = args[1:]; # Remove first arg
|
|
150
|
+
new_kwargs = {k: v+1 for (k, v) in kwargs.items()}; # Increment all values
|
|
151
|
+
|
|
152
|
+
return recursive_varargs(depth-1, *new_args, **new_kwargs);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
result = recursive_varargs(3, "a", "b", "c", "d", x=1, y=2);
|
|
157
|
+
print("✅ PASS: Recursive varargs:", result);
|
|
158
|
+
} except Exception as e {
|
|
159
|
+
print("❌ FAIL: Recursive varargs failed");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
with entry {
|
|
164
|
+
print("=== TESTING VARIABLE ARGUMENTS FAILURES ===");
|
|
165
|
+
|
|
166
|
+
# Test 1: Wrong types in *args (if type checking enabled)
|
|
167
|
+
try {
|
|
168
|
+
result = simple_args_only(1, 2, "not_int", 4);
|
|
169
|
+
print("❌ FAIL: Should catch mixed types in *args");
|
|
170
|
+
} except Exception as e {
|
|
171
|
+
print("✅ PASS: Caught *args type error");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Test 2: Wrong types in **kwargs (if type checking enabled)
|
|
175
|
+
try {
|
|
176
|
+
result = simple_kwargs_only(12,a="string", b=123, c="string");
|
|
177
|
+
print("❌ FAIL: Should catch mixed types in **kwargs");
|
|
178
|
+
} except Exception as e {
|
|
179
|
+
print("✅ PASS: Caught **kwargs type error");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Test 3: Missing required before *args
|
|
183
|
+
try {
|
|
184
|
+
result = args_with_required(1, 2, 3, apple=90);
|
|
185
|
+
} except Exception as e {
|
|
186
|
+
print("✅ PASS: Caught required param type error");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Test 4: Missing required before **kwargs
|
|
190
|
+
try {
|
|
191
|
+
result = kwargs_with_required(name="test", value="data");
|
|
192
|
+
print("❌ FAIL: Should catch wrong types for required");
|
|
193
|
+
} except Exception as e {
|
|
194
|
+
print("✅ PASS: Caught required before kwargs error");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# Test 5: Complex signature - pos-only passed as keyword
|
|
198
|
+
try {
|
|
199
|
+
result = full_varargs_signature(
|
|
200
|
+
pos=1, reg="test", kw_req=True
|
|
201
|
+
);
|
|
202
|
+
print("❌ FAIL: Should reject pos-only as keyword in varargs");
|
|
203
|
+
} except Exception as e {
|
|
204
|
+
print("✅ PASS: Caught pos-only in varargs error");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Test 6: Complex signature - missing required kw-only
|
|
208
|
+
try {
|
|
209
|
+
result = full_varargs_signature(1, "test", 2.0, 10, 20);
|
|
210
|
+
print("❌ FAIL: Should reject missing kw-only in varargs");
|
|
211
|
+
} except Exception as e {
|
|
212
|
+
print("✅ PASS: Caught missing kw-only in varargs");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Test 7: Name conflicts between params and kwargs
|
|
216
|
+
try {
|
|
217
|
+
result = conflicting_names("test", "arg1", name_kw="kw_test", name="conflict");
|
|
218
|
+
print("❌ FAIL: Should catch parameter name conflicts");
|
|
219
|
+
} except Exception as e {
|
|
220
|
+
print("✅ PASS: Caught name conflict error");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# Test 8: Type-sensitive varargs with wrong types
|
|
224
|
+
try {
|
|
225
|
+
result = type_sensitive_varargs(1, 2, 3, a="not_int", b=4.5);
|
|
226
|
+
print("❌ FAIL: Should catch type mismatch in kwargs");
|
|
227
|
+
} except Exception as e {
|
|
228
|
+
print("✅ PASS: Caught varargs type sensitivity error");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Test 9: Double unpacking issues
|
|
233
|
+
try {
|
|
234
|
+
data = {"a": {"nested": "dict"}, "b": "simple"};
|
|
235
|
+
result = double_star_issues(**data);
|
|
236
|
+
print("✅ INFO: Double star result:", result);
|
|
237
|
+
} except Exception as e {
|
|
238
|
+
print("✅ PASS: Caught double star error");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Test 10: Empty varargs but required params missing
|
|
242
|
+
try {
|
|
243
|
+
result = edge_case_empty_varargs();
|
|
244
|
+
print("❌ FAIL: Should catch missing required with empty varargs");
|
|
245
|
+
} except Exception as e {
|
|
246
|
+
print("✅ PASS: Caught missing required in empty varargs");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# Test 11: Keyword argument conflicts with *args collection
|
|
251
|
+
try {
|
|
252
|
+
items = [1, 2, 3];
|
|
253
|
+
result = args_with_required("test", *items, args="conflict");
|
|
254
|
+
print("❌ FAIL: Should catch args name conflict");
|
|
255
|
+
} except Exception as e {
|
|
256
|
+
print("✅ PASS: Caught args conflict error");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# Test 12: **kwargs conflicts with explicit keywords
|
|
260
|
+
try {
|
|
261
|
+
extra_data = {"kw_req": True, "extra": "data"};
|
|
262
|
+
result = full_varargs_signature(1, "test", kw_req=False, **extra_data);
|
|
263
|
+
print("❌ FAIL: Should catch kwargs conflict with explicit");
|
|
264
|
+
} except Exception as e {
|
|
265
|
+
print("✅ PASS: Caught kwargs explicit conflict");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Test 13: Valid calls that should work
|
|
269
|
+
try {
|
|
270
|
+
result = simple_args_only(1, 2, 3, 4, 5);
|
|
271
|
+
print("✅ PASS: Valid *args call:", result);
|
|
272
|
+
} except Exception as e {
|
|
273
|
+
print("❌ FAIL: Valid *args call rejected");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
result = simple_kwargs_only(a="test", b="data", c="more");
|
|
278
|
+
print("✅ PASS: Valid **kwargs call:", result);
|
|
279
|
+
} except Exception as e {
|
|
280
|
+
print("❌ FAIL: Valid **kwargs call rejected");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
result = full_varargs_signature(
|
|
285
|
+
1, # pos (pos-only)
|
|
286
|
+
"regular", # reg
|
|
287
|
+
2.5, # def_param
|
|
288
|
+
10, 20, 30, # *args
|
|
289
|
+
kw_req=True, # kw_req (required kw-only)
|
|
290
|
+
kw_opt="custom", # kw_opt (optional kw-only)
|
|
291
|
+
extra="data", # **kwargs
|
|
292
|
+
more="info"
|
|
293
|
+
);
|
|
294
|
+
print("✅ PASS: Valid complex varargs call keys:", list(result.keys()));
|
|
295
|
+
} except Exception as e {
|
|
296
|
+
print("❌ FAIL: Valid complex varargs rejected");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
additional_varargs_edge_cases();
|
|
300
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Test keyword-only parameters
|
|
2
|
+
|
|
3
|
+
def simple_kwonly(*, x: int) -> int {
|
|
4
|
+
return x;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
def kwonly_with_defaults(*, x: int = 10, y: str = "def") -> str {
|
|
8
|
+
return f"{x}-{y}";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def regular_plus_kwonly(normal: int, *, kw: str) -> str {
|
|
12
|
+
return f"{normal}|{kw}";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def mixed_kwonly(pos: int, reg: str = "def", *, kw1: float, kw2: bool = True) -> str {
|
|
16
|
+
return f"{pos}-{reg}-{kw1}-{kw2}";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def all_kwonly(*, a: int, b: str = "test", c: float = 1.0) -> str {
|
|
20
|
+
return f"{a}:{b}:{c}";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
with entry {
|
|
24
|
+
print("KW_SIMPLE:", simple_kwonly(x=42));
|
|
25
|
+
print("KW_DEF:", kwonly_with_defaults(), kwonly_with_defaults(x=20));
|
|
26
|
+
print("REG_KW:", regular_plus_kwonly(10, kw="test"));
|
|
27
|
+
print("MIXED_KW:", mixed_kwonly(1, kw1=2.5), mixed_kwonly(2, "custom", kw1=3.5, kw2=False));
|
|
28
|
+
print("ALL_KW:", all_kwonly(a=100), all_kwonly(a=200, b="hi", c=9.9));
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from typing_extensions import TypeAlias
|
|
2
|
+
import collections.abc
|
|
3
|
+
|
|
4
|
+
_ClassInfo: TypeAlias = type | tuple[object, ...]
|
|
5
|
+
Sized = collections.abc.Sized
|
|
6
|
+
|
|
7
|
+
def isinstance(obj: object, class_or_tuple: _ClassInfo, /) -> bool: ...
|
|
8
|
+
def len(obj: Sized,astt ,/, z: int, j: str,a= 90) -> int: ...
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
glob a = 5, b = 2;
|
|
2
2
|
|
|
3
3
|
test t1 {
|
|
4
|
-
|
|
4
|
+
assert almostEqual(a, 6);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
test t2 {
|
|
8
|
-
|
|
8
|
+
assert a != b;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
test t3 {
|
|
12
|
-
|
|
12
|
+
assert "d" in "abc";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
test t4 {
|
|
16
|
-
|
|
16
|
+
assert a - b == 3;
|
|
17
17
|
}
|
jaclang/tests/test_cli.py
CHANGED
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
|
+
import tempfile
|
|
10
11
|
import traceback
|
|
11
12
|
import unittest
|
|
12
13
|
from jaclang.cli import cli
|
|
@@ -432,6 +433,41 @@ class JacCliTests(TestCase):
|
|
|
432
433
|
"sorted(users, key=lambda x: x['email'], reverse=True)", stdout_value
|
|
433
434
|
)
|
|
434
435
|
|
|
436
|
+
def test_param_arg(self) -> None:
|
|
437
|
+
"""Test for lambda argument annotation."""
|
|
438
|
+
captured_output = io.StringIO()
|
|
439
|
+
sys.stdout = captured_output
|
|
440
|
+
from jaclang.compiler.program import JacProgram
|
|
441
|
+
|
|
442
|
+
filename = self.fixture_abs_path('../../tests/fixtures/params/test_complex_params.jac')
|
|
443
|
+
cli.jac2py(f"{self.fixture_abs_path('../../tests/fixtures/params/test_complex_params.jac')}")
|
|
444
|
+
py_code = JacProgram().compile(file_path=filename).gen.py
|
|
445
|
+
|
|
446
|
+
# Create temporary Python file
|
|
447
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as temp_file:
|
|
448
|
+
temp_file.write(py_code)
|
|
449
|
+
py_file_path = temp_file.name
|
|
450
|
+
|
|
451
|
+
try:
|
|
452
|
+
jac_code = JacProgram().compile(use_str=py_code, file_path=py_file_path).unparse()
|
|
453
|
+
# Create temporary Jac file
|
|
454
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.jac', delete=False) as temp_file:
|
|
455
|
+
temp_file.write(jac_code)
|
|
456
|
+
jac_file_path = temp_file.name
|
|
457
|
+
cli.run(jac_file_path)
|
|
458
|
+
finally:
|
|
459
|
+
os.remove(py_file_path)
|
|
460
|
+
os.remove(jac_file_path)
|
|
461
|
+
|
|
462
|
+
sys.stdout = sys.__stdout__
|
|
463
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
464
|
+
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[-7])
|
|
465
|
+
self.assertEqual("ULTIMATE_FULL: 1|custom|3.14|3|req|200|1", stdout_value[-6])
|
|
466
|
+
self.assertEqual("SEPARATORS: 42", stdout_value[-5])
|
|
467
|
+
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[-4])
|
|
468
|
+
self.assertEqual("RECURSIVE: 7 11", stdout_value[-3])
|
|
469
|
+
self.assertEqual("VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[-2])
|
|
470
|
+
|
|
435
471
|
def test_caching_issue(self) -> None:
|
|
436
472
|
"""Test for Caching Issue."""
|
|
437
473
|
test_file = self.fixture_abs_path("test_caching_issue.jac")
|
|
@@ -441,7 +477,7 @@ class JacCliTests(TestCase):
|
|
|
441
477
|
f.write(
|
|
442
478
|
f"""
|
|
443
479
|
test mytest{{
|
|
444
|
-
|
|
480
|
+
assert 10 == {x};
|
|
445
481
|
}}
|
|
446
482
|
"""
|
|
447
483
|
)
|
jaclang/tests/test_language.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
-
import
|
|
6
|
+
import pytest
|
|
7
7
|
import tempfile
|
|
8
8
|
import subprocess
|
|
9
9
|
from pathlib import Path
|
|
@@ -514,14 +514,13 @@ class JacLanguageTests(TestCase):
|
|
|
514
514
|
),
|
|
515
515
|
prog=JacProgram(),
|
|
516
516
|
).ir_out.unparse()
|
|
517
|
-
self.assertIn("def greet2(**kwargs: Any)", output)
|
|
517
|
+
self.assertIn("def greet2( **kwargs: Any) {", output)
|
|
518
518
|
self.assertEqual(output.count("with entry {"), 14)
|
|
519
519
|
self.assertIn("assert (x == 5) , 'x should be equal to 5' ;", output)
|
|
520
520
|
self.assertIn("if not (x == y) {", output)
|
|
521
|
-
self.assertIn("
|
|
522
|
-
self.assertIn("squares_dict = { x : (x ** 2) for x in numbers };", output)
|
|
521
|
+
self.assertIn("squares_dict = {x : (x ** 2) for x in numbers};", output)
|
|
523
522
|
self.assertIn(
|
|
524
|
-
'\n\n"""Say hello"""\n@
|
|
523
|
+
'\n\n"""Say hello"""\n@my_decorator\n\n def say_hello() {', output
|
|
525
524
|
)
|
|
526
525
|
|
|
527
526
|
def test_pyfunc_2(self) -> None:
|
|
@@ -589,6 +588,27 @@ class JacLanguageTests(TestCase):
|
|
|
589
588
|
self.assertIn("case Container(inner = Inner(x = a, y = b)):\n", output)
|
|
590
589
|
self.assertIn("case _:\n", output)
|
|
591
590
|
|
|
591
|
+
def test_py2jac_params(self) -> None:
|
|
592
|
+
"""Test py ast to Jac ast conversion."""
|
|
593
|
+
from jaclang.compiler.passes.main import PyastBuildPass
|
|
594
|
+
import jaclang.compiler.unitree as ast
|
|
595
|
+
import ast as py_ast
|
|
596
|
+
|
|
597
|
+
py_out_path = os.path.join(self.fixture_abs_path("./"), "py2jac_params.py")
|
|
598
|
+
with open(py_out_path) as f:
|
|
599
|
+
file_source = f.read()
|
|
600
|
+
output = PyastBuildPass(
|
|
601
|
+
ir_in=ast.PythonModuleAst(
|
|
602
|
+
py_ast.parse(file_source),
|
|
603
|
+
orig_src=ast.Source(file_source, py_out_path),
|
|
604
|
+
),
|
|
605
|
+
prog=None,
|
|
606
|
+
).ir_out.unparse()
|
|
607
|
+
self.assertIn(
|
|
608
|
+
"def isinstance( <>obj: object , class_or_tuple: _ClassInfo , /) -> bool {", output)
|
|
609
|
+
self.assertIn(
|
|
610
|
+
"def len(<>obj: Sized, astt: Any, /, z: int, j: str, a: Any = 90) -> int {", output)
|
|
611
|
+
|
|
592
612
|
def test_refs_target(self) -> None:
|
|
593
613
|
"""Test py ast to Jac ast conversion output."""
|
|
594
614
|
captured_output = io.StringIO()
|
|
@@ -712,6 +732,7 @@ class JacLanguageTests(TestCase):
|
|
|
712
732
|
mypass = JacProgram().compile(self.fixture_abs_path("byllmissue.jac"))
|
|
713
733
|
self.assertIn("2:5 - 4:8", mypass.pp())
|
|
714
734
|
|
|
735
|
+
@pytest.mark.xfail(reason="TODO: Support symtable for inheritance")
|
|
715
736
|
def test_inherit_baseclass_sym(self) -> None:
|
|
716
737
|
"""Basic test for symtable support for inheritance."""
|
|
717
738
|
mypass = JacProgram().compile(
|
|
@@ -746,6 +767,43 @@ class JacLanguageTests(TestCase):
|
|
|
746
767
|
stdout_value = captured_output.getvalue()
|
|
747
768
|
self.assertIn("i work", stdout_value)
|
|
748
769
|
|
|
770
|
+
def test_kwonly_params(self) -> None:
|
|
771
|
+
"""Test importing python."""
|
|
772
|
+
captured_output = io.StringIO()
|
|
773
|
+
sys.stdout = captured_output
|
|
774
|
+
Jac.jac_import("test_kwonly_params", base_path=self.fixture_abs_path("./params"))
|
|
775
|
+
sys.stdout = sys.__stdout__
|
|
776
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
777
|
+
self.assertEqual("KW_SIMPLE: 42", stdout_value[0])
|
|
778
|
+
self.assertEqual("KW_DEF: 10-def 20-def", stdout_value[1])
|
|
779
|
+
self.assertEqual("REG_KW: 10|test", stdout_value[2])
|
|
780
|
+
self.assertEqual("MIXED_KW: 1-def-2.5-True 2-custom-3.5-False", stdout_value[3])
|
|
781
|
+
self.assertEqual("ALL_KW: 100:test:1.0 200:hi:9.9", stdout_value[4])
|
|
782
|
+
|
|
783
|
+
def test_complex_params(self) -> None:
|
|
784
|
+
"""Test importing python."""
|
|
785
|
+
captured_output = io.StringIO()
|
|
786
|
+
sys.stdout = captured_output
|
|
787
|
+
Jac.jac_import("test_complex_params", base_path=self.fixture_abs_path("./params"))
|
|
788
|
+
sys.stdout = sys.__stdout__
|
|
789
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
790
|
+
self.assertEqual("ULTIMATE_MIN: 1|def|2.5|0|test|100|0", stdout_value[0])
|
|
791
|
+
self.assertEqual("ULTIMATE_FULL: 1|custom|3.14|3|req|200|1", stdout_value[1])
|
|
792
|
+
self.assertEqual("SEPARATORS: 42", stdout_value[2])
|
|
793
|
+
self.assertEqual("EDGE_MIX: 1-test-2-True-1", stdout_value[3])
|
|
794
|
+
self.assertEqual("RECURSIVE: 7 11", stdout_value[4])
|
|
795
|
+
self.assertEqual("VALIDATION: x:1,y:2.5,z:10,args:1,w:True,kwargs:1", stdout_value[5])
|
|
796
|
+
|
|
797
|
+
def test_param_failing(self) -> None:
|
|
798
|
+
"""Test importing python."""
|
|
799
|
+
captured_output = io.StringIO()
|
|
800
|
+
sys.stdout = captured_output
|
|
801
|
+
for i in ["test_failing_posonly", "test_failing_kwonly", "test_failing_varargs"]:
|
|
802
|
+
Jac.jac_import(i, base_path=self.fixture_abs_path("./params"))
|
|
803
|
+
sys.stdout = sys.__stdout__
|
|
804
|
+
stdout_value = captured_output.getvalue()
|
|
805
|
+
self.assertNotIn('FAILED', stdout_value)
|
|
806
|
+
|
|
749
807
|
def test_double_import_exec(self) -> None:
|
|
750
808
|
"""Test importing python."""
|
|
751
809
|
captured_output = io.StringIO()
|
|
@@ -1238,9 +1296,9 @@ class JacLanguageTests(TestCase):
|
|
|
1238
1296
|
).ir_out.unparse()
|
|
1239
1297
|
self.assertIn("(prev_token_index is None)", output)
|
|
1240
1298
|
self.assertIn("(next_token_index is None)", output)
|
|
1241
|
-
self.assertIn("(tok[
|
|
1242
|
-
self.assertIn("(tok[
|
|
1243
|
-
self.assertIn("(tok[
|
|
1299
|
+
self.assertIn("(tok[0] > change_end_line)", output)
|
|
1300
|
+
self.assertIn("(tok[0] == change_end_line)", output)
|
|
1301
|
+
self.assertIn("(tok[1] > change_end_char)", output)
|
|
1244
1302
|
|
|
1245
1303
|
def test_here_visitor_usage(self) -> None:
|
|
1246
1304
|
"""Test visitor, here keyword usage in jaclang."""
|
|
@@ -1378,7 +1436,7 @@ class JacLanguageTests(TestCase):
|
|
|
1378
1436
|
test_content = "Hello, 世界! 🌍 Testing UTF-8 encoding."
|
|
1379
1437
|
f.write(test_content)
|
|
1380
1438
|
temp_path = f.name
|
|
1381
|
-
|
|
1439
|
+
|
|
1382
1440
|
try:
|
|
1383
1441
|
result = read_file_with_encoding(temp_path)
|
|
1384
1442
|
self.assertEqual(result, test_content)
|
|
@@ -1391,7 +1449,7 @@ class JacLanguageTests(TestCase):
|
|
|
1391
1449
|
test_content = "Hello, 世界! UTF-16 encoding test."
|
|
1392
1450
|
f.write(test_content)
|
|
1393
1451
|
temp_path = f.name
|
|
1394
|
-
|
|
1452
|
+
|
|
1395
1453
|
try:
|
|
1396
1454
|
result = read_file_with_encoding(temp_path)
|
|
1397
1455
|
self.assertEqual(result, test_content)
|
|
@@ -1404,7 +1462,7 @@ class JacLanguageTests(TestCase):
|
|
|
1404
1462
|
test_content = "Hello, UTF-8 BOM test! 🚀"
|
|
1405
1463
|
f.write(test_content)
|
|
1406
1464
|
temp_path = f.name
|
|
1407
|
-
|
|
1465
|
+
|
|
1408
1466
|
try:
|
|
1409
1467
|
result = read_file_with_encoding(temp_path)
|
|
1410
1468
|
self.assertEqual(result, test_content)
|
|
@@ -1431,9 +1489,9 @@ class JacLanguageTests(TestCase):
|
|
|
1431
1489
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
|
1432
1490
|
binary_data = bytes([0xFF, 0xFE, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F])
|
|
1433
1491
|
f.write(binary_data)
|
|
1434
|
-
f.flush()
|
|
1492
|
+
f.flush()
|
|
1435
1493
|
temp_path = f.name
|
|
1436
|
-
|
|
1494
|
+
|
|
1437
1495
|
try:
|
|
1438
1496
|
result = read_file_with_encoding(temp_path)
|
|
1439
1497
|
self.assertIsInstance(result, str)
|
|
@@ -1446,15 +1504,15 @@ class JacLanguageTests(TestCase):
|
|
|
1446
1504
|
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', delete=False) as f:
|
|
1447
1505
|
test_content = (
|
|
1448
1506
|
"Special chars: åäö ñ ü ç é\n"
|
|
1449
|
-
"Symbols: ©®™ §¶†‡•\n"
|
|
1507
|
+
"Symbols: ©®™ §¶†‡•\n"
|
|
1450
1508
|
"Math: ∑∏∫√±≤≥≠\n"
|
|
1451
1509
|
"Arrows: ←→↑↓↔\n"
|
|
1452
1510
|
"Emoji: 😀😍🎉🔥💯\n"
|
|
1453
1511
|
)
|
|
1454
1512
|
f.write(test_content)
|
|
1455
|
-
f.flush()
|
|
1513
|
+
f.flush()
|
|
1456
1514
|
temp_path = f.name
|
|
1457
|
-
|
|
1515
|
+
|
|
1458
1516
|
try:
|
|
1459
1517
|
result = read_file_with_encoding(temp_path)
|
|
1460
1518
|
|
jaclang/utils/helpers.py
CHANGED
|
@@ -192,6 +192,37 @@ def dump_traceback(e: Exception) -> str:
|
|
|
192
192
|
return trace_dump
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
# FIXME: Use a proper color library and/or move this somewhere common to jac stack and use it everywhere.
|
|
196
|
+
# Reference: https://gist.github.com/rene-d/9e584a7dd2935d0f461904b9f2950007
|
|
197
|
+
class ANSIColors:
|
|
198
|
+
"""ANSI color codes."""
|
|
199
|
+
|
|
200
|
+
BLACK = "\033[0;30m"
|
|
201
|
+
RED = "\033[0;31m"
|
|
202
|
+
GREEN = "\033[0;32m"
|
|
203
|
+
BROWN = "\033[0;33m"
|
|
204
|
+
BLUE = "\033[0;34m"
|
|
205
|
+
PURPLE = "\033[0;35m"
|
|
206
|
+
CYAN = "\033[0;36m"
|
|
207
|
+
LIGHT_GRAY = "\033[0;37m"
|
|
208
|
+
DARK_GRAY = "\033[1;30m"
|
|
209
|
+
LIGHT_RED = "\033[1;31m"
|
|
210
|
+
LIGHT_GREEN = "\033[1;32m"
|
|
211
|
+
YELLOW = "\033[1;33m"
|
|
212
|
+
LIGHT_BLUE = "\033[1;34m"
|
|
213
|
+
LIGHT_PURPLE = "\033[1;35m"
|
|
214
|
+
LIGHT_CYAN = "\033[1;36m"
|
|
215
|
+
LIGHT_WHITE = "\033[1;37m"
|
|
216
|
+
BOLD = "\033[1m"
|
|
217
|
+
FAINT = "\033[2m"
|
|
218
|
+
ITALIC = "\033[3m"
|
|
219
|
+
UNDERLINE = "\033[4m"
|
|
220
|
+
BLINK = "\033[5m"
|
|
221
|
+
NEGATIVE = "\033[7m"
|
|
222
|
+
CROSSED = "\033[9m"
|
|
223
|
+
END = "\033[0m"
|
|
224
|
+
|
|
225
|
+
|
|
195
226
|
# TODO: After implementing the TextRange (or simillar named) class to mark a text range
|
|
196
227
|
# refactor the parameter to accept an instace of that text range object.
|
|
197
228
|
def pretty_print_source_location(
|
|
@@ -200,6 +231,8 @@ def pretty_print_source_location(
|
|
|
200
231
|
error_line: int,
|
|
201
232
|
pos_start: int,
|
|
202
233
|
pos_end: int,
|
|
234
|
+
*,
|
|
235
|
+
colors: bool = False,
|
|
203
236
|
) -> str:
|
|
204
237
|
"""Pretty print internal method for the pretty_print method."""
|
|
205
238
|
# NOTE: The Line numbers and the column numbers are starts with 1.
|
|
@@ -241,7 +274,16 @@ def pretty_print_source_location(
|
|
|
241
274
|
idx_line_start = idx
|
|
242
275
|
while idx < len(file_source) and file_source[idx] != "\n":
|
|
243
276
|
idx += 1 # Run to the line end.
|
|
244
|
-
|
|
277
|
+
|
|
278
|
+
if colors and (curr_line == error_line):
|
|
279
|
+
pretty_dump += (
|
|
280
|
+
file_source[idx_line_start:pos_start]
|
|
281
|
+
+ f"{ANSIColors.RED}{file_source[pos_start:pos_end]}{ANSIColors.END}"
|
|
282
|
+
+ file_source[pos_end:idx]
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
pretty_dump += file_source[idx_line_start:idx]
|
|
286
|
+
|
|
245
287
|
pretty_dump += "\n"
|
|
246
288
|
|
|
247
289
|
if curr_line == error_line: # Print the current line with indicator.
|
|
@@ -252,7 +294,10 @@ def pretty_print_source_location(
|
|
|
252
294
|
spaces += "\t" if file_source[idx_pre] == "\t" else " "
|
|
253
295
|
|
|
254
296
|
err_token_len = pos_end - pos_start
|
|
255
|
-
|
|
297
|
+
underline = "^" * err_token_len
|
|
298
|
+
if colors:
|
|
299
|
+
underline = f"{ANSIColors.RED}{underline}{ANSIColors.END}"
|
|
300
|
+
pretty_dump += spaces + underline + "\n"
|
|
256
301
|
|
|
257
302
|
if idx == len(file_source):
|
|
258
303
|
break
|
jaclang/utils/module_resolver.py
CHANGED
|
@@ -30,6 +30,16 @@ def get_jac_search_paths(base_path: Optional[str] = None) -> list[str]:
|
|
|
30
30
|
return list(dict.fromkeys(filter(None, paths)))
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
# TODO: need to be removed once python modules are fully supported in jac
|
|
34
|
+
def get_py_search_paths(base_path: Optional[str] = None) -> list[str]:
|
|
35
|
+
"""Construct a list of paths to search for Python modules."""
|
|
36
|
+
paths = []
|
|
37
|
+
if base_path:
|
|
38
|
+
paths.append(base_path)
|
|
39
|
+
paths.append(os.getcwd())
|
|
40
|
+
return list(dict.fromkeys(filter(None, paths)))
|
|
41
|
+
|
|
42
|
+
|
|
33
43
|
def _candidate_from(base: str, parts: list[str]) -> Optional[Tuple[str, str]]:
|
|
34
44
|
candidate = os.path.join(base, *parts)
|
|
35
45
|
if os.path.isdir(candidate):
|