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.

Files changed (53) hide show
  1. jaclang/cli/cli.py +74 -22
  2. jaclang/compiler/jac.lark +3 -3
  3. jaclang/compiler/larkparse/jac_parser.py +2 -2
  4. jaclang/compiler/parser.py +14 -21
  5. jaclang/compiler/passes/main/__init__.py +3 -1
  6. jaclang/compiler/passes/main/binder_pass.py +594 -0
  7. jaclang/compiler/passes/main/import_pass.py +8 -256
  8. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  9. jaclang/compiler/passes/main/pyast_gen_pass.py +35 -69
  10. jaclang/compiler/passes/main/pyast_load_pass.py +24 -13
  11. jaclang/compiler/passes/main/sem_def_match_pass.py +1 -1
  12. jaclang/compiler/passes/main/tests/fixtures/M1.jac +3 -0
  13. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +47 -0
  14. jaclang/compiler/passes/main/tests/test_binder_pass.py +111 -0
  15. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +13 -13
  16. jaclang/compiler/passes/main/tests/test_sem_def_match_pass.py +6 -6
  17. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +2 -0
  18. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +6 -0
  19. jaclang/compiler/program.py +15 -8
  20. jaclang/compiler/tests/test_sr_errors.py +32 -0
  21. jaclang/compiler/unitree.py +21 -15
  22. jaclang/langserve/engine.jac +23 -4
  23. jaclang/langserve/tests/test_server.py +13 -0
  24. jaclang/runtimelib/importer.py +33 -62
  25. jaclang/runtimelib/utils.py +29 -0
  26. jaclang/tests/fixtures/pyfunc_fmt.py +60 -0
  27. jaclang/tests/fixtures/pyfunc_fstr.py +25 -0
  28. jaclang/tests/fixtures/pyfunc_kwesc.py +33 -0
  29. jaclang/tests/fixtures/python_run_test.py +19 -0
  30. jaclang/tests/test_cli.py +67 -0
  31. jaclang/tests/test_language.py +96 -1
  32. jaclang/utils/lang_tools.py +3 -3
  33. jaclang/utils/module_resolver.py +90 -0
  34. jaclang/utils/symtable_test_helpers.py +125 -0
  35. jaclang/utils/test.py +3 -4
  36. jaclang/vendor/interegular/__init__.py +34 -0
  37. jaclang/vendor/interegular/comparator.py +163 -0
  38. jaclang/vendor/interegular/fsm.py +1015 -0
  39. jaclang/vendor/interegular/patterns.py +732 -0
  40. jaclang/vendor/interegular/py.typed +0 -0
  41. jaclang/vendor/interegular/utils/__init__.py +15 -0
  42. jaclang/vendor/interegular/utils/simple_parser.py +165 -0
  43. jaclang/vendor/interegular-0.3.3.dist-info/INSTALLER +1 -0
  44. jaclang/vendor/interegular-0.3.3.dist-info/LICENSE.txt +21 -0
  45. jaclang/vendor/interegular-0.3.3.dist-info/METADATA +64 -0
  46. jaclang/vendor/interegular-0.3.3.dist-info/RECORD +20 -0
  47. jaclang/vendor/interegular-0.3.3.dist-info/REQUESTED +0 -0
  48. jaclang/vendor/interegular-0.3.3.dist-info/WHEEL +5 -0
  49. jaclang/vendor/interegular-0.3.3.dist-info/top_level.txt +1 -0
  50. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/METADATA +1 -1
  51. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/RECORD +53 -29
  52. {jaclang-0.8.4.dist-info → jaclang-0.8.5.dist-info}/WHEEL +0 -0
  53. {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.compiler.passes.main import SymTabBuildPass
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
- with open(py_init_path, "r") as f:
106
- file_source = f.read()
107
- mod = uni.Module.make_stub(
108
- inject_name=target.split(os.path.sep)[-1],
109
- inject_src=uni.Source(file_source, py_init_path),
110
- )
111
- self.prog.mod.hub[py_init_path] = mod
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.fetch_sym_tab
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.fetch_sym_tab
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
- node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
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.FuncCall):
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
- node.body.body if not isinstance(node.body.body, uni.FuncCall) else None
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.FuncCall)
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.FuncCall)
832
+ if isinstance(node.body, uni.Expr)
869
833
  or (
870
834
  isinstance(node.body, uni.ImplDef)
871
- and isinstance(node.body.body, uni.FuncCall)
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.FuncCall)
853
+ and not isinstance(node.body.body, uni.Expr)
890
854
  else (
891
855
  node.body
892
- if not isinstance(node.body, uni.FuncCall)
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.FuncCall):
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
- self.sync(
1079
- ast3.arg(
1080
- arg=node.name.sym_name,
1081
- annotation=(
1082
- cast(ast3.expr, node.type_tag.gen.py_ast[0])
1083
- if node.type_tag
1084
- else None
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=node.key.sym_name if node.key else None,
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
- elif node.genai_call:
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.gen.py_ast = [
2985
- self.sync(ast3.Name(id=node.sym_name, ctx=node.py_ctx_func()))
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
- return uni.FString(
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 = uni.Name(
2341
- orig_src=self.orig_src,
2342
- name=Tok.NAME,
2343
- value=node.arg if node.arg else "_",
2344
- line=node.lineno,
2345
- end_line=node.end_lineno if node.end_lineno else node.lineno,
2346
- col_start=node.col_offset,
2347
- col_end=node.col_offset + len(node.arg if node.arg else "_"),
2348
- pos_start=0,
2349
- pos_end=0,
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(key=arg, value=value, kid=[arg, value])
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
 
@@ -47,7 +47,7 @@ class SemDefMatchPass(Transform[uni.Module, uni.Module]):
47
47
  sym = current_sym_tab.lookup(part, deep=False)
48
48
  if sym is None:
49
49
  return None
50
- current_sym_tab = sym.fetch_sym_tab
50
+ current_sym_tab = sym.symbol_table
51
51
  return sym
52
52
 
53
53
  def connect_sems(
@@ -0,0 +1,3 @@
1
+
2
+
3
+ glob k = 19;
@@ -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
+ # }