jaclang 0.7.13__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 (96) 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 +95 -17
  5. jaclang/compiler/jac.lark +4 -3
  6. jaclang/compiler/parser.py +35 -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 +19 -23
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +308 -567
  12. jaclang/compiler/passes/main/pyast_load_pass.py +33 -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/tests/test_import_pass.py +5 -1
  17. jaclang/compiler/passes/main/type_check_pass.py +7 -0
  18. jaclang/compiler/passes/tool/fuse_comments_pass.py +14 -2
  19. jaclang/compiler/passes/tool/jac_formatter_pass.py +144 -94
  20. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
  21. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  23. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  26. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  27. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  28. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  29. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  30. jaclang/compiler/passes/transform.py +4 -0
  31. jaclang/compiler/semtable.py +31 -7
  32. jaclang/compiler/tests/test_importer.py +12 -5
  33. jaclang/langserve/engine.py +82 -143
  34. jaclang/langserve/sem_manager.py +379 -0
  35. jaclang/langserve/server.py +8 -10
  36. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  37. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  38. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  39. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  40. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  41. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  42. jaclang/langserve/tests/test_server.py +96 -16
  43. jaclang/langserve/utils.py +163 -96
  44. jaclang/plugin/builtin.py +1 -1
  45. jaclang/plugin/default.py +214 -24
  46. jaclang/plugin/feature.py +59 -11
  47. jaclang/plugin/spec.py +58 -6
  48. jaclang/{core → runtimelib}/architype.py +1 -1
  49. jaclang/{core → runtimelib}/context.py +8 -1
  50. jaclang/runtimelib/importer.py +361 -0
  51. jaclang/runtimelib/machine.py +94 -0
  52. jaclang/{core → runtimelib}/utils.py +13 -5
  53. jaclang/settings.py +4 -1
  54. jaclang/tests/fixtures/abc.jac +3 -3
  55. jaclang/tests/fixtures/blankwithentry.jac +3 -0
  56. jaclang/tests/fixtures/byllmissue.jac +1 -5
  57. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  58. jaclang/tests/fixtures/cls_method.jac +41 -0
  59. jaclang/tests/fixtures/dblhello.jac +6 -0
  60. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  61. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  62. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  63. jaclang/tests/fixtures/err.impl.jac +3 -0
  64. jaclang/tests/fixtures/err.jac +4 -2
  65. jaclang/tests/fixtures/err.test.jac +3 -0
  66. jaclang/tests/fixtures/err_runtime.jac +15 -0
  67. jaclang/tests/fixtures/game1.jac +1 -1
  68. jaclang/tests/fixtures/hello.jac +4 -0
  69. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  70. jaclang/tests/fixtures/impl_grab.jac +4 -1
  71. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  72. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  73. jaclang/tests/fixtures/needs_import.jac +2 -2
  74. jaclang/tests/fixtures/pyfunc_2.py +3 -0
  75. jaclang/tests/fixtures/registry.jac +9 -0
  76. jaclang/tests/fixtures/run_test.jac +4 -4
  77. jaclang/tests/fixtures/semstr.jac +1 -4
  78. jaclang/tests/fixtures/simple_archs.jac +1 -1
  79. jaclang/tests/test_cli.py +65 -2
  80. jaclang/tests/test_language.py +83 -7
  81. jaclang/tests/test_man_code.py +17 -0
  82. jaclang/tests/test_reference.py +6 -0
  83. jaclang/utils/helpers.py +45 -21
  84. jaclang/utils/test.py +9 -0
  85. jaclang/utils/treeprinter.py +0 -4
  86. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
  87. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/RECORD +93 -77
  88. jaclang/core/importer.py +0 -344
  89. jaclang/tests/fixtures/aott_raise.jac +0 -25
  90. jaclang/tests/fixtures/package_import.jac +0 -6
  91. /jaclang/{core → runtimelib}/__init__.py +0 -0
  92. /jaclang/{core → runtimelib}/constructs.py +0 -0
  93. /jaclang/{core → runtimelib}/memory.py +0 -0
  94. /jaclang/{core → runtimelib}/test.py +0 -0
  95. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
  96. {jaclang-0.7.13.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:
@@ -434,14 +473,7 @@ class PyastGenPass(Pass):
434
473
  body: SubNodeList[CodeBlockStmt],
435
474
  doc: Optional[String],
436
475
  """
437
- if node.doc:
438
- doc = self.sync(ast3.Expr(value=node.doc.gen.py_ast[0]), jac_node=node.doc)
439
- if isinstance(node.body.gen.py_ast, list):
440
- node.gen.py_ast = [doc] + node.body.gen.py_ast
441
- else:
442
- raise self.ice()
443
- else:
444
- node.gen.py_ast = node.body.gen.py_ast
476
+ node.gen.py_ast = self.resolve_stmt_block(node.body, doc=node.doc)
445
477
  if node.name:
446
478
  node.gen.py_ast = [
447
479
  self.sync(
@@ -599,23 +631,12 @@ class PyastGenPass(Pass):
599
631
  ),
600
632
  )
601
633
  ),
602
- self.sync(
603
- ast3.keyword(
604
- arg="mod_bundle",
605
- value=self.sync(
606
- ast3.Name(
607
- id="__name__",
608
- ctx=ast3.Load(),
609
- )
610
- ),
611
- )
612
- ),
613
634
  self.sync(
614
635
  ast3.keyword(
615
636
  arg="lng",
616
637
  value=self.sync(
617
638
  ast3.Constant(
618
- value=node.hint.tag.value
639
+ value="py" if node.is_py else "jac"
619
640
  ),
620
641
  node.hint,
621
642
  ),
@@ -830,10 +851,17 @@ class PyastGenPass(Pass):
830
851
  )
831
852
  )
832
853
  )
854
+ self.needs_typing()
833
855
  py_nodes.append(
834
856
  self.sync(
835
857
  ast3.If(
836
- 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
+ ),
837
865
  body=typecheck_nodes,
838
866
  orelse=runtime_nodes,
839
867
  )
@@ -906,6 +934,7 @@ class PyastGenPass(Pass):
906
934
  if isinstance(node.decorators, ast.SubNodeList)
907
935
  else []
908
936
  )
937
+
909
938
  ds_on_entry, ds_on_exit = self.collect_events(node)
910
939
  if node.arch_type.name != Tok.KW_CLASS:
911
940
  self.needs_jac_feature()
@@ -978,19 +1007,11 @@ class PyastGenPass(Pass):
978
1007
  )
979
1008
  )
980
1009
  if node.is_abstract:
981
- self.needs_jac_feature()
1010
+ self.needs_abc()
982
1011
  base_classes.append(
983
1012
  self.sync(
984
1013
  ast3.Attribute(
985
- value=self.sync(
986
- ast3.Attribute(
987
- value=self.sync(
988
- ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
989
- ),
990
- attr="abc",
991
- ctx=ast3.Load(),
992
- )
993
- ),
1014
+ value=self.sync(ast3.Name(id="_jac_abc", ctx=ast3.Load())),
994
1015
  attr="ABC",
995
1016
  ctx=ast3.Load(),
996
1017
  )
@@ -1140,6 +1161,13 @@ class PyastGenPass(Pass):
1140
1161
  if isinstance(node.body, ast.AstImplOnlyNode):
1141
1162
  self.traverse(node.body)
1142
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
+
1143
1171
  def exit_ability(self, node: ast.Ability) -> None:
1144
1172
  """Sub objects.
1145
1173
 
@@ -1186,20 +1214,40 @@ class PyastGenPass(Pass):
1186
1214
  node,
1187
1215
  )
