jaclang 0.7.14__py3-none-any.whl → 0.7.16__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 (92) hide show
  1. jaclang/cli/cli.py +15 -10
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +19 -53
  4. jaclang/compiler/absyntree.py +86 -13
  5. jaclang/compiler/jac.lark +4 -3
  6. jaclang/compiler/parser.py +31 -23
  7. jaclang/compiler/passes/ir_pass.py +4 -13
  8. jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
  9. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
  10. jaclang/compiler/passes/main/import_pass.py +18 -23
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +307 -559
  12. jaclang/compiler/passes/main/pyast_load_pass.py +32 -6
  13. jaclang/compiler/passes/main/registry_pass.py +37 -3
  14. jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
  15. jaclang/compiler/passes/main/tests/__init__.py +1 -1
  16. jaclang/compiler/passes/main/type_check_pass.py +7 -0
  17. jaclang/compiler/passes/tool/jac_formatter_pass.py +124 -86
  18. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
  19. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  20. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  21. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  23. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  26. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  27. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  28. jaclang/compiler/passes/transform.py +4 -0
  29. jaclang/compiler/semtable.py +31 -7
  30. jaclang/compiler/tests/test_importer.py +12 -5
  31. jaclang/langserve/engine.py +65 -118
  32. jaclang/langserve/sem_manager.py +379 -0
  33. jaclang/langserve/server.py +8 -10
  34. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  35. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  36. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  37. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  38. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  39. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  40. jaclang/langserve/tests/test_server.py +72 -16
  41. jaclang/langserve/utils.py +163 -96
  42. jaclang/plugin/builtin.py +1 -1
  43. jaclang/plugin/default.py +212 -24
  44. jaclang/plugin/feature.py +59 -11
  45. jaclang/plugin/spec.py +58 -6
  46. jaclang/{core → runtimelib}/architype.py +1 -1
  47. jaclang/{core → runtimelib}/context.py +8 -1
  48. jaclang/runtimelib/importer.py +361 -0
  49. jaclang/runtimelib/machine.py +94 -0
  50. jaclang/{core → runtimelib}/utils.py +13 -5
  51. jaclang/settings.py +4 -1
  52. jaclang/tests/fixtures/abc.jac +3 -3
  53. jaclang/tests/fixtures/byllmissue.jac +1 -5
  54. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  55. jaclang/tests/fixtures/cls_method.jac +41 -0
  56. jaclang/tests/fixtures/dblhello.jac +6 -0
  57. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  58. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  59. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  60. jaclang/tests/fixtures/err.impl.jac +3 -0
  61. jaclang/tests/fixtures/err.jac +4 -2
  62. jaclang/tests/fixtures/err.test.jac +3 -0
  63. jaclang/tests/fixtures/err_runtime.jac +15 -0
  64. jaclang/tests/fixtures/game1.jac +1 -1
  65. jaclang/tests/fixtures/hello.jac +4 -0
  66. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  67. jaclang/tests/fixtures/impl_grab.jac +4 -1
  68. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  69. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  70. jaclang/tests/fixtures/needs_import.jac +2 -2
  71. jaclang/tests/fixtures/pyfunc_2.py +3 -0
  72. jaclang/tests/fixtures/registry.jac +9 -0
  73. jaclang/tests/fixtures/run_test.jac +4 -4
  74. jaclang/tests/fixtures/semstr.jac +1 -4
  75. jaclang/tests/fixtures/simple_archs.jac +1 -1
  76. jaclang/tests/test_cli.py +65 -2
  77. jaclang/tests/test_language.py +74 -7
  78. jaclang/tests/test_reference.py +6 -0
  79. jaclang/utils/helpers.py +45 -21
  80. jaclang/utils/test.py +9 -0
  81. jaclang/utils/treeprinter.py +0 -4
  82. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
  83. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/RECORD +89 -74
  84. jaclang/core/importer.py +0 -344
  85. jaclang/tests/fixtures/aott_raise.jac +0 -25
  86. jaclang/tests/fixtures/package_import.jac +0 -6
  87. /jaclang/{core → runtimelib}/__init__.py +0 -0
  88. /jaclang/{core → runtimelib}/constructs.py +0 -0
  89. /jaclang/{core → runtimelib}/memory.py +0 -0
  90. /jaclang/{core → runtimelib}/test.py +0 -0
  91. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
  92. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/entry_points.txt +0 -0
@@ -6,12 +6,12 @@ in each node. Module nodes contain the entire module code.
6
6
 
7
7
  import ast as ast3
8
8
  import textwrap
9
+ from dataclasses import dataclass
9
10
  from typing import Optional, Sequence, TypeVar
10
11
 
11
12
  import jaclang.compiler.absyntree as ast
12
13
  from jaclang.compiler.constant import Constants as Con, EdgeDir, Tokens as Tok
13
14
  from jaclang.compiler.passes import Pass
14
- from jaclang.core.utils import extract_params, extract_type, get_sem_scope
15
15
 
16
16
  T = TypeVar("T", bound=ast3.AST)
17
17
 
@@ -19,24 +19,25 @@ T = TypeVar("T", bound=ast3.AST)
19
19
  class PyastGenPass(Pass):
20
20
  """Jac blue transpilation to python pass."""
21
21
 
