jaclang 0.8.4__py3-none-any.whl → 0.8.5__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 +74 -22
- jaclang/compiler/jac.lark +3 -3
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +14 -21
- jaclang/compiler/passes/main/__init__.py +3 -1
- jaclang/compiler/passes/main/binder_pass.py +594 -0
- jaclang/compiler/passes/main/import_pass.py +8 -256
- jaclang/compiler/passes/main/inheritance_pass.py +2 -2
- jaclang/compiler/passes/main/pyast_gen_pass.py +35 -69
- jaclang/compiler/passes/main/pyast_load_pass.py +24 -13
- jaclang/compiler/passes/main/sem_def_match_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/M1.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +47 -0
- jaclang/compiler/passes/main/tests/test_binder_pass.py +111 -0
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +13 -13
- jaclang/compiler/passes/main/tests/test_sem_def_match_pass.py +6 -6
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +2 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +6 -0
- jaclang/compiler/program.py +15 -8
- jaclang/compiler/tests/test_sr_errors.py +32 -0
- jaclang/compiler/unitree.py +21 -15
- jaclang/langserve/engine.jac +23 -4
- jaclang/langserve/tests/test_server.py +13 -0
- jaclang/runtimelib/importer.py +33 -62
- jaclang/runtimelib/utils.py +29 -0
- jaclang/tests/fixtures/pyfunc_fmt.py +60 -0
- jaclang/tests/fixtures/pyfunc_fstr.py +25 -0
- jaclang/tests/fixtures/pyfunc_kwesc.py +33 -0
- jaclang/tests/fixtures/python_run_test.py +19 -0
- jaclang/tests/test_cli.py +67 -0
- jaclang/tests/test_language.py +96 -1
- jaclang/utils/lang_tools.py +3 -3
- jaclang/utils/module_resolver.py +90 -0
- jaclang/utils/symtable_test_helpers.py +125 -0
- jaclang/utils/test.py +3 -4
- jaclang/vendor/interegular/__init__.py +34 -0
- jaclang/vendor/interegular/comparator.py +163 -0
- jaclang/vendor/interegular/fsm.py +1015 -0
- jaclang/vendor/interegular/patterns.py +732 -0
- jaclang/vendor/interegular/py.typed +0 -0
- jaclang/vendor/interegular/utils/__init__.py +15 -0
- jaclang/vendor/interegular/utils/simple_parser.py +165 -0
- jaclang/vendor/interegular-0.3.3.dist-info/INSTALLER +1 -0
- jaclang/vendor/interegular-0.3.3.dist-info/LICENSE.txt +21 -0
- jaclang/vendor/interegular-0.3.3.dist-info/METADATA +64 -0
- jaclang/vendor/interegular-0.3.3.dist-info/RECORD +20 -0
- jaclang/vendor/interegular-0.3.3.dist-info/REQUESTED +0 -0
- jaclang/vendor/interegular-0.3.3.dist-info/WHEEL +5 -0
- jaclang/vendor/interegular-0.3.3.dist-info/top_level.txt +1 -0
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/METADATA +1 -1
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/RECORD +53 -29
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/WHEEL +0 -0
- {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/entry_points.txt +0 -0
|
@@ -17,14 +17,11 @@ The pass runs early in the compilation pipeline to ensure all symbols from impor
|
|
|
17
17
|
modules are available for subsequent passes like symbol table building and type checking.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
import ast as py_ast
|
|
21
20
|
import os
|
|
22
|
-
from typing import Optional
|
|
23
|
-
|
|
24
21
|
|
|
25
22
|
import jaclang.compiler.unitree as uni
|
|
26
23
|
from jaclang.compiler.passes import Transform, UniPass
|
|
27
|
-
from jaclang.
|
|
24
|
+
from jaclang.runtimelib.utils import read_file_with_encoding
|
|
28
25
|
from jaclang.utils.log import logging
|
|
29
26
|
|
|
30
27
|
|
|
@@ -102,260 +99,15 @@ class JacImportDepsPass(Transform[uni.Module, uni.Module]):
|
|
|
102
99
|
return self.prog.mod.hub[jac_init_path]
|
|
103
100
|
return self.prog.compile(file_path=jac_init_path)
|
|
104
101
|
elif os.path.exists(py_init_path := os.path.join(target, "__init__.py")):
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return mod
|
|
102
|
+
file_source = read_file_with_encoding(py_init_path)
|
|
103
|
+
mod = uni.Module.make_stub(
|
|
104
|
+
inject_name=target.split(os.path.sep)[-1],
|
|
105
|
+
inject_src=uni.Source(file_source, py_init_path),
|
|
106
|
+
)
|
|
107
|
+
self.prog.mod.hub[py_init_path] = mod
|
|
108
|
+
return mod
|
|
113
109
|
else:
|
|
114
110
|
return uni.Module.make_stub(
|
|
115
111
|
inject_name=target.split(os.path.sep)[-1],
|
|
116
112
|
inject_src=uni.Source("", target),
|
|
117
113
|
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class PyImportDepsPass(JacImportDepsPass):
|
|
121
|
-
"""Jac statically imports Python modules."""
|
|
122
|
-
|
|
123
|
-
def pre_transform(self) -> None:
|
|
124
|
-
"""Initialize the PyImportPass."""
|
|
125
|
-
self.import_from_build_list: list[tuple[uni.Import, uni.Module]] = []
|
|
126
|
-
|
|
127
|
-
def transform(self, ir_in: uni.Module) -> uni.Module:
|
|
128
|
-
"""Run Importer."""
|
|
129
|
-
self.__load_builtins()
|
|
130
|
-
self.import_from_build_list = []
|
|
131
|
-
|
|
132
|
-
# Add all modules from the program hub to last_imported to process their imports
|
|
133
|
-
self.last_imported = list(self.prog.mod.hub.values())
|
|
134
|
-
|
|
135
|
-
# Process imports until no more imported modules to process
|
|
136
|
-
while self.last_imported:
|
|
137
|
-
current_module = self.last_imported.pop(0)
|
|
138
|
-
all_imports = UniPass.get_all_sub_nodes(current_module, uni.ModulePath)
|
|
139
|
-
for i in all_imports:
|
|
140
|
-
self.process_import(i)
|
|
141
|
-
|
|
142
|
-
return ir_in
|
|
143
|
-
|
|
144
|
-
def process_import(self, i: uni.ModulePath) -> None:
|
|
145
|
-
"""Process an import."""
|
|
146
|
-
# Process import is orginally implemented to handle ModulePath in Jaclang
|
|
147
|
-
# This won't work with py imports as this will fail to import stuff in form of
|
|
148
|
-
# from a import b
|
|
149
|
-
# from a import (c, d, e)
|
|
150
|
-
# Solution to that is to get the import node and check the from loc `then`
|
|
151
|
-
# handle it based on if there a from loc or not
|
|
152
|
-
imp_node = i.parent_of_type(uni.Import)
|
|
153
|
-
|
|
154
|
-
if imp_node.is_py:
|
|
155
|
-
if imp_node.from_loc:
|
|
156
|
-
msg = "Processing import from node at href="
|
|
157
|
-
msg += uni.Module.get_href_path(imp_node)
|
|
158
|
-
msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
|
|
159
|
-
self.__process_import_from(imp_node)
|
|
160
|
-
else:
|
|
161
|
-
msg = "Processing import node at href="
|
|
162
|
-
msg += uni.Module.get_href_path(imp_node)
|
|
163
|
-
msg += f' path="{imp_node.loc.mod_path}, {imp_node.loc}"'
|
|
164
|
-
self.__process_import(imp_node)
|
|
165
|
-
|
|
166
|
-
def __process_import_from(self, imp_node: uni.Import) -> None:
|
|
167
|
-
"""Process imports in the form of `from X import I`."""
|
|
168
|
-
assert isinstance(imp_node.from_loc, uni.ModulePath)
|
|
169
|
-
|
|
170
|
-
# Attempt to import the Python module X and process it
|
|
171
|
-
imported_mod = self.__import_py_module(
|
|
172
|
-
parent_node_path=uni.Module.get_href_path(imp_node),
|
|
173
|
-
mod_path=imp_node.from_loc.dot_path_str,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
if imported_mod:
|
|
177
|
-
# Cyclic imports will happen in case of import sys
|
|
178
|
-
# sys stub file imports sys module which means that we need
|
|
179
|
-
# to import sys stub file again in the sys stub file and so on
|
|
180
|
-
# This can be detected by iterating over all the parents and make sure
|
|
181
|
-
# that the parent is in another file than the imported module
|
|
182
|
-
if self.__check_cyclic_imports(imp_node, imported_mod):
|
|
183
|
-
return
|
|
184
|
-
|
|
185
|
-
if imported_mod.name == "builtins":
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
msg = f"\tRegistering module:{imported_mod.name} to "
|
|
189
|
-
msg += f"import_from handling with {imp_node.loc.mod_path}:{imp_node.loc}"
|
|
190
|
-
|
|
191
|
-
self.load_mod(imported_mod)
|
|
192
|
-
self.import_from_build_list.append((imp_node, imported_mod))
|
|
193
|
-
SymTabBuildPass(ir_in=imported_mod, prog=self.prog)
|
|
194
|
-
|
|
195
|
-
def __process_import(self, imp_node: uni.Import) -> None:
|
|
196
|
-
"""Process the imports in form of `import X`."""
|
|
197
|
-
# Expected that each ImportStatement will import one item
|
|
198
|
-
# In case of this assertion fired then we need to revisit this item
|
|
199
|
-
assert len(imp_node.items) == 1
|
|
200
|
-
imported_item = imp_node.items[0]
|
|
201
|
-
assert isinstance(imported_item, uni.ModulePath)
|
|
202
|
-
|
|
203
|
-
imported_mod = self.__import_py_module(
|
|
204
|
-
parent_node_path=uni.Module.get_href_path(imported_item),
|
|
205
|
-
mod_path=imported_item.dot_path_str,
|
|
206
|
-
imported_mod_name=(
|
|
207
|
-
# TODO: Check this replace
|
|
208
|
-
imported_item.dot_path_str.replace(".", "")
|
|
209
|
-
if not imported_item.alias
|
|
210
|
-
else imported_item.alias.sym_name
|
|
211
|
-
),
|
|
212
|
-
)
|
|
213
|
-
if imported_mod:
|
|
214
|
-
if (
|
|
215
|
-
self.__check_cyclic_imports(imp_node, imported_mod)
|
|
216
|
-
or imported_mod.name == "builtins"
|
|
217
|
-
):
|
|
218
|
-
return
|
|
219
|
-
self.load_mod(imported_mod)
|
|
220
|
-
|
|
221
|
-
if imp_node.is_absorb:
|
|
222
|
-
msg = f"\tRegistering module:{imported_mod.name} to "
|
|
223
|
-
msg += f"import_from (import all) handling with {imp_node.loc.mod_path}:{imp_node.loc}"
|
|
224
|
-
|
|
225
|
-
self.import_from_build_list.append((imp_node, imported_mod))
|
|
226
|
-
SymTabBuildPass(ir_in=imported_mod, prog=self.prog)
|
|
227
|
-
|
|
228
|
-
def __import_py_module(
|
|
229
|
-
self,
|
|
230
|
-
parent_node_path: str,
|
|
231
|
-
mod_path: str,
|
|
232
|
-
imported_mod_name: Optional[str] = None,
|
|
233
|
-
) -> Optional[uni.Module]:
|
|
234
|
-
"""Import a python module."""
|
|
235
|
-
from jaclang.compiler.passes.main import PyastBuildPass
|
|
236
|
-
|
|
237
|
-
python_raise_map = self.prog.py_raise_map
|
|
238
|
-
file_to_raise: Optional[str] = None
|
|
239
|
-
|
|
240
|
-
if mod_path in python_raise_map:
|
|
241
|
-
file_to_raise = python_raise_map[mod_path]
|
|
242
|
-
else:
|
|
243
|
-
# TODO: Is it fine to use imported_mod_name or get it from mod_path?
|
|
244
|
-
resolved_mod_path = f"{parent_node_path}.{mod_path}"
|
|
245
|
-
resolved_mod_path = resolved_mod_path.replace("..", ".")
|
|
246
|
-
resolved_mod_path = resolved_mod_path.replace(
|
|
247
|
-
f"{list(self.prog.mod.hub.values())[0]}.", ""
|
|
248
|
-
)
|
|
249
|
-
file_to_raise = python_raise_map.get(resolved_mod_path)
|
|
250
|
-
|
|
251
|
-
if file_to_raise is None:
|
|
252
|
-
return None
|
|
253
|
-
|
|
254
|
-
try:
|
|
255
|
-
if file_to_raise in {None, "built-in", "frozen"}:
|
|
256
|
-
return None
|
|
257
|
-
|
|
258
|
-
with open(file_to_raise, "r", encoding="utf-8") as f:
|
|
259
|
-
file_source = f.read()
|
|
260
|
-
mod = PyastBuildPass(
|
|
261
|
-
ir_in=uni.PythonModuleAst(
|
|
262
|
-
py_ast.parse(file_source),
|
|
263
|
-
orig_src=uni.Source(file_source, file_to_raise),
|
|
264
|
-
),
|
|
265
|
-
prog=self.prog,
|
|
266
|
-
).ir_out
|
|
267
|
-
|
|
268
|
-
if mod:
|
|
269
|
-
mod.name = imported_mod_name if imported_mod_name else mod.name
|
|
270
|
-
if mod.name == "__init__":
|
|
271
|
-
# (thakee): This needs to be re-done after implementing path handling properly.
|
|
272
|
-
mod_name = mod.loc.mod_path.split(os.path.sep)[-2]
|
|
273
|
-
mod.name = mod_name
|
|
274
|
-
mod.scope_name = mod_name
|
|
275
|
-
mod.is_raised_from_py = True
|
|
276
|
-
return mod
|
|
277
|
-
else:
|
|
278
|
-
raise self.ice(f"\tFailed to import python module {mod_path}")
|
|
279
|
-
|
|
280
|
-
except Exception as e:
|
|
281
|
-
self.log_error(f"\tFailed to import python module {mod_path}")
|
|
282
|
-
raise e
|
|
283
|
-
|
|
284
|
-
def __load_builtins(self) -> None:
|
|
285
|
-
"""Load Python builtins using introspection."""
|
|
286
|
-
import builtins
|
|
287
|
-
import inspect
|
|
288
|
-
from jaclang.compiler.passes.main import PyastBuildPass
|
|
289
|
-
|
|
290
|
-
# Python constants that cannot be assigned to
|
|
291
|
-
constants = {"True", "False", "None", "NotImplemented", "Ellipsis", "..."}
|
|
292
|
-
|
|
293
|
-
# Create a synthetic source with all builtins
|
|
294
|
-
builtin_items = []
|
|
295
|
-
for name in dir(builtins):
|
|
296
|
-
if name in constants:
|
|
297
|
-
# Skip constants as they're built into Python
|
|
298
|
-
continue
|
|
299
|
-
|
|
300
|
-
if not name.startswith("_") or name in ("__import__", "__build_class__"):
|
|
301
|
-
obj = getattr(builtins, name)
|
|
302
|
-
# Generate appropriate stub definitions based on obj type
|
|
303
|
-
if inspect.isclass(obj):
|
|
304
|
-
builtin_items.append(f"class {name}: ...")
|
|
305
|
-
elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
|
|
306
|
-
# Try to get signature safely, use generic signature if it fails
|
|
307
|
-
try:
|
|
308
|
-
sig = inspect.signature(obj) if callable(obj) else "()"
|
|
309
|
-
builtin_items.append(f"def {name}{sig}: ...")
|
|
310
|
-
except (ValueError, TypeError):
|
|
311
|
-
builtin_items.append(f"def {name}(*args, **kwargs): ...")
|
|
312
|
-
else:
|
|
313
|
-
# For variables that are not constants
|
|
314
|
-
builtin_items.append(f"{name} = None")
|
|
315
|
-
|
|
316
|
-
file_source = "\n".join(builtin_items)
|
|
317
|
-
mod = PyastBuildPass(
|
|
318
|
-
ir_in=uni.PythonModuleAst(
|
|
319
|
-
py_ast.parse(file_source),
|
|
320
|
-
orig_src=uni.Source(file_source, "builtins"),
|
|
321
|
-
),
|
|
322
|
-
prog=self.prog,
|
|
323
|
-
).ir_out
|
|
324
|
-
SymTabBuildPass(ir_in=mod, prog=self.prog)
|
|
325
|
-
self.prog.mod.hub["builtins"] = mod
|
|
326
|
-
mod.is_raised_from_py = True
|
|
327
|
-
|
|
328
|
-
def annex_impl(self, node: uni.Module) -> None:
|
|
329
|
-
"""Annex impl and test modules."""
|
|
330
|
-
return None
|
|
331
|
-
|
|
332
|
-
def __handle_different_site_packages(self, mod_path: str) -> str:
|
|
333
|
-
if "site-packages" in mod_path:
|
|
334
|
-
mod_path = mod_path[mod_path.index("site-packages") :]
|
|
335
|
-
return mod_path
|
|
336
|
-
|
|
337
|
-
def __check_cyclic_imports(
|
|
338
|
-
self, imp_node: uni.UniNode, imported_module: uni.Module
|
|
339
|
-
) -> bool:
|
|
340
|
-
"""Check cyclic imports that might happen."""
|
|
341
|
-
# Example of cyclic imports is import os
|
|
342
|
-
# In the os stub file it imports the real os module which will cause os
|
|
343
|
-
# stub to be raised again and so on
|
|
344
|
-
# Another example is numpy. numpy init file imports multidim array file
|
|
345
|
-
# which imports again more items from numpy and so on.
|
|
346
|
-
imp_node_file = self.__handle_different_site_packages(imp_node.loc.mod_path)
|
|
347
|
-
imported_module_file = self.__handle_different_site_packages(
|
|
348
|
-
imported_module.loc.mod_path
|
|
349
|
-
)
|
|
350
|
-
if imp_node_file == imported_module_file:
|
|
351
|
-
return True
|
|
352
|
-
|
|
353
|
-
parent: Optional[uni.UniNode] = imp_node.parent
|
|
354
|
-
while parent is not None:
|
|
355
|
-
parent_file = self.__handle_different_site_packages(parent.loc.mod_path)
|
|
356
|
-
if parent_file == imported_module_file:
|
|
357
|
-
return True
|
|
358
|
-
else:
|
|
359
|
-
parent = parent.find_parent_of_type(uni.Module)
|
|
360
|
-
|
|
361
|
-
return False
|
|
@@ -60,7 +60,7 @@ class InheritancePass(Transform[uni.Module, uni.Module]):
|
|
|
60
60
|
if base_class_symbol is None:
|
|
61
61
|
return
|
|
62
62
|
|
|
63
|
-
base_class_symbol_table = base_class_symbol.
|
|
63
|
+
base_class_symbol_table = base_class_symbol.symbol_table
|
|
64
64
|
|
|
65
65
|
if self.is_missing_py_symbol_table(base_class_symbol, base_class_symbol_table):
|
|
66
66
|
return
|
|
@@ -77,7 +77,7 @@ class InheritancePass(Transform[uni.Module, uni.Module]):
|
|
|
77
77
|
sym = self.lookup_symbol(name.sym_name, current_sym_table)
|
|
78
78
|
if sym is None:
|
|
79
79
|
return
|
|
80
|
-
current_sym_table = sym.
|
|
80
|
+
current_sym_table = sym.symbol_table
|
|
81
81
|
# Handle Python base classes or index slice expressions
|
|
82
82
|
if self.is_missing_py_symbol_table(sym, current_sym_table):
|
|
83
83
|
return
|
|
@@ -641,15 +641,13 @@ class PyastGenPass(UniPass):
|
|
|
641
641
|
self.traverse(node.body)
|
|
642
642
|
|
|
643
643
|
def exit_archetype(self, node: uni.Archetype) -> None:
|
|
644
|
-
inner = None
|
|
644
|
+
inner: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None = None
|
|
645
645
|
if isinstance(node.body, uni.ImplDef):
|
|
646
|
-
inner = (
|
|
647
|
-
|
|
648
|
-
)
|
|
649
|
-
elif not isinstance(node.body, uni.FuncCall):
|
|
646
|
+
inner = node.body.body if not isinstance(node.body.body, uni.Expr) else None
|
|
647
|
+
elif not isinstance(node.body, uni.Expr):
|
|
650
648
|
inner = node.body
|
|
651
|
-
body = self.resolve_stmt_block(inner, doc=node.doc)
|
|
652
|
-
if not body and not isinstance(node.body, uni.
|
|
649
|
+
body: list[ast3.AST] = self.resolve_stmt_block(inner, doc=node.doc)
|
|
650
|
+
if not body and not isinstance(node.body, uni.Expr):
|
|
653
651
|
self.log_error(
|
|
654
652
|
"Archetype has no body. Perhaps an impl must be imported.", node
|
|
655
653
|
)
|
|
@@ -699,12 +697,10 @@ class PyastGenPass(UniPass):
|
|
|
699
697
|
|
|
700
698
|
def exit_enum(self, node: uni.Enum) -> None:
|
|
701
699
|
self.needs_enum()
|
|
702
|
-
inner = None
|
|
700
|
+
inner: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None = None
|
|
703
701
|
if isinstance(node.body, uni.ImplDef):
|
|
704
|
-
inner = (
|
|
705
|
-
|
|
706
|
-
)
|
|
707
|
-
elif not isinstance(node.body, uni.FuncCall):
|
|
702
|
+
inner = node.body.body if not isinstance(node.body.body, uni.Expr) else None
|
|
703
|
+
elif not isinstance(node.body, uni.Expr):
|
|
708
704
|
inner = node.body
|
|
709
705
|
body = self.resolve_stmt_block(inner, doc=node.doc)
|
|
710
706
|
decorators = (
|
|
@@ -766,41 +762,9 @@ class PyastGenPass(UniPass):
|
|
|
766
762
|
)
|
|
767
763
|
)
|
|
768
764
|
|
|
769
|
-
def gen_llm_call_override(self, node: uni.FuncCall) -> list[ast3.AST]:
|
|
770
|
-
"""Generate python ast nodes for llm function body override syntax.
|
|
771
|
-
|
|
772
|
-
example:
|
|
773
|
-
foo() by llm();
|
|
774
|
-
"""
|
|
775
|
-
# from jaclang.runtimelib.machine import JacMachineInterface
|
|
776
|
-
# return JacMachineInterface.gen_llm_call_override(self, node)
|
|
777
|
-
if node.body_genai_call is None:
|
|
778
|
-
raise self.ice()
|
|
779
|
-
|
|
780
|
-
model = cast(ast3.expr, node.body_genai_call.gen.py_ast[0])
|
|
781
|
-
caller = cast(ast3.expr, node.target.gen.py_ast[0])
|
|
782
|
-
|
|
783
|
-
# Construct the arguments for the LLM call.
|
|
784
|
-
keys: list[ast3.expr | None] = []
|
|
785
|
-
values: list[ast3.expr] = []
|
|
786
|
-
for idx, call_arg in enumerate(node.params):
|
|
787
|
-
if isinstance(call_arg, uni.Expr):
|
|
788
|
-
keys.append(self.sync(ast3.Constant(value=idx)))
|
|
789
|
-
values.append(cast(ast3.expr, call_arg.gen.py_ast[0]))
|
|
790
|
-
else:
|
|
791
|
-
if call_arg.key:
|
|
792
|
-
keys.append(self.sync(ast3.Constant(value=call_arg.key.sym_name)))
|
|
793
|
-
else:
|
|
794
|
-
keys.append(self.sync(ast3.Constant(value=idx)))
|
|
795
|
-
values.append(cast(ast3.expr, call_arg.value.gen.py_ast[0]))
|
|
796
|
-
|
|
797
|
-
args = self.sync(ast3.Dict(keys=keys, values=values))
|
|
798
|
-
|
|
799
|
-
return [self._invoke_llm_call(model, caller, args)]
|
|
800
|
-
|
|
801
765
|
def gen_llm_body(self, node: uni.Ability) -> list[ast3.stmt]:
|
|
802
766
|
"""Generate the by LLM body."""
|
|
803
|
-
assert isinstance(node.body, uni.
|
|
767
|
+
assert isinstance(node.body, uni.Expr)
|
|
804
768
|
assert isinstance(node.signature, uni.FuncSignature)
|
|
805
769
|
|
|
806
770
|
# Codegen for the caller of the LLM call.
|
|
@@ -865,10 +829,10 @@ class PyastGenPass(UniPass):
|
|
|
865
829
|
func_type = ast3.AsyncFunctionDef if node.is_async else ast3.FunctionDef
|
|
866
830
|
body = (
|
|
867
831
|
self.gen_llm_body(node)
|
|
868
|
-
if isinstance(node.body, uni.
|
|
832
|
+
if isinstance(node.body, uni.Expr)
|
|
869
833
|
or (
|
|
870
834
|
isinstance(node.body, uni.ImplDef)
|
|
871
|
-
and isinstance(node.body.body, uni.
|
|
835
|
+
and isinstance(node.body.body, uni.Expr)
|
|
872
836
|
)
|
|
873
837
|
else (
|
|
874
838
|
[
|
|
@@ -886,10 +850,10 @@ class PyastGenPass(UniPass):
|
|
|
886
850
|
(
|
|
887
851
|
node.body.body
|
|
888
852
|
if isinstance(node.body, uni.ImplDef)
|
|
889
|
-
and not isinstance(node.body.body, uni.
|
|
853
|
+
and not isinstance(node.body.body, uni.Expr)
|
|
890
854
|
else (
|
|
891
855
|
node.body
|
|
892
|
-
if not isinstance(node.body, uni.
|
|
856
|
+
if not isinstance(node.body, uni.Expr)
|
|
893
857
|
else None
|
|
894
858
|
)
|
|
895
859
|
),
|
|
@@ -939,7 +903,7 @@ class PyastGenPass(UniPass):
|
|
|
939
903
|
decorator_list.insert(
|
|
940
904
|
0, self.sync(ast3.Name(id="staticmethod", ctx=ast3.Load()))
|
|
941
905
|
)
|
|
942
|
-
if not body and not isinstance(node.body, uni.
|
|
906
|
+
if not body and not isinstance(node.body, uni.Expr):
|
|
943
907
|
self.log_error(
|
|
944
908
|
"Ability has no body. Perhaps an impl must be imported.", node
|
|
945
909
|
)
|
|
@@ -1074,18 +1038,20 @@ class PyastGenPass(UniPass):
|
|
|
1074
1038
|
]
|
|
1075
1039
|
|
|
1076
1040
|
def exit_param_var(self, node: uni.ParamVar) -> None:
|
|
1077
|
-
node.gen.py_ast
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1041
|
+
if isinstance(node.name.gen.py_ast[0], ast3.Name):
|
|
1042
|
+
name = node.name.gen.py_ast[0].id
|
|
1043
|
+
node.gen.py_ast = [
|
|
1044
|
+
self.sync(
|
|
1045
|
+
ast3.arg(
|
|
1046
|
+
arg=name,
|
|
1047
|
+
annotation=(
|
|
1048
|
+
cast(ast3.expr, node.type_tag.gen.py_ast[0])
|
|
1049
|
+
if node.type_tag
|
|
1050
|
+
else None
|
|
1051
|
+
),
|
|
1052
|
+
)
|
|
1086
1053
|
)
|
|
1087
|
-
|
|
1088
|
-
]
|
|
1054
|
+
]
|
|
1089
1055
|
|
|
1090
1056
|
def exit_arch_has(self, node: uni.ArchHas) -> None:
|
|
1091
1057
|
vars_py: list[ast3.AST] = self.flatten([v.gen.py_ast for v in node.vars])
|
|
@@ -2297,7 +2263,11 @@ class PyastGenPass(UniPass):
|
|
|
2297
2263
|
node.gen.py_ast = [
|
|
2298
2264
|
self.sync(
|
|
2299
2265
|
ast3.keyword(
|
|
2300
|
-
arg=
|
|
2266
|
+
arg=(
|
|
2267
|
+
node.key.gen.py_ast[0].id
|
|
2268
|
+
if node.key and isinstance(node.key.gen.py_ast[0], ast3.Name)
|
|
2269
|
+
else None
|
|
2270
|
+
),
|
|
2301
2271
|
value=cast(ast3.expr, node.value.gen.py_ast[0]),
|
|
2302
2272
|
)
|
|
2303
2273
|
)
|
|
@@ -2492,11 +2462,8 @@ class PyastGenPass(UniPass):
|
|
|
2492
2462
|
return args, keywords
|
|
2493
2463
|
|
|
2494
2464
|
def exit_func_call(self, node: uni.FuncCall) -> None:
|
|
2495
|
-
if node.body_genai_call:
|
|
2496
|
-
node.gen.py_ast = self.gen_llm_call_override(node)
|
|
2497
|
-
|
|
2498
2465
|
# TODO: This needs to be changed to only generate parameters no the body.
|
|
2499
|
-
|
|
2466
|
+
if node.genai_call:
|
|
2500
2467
|
self.ice("Type(by llm()) call feature is temporarily disabled.")
|
|
2501
2468
|
|
|
2502
2469
|
else:
|
|
@@ -2981,9 +2948,8 @@ class PyastGenPass(UniPass):
|
|
|
2981
2948
|
node.gen.py_ast = [self.sync(op_cls())]
|
|
2982
2949
|
|
|
2983
2950
|
def exit_name(self, node: uni.Name) -> None:
|
|
2984
|
-
node.
|
|
2985
|
-
|
|
2986
|
-
]
|
|
2951
|
+
name = node.sym_name[2:] if node.sym_name.startswith("<>") else node.sym_name
|
|
2952
|
+
node.gen.py_ast = [self.sync(ast3.Name(id=name, ctx=node.py_ctx_func()))]
|
|
2987
2953
|
|
|
2988
2954
|
def exit_float(self, node: uni.Float) -> None:
|
|
2989
2955
|
node.gen.py_ast = [self.sync(ast3.Constant(value=float(node.value)))]
|
|
@@ -1374,10 +1374,11 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
|
|
|
1374
1374
|
valid = [
|
|
1375
1375
|
value for value in values if isinstance(value, (uni.String, uni.ExprStmt))
|
|
1376
1376
|
]
|
|
1377
|
-
|
|
1377
|
+
fstr = uni.FString(
|
|
1378
1378
|
parts=valid,
|
|
1379
1379
|
kid=[*valid] if valid else [uni.EmptyToken()],
|
|
1380
1380
|
)
|
|
1381
|
+
return uni.MultiString(strings=[fstr], kid=[fstr])
|
|
1381
1382
|
|
|
1382
1383
|
def proc_lambda(self, node: py_ast.Lambda) -> uni.LambdaExpr:
|
|
1383
1384
|
"""Process python node.
|
|
@@ -2337,20 +2338,30 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
|
|
|
2337
2338
|
arg: _Identifier | None
|
|
2338
2339
|
value: expr
|
|
2339
2340
|
"""
|
|
2340
|
-
arg =
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2341
|
+
arg = None
|
|
2342
|
+
if node.arg:
|
|
2343
|
+
from jaclang.compiler import TOKEN_MAP
|
|
2344
|
+
|
|
2345
|
+
reserved_keywords = [v for _, v in TOKEN_MAP.items()]
|
|
2346
|
+
arg_value = (
|
|
2347
|
+
node.arg if node.arg not in reserved_keywords else f"<>{node.arg}"
|
|
2348
|
+
)
|
|
2349
|
+
arg = uni.Name(
|
|
2350
|
+
orig_src=self.orig_src,
|
|
2351
|
+
name=Tok.NAME,
|
|
2352
|
+
value=arg_value,
|
|
2353
|
+
line=node.lineno,
|
|
2354
|
+
end_line=node.end_lineno if node.end_lineno else node.lineno,
|
|
2355
|
+
col_start=node.col_offset,
|
|
2356
|
+
col_end=node.col_offset + len(node.arg if node.arg else "_"),
|
|
2357
|
+
pos_start=0,
|
|
2358
|
+
pos_end=0,
|
|
2359
|
+
)
|
|
2351
2360
|
value = self.convert(node.value)
|
|
2352
2361
|
if isinstance(value, uni.Expr):
|
|
2353
|
-
return uni.KWPair(
|
|
2362
|
+
return uni.KWPair(
|
|
2363
|
+
key=arg, value=value, kid=[arg, value] if arg else [value]
|
|
2364
|
+
)
|
|
2354
2365
|
else:
|
|
2355
2366
|
raise self.ice()
|
|
2356
2367
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import M1;
|
|
2
|
+
# import M2 as m2alias;
|
|
3
|
+
# import M3, M4;
|
|
4
|
+
# import M5 as m5alias, M6 as m6alias;
|
|
5
|
+
# import from M7 {M7S1}
|
|
6
|
+
# import from M8 {M8S1 as m8s1alias, M8S2 as m8s2alias}
|
|
7
|
+
# include M9;
|
|
8
|
+
# glob a = 5, k = 9;
|
|
9
|
+
glob aa = 9;
|
|
10
|
+
with entry{
|
|
11
|
+
Y: int;
|
|
12
|
+
Y = 0;
|
|
13
|
+
# b.Person.ss = 9;
|
|
14
|
+
n = 0;
|
|
15
|
+
z = Y;
|
|
16
|
+
aa = 9;
|
|
17
|
+
}
|
|
18
|
+
with entry {
|
|
19
|
+
Y = 99;
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
def ccc() {
|
|
23
|
+
# aa = 99; #Error:name 'aa' is assigned to before global declaration
|
|
24
|
+
if (0) {
|
|
25
|
+
global aa;
|
|
26
|
+
global bb;
|
|
27
|
+
aa = 88;
|
|
28
|
+
bb = 0;
|
|
29
|
+
p = 90;
|
|
30
|
+
M1.k = 0;
|
|
31
|
+
}
|
|
32
|
+
aa = 99;
|
|
33
|
+
print(aa);
|
|
34
|
+
}
|
|
35
|
+
with entry {
|
|
36
|
+
ccc();
|
|
37
|
+
print(aa);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# import random;
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# with entry{
|
|
46
|
+
# d = random.randint(1, 100);
|
|
47
|
+
# }
|