1188
1216
  decorator_list = node.decorators.gen.py_ast if node.decorators else []
1189
- if node.is_abstract:
1217
+ if isinstance(node.body, ast.AstImplOnlyNode):
1190
1218
  self.needs_jac_feature()
1191
1219
  decorator_list.append(
1192
1220
  self.sync(
1193
- ast3.Attribute(
1194
- value=self.sync(
1221
+ ast3.Call(
1222
+ func=self.sync(
1195
1223
  ast3.Attribute(
1196
1224
  value=self.sync(
1197
1225
  ast3.Name(id=Con.JAC_FEATURE.value, ctx=ast3.Load())
1198
1226
  ),
1199
- attr="abc",
1227
+ attr="impl_patch_filename",
1200
1228
  ctx=ast3.Load(),
1201
1229
  )
1202
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())),
1203
1251
  attr="abstractmethod",
1204
1252
  ctx=ast3.Load(),
1205
1253
  )
@@ -1241,289 +1289,6 @@ class PyastGenPass(Pass):
1241
1289
  )
1242
1290
  ]
1243
1291
 
1244
- def gen_llm_body(self, node: ast.Ability) -> list[ast3.AST]:
1245
- """Generate llm body."""
1246
- self.needs_jac_feature()
1247
- if isinstance(node.body, ast.FuncCall):
1248
- model = node.body.target.gen.py_ast[0]
1249
- extracted_type = (
1250
- "".join(extract_type(node.signature.return_type))
1251
- if isinstance(node.signature, ast.FuncSignature)
1252
- and node.signature.return_type
1253
- else None
1254
- )
1255
- scope = self.sync(ast3.Constant(value=str(get_sem_scope(node))))
1256
- model_params, include_info, exclude_info = extract_params(node.body)
1257
- inputs = (
1258
- [
1259
- self.sync(
1260
- ast3.Tuple(
1261
- elts=[
1262
- (
1263
- self.sync(
1264
- ast3.Constant(
1265
- value=(
1266
- param.semstr.lit_value
1267
- if param.semstr
1268
- else None
1269
- )
1270
- )
1271
- )
1272
- ),
1273
- (
1274
- param.type_tag.tag.gen.py_ast[0]
1275
- if param.type_tag
1276
- else None
1277
- ),
1278
- self.sync(ast3.Constant(value=param.name.value)),
1279
- self.sync(
1280
- ast3.Name(
1281
- id=param.name.value,
1282
- ctx=ast3.Load(),
1283
- )
1284
- ),
1285
- ],
1286
- ctx=ast3.Load(),
1287
- )
1288
- )
1289
- for param in node.signature.params.items
1290
- ]
1291
- if isinstance(node.signature, ast.FuncSignature)
1292
- and node.signature.params
1293
- else []
1294
- )
1295
- outputs = (
1296
- [
1297
- (
1298
- self.sync(
1299
- ast3.Constant(
1300
- value=(
1301
- node.signature.semstr.lit_value
1302
- if node.signature.semstr
1303
- else ""
1304
- )
1305
- )
1306
- )
1307
- ),
1308
- (self.sync(ast3.Constant(value=(extracted_type)))),
1309
- ]
1310
- if isinstance(node.signature, ast.FuncSignature)
1311
- else []
1312
- )
1313
- action = (
1314
- node.semstr.gen.py_ast[0]
1315
- if node.semstr
1316
- else self.sync(ast3.Constant(value=node.name_ref.sym_name))
1317
- )
1318
- return [
1319
- self.sync(
1320
- ast3.Assign(
1321
- targets=[self.sync(ast3.Name(id="output", ctx=ast3.Store()))],
1322
- value=self.by_llm_call(
1323
- model,
1324
- model_params,
1325
- scope,
1326
- inputs,
1327
- outputs,
1328
- action,
1329
- include_info,
1330
- exclude_info,
1331
- ),
1332
- )
1333
- ),
1334
- self.sync(
1335
- ast3.Try(
1336
- body=[
1337
- self.sync(
1338
- ast3.Return(
1339
- value=self.sync(
1340
- ast3.Call(
1341
- func=self.sync(
1342
- ast3.Name(id="eval", ctx=ast3.Load())
1343
- ),
1344
- args=[
1345
- self.sync(
1346
- ast3.Name(
1347
- id="output", ctx=ast3.Load()
1348
- )
1349
- )
1350
- ],
1351
- keywords=[],
1352
- )
1353
- )
1354
- )
1355
- )
1356
- ],
1357
- handlers=[
1358
- self.sync(
1359
- ast3.ExceptHandler(
1360
- type=None,
1361
- name=None,
1362
- body=[
1363
- self.sync(
1364
- ast3.Return(
1365
- value=self.sync(
1366
- ast3.Name(
1367
- id="output", ctx=ast3.Load()
1368
- )
1369
- )
1370
- )
1371
- )
1372
- ],
1373
- )
1374
- )
1375
- ],
1376
- orelse=[],
1377
- finalbody=[],
1378
- )
1379
- ),
1380
- ]
1381
- else:
1382
- return []
1383
-
1384
- def by_llm_call(
1385
- self,
1386
- model: ast3.AST,
1387
- model_params: dict[str, ast.Expr],
1388
- scope: ast3.AST,
1389
- inputs: Sequence[Optional[ast3.AST]],
1390
- outputs: Sequence[Optional[ast3.AST]] | ast3.Call,
1391
- action: Optional[ast3.AST],
1392
- include_info: list[tuple[str, ast3.AST]],
1393
- exclude_info: list[tuple[str, ast3.AST]],
1394
- ) -> ast3.Call:
1395
- """Return the LLM Call, e.g. _Jac.with_llm()."""
1396
- return self.sync(
1397
- ast3.Call(
1398
- func=self.sync(
1399
- ast3.Attribute(
1400
- value=self.sync(
1401
- ast3.Name(
1402
- id=Con.JAC_FEATURE.value,
1403
- ctx=ast3.Load(),
1404
- )
1405
- ),
1406
- attr="with_llm",
1407
- ctx=ast3.Load(),
1408
- )
1409
- ),
1410
- args=[],
1411
- keywords=[
1412
- self.sync(
1413
- ast3.keyword(
1414
- arg="file_loc",
1415
- value=self.sync(ast3.Name(id="__file__", ctx=ast3.Load())),
1416
- )
1417
- ),
1418
- self.sync(
1419
- ast3.keyword(
1420
- arg="model",
1421
- value=model,
1422
- )
1423
- ),
1424
- self.sync(
1425
- ast3.keyword(
1426
- arg="model_params",
1427
- value=self.sync(
1428
- ast3.Dict(
1429
- keys=[
1430
- self.sync(ast3.Constant(value=key))
1431
- for key in model_params.keys()
1432
- ],
1433
- values=[
1434
- value.gen.py_ast[0]
1435
- for value in model_params.values()
1436
- ],
1437
- )
1438
- ),
1439
- )
1440
- ),
1441
- self.sync(
1442
- ast3.keyword(
1443
- arg="scope",
1444
- value=scope,
1445
- )
1446
- ),
1447
- self.sync(
1448
- ast3.keyword(
1449
- arg="incl_info",
1450
- value=self.sync(
1451
- ast3.List(
1452
- elts=[
1453
- self.sync(
1454
- ast3.Tuple(
1455
- elts=[
1456
- self.sync(ast3.Constant(value=key)),
1457
- value,
1458
- ],
1459
- ctx=ast3.Load(),
1460
- )
1461
- )
1462
- for key, value in include_info
1463
- ],
1464
- ctx=ast3.Load(),
1465
- )
1466
- ),
1467
- )
1468
- ),
1469
- self.sync(
1470
- ast3.keyword(
1471
- arg="excl_info",
1472
- value=self.sync(
1473
- ast3.List(
1474
- elts=[
1475
- self.sync(
1476
- ast3.Tuple(
1477
- elts=[
1478
- self.sync(ast3.Constant(value=key)),
1479
- value,
1480
- ],
1481
- ctx=ast3.Load(),
1482
- )
1483
- )
1484
- for key, value in exclude_info
1485
- ],
1486
- ctx=ast3.Load(),
1487
- )
1488
- ),
1489
- ),
1490
- ),
1491
- self.sync(
1492
- ast3.keyword(
1493
- arg="inputs",
1494
- value=self.sync(
1495
- ast3.List(
1496
- elts=inputs,
1497
- ctx=ast3.Load(),
1498
- )
1499
- ),
1500
- )
1501
- ),
1502
- self.sync(
1503
- ast3.keyword(
1504
- arg="outputs",
1505
- value=(
1506
- self.sync(
1507
- ast3.Tuple(
1508
- elts=outputs,
1509
- ctx=ast3.Load(),
1510
- )
1511
- )
1512
- if not isinstance(outputs, ast3.Call)
1513
- else outputs
1514
- ),
1515
- )
1516
- ),
1517
- self.sync(
1518
- ast3.keyword(
1519
- arg="action",
1520
- value=action,
1521
- )
1522
- ),
1523
- ],
1524
- )
1525
- )
1526
-
1527
1292
  def exit_ability_def(self, node: ast.AbilityDef) -> None:
1528
1293
  """Sub objects.
1529
1294
 
@@ -1542,7 +1307,7 @@ class PyastGenPass(Pass):
1542
1307
  """
1543
1308
  params = (
1544
1309
  [self.sync(ast3.arg(arg="self", annotation=None))]
1545
- 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
1546
1311
  else []
1547
1312
  )
1548
1313
  vararg = None
@@ -2087,33 +1852,160 @@ class PyastGenPass(Pass):
2087
1852
 
2088
1853
  target: ExprType,
2089
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.
2090
1982
  if isinstance(node.target, ast.FuncCall) and isinstance(
2091
1983
  node.target.gen.py_ast[0], ast3.Call
2092
1984
  ):
2093
- func = node.target.target.gen.py_ast[0]
2094
- if isinstance(func, ast3.Name):
2095
- new_func: ast3.expr = self.sync(
2096
- ast3.Attribute(
2097
- value=self.sync(ast3.Name(id="_jac_check", ctx=ast3.Load())),
2098
- attr=func.id,
2099
- ctx=ast3.Load(),
2100
- )
2101
- )
2102
- node.target.gen.py_ast[0].func = new_func
2103
- node.gen.py_ast = [
2104
- self.sync(
2105
- ast3.Expr(
2106
- value=node.target.gen.py_ast[0],
2107
- )
2108
- )
2109
- ]
2110
- return
2111
- self.error(
2112
- "For now, check statements must be function calls "
2113
- "in the style of assertTrue(), assertEqual(), etc.",
2114
- 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
+ )
2115
2000
  )