22
- @staticmethod
23
- def node_compilable_test(node: ast3.AST) -> None:
24
- """Convert any AST node to a compilable module node."""
25
- if isinstance(node, ast3.Module):
26
- pass
27
- elif isinstance(node, (ast3.Expr, ast3.stmt)):
28
- node = ast3.Module(body=[node], type_ignores=[])
29
- elif isinstance(node, list) and all(isinstance(n, ast3.stmt) for n in node):
30
- node = ast3.Module(body=node, type_ignores=[])
31
- else:
32
- node = ast3.Module(body=[], type_ignores=[])
33
- try:
34
- compile(node, "<ast>", "exec")
35
- except TypeError as e:
36
- print(ast3.dump(node, indent=2))
37
- raise e
38
- except Exception:
39
- pass
22
+ # TODO: This should live in utils and perhaps a test added using it
23
+ # @staticmethod
24
+ # def node_compilable_test(node: ast3.AST) -> None:
25
+ # """Convert any AST node to a compilable module node."""
26
+ # if isinstance(node, ast3.Module):
27
+ # pass
28
+ # elif isinstance(node, (ast3.Expr, ast3.stmt)):
29
+ # node = ast3.Module(body=[node], type_ignores=[])
30
+ # elif isinstance(node, list) and all(isinstance(n, ast3.stmt) for n in node):
31
+ # node = ast3.Module(body=node, type_ignores=[])
32
+ # else:
33
+ # node = ast3.Module(body=[], type_ignores=[])
34
+ # try:
35
+ # compile(node, "<ast>", "exec")
36
+ # except TypeError as e:
37
+ # print(ast3.dump(node, indent=2))
38
+ # raise e
39
+ # except Exception:
40
+ # pass
40
41
 
41
42
  def before_pass(self) -> None:
42
43
  """Initialize pass."""
@@ -51,14 +52,6 @@ class PyastGenPass(Pass):
51
52
  ),
52
53
  jac_node=self.ir,
53
54
  ),
54
- self.sync(
55
- ast3.ImportFrom(
56
- module="typing",
57
- names=[self.sync(ast3.alias(name="TYPE_CHECKING", asname=None))],
58
- level=0,
59
- ),
60
- jac_node=self.ir,
61
- ),
62
55
  ]
63
56
 
64
57
  def enter_node(self, node: ast.AstNode) -> None:
@@ -80,7 +73,7 @@ class PyastGenPass(Pass):
80
73
 
81
74
  def needs_jac_import(self) -> None:
82
75
  """Check if import is needed."""
83
- if "jimport" in self.already_added:
76
+ if self.needs_jac_import.__name__ in self.already_added:
84
77
  return
85
78
  self.preamble.append(
86
79
  self.sync(
@@ -96,11 +89,11 @@ class PyastGenPass(Pass):
96
89
  jac_node=self.ir,
97
90
  )
98
91
  )
99
- self.already_added.append("jimport")
92
+ self.already_added.append(self.needs_jac_import.__name__)
100
93
 
101
94
  def needs_typing(self) -> None:
102
95
  """Check if enum is needed."""
103
- if "typing" in self.already_added:
96
+ if self.needs_typing.__name__ in self.already_added:
104
97
  return
105
98
  self.preamble.append(
106
99
  self.sync(
@@ -115,11 +108,30 @@ class PyastGenPass(Pass):
115
108
  jac_node=self.ir,
116
109
  )
117
110
  )
118
- self.already_added.append("typing")
111
+ self.already_added.append(self.needs_typing.__name__)
112
+
113
+ def needs_abc(self) -> None:
114
+ """Check if enum is needed."""
115
+ if self.needs_abc.__name__ in self.already_added:
116
+ return
117
+ self.preamble.append(
118
+ self.sync(
119
+ ast3.Import(
120
+ names=[
121
+ self.sync(
122
+ ast3.alias(name="abc", asname="_jac_abc"),
123
+ jac_node=self.ir,
124
+ ),
125
+ ]
126
+ ),
127
+ jac_node=self.ir,
128
+ )
129
+ )
130
+ self.already_added.append(self.needs_abc.__name__)
119
131
 
120
132
  def needs_enum(self) -> None:
121
133
  """Check if enum is needed."""
122
- if "enum" in self.already_added:
134
+ if self.needs_enum.__name__ in self.already_added:
123
135
  return
124
136
  self.preamble.append(
125
137
  self.sync(
@@ -134,11 +146,11 @@ class PyastGenPass(Pass):
134
146
  jac_node=self.ir,
135
147
  )
136
148
  )
137
- self.already_added.append("enum")
149
+ self.already_added.append(self.needs_enum.__name__)
138
150
 
139
151
  def needs_jac_feature(self) -> None:
140
152
  """Check if enum is needed."""
141
- if "jac_feature" in self.already_added:
153
+ if self.needs_jac_feature.__name__ in self.already_added:
142
154
  return
143
155
  self.preamble.append(
144
156
  self.sync(
@@ -164,11 +176,11 @@ class PyastGenPass(Pass):
164
176
  jac_node=self.ir,
165
177
  )
166
178
  )
167
- self.already_added.append("jac_feature")
179
+ self.already_added.append(self.needs_jac_feature.__name__)
168
180
 
169
181
  def needs_dataclass(self) -> None:
170
182
  """Check if enum is needed."""
171
- if "dataclass" in self.already_added:
183
+ if self.needs_dataclass.__name__ in self.already_added:
172
184
  return
