jaclang 0.8.4__py3-none-any.whl → 0.8.6__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.md +1 -0
- jaclang/cli/cli.py +109 -37
- 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 +5 -1
- jaclang/compiler/passes/main/binder_pass.py +594 -0
- jaclang/compiler/passes/main/cfg_build_pass.py +21 -1
- jaclang/compiler/passes/main/import_pass.py +8 -256
- jaclang/compiler/passes/main/inheritance_pass.py +10 -3
- jaclang/compiler/passes/main/pyast_gen_pass.py +92 -77
- 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/sym_tab_build_pass.py +4 -0
- jaclang/compiler/passes/main/tests/fixtures/M1.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_has_var.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_if_no_else.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_return.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_imported.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_importer.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/import_symbol_type_infer.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/infer_type_assignment.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +47 -0
- jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac +8 -0
- jaclang/compiler/passes/main/tests/test_binder_pass.py +111 -0
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +62 -24
- jaclang/compiler/passes/main/tests/test_checker_pass.py +87 -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/main/type_checker_pass.py +128 -0
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +2 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +3 -0
- jaclang/compiler/program.py +32 -11
- jaclang/compiler/tests/test_sr_errors.py +32 -0
- jaclang/compiler/type_system/__init__.py +1 -0
- jaclang/compiler/type_system/type_evaluator.py +421 -0
- jaclang/compiler/type_system/type_utils.py +41 -0
- jaclang/compiler/type_system/types.py +240 -0
- jaclang/compiler/unitree.py +36 -24
- jaclang/langserve/dev_engine.jac +645 -0
- jaclang/langserve/dev_server.jac +201 -0
- jaclang/langserve/engine.jac +24 -5
- jaclang/langserve/tests/server_test/test_lang_serve.py +2 -2
- jaclang/langserve/tests/test_dev_server.py +80 -0
- jaclang/langserve/tests/test_server.py +13 -0
- jaclang/runtimelib/builtin.py +28 -39
- jaclang/runtimelib/importer.py +34 -63
- jaclang/runtimelib/machine.py +48 -64
- jaclang/runtimelib/memory.py +23 -5
- jaclang/runtimelib/tests/fixtures/savable_object.jac +10 -2
- jaclang/runtimelib/utils.py +42 -6
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/py_run.jac +8 -0
- jaclang/tests/fixtures/py_run.py +23 -0
- jaclang/tests/fixtures/pyfunc.py +2 -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 +107 -0
- jaclang/tests/test_language.py +106 -5
- jaclang/utils/lang_tools.py +6 -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.6.dist-info}/METADATA +2 -1
- {jaclang-0.8.4.dist-info → jaclang-0.8.6.dist-info}/RECORD +88 -43
- {jaclang-0.8.4.dist-info → jaclang-0.8.6.dist-info}/WHEEL +0 -0
- {jaclang-0.8.4.dist-info → jaclang-0.8.6.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,11 +60,18 @@ 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
|
+
|
|
65
|
+
# FIXME: If the base class symbol is imported from another module, the symbol table
|
|
66
|
+
# will be None. The imported symbols were ignored and introduced in the typecheck
|
|
67
|
+
# for imported module items. This needs to be investigated to ensure that even imported
|
|
68
|
+
# classes should have a symbol table (unless the module is not parsed and decided not to).
|
|
69
|
+
if base_class_symbol_table is None:
|
|
70
|
+
return
|
|
64
71
|
|
|
65
72
|
if self.is_missing_py_symbol_table(base_class_symbol, base_class_symbol_table):
|
|
66
73
|
return
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
node.sym_tab.inherit_sym_tab(base_class_symbol_table)
|
|
69
76
|
|
|
70
77
|
def inherit_from_atom_trailer(
|
|
@@ -77,7 +84,7 @@ class InheritancePass(Transform[uni.Module, uni.Module]):
|
|
|
77
84
|
sym = self.lookup_symbol(name.sym_name, current_sym_table)
|
|
78
85
|
if sym is None:
|
|
79
86
|
return
|
|
80
|
-
current_sym_table = sym.
|
|
87
|
+
current_sym_table = sym.symbol_table
|
|
81
88
|
# Handle Python base classes or index slice expressions
|
|
82
89
|
if self.is_missing_py_symbol_table(sym, current_sym_table):
|
|
83
90
|
return
|
|
@@ -187,6 +187,15 @@ class PyastGenPass(UniPass):
|
|
|
187
187
|
),
|
|
188
188
|
)
|
|
189
189
|
|
|
190
|
+
def needs_mtllm(self) -> None:
|
|
191
|
+
"""Ensure byLLM is imported only once."""
|
|
192
|
+
self._add_preamble_once(
|
|
193
|
+
self.needs_mtllm.__name__,
|
|
194
|
+
ast3.Import(
|
|
195
|
+
names=[self.sync(ast3.alias(name="byllm"), jac_node=self.ir_out)]
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
190
199
|
def needs_enum(self) -> None:
|
|
191
200
|
"""Ensure Enum utilities are imported only once."""
|
|
192
201
|
self._add_preamble_once(
|
|
@@ -641,15 +650,13 @@ class PyastGenPass(UniPass):
|
|
|
641
650
|
self.traverse(node.body)
|
|
642
651
|
|
|
643
652
|
def exit_archetype(self, node: uni.Archetype) -> None:
|
|
644
|
-
inner = None
|
|
653
|
+
inner: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None = None
|
|
645
654
|
if isinstance(node.body, uni.ImplDef):
|
|
646
|
-
inner = (
|
|
647
|
-
|
|
648
|
-
)
|
|
649
|
-
elif not isinstance(node.body, uni.FuncCall):
|
|
655
|
+
inner = node.body.body if not isinstance(node.body.body, uni.Expr) else None
|
|
656
|
+
elif not isinstance(node.body, uni.Expr):
|
|
650
657
|
inner = node.body
|
|
651
|
-
body = self.resolve_stmt_block(inner, doc=node.doc)
|
|
652
|
-
if not body and not isinstance(node.body, uni.
|
|
658
|
+
body: list[ast3.AST] = self.resolve_stmt_block(inner, doc=node.doc)
|
|
659
|
+
if not body and not isinstance(node.body, uni.Expr):
|
|
653
660
|
self.log_error(
|
|
654
661
|
"Archetype has no body. Perhaps an impl must be imported.", node
|
|
655
662
|
)
|
|
@@ -699,12 +706,10 @@ class PyastGenPass(UniPass):
|
|
|
699
706
|
|
|
700
707
|
def exit_enum(self, node: uni.Enum) -> None:
|
|
701
708
|
self.needs_enum()
|
|
702
|
-
inner = None
|
|
709
|
+
inner: Sequence[uni.CodeBlockStmt] | Sequence[uni.EnumBlockStmt] | None = None
|
|
703
710
|
if isinstance(node.body, uni.ImplDef):
|
|
704
|
-
inner = (
|
|
705
|
-
|
|
706
|
-
)
|
|
707
|
-
elif not isinstance(node.body, uni.FuncCall):
|
|
711
|
+
inner = node.body.body if not isinstance(node.body.body, uni.Expr) else None
|
|
712
|
+
elif not isinstance(node.body, uni.Expr):
|
|
708
713
|
inner = node.body
|
|
709
714
|
body = self.resolve_stmt_block(inner, doc=node.doc)
|
|
710
715
|
decorators = (
|
|
@@ -739,17 +744,25 @@ class PyastGenPass(UniPass):
|
|
|
739
744
|
self, model: ast3.expr, caller: ast3.expr, args: ast3.Dict
|
|
740
745
|
) -> ast3.Call:
|
|
741
746
|
"""Reusable method to codegen call_llm(model, caller, args)."""
|
|
742
|
-
|
|
747
|
+
self.needs_mtllm()
|
|
748
|
+
mtir_cls_ast = self.sync(
|
|
749
|
+
ast3.Attribute(
|
|
750
|
+
value=self.sync(ast3.Name(id="byllm", ctx=ast3.Load())),
|
|
751
|
+
attr="MTIR",
|
|
752
|
+
ctx=ast3.Load(),
|
|
753
|
+
)
|
|
754
|
+
)
|
|
755
|
+
mtir_ast = self.sync(
|
|
743
756
|
ast3.Call(
|
|
744
|
-
func=self.
|
|
757
|
+
func=self.sync(
|
|
758
|
+
ast3.Attribute(
|
|
759
|
+
value=mtir_cls_ast,
|
|
760
|
+
attr="factory",
|
|
761
|
+
ctx=ast3.Load(),
|
|
762
|
+
)
|
|
763
|
+
),
|
|
745
764
|
args=[],
|
|
746
765
|
keywords=[
|
|
747
|
-
self.sync(
|
|
748
|
-
ast3.keyword(
|
|
749
|
-
arg="model",
|
|
750
|
-
value=model,
|
|
751
|
-
)
|
|
752
|
-
),
|
|
753
766
|
self.sync(
|
|
754
767
|
ast3.keyword(
|
|
755
768
|
arg="caller",
|
|
@@ -762,45 +775,45 @@ class PyastGenPass(UniPass):
|
|
|
762
775
|
value=args,
|
|
763
776
|
)
|
|
764
777
|
),
|
|
778
|
+
self.sync(
|
|
779
|
+
ast3.keyword(
|
|
780
|
+
arg="call_params",
|
|
781
|
+
value=self.sync(
|
|
782
|
+
ast3.Attribute(
|
|
783
|
+
value=model,
|
|
784
|
+
attr="call_params",
|
|
785
|
+
ctx=ast3.Load(),
|
|
786
|
+
),
|
|
787
|
+
),
|
|
788
|
+
)
|
|
789
|
+
),
|
|
790
|
+
],
|
|
791
|
+
)
|
|
792
|
+
)
|
|
793
|
+
return self.sync(
|
|
794
|
+
ast3.Call(
|
|
795
|
+
func=self.jaclib_obj("call_llm"),
|
|
796
|
+
args=[],
|
|
797
|
+
keywords=[
|
|
798
|
+
self.sync(
|
|
799
|
+
ast3.keyword(
|
|
800
|
+
arg="model",
|
|
801
|
+
value=model,
|
|
802
|
+
)
|
|
803
|
+
),
|
|
804
|
+
self.sync(
|
|
805
|
+
ast3.keyword(
|
|
806
|
+
arg="mtir",
|
|
807
|
+
value=mtir_ast,
|
|
808
|
+
)
|
|
809
|
+
),
|
|
765
810
|
],
|
|
766
811
|
)
|
|
767
812
|
)
|
|
768
|
-
|
|
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
813
|
|
|
801
814
|
def gen_llm_body(self, node: uni.Ability) -> list[ast3.stmt]:
|
|
802
815
|
"""Generate the by LLM body."""
|
|
803
|
-
assert isinstance(node.body, uni.
|
|
816
|
+
assert isinstance(node.body, uni.Expr)
|
|
804
817
|
assert isinstance(node.signature, uni.FuncSignature)
|
|
805
818
|
|
|
806
819
|
# Codegen for the caller of the LLM call.
|
|
@@ -865,10 +878,10 @@ class PyastGenPass(UniPass):
|
|
|
865
878
|
func_type = ast3.AsyncFunctionDef if node.is_async else ast3.FunctionDef
|
|
866
879
|
body = (
|
|
867
880
|
self.gen_llm_body(node)
|
|
868
|
-
if isinstance(node.body, uni.
|
|
881
|
+
if isinstance(node.body, uni.Expr)
|
|
869
882
|
or (
|
|
870
883
|
isinstance(node.body, uni.ImplDef)
|
|
871
|
-
and isinstance(node.body.body, uni.
|
|
884
|
+
and isinstance(node.body.body, uni.Expr)
|
|
872
885
|
)
|
|
873
886
|
else (
|
|
874
887
|
[
|
|
@@ -886,10 +899,10 @@ class PyastGenPass(UniPass):
|
|
|
886
899
|
(
|
|
887
900
|
node.body.body
|
|
888
901
|
if isinstance(node.body, uni.ImplDef)
|
|
889
|
-
and not isinstance(node.body.body, uni.
|
|
902
|
+
and not isinstance(node.body.body, uni.Expr)
|
|
890
903
|
else (
|
|
891
904
|
node.body
|
|
892
|
-
if not isinstance(node.body, uni.
|
|
905
|
+
if not isinstance(node.body, uni.Expr)
|
|
893
906
|
else None
|
|
894
907
|
)
|
|
895
908
|
),
|
|
@@ -939,7 +952,7 @@ class PyastGenPass(UniPass):
|
|
|
939
952
|
decorator_list.insert(
|
|
940
953
|
0, self.sync(ast3.Name(id="staticmethod", ctx=ast3.Load()))
|
|
941
954
|
)
|
|
942
|
-
if not body and not isinstance(node.body, uni.
|
|
955
|
+
if not body and not isinstance(node.body, uni.Expr):
|
|
943
956
|
self.log_error(
|
|
944
957
|
"Ability has no body. Perhaps an impl must be imported.", node
|
|
945
958
|
)
|
|
@@ -1074,18 +1087,20 @@ class PyastGenPass(UniPass):
|
|
|
1074
1087
|
]
|
|
1075
1088
|
|
|
1076
1089
|
def exit_param_var(self, node: uni.ParamVar) -> None:
|
|
1077
|
-
node.gen.py_ast
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1090
|
+
if isinstance(node.name.gen.py_ast[0], ast3.Name):
|
|
1091
|
+
name = node.name.gen.py_ast[0].id
|
|
1092
|
+
node.gen.py_ast = [
|
|
1093
|
+
self.sync(
|
|
1094
|
+
ast3.arg(
|
|
1095
|
+
arg=name,
|
|
1096
|
+
annotation=(
|
|
1097
|
+
cast(ast3.expr, node.type_tag.gen.py_ast[0])
|
|
1098
|
+
if node.type_tag
|
|
1099
|
+
else None
|
|
1100
|
+
),
|
|
1101
|
+
)
|
|
1086
1102
|
)
|
|
1087
|
-
|
|
1088
|
-
]
|
|
1103
|
+
]
|
|
1089
1104
|
|
|
1090
1105
|
def exit_arch_has(self, node: uni.ArchHas) -> None:
|
|
1091
1106
|
vars_py: list[ast3.AST] = self.flatten([v.gen.py_ast for v in node.vars])
|
|
@@ -2297,7 +2312,11 @@ class PyastGenPass(UniPass):
|
|
|
2297
2312
|
node.gen.py_ast = [
|
|
2298
2313
|
self.sync(
|
|
2299
2314
|
ast3.keyword(
|
|
2300
|
-
arg=
|
|
2315
|
+
arg=(
|
|
2316
|
+
node.key.gen.py_ast[0].id
|
|
2317
|
+
if node.key and isinstance(node.key.gen.py_ast[0], ast3.Name)
|
|
2318
|
+
else None
|
|
2319
|
+
),
|
|
2301
2320
|
value=cast(ast3.expr, node.value.gen.py_ast[0]),
|
|
2302
2321
|
)
|
|
2303
2322
|
)
|
|
@@ -2492,11 +2511,8 @@ class PyastGenPass(UniPass):
|
|
|
2492
2511
|
return args, keywords
|
|
2493
2512
|
|
|
2494
2513
|
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
2514
|
# TODO: This needs to be changed to only generate parameters no the body.
|
|
2499
|
-
|
|
2515
|
+
if node.genai_call:
|
|
2500
2516
|
self.ice("Type(by llm()) call feature is temporarily disabled.")
|
|
2501
2517
|
|
|
2502
2518
|
else:
|
|
@@ -2981,9 +2997,8 @@ class PyastGenPass(UniPass):
|
|
|
2981
2997
|
node.gen.py_ast = [self.sync(op_cls())]
|
|
2982
2998
|
|
|
2983
2999
|
def exit_name(self, node: uni.Name) -> None:
|
|
2984
|
-
node.
|
|
2985
|
-
|
|
2986
|
-
]
|
|
3000
|
+
name = node.sym_name[2:] if node.sym_name.startswith("<>") else node.sym_name
|
|
3001
|
+
node.gen.py_ast = [self.sync(ast3.Name(id=name, ctx=node.py_ctx_func()))]
|
|
2987
3002
|
|
|
2988
3003
|
def exit_float(self, node: uni.Float) -> None:
|
|
2989
3004
|
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
|
|
|
@@ -84,6 +84,10 @@ class SymTabBuildPass(UniPass):
|
|
|
84
84
|
else:
|
|
85
85
|
pass # Need to support pythonic import symbols with dots in it
|
|
86
86
|
|
|
87
|
+
def exit_module_item(self, node: uni.ModuleItem) -> None:
|
|
88
|
+
sym_node = node.alias or node.name
|
|
89
|
+
sym_node.sym_tab.def_insert(sym_node, single_decl="import")
|
|
90
|
+
|
|
87
91
|
def enter_archetype(self, node: uni.Archetype) -> None:
|
|
88
92
|
self.push_scope_and_link(node)
|
|
89
93
|
assert node.parent_scope is not None
|