2116
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=[])
2005
+ )
2006
+
2007
+ node.gen.py_ast = [self.sync(ast3.Expr(assert_call_expr))]
2008
+
2117
2009
  def exit_ctrl_stmt(self, node: ast.CtrlStmt) -> None:
2118
2010
  """Sub objects.
2119
2011
 
@@ -3080,6 +2972,40 @@ class PyastGenPass(Pass):
3080
2972
  """
3081
2973
  node.gen.py_ast = node.value.gen.py_ast
3082
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
+
3083
3009
  def exit_func_call(self, node: ast.FuncCall) -> None:
3084
3010
  """Sub objects.
3085
3011
 
@@ -3105,193 +3031,8 @@ class PyastGenPass(Pass):
3105
3031
  self.ice("Invalid Parameter")
3106
3032
  if node.genai_call:
3107
3033
  self.needs_jac_feature()
3108
- model = node.genai_call.target.gen.py_ast[0]
3109
- model_params, include_info, exclude_info = extract_params(node.genai_call)
3110
- action = self.sync(
3111
- ast3.Constant(
3112
- value="Create an object of the specified type, using the specifically "
3113
- " provided input value(s) and look up any missing attributes from reliable"
3114
- " online sources to fill them in accurately."
3115
- )
3116
- )
3117
- _output_ = "".join(extract_type(node.target))
3118
- include_info.append(
3119
- (
3120
- _output_.split(".")[0],
3121
- self.sync(ast3.Name(id=_output_.split(".")[0], ctx=ast3.Load())),
3122
- )
3123
- )
3124
- scope = self.sync(
3125
- ast3.Call(
3126
- func=self.sync(
3127
- ast3.Attribute(
3128
- value=self.sync(
3129
- ast3.Name(
3130
- id=Con.JAC_FEATURE.value,
3131
- ctx=ast3.Load(),
3132
- )
3133
- ),
3134
- attr="obj_scope",
3135
- ctx=ast3.Load(),
3136
- )
3137
- ),
3138
- args=[
3139
- self.sync(
3140
- ast3.Name(
3141
- id="__file__",
3142
- ctx=ast3.Load(),
3143
- )
3144
- ),
3145
- self.sync(ast3.Constant(value=_output_)),
3146
- ],
3147
- keywords=[],
3148
- )
3149
- )
3150
- outputs = self.sync(
3151
- ast3.Call(
3152
- func=self.sync(
3153
- ast3.Attribute(
3154
- value=self.sync(
3155
- ast3.Name(
3156
- id=Con.JAC_FEATURE.value,
3157
- ctx=ast3.Load(),
3158
- )
3159
- ),
3160
- attr="get_sem_type",
3161
- ctx=ast3.Load(),
3162
- )
3163
- ),
3164
- args=[
3165
- self.sync(
3166
- ast3.Name(
3167
- id="__file__",
3168
- ctx=ast3.Load(),
3169
- )
3170
- ),
3171
- self.sync(ast3.Constant(value=str(_output_))),
3172
- ],
3173
- keywords=[],
3174
- )
3175
- )
3176
- if node.params and node.params.items:
3177
- inputs = [
3178
- self.sync(
3179
- ast3.Tuple(
3180
- elts=[
3181
- self.sync(
3182
- ast3.Call(
3183
- func=self.sync(
3184
- ast3.Attribute(
3185
- value=self.sync(
3186
- ast3.Name(
3187
- id=Con.JAC_FEATURE.value,
3188
- ctx=ast3.Load(),
3189
- )
3190
- ),
3191
- attr="get_semstr_type",
3192
- ctx=ast3.Load(),
3193
- )
3194
- ),
3195
- args=[
3196
- self.sync(
3197
- ast3.Name(
3198
- id="__file__", ctx=ast3.Load()
3199
- )
3200
- ),
3201
- scope,
3202
- self.sync(
3203
- ast3.Constant(
3204
- value=(
3205
- kw_pair.key.value
3206
- if isinstance(
3207
- kw_pair.key, ast.Name
3208
- )
3209
- else None
3210
- )
3211
- )
3212
- ),
3213
- self.sync(ast3.Constant(value=True)),
3214
- ],
3215
- keywords=[],
3216
- )
3217
- ),
3218
- self.sync(
3219
- ast3.Call(
3220
- func=self.sync(
3221
- ast3.Attribute(
3222
- value=self.sync(
3223
- ast3.Name(
3224
- id=Con.JAC_FEATURE.value,
3225
- ctx=ast3.Load(),
3226
- )
3227
- ),
3228
- attr="get_semstr_type",
3229
- ctx=ast3.Load(),
3230
- )
3231
- ),
3232
- args=[
3233
- self.sync(
3234
- ast3.Name(
3235
- id="__file__", ctx=ast3.Load()
3236
- )
3237
- ),
3238
- scope,
3239
- self.sync(
3240
- ast3.Constant(
3241
- value=(
3242
- kw_pair.key.value
3243
- if isinstance(
3244
- kw_pair.key, ast.Name
3245
- )
3246
- else None
3247
- )
3248
- )
3249
- ),
3250
- self.sync(ast3.Constant(value=False)),
3251
- ],
3252
- keywords=[],
3253
- )
3254
- ),
3255
- self.sync(
3256
- ast3.Constant(
3257
- value=(
3258
- kw_pair.key.value
3259
- if isinstance(kw_pair.key, ast.Name)
3260
- else None
3261
- )
3262
- )
3263
- ),
3264
- kw_pair.value.gen.py_ast[0],
3265
- ],
3266
- ctx=ast3.Load(),
3267
- )
3268
- )
3269
- for kw_pair in node.params.items
3270
- if isinstance(kw_pair, ast.KWPair)
3271
- ]
3272
- else:
3273
- inputs = []
3274
-
3275
- node.gen.py_ast = [
3276
- self.sync(
3277
- ast3.Call(
3278
- func=self.sync(ast3.Name(id="eval", ctx=ast3.Load())),
3279
- args=[
3280
- self.by_llm_call(
3281
- model,
3282
- model_params,
3283
- scope,
3284
- inputs,
3285
- outputs,
3286
- action,
3287
- include_info,
3288
- exclude_info,
3289
- ),
3290
- ],
3291
- keywords=[],
3292
- )
3293
- )
3294
- ]
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))]
3295
3036
  else:
3296
3037
  node.gen.py_ast = [
3297
3038
  self.sync(ast3.Call(func=func, args=args, keywords=keywords))