173
185
  self.preamble.append(
174
186
  self.sync(
@@ -184,11 +196,11 @@ class PyastGenPass(Pass):
184
196
  jac_node=self.ir,
185
197
  )
186
198
  )
187
- self.already_added.append("dataclass")
199
+ self.already_added.append(self.needs_dataclass.__name__)
188
200
 
189
201
  def needs_dataclass_field(self) -> None:
190
202
  """Check if enum is needed."""
191
- if "dataclass_field" in self.already_added:
203
+ if self.needs_dataclass_field.__name__ in self.already_added:
192
204
  return
193
205
  self.preamble.append(
194
206
  self.sync(
@@ -202,7 +214,7 @@ class PyastGenPass(Pass):
202
214
  jac_node=self.ir,
203
215
  )
204
216
  )
205
- self.already_added.append("dataclass_field")
217
+ self.already_added.append(self.needs_dataclass_field.__name__)
206
218
 
207
219
  def flatten(self, body: list[T | list[T] | None]) -> list[T]:
208
220
  """Flatten ast list."""
@@ -425,6 +437,33 @@ class PyastGenPass(Pass):
425
437
  type_params=[],
426
438
  ),
427
439
  )
440
+ if node.loc.mod_path != self.ir.loc.mod_path:
441
+ func.decorator_list.append(
442
+ self.sync(
443
+ ast3.Call(
444
+ func=self.sync(
445
+ ast3.Attribute(
446
+ value=self.sync(
447
+ ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
448
+ ),
449
+ attr="impl_patch_filename",
450
+ ctx=ast3.Load(),
451
+ )
452
+ ),
453
+ args=[],
454
+ keywords=[
455
+ self.sync(
456
+ ast3.keyword(
457
+ arg="file_loc",
458
+ value=self.sync(
459
+ ast3.Constant(value=node.body.loc.mod_path)
460
+ ),
461
+ )
462
+ ),
463
+ ],
464
+ )
465
+ )
466
+ )
428
467
  node.gen.py_ast = [func]
429
468
 
430
469
  def exit_module_code(self, node: ast.ModuleCode) -> None:
@@ -592,23 +631,12 @@ class PyastGenPass(Pass):
592
631
  ),
593
632
  )
594
633
  ),
595
- self.sync(
596
- ast3.keyword(
597
- arg="mod_bundle",
598
- value=self.sync(
599
- ast3.Name(
600
- id="__name__",
601
- ctx=ast3.Load(),
602
- )
603
- ),
604
- )
605
- ),
606
634
  self.sync(
607
635
  ast3.keyword(
608
636
  arg="lng",
609
637
  value=self.sync(
610
638
  ast3.Constant(
611
- value=node.hint.tag.value
639
+ value="py" if node.is_py else "jac"
612
640
  ),
613
641
  node.hint,
614
642
  ),
@@ -823,10 +851,17 @@ class PyastGenPass(Pass):
823
851
  )
824
852
  )
825
853
  )
854
+ self.needs_typing()
826
855
  py_nodes.append(
827
856
  self.sync(
828
857
  ast3.If(
829
- test=self.sync(ast3.Name(id="TYPE_CHECKING", ctx=ast3.Load())),
858
+ test=self.sync(
859
+ ast3.Attribute(
860
+ value=self.sync(ast3.Name(id="_jac_typ", ctx=ast3.Load())),
861
+ attr="TYPE_CHECKING",
862
+ ctx=ast3.Load(),
863
+ )
864
+ ),
830
865
  body=typecheck_nodes,
831
866
  orelse=runtime_nodes,
832
867
  )
@@ -899,6 +934,7 @@ class PyastGenPass(Pass):
899
934
  if isinstance(node.decorators, ast.SubNodeList)
900
935
  else []
901
936
  )
937
+
902
938
  ds_on_entry, ds_on_exit = self.collect_events(node)
903
939
  if node.arch_type.name != Tok.KW_CLASS:
904
940
  self.needs_jac_feature()
@@ -971,19 +1007,11 @@ class PyastGenPass(Pass):
971
1007
  )
972
1008
  )
973
1009
  if node.is_abstract:
974
- self.needs_jac_feature()
1010
+ self.needs_abc()
975
1011
  base_classes.append(
976
1012
  self.sync(
977
1013
  ast3.Attribute(
978
- value=self.sync(
979
- ast3.Attribute(
980
- value=self.sync(
981
- ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
982
- ),
983
- attr="abc",
984
- ctx=ast3.Load(),
985
- )
986
- ),
1014
+ value=self.sync(ast3.Name(id="_jac_abc", ctx=ast3.Load())),
987
1015
  attr="ABC",
988
1016
  ctx=ast3.Load(),
989
1017
  )
@@ -1133,6 +1161,13 @@ class PyastGenPass(Pass):
1133
1161
  if isinstance(node.body, ast.AstImplOnlyNode):
1134
1162
  self.traverse(node.body)
1135
1163
 
1164
+ def gen_llm_body(self, node: ast.Ability) -> list[ast3.AST]:
1165
+ """Generate the by LLM body."""
1166
+ # to Avoid circular import
1167
+ from jaclang.plugin.feature import JacFeature
1168
+
1169
+ return JacFeature.gen_llm_body(self, node)
1170
+
1136
1171
  def exit_ability(self, node: ast.Ability) -> None:
1137
1172
  """Sub objects.
1138
1173
 
@@ -1179,20 +1214,40 @@ class PyastGenPass(Pass):
1179
1214
  node,
1180
1215
  )
1181
1216
  decorator_list = node.decorators.gen.py_ast if node.decorators else []
1182
- if node.is_abstract:
1217
+ if isinstance(node.body, ast.AstImplOnlyNode):
1183
1218
  self.needs_jac_feature()
1184
1219
  decorator_list.append(
1185
1220
  self.sync(
1186
- ast3.Attribute(
1187
- value=self.sync(
1221
+ ast3.Call(
1222
+ func=self.sync(
1188
1223
  ast3.Attribute(
1189
1224
  value=self.sync(
1190
1225
  ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
1191
1226
  ),
1192
- attr="abc",
1227
+ attr="impl_patch_filename",
1193
1228
  ctx=ast3.Load(),
1194
1229
  )
1195
1230
  ),
1231
+ args=[],
1232
+ keywords=[
1233
+ self.sync(
1234
+ ast3.keyword(
1235
+ arg="file_loc",
1236
+ value=self.sync(
1237
+ ast3.Constant(value=node.body.loc.mod_path)
1238
+ ),
1239
+ )
1240
+ ),
1241
+ ],
1242
+ )
1243
+ )
1244
+ )
1245
+ if node.is_abstract:
1246
+ self.needs_abc()
1247
+ decorator_list.append(
1248
+ self.sync(
1249
+ ast3.Attribute(
1250
+ value=self.sync(ast3.Name(id="_jac_abc", ctx=ast3.Load())),
1196
1251
  attr="abstractmethod",
1197
1252
  ctx=ast3.Load(),
1198
1253
  )
@@ -1234,289 +1289,6 @@ class PyastGenPass(Pass):
1234
1289
  )
1235
1290
  ]
1236
1291
 
1237
- def gen_llm_body(self, node: ast.Ability) -> list[ast3.AST]:
1238
- """Generate llm body."""
1239
- self.needs_jac_feature()
1240
- if isinstance(node.body, ast.FuncCall):
1241
- model = node.body.target.gen.py_ast[0]
1242
- extracted_type = (
1243
- "".join(extract_type(node.signature.return_type))
1244
- if isinstance(node.signature, ast.FuncSignature)
1245
- and node.signature.return_type
1246
- else None
1247
- )
1248
- scope = self.sync(ast3.Constant(value=str(get_sem_scope(node))))
1249
- model_params, include_info, exclude_info = extract_params(node.body)
1250
- inputs = (
1251
- [
1252
- self.sync(
1253
- ast3.Tuple(
1254
- elts=[
1255
- (
1256
- self.sync(
1257
- ast3.Constant(
1258
- value=(
1259
- param.semstr.lit_value
1260
- if param.semstr
1261
- else None
1262
- )
1263
- )
1264
- )
1265
- ),
1266
- (
1267
- param.type_tag.tag.gen.py_ast[0]
1268
- if param.type_tag
1269
- else None
1270
- ),
1271
- self.sync(ast3.Constant(value=param.name.value)),
1272
- self.sync(
1273
- ast3.Name(
1274
- id=param.name.value,
1275
- ctx=ast3.Load(),
1276
- )
1277
- ),
1278
- ],
1279
- ctx=ast3.Load(),
1280
- )
1281
- )
1282
- for param in node.signature.params.items
1283
- ]
1284
- if isinstance(node.signature, ast.FuncSignature)
1285
- and node.signature.params
1286
- else []
1287
- )
1288
- outputs = (
1289
- [
1290
- (
1291
- self.sync(
1292
- ast3.Constant(
1293
- value=(
1294
- node.signature.semstr.lit_value
1295
- if node.signature.semstr
1296
- else ""
1297
- )
1298
- )
1299
- )
1300
- ),
1301
- (self.sync(ast3.Constant(value=(extracted_type)))),
1302
- ]
1303
- if isinstance(node.signature, ast.FuncSignature)
1304
- else []
1305
- )
1306
- action = (
1307
- node.semstr.gen.py_ast[0]
1308
- if node.semstr
1309
- else self.sync(ast3.Constant(value=node.name_ref.sym_name))
1310
- )
1311
- return [
1312
- self.sync(
1313
- ast3.Assign(
1314
- targets=[self.sync(ast3.Name(id="output", ctx=ast3.Store()))],
1315
- value=self.by_llm_call(
1316
- model,
1317
- model_params,
1318
- scope,
1319
- inputs,
1320
- outputs,
1321
- action,
1322
- include_info,
1323
- exclude_info,
1324
- ),
1325
- )
1326
- ),
1327
- self.sync(
1328
- ast3.Try(
1329
- body=[
1330
- self.sync(
1331
- ast3.Return(
1332
- value=self.sync(
1333
- ast3.Call(
1334
- func=self.sync(
1335
- ast3.Name(id="eval", ctx=ast3.Load())
1336
- ),
1337
- args=[
1338
- self.sync(
1339
- ast3.Name(
1340
- id="output", ctx=ast3.Load()
1341
- )
1342
- )
1343
- ],
1344
- keywords=[],
1345
- )
1346
- )
1347
- )
1348
- )
1349
- ],
1350
- handlers=[
1351
- self.sync(
1352
- ast3.ExceptHandler(
1353
- type=None,
1354
- name=None,
1355
- body=[
1356
- self.sync(
1357
- ast3.Return(
1358
- value=self.sync(
1359
- ast3.Name(
1360
- id="output", ctx=ast3.Load()
1361
- )
1362
- )
1363
- )
1364
- )
1365
- ],
1366
- )
1367
- )
1368
- ],
1369
- orelse=[],
1370
- finalbody=[],
1371
- )
1372
- ),
1373
- ]
1374
- else:
1375
- return []
1376
-
1377
- def by_llm_call(
1378
- self,
1379
- model: ast3.AST,
1380
- model_params: dict[str, ast.Expr],
1381
- scope: ast3.AST,
1382
- inputs: Sequence[Optional[ast3.AST]],
1383
- outputs: Sequence[Optional[ast3.AST]] | ast3.Call,
1384
- action: Optional[ast3.AST],
1385
- include_info: list[tuple[str, ast3.AST]],
1386
- exclude_info: list[tuple[str, ast3.AST]],
1387
- ) -> ast3.Call:
1388
- """Return the LLM Call, e.g. _Jac.with_llm()."""
1389
- return self.sync(
1390
- ast3.Call(
1391
- func=self.sync(
1392
- ast3.Attribute(
1393
- value=self.sync(
1394
- ast3.Name(
1395
- id=Con.JAC_FEATURE.value,
1396
- ctx=ast3.Load(),
1397
- )
1398
- ),
1399
- attr="with_llm",
1400
- ctx=ast3.Load(),
1401
- )
1402
- ),
1403
- args=[],
1404
- keywords=[
1405
- self.sync(
1406
- ast3.keyword(
1407
- arg="file_loc",
1408
- value=self.sync(ast3.Name(id="__file__", ctx=ast3.Load())),
1409
- )
1410
- ),
1411
- self.sync(
1412
- ast3.keyword(
1413
- arg="model",
1414
- value=model,
1415
- )
1416
- ),
1417
- self.sync(
1418
- ast3.keyword(
1419
- arg="model_params",
1420
- value=self.sync(
1421
- ast3.Dict(
1422
- keys=[
1423
- self.sync(ast3.Constant(value=key))
1424
- for key in model_params.keys()
1425
- ],
1426
- values=[
1427
- value.gen.py_ast[0]
1428
- for value in model_params.values()
1429
- ],
1430
- )
1431
- ),
1432
- )
1433
- ),
1434
- self.sync(
1435
- ast3.keyword(
1436
- arg="scope",
1437
- value=scope,
1438
- )
1439
- ),
1440
- self.sync(
1441
- ast3.keyword(
1442
- arg="incl_info",
1443
- value=self.sync(
1444
- ast3.List(
1445
- elts=[
1446
- self.sync(
1447
- ast3.Tuple(
1448
- elts=[
1449
- self.sync(ast3.Constant(value=key)),
1450
- value,
1451
- ],
1452
- ctx=ast3.Load(),
1453
- )
1454
- )
1455
- for key, value in include_info
1456
- ],
1457
- ctx=ast3.Load(),
1458
- )
1459
- ),
1460
- )
1461
- ),
1462
- self.sync(
1463
- ast3.keyword(
1464
- arg="excl_info",
1465
- value=self.sync(
1466
- ast3.List(
1467
- elts=[
1468
- self.sync(
1469
- ast3.Tuple(
1470
- elts=[
1471
- self.sync(ast3.Constant(value=key)),
1472
- value,
1473
- ],
1474
- ctx=ast3.Load(),
1475
- )
1476
- )
1477
- for key, value in exclude_info
1478
- ],
1479
- ctx=ast3.Load(),
1480
- )
1481
- ),
1482
- ),
1483
- ),
1484
- self.sync(
1485
- ast3.keyword(
1486
- arg="inputs",
1487
- value=self.sync(
1488
- ast3.List(
1489
- elts=inputs,
1490
- ctx=ast3.Load(),
1491
- )
1492
- ),
1493
- )
1494
- ),
1495
- self.sync(
1496
- ast3.keyword(
1497
- arg="outputs",
1498
- value=(
1499
- self.sync(
1500
- ast3.Tuple(
1501
- elts=outputs,
1502
- ctx=ast3.Load(),
1503
- )
1504
- )
1505
- if not isinstance(outputs, ast3.Call)
1506
- else outputs
1507
- ),
1508
- )
1509
- ),
1510
- self.sync(
1511
- ast3.keyword(
1512
- arg="action",
1513
- value=action,
1514
- )
1515
- ),
1516
- ],
1517
- )
1518
- )
1519
-
1520
1292
  def exit_ability_def(self, node: ast.AbilityDef) -> None:
1521
1293
  """Sub objects.
1522
1294
 
@@ -1535,7 +1307,7 @@ class PyastGenPass(Pass):
1535
1307
  """
1536
1308
  params = (
1537
1309
  [self.sync(ast3.arg(arg="self", annotation=None))]
1538
- if node.is_method and not node.is_static
1310
+ if node.is_method and not node.is_static and not node.is_in_py_class
1539
1311
  else []
1540
1312
  )
1541
1313
  vararg = None
@@ -2080,33 +1852,160 @@ class PyastGenPass(Pass):
2080
1852
 
2081
1853
  target: ExprType,
2082
1854
  """
1855
+ # TODO: Here is the list of assertions which are not implemented instead a simpler version of them will work.
1856
+ # ie. [] == [] will be assertEqual instead of assertListEqual. However I don't think this is needed since it can
1857
+ # only detected if both operand are compile time literal list or type inferable.
1858
+ #
1859
+ # assertAlmostEqual
1860
+ # assertNotAlmostEqual
1861
+ # assertSequenceEqual
1862
+ # assertListEqual
1863
+ # assertTupleEqual
1864
+ # assertSetEqual
1865
+ # assertDictEqual
1866
+ # assertCountEqual
1867
+ # assertMultiLineEqual
1868
+ # assertRaisesRegex
1869
+ # assertWarnsRegex
1870
+ # assertRegex
1871
+ # assertNotRegex
1872
+
1873
+ # The return type "struct" for the bellow check_node_isinstance_call.
1874
+ @dataclass
1875
+ class CheckNodeIsinstanceCallResult:
1876
+ isit: bool = False
1877
+ inst: ast3.AST | None = None
1878
+ clss: ast3.AST | None = None
1879
+
1880
+ # This will check if a node is `isinstance(<expr>, <expr>)`, we're
1881
+ # using a function because it's reusable to check not isinstance(<expr>, <expr>).
1882
+ def check_node_isinstance_call(
1883
+ node: ast.FuncCall,
1884
+ ) -> CheckNodeIsinstanceCallResult:
1885
+
1886
+ # Ensure the type of the FuncCall node is SubNodeList[Expr]
1887
+ # since the type can be: Optional[SubNodeList[Expr | KWPair]].
1888
+ if not (
1889
+ node.params is not None
1890
+ and len(node.params.items) == 2
1891
+ and isinstance(node.params.items[0], ast.Expr)
1892
+ and isinstance(node.params.items[1], ast.Expr)
1893
+ ):
1894
+ return CheckNodeIsinstanceCallResult()
1895
+
1896
+ func = node.target.gen.py_ast[0]
1897
+ if not (isinstance(func, ast3.Name) and func.id == "isinstance"):
1898
+ return CheckNodeIsinstanceCallResult()
1899
+
1900
+ return CheckNodeIsinstanceCallResult(
1901
+ True,
1902
+ node.params.items[0].gen.py_ast[0],
1903
+ node.params.items[1].gen.py_ast[0],
1904
+ )
1905
+
1906
+ # By default the check expression will become assertTrue(<expr>), unless any pattern detected.
1907
+ assert_func_name = "assertTrue"
1908
+ assert_args_list = node.target.gen.py_ast
1909
+
1910
+ # Compare operations. Note that We're only considering the compare
1911
+ # operation with a single operation ie. a < b < c is ignored here.
1912
+ if (
1913
+ isinstance(node.target, ast.CompareExpr)
1914
+ and isinstance(node.target.gen.py_ast[0], ast3.Compare)
1915
+ and len(node.target.ops) == 1
1916
+ ):
1917
+ expr: ast.CompareExpr = node.target
1918
+ opty: ast.Token = expr.ops[0]
1919
+
1920
+ optype2fn = {
1921
+ Tok.EE.name: "assertEqual",
1922
+ Tok.NE.name: "assertNotEqual",
1923
+ Tok.LT.name: "assertLess",
1924
+ Tok.LTE.name: "assertLessEqual",
1925
+ Tok.GT.name: "assertGreater",
1926
+ Tok.GTE.name: "assertGreaterEqual",
1927
+ Tok.KW_IN.name: "assertIn",
1928
+ Tok.KW_NIN.name: "assertNotIn",
1929
+ Tok.KW_IS.name: "assertIs",
1930
+ Tok.KW_ISN.name: "assertIsNot",
1931
+ }
1932
+
1933
+ if opty.name in optype2fn:
1934
+ assert_func_name = optype2fn[opty.name]
1935
+ assert_args_list = [
1936
+ expr.left.gen.py_ast[0],
1937
+ expr.rights[0].gen.py_ast[0],
1938
+ ]
1939
+
1940
+ # Override for <expr> is None.
1941
+ if opty.name == Tok.KW_IS and isinstance(expr.rights[0], ast.Null):
1942
+ assert_func_name = "assertIsNone"
1943
+ assert_args_list.pop()
1944
+
1945
+ # Override for <expr> is not None.
1946
+ elif opty.name == Tok.KW_ISN and isinstance(expr.rights[0], ast.Null):
1947
+ assert_func_name = "assertIsNotNone"
1948
+ assert_args_list.pop()
1949
+
1950
+ # Check if 'isinstance' is called.
1951
+ elif isinstance(node.target, ast.FuncCall) and isinstance(
1952
+ node.target.gen.py_ast[0], ast3.Call
1953
+ ):
1954
+ res = check_node_isinstance_call(node.target)
1955
+ if res.isit:
1956
+ # These assertions will make mypy happy.
1957
+ assert isinstance(res.inst, ast3.AST)
1958
+ assert isinstance(res.clss, ast3.AST)
1959
+ assert_func_name = "assertIsInstance"
1960
+ assert_args_list = [res.inst, res.clss]
1961
+
1962
+ # Check if 'not isinstance(<expr>, <expr>)' is called.
1963
+ elif (
1964
+ isinstance(node.target, ast.UnaryExpr)
1965
+ and isinstance(node.target, ast.UnaryExpr)
1966
+ and isinstance(node.target.operand, ast.FuncCall)
1967
+ and isinstance(node.target.operand, ast.UnaryExpr)
1968
+ ):
1969
+ res = check_node_isinstance_call(node.target.operand)
1970
+ if res.isit:
1971
+ # These assertions will make mypy happy.
1972
+ assert isinstance(res.inst, ast3.AST)
1973
+ assert isinstance(res.clss, ast3.AST)
1974
+ assert_func_name = "assertIsNotInstance"
1975
+ assert_args_list = [res.inst, res.clss]
1976
+
1977
+ # NOTE That the almost equal is NOT a builtin function of jaclang and won't work outside of the
1978
+ # check statement. And we're hacking the node here. Not sure if this is a hacky workaround to support
1979
+ # the almost equal functionality (snice there is no almost equal operator in jac and never needed ig.).
1980
+
1981
+ # Check if 'almostEqual' is called.
2083
1982
  if isinstance(node.target, ast.FuncCall) and isinstance(
2084
1983
  node.target.gen.py_ast[0], ast3.Call
2085
1984
  ):
2086
- func = node.target.target.gen.py_ast[0]
2087
- if isinstance(func, ast3.Name):
2088
- new_func: ast3.expr = self.sync(
2089
- ast3.Attribute(
2090
- value=self.sync(ast3.Name(id="_jac_check", ctx=ast3.Load())),
2091
- attr=func.id,
2092
- ctx=ast3.Load(),
2093
- )
2094
- )
2095
- node.target.gen.py_ast[0].func = new_func
2096
- node.gen.py_ast = [
2097
- self.sync(
2098
- ast3.Expr(
2099
- value=node.target.gen.py_ast[0],
2100
- )
2101
- )
2102
- ]
2103
- return
2104
- self.error(
2105
- "For now, check statements must be function calls "
2106
- "in the style of assertTrue(), assertEqual(), etc.",
2107
- node,
1985
+ func = node.target.target
1986
+ if isinstance(func, ast.Name) and func.value == "almostEqual":
1987
+ assert_func_name = "assertAlmostEqual"
1988
+ assert_args_list = []
1989
+ if node.target.params is not None:
1990
+ for param in node.target.params.items:
1991
+ assert_args_list.append(param.gen.py_ast[0])
1992
+
1993
+ # assert_func_expr = "_jac_check.assertXXX"
1994
+ assert_func_expr: ast3.Attribute = self.sync(
1995
+ ast3.Attribute(
1996
+ value=self.sync(ast3.Name(id="_jac_check", ctx=ast3.Load())),
1997
+ attr=assert_func_name,
1998
+ ctx=ast3.Load(),
1999
+ )
2000
+ )
2001
+
2002
+ # assert_call_expr = "(_jac_check.assertXXX)(args)"
2003
+ assert_call_expr: ast3.Call = self.sync(
2004
+ ast3.Call(func=assert_func_expr, args=assert_args_list, keywords=[])
2108
2005
  )
2109
2006
 
2007
+ node.gen.py_ast = [self.sync(ast3.Expr(assert_call_expr))]
2008
+
2110
2009
  def exit_ctrl_stmt(self, node: ast.CtrlStmt) -> None:
2111
2010
  """Sub objects.
2112
2011
 
@@ -3073,6 +2972,40 @@ class PyastGenPass(Pass):
3073
2972
  """
3074
2973
  node.gen.py_ast = node.value.gen.py_ast
3075
2974
 
2975
+ def by_llm_call(
2976
+ self,
2977
+ model: ast3.AST,
2978
+ model_params: dict[str, ast.Expr],
2979
+ scope: ast3.AST,
2980
+ inputs: Sequence[Optional[ast3.AST]],
2981
+ outputs: Sequence[Optional[ast3.AST]] | ast3.Call,
2982
+ action: Optional[ast3.AST],
2983
+ include_info: list[tuple[str, ast3.AST]],
2984
+ exclude_info: list[tuple[str, ast3.AST]],
2985
+ ) -> ast3.Call:
2986
+ """Return the LLM Call, e.g. _Jac.with_llm()."""
2987
+ # to avoid circular import
2988
+ from jaclang.plugin.feature import JacFeature
2989
+
2990
+ return JacFeature.by_llm_call(
2991
+ self,
2992
+ model,
2993
+ model_params,
2994
+ scope,
2995
+ inputs,
2996
+ outputs,
2997
+ action,
2998
+ include_info,
2999
+ exclude_info,
3000
+ )
3001
+
3002
+ def get_by_llm_call_args(self, node: ast.FuncCall) -> dict:
3003
+ """Get the arguments for the by_llm_call."""
3004
+ # to avoid circular import
3005
+ from jaclang.plugin.feature import JacFeature
3006
+
3007
+ return JacFeature.get_by_llm_call_args(self, node)
3008
+
3076
3009
  def exit_func_call(self, node: ast.FuncCall) -> None:
3077
3010
  """Sub objects.
3078
3011
 
@@ -3098,193 +3031,8 @@ class PyastGenPass(Pass):
3098
3031
  self.ice("Invalid Parameter")
3099
3032
  if node.genai_call:
3100
3033
  self.needs_jac_feature()
3101
- model = node.genai_call.target.gen.py_ast[0]
3102
- model_params, include_info, exclude_info = extract_params(node.genai_call)
3103
- action = self.sync(
3104
- ast3.Constant(
3105
- value="Create an object of the specified type, using the specifically "
3106
- " provided input value(s) and look up any missing attributes from reliable"
3107
- " online sources to fill them in accurately."
3108
- )
3109
- )
3110
- _output_ = "".join(extract_type(node.target))
3111
- include_info.append(
3112
- (
3113
- _output_.split(".")[0],
3114
- self.sync(ast3.Name(id=_output_.split(".")[0], ctx=ast3.Load())),
3115
- )
3116
- )
3117
- scope = self.sync(
3118
- ast3.Call(
3119
- func=self.sync(
3120
- ast3.Attribute(
3121
- value=self.sync(
3122
- ast3.Name(
3123
- id=Con.JAC_FEATURE.value,
3124
- ctx=ast3.Load(),
3125
- )
3126
- ),
3127
- attr="obj_scope",
3128
- ctx=ast3.Load(),
3129
- )
3130
- ),
3131
- args=[
3132
- self.sync(
3133
- ast3.Name(
3134
- id="__file__",
3135
- ctx=ast3.Load(),
3136
- )
3137
- ),
3138
- self.sync(ast3.Constant(value=_output_)),
3139
- ],
3140
- keywords=[],
3141
- )
3142
- )
3143
- outputs = self.sync(
3144
- ast3.Call(
3145
- func=self.sync(
3146
- ast3.Attribute(
3147
- value=self.sync(
3148
- ast3.Name(
3149
- id=Con.JAC_FEATURE.value,
3150
- ctx=ast3.Load(),
3151
- )
3152
- ),
3153
- attr="get_sem_type",
3154
- ctx=ast3.Load(),
3155
- )
3156
- ),
3157
- args=[
3158
- self.sync(
3159
- ast3.Name(
3160
- id="__file__",
3161
- ctx=ast3.Load(),
3162
- )
3163
- ),
3164
- self.sync(ast3.Constant(value=str(_output_))),
3165
- ],
3166
- keywords=[],
3167
- )
3168
- )
3169
- if node.params and node.params.items:
3170
- inputs = [
3171
- self.sync(
3172
- ast3.Tuple(
3173
- elts=[
3174
- self.sync(
3175
- ast3.Call(
3176
- func=self.sync(
3177
- ast3.Attribute(
3178
- value=self.sync(
3179
- ast3.Name(
3180
- id=Con.JAC_FEATURE.value,
3181
- ctx=ast3.Load(),
3182
- )
3183
- ),
3184
- attr="get_semstr_type",
3185
- ctx=ast3.Load(),
3186
- )
3187
- ),
3188
- args=[
3189
- self.sync(
3190
- ast3.Name(
3191
- id="__file__", ctx=ast3.Load()
3192
- )
3193
- ),
3194
- scope,
3195
- self.sync(
3196
- ast3.Constant(
3197
- value=(
3198
- kw_pair.key.value
3199
- if isinstance(
3200
- kw_pair.key, ast.Name
3201
- )
3202
- else None
3203
- )
3204
- )
3205
- ),
3206
- self.sync(ast3.Constant(value=True)),
3207
- ],
3208
- keywords=[],
3209
- )
3210
- ),
3211
- self.sync(
3212
- ast3.Call(
3213
- func=self.sync(
3214
- ast3.Attribute(
3215
- value=self.sync(
3216
- ast3.Name(
3217
- id=Con.JAC_FEATURE.value,
3218
- ctx=ast3.Load(),
3219
- )
3220
- ),
3221
- attr="get_semstr_type",
3222
- ctx=ast3.Load(),
3223
- )
3224
- ),
3225
- args=[
3226
- self.sync(
3227
- ast3.Name(
3228
- id="__file__", ctx=ast3.Load()
3229
- )
3230
- ),
3231
- scope,
3232
- self.sync(
3233
- ast3.Constant(
3234
- value=(
3235
- kw_pair.key.value
3236
- if isinstance(
3237
- kw_pair.key, ast.Name
3238
- )
3239
- else None
3240
- )
3241
- )
3242
- ),
3243
- self.sync(ast3.Constant(value=False)),
3244
- ],
3245
- keywords=[],
3246
- )
3247
- ),
3248
- self.sync(
3249
- ast3.Constant(
3250
- value=(
3251
- kw_pair.key.value
3252
- if isinstance(kw_pair.key, ast.Name)
3253
- else None
3254
- )
3255
- )
3256
- ),
3257
- kw_pair.value.gen.py_ast[0],
3258
- ],
3259
- ctx=ast3.Load(),
3260
- )
3261
- )
3262
- for kw_pair in node.params.items
3263
- if isinstance(kw_pair, ast.KWPair)
3264
- ]
3265
- else:
3266
- inputs = []
3267
-
3268
- node.gen.py_ast = [
3269
- self.sync(
3270
- ast3.Call(
3271
- func=self.sync(ast3.Name(id="eval", ctx=ast3.Load())),
3272
- args=[
3273
- self.by_llm_call(
3274
- model,
3275
- model_params,
3276
- scope,
3277
- inputs,
3278
- outputs,
3279
- action,
3280
- include_info,
3281
- exclude_info,
3282
- ),
3283
- ],
3284
- keywords=[],
3285
- )
3286
- )
3287
- ]
3034
+ by_llm_call_args = self.get_by_llm_call_args(node)
3035
+ node.gen.py_ast = [self.sync(self.by_llm_call(**by_llm_call_args))]
3288
3036
  else:
3289
3037
  node.gen.py_ast = [
3290
3038
  self.sync(ast3.Call(func=func, args=args, keywords=keywords))