jaclang 0.5.6__py3-none-any.whl → 0.5.8__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 (51) hide show
  1. jaclang/__init__.py +6 -1
  2. jaclang/cli/cli.py +63 -20
  3. jaclang/cli/cmdreg.py +42 -12
  4. jaclang/compiler/__init__.py +6 -3
  5. jaclang/compiler/__jac_gen__/jac_parser.py +2 -2
  6. jaclang/compiler/absyntree.py +1740 -61
  7. jaclang/compiler/codeloc.py +7 -0
  8. jaclang/compiler/compile.py +4 -5
  9. jaclang/compiler/constant.py +52 -6
  10. jaclang/compiler/parser.py +220 -129
  11. jaclang/compiler/passes/main/def_impl_match_pass.py +19 -3
  12. jaclang/compiler/passes/main/def_use_pass.py +1 -1
  13. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +357 -0
  14. jaclang/compiler/passes/main/import_pass.py +7 -3
  15. jaclang/compiler/passes/main/pyast_gen_pass.py +333 -93
  16. jaclang/compiler/passes/main/pyast_load_pass.py +1779 -206
  17. jaclang/compiler/passes/main/pyout_pass.py +2 -2
  18. jaclang/compiler/passes/main/schedules.py +2 -1
  19. jaclang/compiler/passes/main/sym_tab_build_pass.py +20 -28
  20. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
  21. jaclang/compiler/passes/main/tests/test_pyast_build_pass.py +14 -5
  22. jaclang/compiler/passes/main/tests/test_sym_tab_build_pass.py +8 -8
  23. jaclang/compiler/passes/main/tests/test_typeinfo_pass.py +7 -0
  24. jaclang/compiler/passes/main/type_check_pass.py +0 -1
  25. jaclang/compiler/passes/tool/jac_formatter_pass.py +8 -17
  26. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +43 -0
  27. jaclang/compiler/passes/utils/mypy_ast_build.py +28 -14
  28. jaclang/compiler/symtable.py +23 -2
  29. jaclang/compiler/tests/test_parser.py +53 -0
  30. jaclang/compiler/workspace.py +52 -26
  31. jaclang/core/aott.py +68 -0
  32. jaclang/core/construct.py +58 -6
  33. jaclang/core/importer.py +9 -10
  34. jaclang/core/utils.py +65 -3
  35. jaclang/plugin/builtin.py +42 -0
  36. jaclang/plugin/default.py +163 -18
  37. jaclang/plugin/feature.py +38 -10
  38. jaclang/plugin/spec.py +33 -6
  39. jaclang/utils/helpers.py +25 -0
  40. jaclang/utils/lang_tools.py +4 -1
  41. jaclang/utils/test.py +1 -0
  42. jaclang/utils/tests/test_lang_tools.py +12 -15
  43. jaclang/utils/treeprinter.py +10 -2
  44. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/METADATA +1 -1
  45. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/RECORD +48 -46
  46. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/WHEEL +1 -1
  47. jaclang/compiler/tests/fixtures/__jac_gen__/__init__.py +0 -0
  48. jaclang/compiler/tests/fixtures/__jac_gen__/hello_world.py +0 -5
  49. jaclang/core/jacbuiltins.py +0 -10
  50. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/entry_points.txt +0 -0
  51. {jaclang-0.5.6.dist-info → jaclang-0.5.8.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,10 @@
1
- # type: ignore
2
1
  """Lark parser for Jac Lang."""
3
2
 
4
3
  from __future__ import annotations
5
4
 
6
5
  import ast as py_ast
7
6
  import os
8
- from typing import Optional, TypeVar, Union
7
+ from typing import Optional, TypeAlias, TypeVar
9
8
 
10
9
  import jaclang.compiler.absyntree as ast
11
10
  from jaclang.compiler.constant import Tokens as Tok
@@ -36,17 +35,53 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
36
35
 
37
36
  def convert(self, node: py_ast.AST) -> ast.AstNode:
38
37
  """Get python node type."""
39
- print(f"working on {type(node).__name__}")
38
+ # print(
39
+ # f"working on {type(node).__name__} line {node.lineno if hasattr(node, 'lineno') else 0}"
40
+ # )
40
41
  if hasattr(self, f"proc_{pascal_to_snake(type(node).__name__)}"):
41
- return getattr(self, f"proc_{pascal_to_snake(type(node).__name__)}")(node)
42
+ ret = getattr(self, f"proc_{pascal_to_snake(type(node).__name__)}")(node)
42
43
  else:
43
44
  raise self.ice(f"Unknown node type {type(node).__name__}")
45
+ # print(f"finshed {type(node).__name__} ---------------------")
46
+ # print("normalizing", ret.__class__.__name__)
47
+ # print(ret.unparse())
48
+ return ret
44
49
 
45
50
  def transform(self, ir: ast.PythonModuleAst) -> ast.Module:
46
51
  """Transform input IR."""
47
52
  self.ir: ast.Module = self.proc_module(ir.ast)
48
53
  return self.ir
49
54
 
55
+ def extract_with_entry(
56
+ self, body: list[ast.AstNode], exclude_types: TypeAlias = T
57
+ ) -> list[T | ast.ModuleCode]:
58
+ """Extract with entry from a body."""
59
+
60
+ def gen_mod_code(with_entry_body: list[ast.CodeBlockStmt]) -> ast.ModuleCode:
61
+ with_entry_subnodelist = ast.SubNodeList[ast.CodeBlockStmt](
62
+ items=with_entry_body, delim=Tok.WS, kid=with_entry_body
63
+ )
64
+ return ast.ModuleCode(
65
+ name=None,
66
+ body=with_entry_subnodelist,
67
+ kid=with_entry_body,
68
+ doc=None,
69
+ )
70
+
71
+ extracted: list[T | ast.ModuleCode] = []
72
+ with_entry_body: list[ast.CodeBlockStmt] = []
73
+ for i in body:
74
+ if isinstance(i, exclude_types):
75
+ extracted.append(i)
76
+ elif isinstance(i, ast.CodeBlockStmt):
77
+ with_entry_body.append(i)
78
+ else:
79
+ self.ice("Invalid type for with entry body")
80
+
81
+ if len(with_entry_body):
82
+ extracted.append(gen_mod_code(with_entry_body))
83
+ return extracted
84
+
50
85
  def proc_module(self, node: py_ast.Module) -> ast.Module:
51
86
  """Process python node.
52
87
 
@@ -56,20 +91,22 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
56
91
  type_ignores: list[TypeIgnore]
57
92
  """
58
93
  elements: list[ast.AstNode] = [self.convert(i) for i in node.body]
59
- valid = [
60
- i
61
- for i in elements
62
- if isinstance(i, (ast.ElementStmt, ast.String, ast.EmptyToken))
63
- ]
64
- if len(valid) != len(elements):
65
- self.error("Invalid module body")
94
+ elements[0] = (
95
+ elements[0].expr
96
+ if isinstance(elements[0], ast.ExprStmt)
97
+ and isinstance(elements[0].expr, ast.String)
98
+ else elements[0]
99
+ )
100
+ valid = (
101
+ [elements[0]] if isinstance(elements[0], ast.String) else []
102
+ ) + self.extract_with_entry(elements[1:], (ast.ElementStmt, ast.EmptyToken))
66
103
  ret = ast.Module(
67
104
  name=self.mod_path.split(os.path.sep)[-1].split(".")[0],
68
105
  source=ast.JacSource("", mod_path=self.mod_path),
69
- doc=elements[0] if isinstance(elements[0], ast.String) else None,
70
- body=valid,
106
+ doc=(elements[0] if isinstance(elements[0], ast.String) else None),
107
+ body=valid[1:] if isinstance(valid[0], ast.String) else valid,
71
108
  is_imported=False,
72
- kid=elements,
109
+ kid=valid,
73
110
  )
74
111
  ret.gen.py_ast = [node]
75
112
  return self.nu(ret)
@@ -80,8 +117,6 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
80
117
  """Process python node.
81
118
 
82
119
  class FunctionDef(stmt):
83
- __match_args__ = ("name", "args", "body", "decorator_list",
84
- "returns", "type_comment", "type_params")
85
120
  name: _Identifier
86
121
  args: arguments
87
122
  body: list[stmt]
@@ -99,41 +134,57 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
99
134
  col_end=node.col_offset + len(node.name),
100
135
  pos_start=0,
101
136
  pos_end=0,
102
- kid=[],
103
137
  )
104
138
  body = [self.convert(i) for i in node.body]
105
- valid = [i for i in body if isinstance(i, ast.CodeBlockStmt)]
139
+ valid = [i for i in body if isinstance(i, (ast.CodeBlockStmt))]
106
140
  if len(valid) != len(body):
107
- self.error("Length mismatch in function body")
108
- valid_body = ast.SubNodeList[ast.CodeBlockStmt](items=valid, kid=body)
109
- doc = None
141
+ raise self.ice("Length mismatch in function body")
142
+
143
+ if (
144
+ len(valid)
145
+ and isinstance(valid[0], ast.ExprStmt)
146
+ and isinstance(valid[0].expr, ast.String)
147
+ ):
148
+ doc = valid[0].expr
149
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
150
+ items=valid[1:], delim=Tok.WS, kid=valid[1:]
151
+ )
152
+ else:
153
+ doc = None
154
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
155
+ items=valid, delim=Tok.WS, kid=valid
156
+ )
110
157
  decorators = [self.convert(i) for i in node.decorator_list]
111
158
  valid_dec = [i for i in decorators if isinstance(i, ast.Expr)]
112
159
  if len(valid_dec) != len(decorators):
113
- self.error("Length mismatch in decorators on function")
160
+ raise self.ice("Length mismatch in decorators on function")
114
161
  valid_decorators = (
115
- ast.SubNodeList[ast.Expr](items=valid_dec, kid=decorators)
162
+ ast.SubNodeList[ast.Expr](
163
+ items=valid_dec, delim=Tok.DECOR_OP, kid=decorators
164
+ )
116
165
  if len(valid_dec)
117
166
  else None
118
167
  )
119
168
  res = self.convert(node.args)
120
- sig: Optional[ast.FuncSignature] | ast.Expr = (
169
+ sig: Optional[ast.FuncSignature] = (
121
170
  res if isinstance(res, ast.FuncSignature) else None
122
171
  )
123
172
  ret_sig = self.convert(node.returns) if node.returns else None
124
173
  if isinstance(ret_sig, ast.Expr):
125
174
  if not sig:
126
- sig = ret_sig
175
+ sig = ast.FuncSignature(params=None, return_type=ret_sig, kid=[ret_sig])
127
176
  else:
128
- sig.return_type = ast.SubTag[ast.Expr](tag=ret_sig, kid=[ret_sig])
177
+ sig.return_type = ret_sig
129
178
  sig.add_kids_right([sig.return_type])
130
- kid = [name, sig, valid_body] if sig else [name, valid_body]
131
- return ast.Ability(
179
+ kid = ([doc] if doc else []) + (
180
+ [name, sig, valid_body] if sig else [name, valid_body]
181
+ )
182
+ ret = ast.Ability(
132
183
  name_ref=name,
133
- is_func=True,
134
184
  is_async=False,
135
185
  is_static=False,
136
186
  is_abstract=False,
187
+ is_override=False,
137
188
  access=None,
138
189
  signature=sig,
139
190
  body=valid_body,
@@ -141,6 +192,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
141
192
  doc=doc,
142
193
  kid=kid,
143
194
  )
195
+ return ret
144
196
 
145
197
  def proc_async_function_def(self, node: py_ast.AsyncFunctionDef) -> ast.Ability:
146
198
  """Process python node.
@@ -183,7 +235,6 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
183
235
  col_end=node.col_offset + len(node.name),
184
236
  pos_start=0,
185
237
  pos_end=0,
186
- kid=[],
187
238
  )
188
239
  arch_type = ast.Token(
189
240
  file_path=self.mod_path,
@@ -194,30 +245,36 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
194
245
  col_end=0,
195
246
  pos_start=0,
196
247
  pos_end=0,
197
- kid=[],
198
248
  )
249
+ body = [self.convert(i) for i in node.body]
250
+ valid: list[ast.ArchBlockStmt] = self.extract_with_entry(
251
+ body, ast.ArchBlockStmt
252
+ )
253
+ valid_body = ast.SubNodeList[ast.ArchBlockStmt](
254
+ items=valid, delim=Tok.WS, kid=body
255
+ )
256
+
199
257
  base_classes = [self.convert(base) for base in node.bases]
200
- valid_bases = [base for base in base_classes if isinstance(base, ast.AtomExpr)]
201
- if len(valid_bases) != len(base_classes):
202
- self.error("Length mismatch in base classes")
258
+ valid2: list[ast.Expr] = [
259
+ base for base in base_classes if isinstance(base, ast.Expr)
260
+ ]
261
+ if len(valid2) != len(base_classes):
262
+ raise self.ice("Length mismatch in base classes")
203
263
  valid_bases = (
204
- ast.SubNodeList[ast.AtomExpr](items=valid_bases, kid=base_classes)
205
- if len(valid_bases)
264
+ ast.SubNodeList[ast.Expr](items=valid2, delim=Tok.COMMA, kid=base_classes)
265
+ if len(valid2)
206
266
  else None
207
267
  )
208
- body = [self.convert(i) for i in node.body]
209
- valid_body = [i for i in body if isinstance(i, ast.ArchBlockStmt)]
210
- if len(valid_body) != len(body):
211
- self.error("Length mismatch in classes body")
212
- valid_body = ast.SubNodeList[ast.ArchBlockStmt](items=valid_body, kid=body)
213
268
  doc = None
214
269
  decorators = [self.convert(i) for i in node.decorator_list]
215
- valid_decorators = [i for i in decorators if isinstance(i, ast.Expr)]
216
- if len(valid_decorators) != len(decorators):
217
- self.error("Length mismatch in decorators in class")
270
+ valid_dec = [i for i in decorators if isinstance(i, ast.Expr)]
271
+ if len(valid_dec) != len(decorators):
272
+ raise self.ice("Length mismatch in decorators in class")
218
273
  valid_decorators = (
219
- ast.SubNodeList[ast.Expr](items=valid_decorators, kid=decorators)
220
- if len(valid_decorators)
274
+ ast.SubNodeList[ast.Expr](
275
+ items=valid_dec, delim=Tok.DECOR_OP, kid=decorators
276
+ )
277
+ if len(valid_dec)
221
278
  else None
222
279
  )
223
280
  kid = [name, valid_bases, valid_body] if valid_bases else [name, valid_body]
@@ -232,7 +289,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
232
289
  decorators=valid_decorators,
233
290
  )
234
291
 
235
- def proc_return(self, node: py_ast.Return) -> ast.Expr | None:
292
+ def proc_return(self, node: py_ast.Return) -> ast.ReturnStmt | None:
236
293
  """Process python node.
237
294
 
238
295
  class Return(stmt):
@@ -240,10 +297,14 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
240
297
  value: expr | None
241
298
  """
242
299
  value = self.convert(node.value) if node.value else None
243
- if value and not isinstance(value, ast.Expr):
244
- self.error("Invalid return value")
300
+ if not value:
301
+ return ast.ReturnStmt(
302
+ expr=None, kid=[self.operator(Tok.KW_RETURN, "return")]
303
+ )
304
+ elif value and isinstance(value, ast.Expr):
305
+ return ast.ReturnStmt(expr=value, kid=[value])
245
306
  else:
246
- return value
307
+ raise self.ice("Invalid return value")
247
308
 
248
309
  def proc_delete(self, node: py_ast.Delete) -> ast.DeleteStmt:
249
310
  """Process python node.
@@ -253,11 +314,18 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
253
314
  targets: list[expr]
254
315
  """
255
316
  exprs = [self.convert(target) for target in node.targets]
256
- valid_exprs = [expr for expr in exprs if isinstance(expr, ast.AtomExpr)]
317
+ valid_exprs = [expr for expr in exprs if isinstance(expr, ast.Expr)]
257
318
  if not len(valid_exprs) or len(valid_exprs) != len(exprs):
258
- self.error("Length mismatch in delete targets")
319
+ raise self.ice("Length mismatch in delete targets")
320
+ target = ast.SubNodeList[ast.Expr | ast.KWPair](
321
+ items=[*valid_exprs], delim=Tok.COMMA, kid=exprs
322
+ )
259
323
  return ast.DeleteStmt(
260
- target=ast.SubNodeList[ast.AtomExpr](items=valid_exprs, kid=exprs),
324
+ target=(
325
+ valid_exprs[0]
326
+ if len(valid_exprs) > 1
327
+ else ast.TupleVal(values=target, kid=[target])
328
+ ),
261
329
  kid=exprs,
262
330
  )
263
331
 
@@ -268,17 +336,15 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
268
336
  targets: list[expr]
269
337
  value: expr
270
338
  """
339
+ value = self.convert(node.value)
271
340
  targets = [self.convert(target) for target in node.targets]
272
- valid_targets = [
273
- target for target in targets if isinstance(target, ast.AtomExpr)
274
- ]
275
- if len(valid_targets) == len(targets):
276
- valid_targets = ast.SubNodeList[ast.AtomExpr](
277
- items=valid_targets, kid=targets
341
+ valid = [target for target in targets if isinstance(target, ast.Expr)]
342
+ if len(valid) == len(targets):
343
+ valid_targets = ast.SubNodeList[ast.Expr](
344
+ items=valid, delim=Tok.EQ, kid=valid
278
345
  )
279
346
  else:
280
347
  raise self.ice("Length mismatch in assignment targets")
281
- value = self.convert(node.value)
282
348
  if isinstance(value, ast.Expr):
283
349
  return ast.Assignment(
284
350
  target=valid_targets,
@@ -289,7 +355,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
289
355
  else:
290
356
  raise self.ice()
291
357
 
292
- def proc_aug_assign(self, node: py_ast.AugAssign) -> ast.BinaryExpr:
358
+ def proc_aug_assign(self, node: py_ast.AugAssign) -> ast.Assignment:
293
359
  """Process python node.
294
360
 
295
361
  class AugAssign(stmt):
@@ -306,10 +372,15 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
306
372
  and isinstance(target, ast.Expr)
307
373
  and isinstance(op, ast.Token)
308
374
  ):
309
- return ast.BinaryExpr(
310
- left=target,
311
- op=op,
312
- right=value,
375
+ targ = ast.SubNodeList[ast.Expr](
376
+ items=[target], delim=Tok.COMMA, kid=[target]
377
+ )
378
+ return ast.Assignment(
379
+ target=targ,
380
+ type_tag=None,
381
+ mutable=True,
382
+ aug_op=op,
383
+ value=value,
313
384
  kid=[target, op, value],
314
385
  )
315
386
  else:
@@ -328,21 +399,26 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
328
399
  target = self.convert(node.target)
329
400
  annotation = self.convert(node.annotation)
330
401
  if isinstance(annotation, ast.Expr):
331
- annotation = ast.SubTag[ast.Expr](tag=annotation, kid=[annotation])
402
+ annotation_subtag = ast.SubTag[ast.Expr](tag=annotation, kid=[annotation])
332
403
  else:
333
404
  raise self.ice()
334
405
  value = self.convert(node.value) if node.value else None
335
- valid_types = Union[ast.Expr, ast.YieldExpr]
336
406
  if (
337
- isinstance(target, ast.SubNodeList)
338
- and (isinstance(value, valid_types) or not value)
407
+ (isinstance(value, (ast.Expr, ast.YieldExpr)) or not value)
339
408
  and isinstance(annotation, ast.Expr)
409
+ and isinstance(target, ast.Expr)
340
410
  ):
341
411
  return ast.Assignment(
342
- target=target,
343
- value=value,
344
- type_tag=annotation,
345
- kid=[target, annotation, value] if value else [target, annotation],
412
+ target=ast.SubNodeList[ast.Expr](
413
+ items=[target], delim=Tok.EQ, kid=[target]
414
+ ),
415
+ value=value if isinstance(value, (ast.Expr, ast.YieldExpr)) else None,
416
+ type_tag=annotation_subtag,
417
+ kid=(
418
+ [target, annotation_subtag, value]
419
+ if value
420
+ else [target, annotation_subtag]
421
+ ),
346
422
  )
347
423
  else:
348
424
  raise self.ice()
@@ -360,18 +436,43 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
360
436
  target = self.convert(node.target)
361
437
  iter = self.convert(node.iter)
362
438
  body = [self.convert(i) for i in node.body]
363
- valid_body = [i for i in body if isinstance(i, ast.CodeBlockStmt)]
364
- if len(valid_body) != len(body):
365
- self.error("Length mismatch in for body")
366
- valid_body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
439
+ val_body = [i for i in body if isinstance(i, ast.CodeBlockStmt)]
440
+ if len(val_body) != len(body):
441
+ raise self.ice("Length mismatch in for body")
442
+
443
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
444
+ items=val_body, delim=Tok.WS, kid=val_body
445
+ )
367
446
  orelse = [self.convert(i) for i in node.orelse]
368
- valid_orelse = [i for i in orelse if isinstance(i, ast.CodeBlockStmt)]
369
- if len(valid_orelse) != len(orelse):
370
- self.error("Length mismatch in for orelse")
371
- valid_orelse = ast.SubNodeList[ast.CodeBlockStmt](
372
- items=valid_orelse, kid=orelse
447
+ val_orelse = [i for i in orelse if isinstance(i, ast.CodeBlockStmt)]
448
+ if len(val_orelse) != len(orelse):
449
+ raise self.ice("Length mismatch in for orelse")
450
+ if orelse:
451
+ valid_orelse = ast.SubNodeList[ast.CodeBlockStmt](
452
+ items=val_orelse, delim=Tok.WS, kid=orelse
453
+ )
454
+ else:
455
+ valid_orelse = None
456
+ fin_orelse = (
457
+ ast.ElseStmt(body=valid_orelse, kid=[valid_orelse])
458
+ if valid_orelse
459
+ else None
373
460
  )
374
- raise self.ice(f"IMPLEMENT ME{target}{iter}")
461
+ if isinstance(target, ast.Expr) and isinstance(iter, ast.Expr):
462
+ return ast.InForStmt(
463
+ target=target,
464
+ is_async=False,
465
+ collection=iter,
466
+ body=valid_body,
467
+ else_body=fin_orelse,
468
+ kid=(
469
+ [target, iter, valid_body, fin_orelse]
470
+ if fin_orelse
471
+ else [target, iter, valid_body]
472
+ ),
473
+ )
474
+ else:
475
+ raise self.ice()
375
476
 
376
477
  def proc_async_for(self, node: py_ast.AsyncFor) -> ast.InForStmt:
377
478
  """Process AsyncFor node.
@@ -385,19 +486,46 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
385
486
  """
386
487
  target = self.convert(node.target)
387
488
  iter = self.convert(node.iter)
388
- body = [self.convert(stmt) for stmt in node.body]
389
- valid_body = [stmt for stmt in body if isinstance(stmt, ast.CodeBlockStmt)]
390
- if len(valid_body) != len(body):
391
- self.error("Length mismatch in async for body")
392
- body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
393
- orelse = [self.convert(stmt) for stmt in node.orelse]
394
- valid_orelse = [stmt for stmt in orelse if isinstance(stmt, ast.CodeBlockStmt)]
395
- if len(valid_orelse) != len(orelse):
396
- self.error("Length mismatch in async for orelse")
397
- orelse = ast.SubNodeList[ast.CodeBlockStmt](items=valid_orelse, kid=orelse)
398
- raise self.ice(f"IMPLEMENT ME {target} {iter} ")
489
+ body = [self.convert(i) for i in node.body]
490
+ val_body = [i for i in body if isinstance(i, ast.CodeBlockStmt)]
491
+ if len(val_body) != len(body):
492
+ raise self.ice("Length mismatch in for body")
493
+
494
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
495
+ items=val_body, delim=Tok.WS, kid=val_body
496
+ )
497
+ orelse = [self.convert(i) for i in node.orelse]
498
+ val_orelse = [i for i in orelse if isinstance(i, ast.CodeBlockStmt)]
499
+ if len(val_orelse) != len(orelse):
500
+ raise self.ice("Length mismatch in for orelse")
501
+ if orelse:
502
+ valid_orelse = ast.SubNodeList[ast.CodeBlockStmt](
503
+ items=val_orelse, delim=Tok.WS, kid=orelse
504
+ )
505
+ else:
506
+ valid_orelse = None
507
+ fin_orelse = (
508
+ ast.ElseStmt(body=valid_orelse, kid=[valid_orelse])
509
+ if valid_orelse
510
+ else None
511
+ )
512
+ if isinstance(target, ast.Expr) and isinstance(iter, ast.Expr):
513
+ return ast.InForStmt(
514
+ target=target,
515
+ is_async=True,
516
+ collection=iter,
517
+ body=valid_body,
518
+ else_body=fin_orelse,
519
+ kid=(
520
+ [target, iter, valid_body, fin_orelse]
521
+ if fin_orelse
522
+ else [target, iter, valid_body]
523
+ ),
524
+ )
525
+ else:
526
+ raise self.ice()
399
527
 
400
- def proc_while(self, node: py_ast.While) -> ast.CodeBlockStmt:
528
+ def proc_while(self, node: py_ast.While) -> ast.WhileStmt:
401
529
  """Process While node.
402
530
 
403
531
  class While(stmt):
@@ -410,16 +538,20 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
410
538
  body = [self.convert(stmt) for stmt in node.body]
411
539
  valid_body = [stmt for stmt in body if isinstance(stmt, ast.CodeBlockStmt)]
412
540
  if len(valid_body) != len(body):
413
- self.error("Length mismatch in async for body")
414
- body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
415
- orelse = [self.convert(stmt) for stmt in node.orelse]
416
- valid_orelse = [stmt for stmt in orelse if isinstance(stmt, ast.CodeBlockStmt)]
417
- if len(valid_orelse) != len(orelse):
418
- self.error("Length mismatch in async for orelse")
419
- orelse = ast.SubNodeList[ast.CodeBlockStmt](items=valid_orelse, kid=orelse)
420
- raise self.ice(f"IMPLEMENT ME{test}")
541
+ raise self.ice("Length mismatch in while body")
542
+ fin_body = ast.SubNodeList[ast.CodeBlockStmt](
543
+ items=valid_body, delim=Tok.WS, kid=valid_body
544
+ )
545
+ if isinstance(test, ast.Expr):
546
+ return ast.WhileStmt(
547
+ condition=test,
548
+ body=fin_body,
549
+ kid=[test, fin_body],
550
+ )
551
+ else:
552
+ raise self.ice()
421
553
 
422
- def proc_if(self, node: py_ast.If) -> ast.CodeBlockStmt:
554
+ def proc_if(self, node: py_ast.If) -> ast.IfStmt:
423
555
  """Process If node.
424
556
 
425
557
  class If(stmt):
@@ -433,15 +565,44 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
433
565
  valid_body = [stmt for stmt in body if isinstance(stmt, ast.CodeBlockStmt)]
434
566
  if len(valid_body) != len(body):
435
567
  self.error("Length mismatch in async for body")
436
- body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
568
+ body2 = ast.SubNodeList[ast.CodeBlockStmt](
569
+ items=valid_body, delim=Tok.WS, kid=body
570
+ )
571
+
437
572
  orelse = [self.convert(stmt) for stmt in node.orelse]
438
- valid_orelse = [stmt for stmt in orelse if isinstance(stmt, ast.CodeBlockStmt)]
439
- if len(valid_orelse) != len(orelse):
440
- self.error("Length mismatch in async for orelse")
441
- orelse = ast.SubNodeList[ast.CodeBlockStmt](items=valid_orelse, kid=orelse)
442
- raise self.ice(f"IMPLEMENT ME{test}")
573
+ valid_orelse = [
574
+ stmt for stmt in orelse if isinstance(stmt, (ast.CodeBlockStmt))
575
+ ]
576
+ if valid_orelse:
577
+ first_elm = valid_orelse[0]
578
+ if isinstance(first_elm, ast.IfStmt):
579
+ else_body: Optional[ast.ElseIf | ast.ElseStmt] = ast.ElseIf(
580
+ condition=first_elm.condition,
581
+ body=first_elm.body,
582
+ else_body=first_elm.else_body,
583
+ kid=first_elm.kid,
584
+ )
585
+ else:
586
+ orelse2 = ast.SubNodeList[ast.CodeBlockStmt](
587
+ items=valid_orelse, delim=Tok.WS, kid=orelse
588
+ )
589
+ else_body = ast.ElseStmt(body=orelse2, kid=[orelse2])
590
+ else:
591
+ else_body = None
592
+ if isinstance(test, ast.Expr):
593
+ ret = ast.IfStmt(
594
+ condition=test,
595
+ body=body2,
596
+ else_body=else_body,
597
+ kid=(
598
+ [test, body2, else_body] if else_body is not None else [test, body2]
599
+ ),
600
+ )
601
+ else:
602
+ raise self.ice()
603
+ return ret
443
604
 
444
- def proc_with(self, node: py_ast.With) -> ast.CodeBlockStmt:
605
+ def proc_with(self, node: py_ast.With) -> ast.WithStmt:
445
606
  """Process With node.
446
607
 
447
608
  class With(stmt):
@@ -452,16 +613,22 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
452
613
  items = [self.convert(item) for item in node.items]
453
614
  valid_items = [item for item in items if isinstance(item, ast.ExprAsItem)]
454
615
  if len(valid_items) != len(items):
455
- self.error("Length mismatch in with items")
456
- items = ast.SubNodeList[ast.ExprAsItem](items=valid_items, kid=items)
616
+ raise self.ice("Length mismatch in with items")
617
+ items_sub = ast.SubNodeList[ast.ExprAsItem](
618
+ items=valid_items, delim=Tok.COMMA, kid=items
619
+ )
457
620
  body = [self.convert(stmt) for stmt in node.body]
458
621
  valid_body = [stmt for stmt in body if isinstance(stmt, ast.CodeBlockStmt)]
459
622
  if len(valid_body) != len(body):
460
- self.error("Length mismatch in async for body")
461
- body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
462
- raise self.ice("IMPLEMENT ME")
623
+ raise self.ice("Length mismatch in async for body")
624
+ body_sub = ast.SubNodeList[ast.CodeBlockStmt](
625
+ items=valid_body, delim=Tok.WS, kid=body
626
+ )
627
+ return ast.WithStmt(
628
+ is_async=False, exprs=items_sub, body=body_sub, kid=[items_sub, body_sub]
629
+ )
463
630
 
464
- def proc_async_with(self, node: py_ast.AsyncWith) -> ast.CodeBlockStmt:
631
+ def proc_async_with(self, node: py_ast.AsyncWith) -> ast.WithStmt:
465
632
  """Process AsyncWith node.
466
633
 
467
634
  class AsyncWith(stmt):
@@ -472,41 +639,260 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
472
639
  items = [self.convert(item) for item in node.items]
473
640
  valid_items = [item for item in items if isinstance(item, ast.ExprAsItem)]
474
641
  if len(valid_items) != len(items):
475
- self.error("Length mismatch in with items")
476
- items = ast.SubNodeList[ast.ExprAsItem](items=valid_items, kid=items)
642
+ raise self.ice("Length mismatch in with items")
643
+ items_sub = ast.SubNodeList[ast.ExprAsItem](
644
+ items=valid_items, delim=Tok.COMMA, kid=items
645
+ )
477
646
  body = [self.convert(stmt) for stmt in node.body]
478
647
  valid_body = [stmt for stmt in body if isinstance(stmt, ast.CodeBlockStmt)]
479
648
  if len(valid_body) != len(body):
480
- self.error("Length mismatch in async for body")
481
- body = ast.SubNodeList[ast.CodeBlockStmt](items=valid_body, kid=body)
482
- raise self.ice("IMPLEMENT ME")
649
+ raise self.ice("Length mismatch in async for body")
650
+ body_sub = ast.SubNodeList[ast.CodeBlockStmt](
651
+ items=valid_body, delim=Tok.WS, kid=body
652
+ )
653
+ return ast.WithStmt(
654
+ is_async=True, exprs=items_sub, body=body_sub, kid=[items_sub, body_sub]
655
+ )
483
656
 
484
- def proc_raise(self, node: py_ast.Raise) -> None:
485
- """Process python node."""
657
+ def proc_raise(self, node: py_ast.Raise) -> ast.RaiseStmt:
658
+ """Process python node.
486
659
 
487
- def proc_assert(self, node: py_ast.Assert) -> None:
488
- """Process python node."""
660
+ class Raise(stmt):
661
+ exc: expr | None
662
+ cause: expr | None
663
+ """
664
+ exc = self.convert(node.exc) if node.exc else None
665
+ cause = self.convert(node.cause) if node.cause else None
666
+ kid: list[ast.Expr | ast.Token] = []
667
+ if isinstance(exc, ast.Expr):
668
+ kid = [exc]
669
+ if isinstance(cause, ast.Expr):
670
+ kid.append(cause)
671
+ if not (exc and cause):
672
+ kid.append(self.operator(Tok.KW_RAISE, "raise"))
673
+ if (isinstance(cause, ast.Expr) or cause is None) and (
674
+ isinstance(exc, ast.Expr) or exc is None
675
+ ):
676
+ if node.exc and not node.cause:
677
+ return ast.RaiseStmt(
678
+ cause=None,
679
+ from_target=None,
680
+ kid=[self.operator(Tok.KW_RAISE, "raise")],
681
+ )
682
+ else:
683
+ return ast.RaiseStmt(cause=cause, from_target=exc, kid=kid)
684
+ else:
685
+ raise self.ice()
489
686
 
490
- def proc_attribute(self, node: py_ast.Attribute) -> None:
491
- """Process python node."""
687
+ def proc_assert(self, node: py_ast.Assert) -> ast.AssertStmt:
688
+ """Process python node.
492
689
 
493
- def proc_await(self, node: py_ast.Await) -> None:
494
- """Process python node."""
690
+ class Assert(stmt):
691
+ test: expr
692
+ msg: expr | None
693
+ """
694
+ test = self.convert(node.test)
695
+ msg = self.convert(node.msg) if node.msg is not None else None
696
+ if isinstance(test, ast.Expr) and (isinstance(msg, ast.Expr) or msg is None):
697
+ return ast.AssertStmt(
698
+ condition=test,
699
+ error_msg=msg,
700
+ kid=[test, msg] if msg is not None else [test],
701
+ )
702
+ else:
703
+ raise self.ice()
495
704
 
496
- def proc_bin_op(self, node: py_ast.BinOp) -> None:
497
- """Process python node."""
705
+ def proc_attribute(self, node: py_ast.Attribute) -> ast.AtomTrailer:
706
+ """Proassignment targetscess python node.
498
707
 
499
- def proc_bool_op(self, node: py_ast.BoolOp) -> None:
500
- """Process python node."""
708
+ class Attribute(expr):
709
+ if sys.version_info >= (3, 10):
710
+ __match_args__ = ("value", "attr", "ctx")
711
+ value: expr
712
+ attr: _Identifier
713
+ ctx: expr_context
714
+ """
715
+ value = self.convert(node.value)
501
716
 
502
- def proc_break(self, node: py_ast.Break) -> None:
503
- """Process python node."""
717
+ attribute = ast.Name(
718
+ file_path=self.mod_path,
719
+ name=Tok.NAME,
720
+ value=node.attr,
721
+ line=node.lineno,
722
+ col_start=node.col_offset,
723
+ col_end=node.col_offset + len(node.attr),
724
+ pos_start=0,
725
+ pos_end=0,
726
+ )
727
+ if isinstance(value, ast.Expr):
728
+ return ast.AtomTrailer(
729
+ target=value,
730
+ right=attribute,
731
+ is_attr=True,
732
+ is_null_ok=False,
733
+ kid=[value, attribute],
734
+ )
735
+ else:
736
+ raise self.ice()
504
737
 
505
- def proc_call(self, node: py_ast.Call) -> None:
506
- """Process python node."""
738
+ def proc_await(self, node: py_ast.Await) -> ast.AwaitExpr:
739
+ """Process python node.
740
+
741
+ class Await(expr):
742
+ value: expr
743
+ """
744
+ value = self.convert(node.value)
745
+ if isinstance(value, ast.Expr):
746
+ return ast.AwaitExpr(target=value, kid=[value])
747
+ else:
748
+ raise self.ice()
749
+
750
+ def proc_bin_op(self, node: py_ast.BinOp) -> ast.BinaryExpr:
751
+ """Process python node.
752
+
753
+ class BinOp(expr):
754
+ if sys.version_info >= (3, 10):
755
+ __match_args__ = ("left", "op", "right")
756
+ left: expr
757
+ op: operator
758
+ right: expr
759
+ """
760
+ left = self.convert(node.left)
761
+ op = self.convert(node.op)
762
+ right = self.convert(node.right)
763
+ if (
764
+ isinstance(left, ast.Expr)
765
+ and isinstance(op, ast.Token)
766
+ and isinstance(right, ast.Expr)
767
+ ):
768
+ return ast.BinaryExpr(
769
+ left=left,
770
+ op=op,
771
+ right=right,
772
+ kid=[left, op, right],
773
+ )
774
+ else:
775
+ raise self.ice()
776
+
777
+ def proc_unary_op(self, node: py_ast.UnaryOp) -> ast.UnaryExpr:
778
+ """Process python node.
779
+
780
+ class UnaryOp(expr):
781
+ op: unaryop
782
+ operand: expr
783
+ """
784
+ op = self.convert(node.op)
785
+ operand = self.convert(node.operand)
786
+ if isinstance(op, ast.Token) and isinstance(operand, ast.Expr):
787
+ return ast.UnaryExpr(
788
+ op=op,
789
+ operand=operand,
790
+ kid=[op, operand],
791
+ )
792
+ else:
793
+ raise self.ice()
794
+
795
+ def proc_bool_op(self, node: py_ast.BoolOp) -> ast.BoolExpr:
796
+ """Process python node.
797
+
798
+ class BoolOp(expr): a and b and c
799
+ op: boolop
800
+ values: list[expr]
801
+ """
802
+ op = self.convert(node.op)
803
+ values = [self.convert(value) for value in node.values]
804
+ valid = [value for value in values if isinstance(value, ast.Expr)]
805
+ valid_values = ast.SubNodeList[ast.Expr](
806
+ items=valid, delim=Tok.COMMA, kid=values
807
+ )
808
+ if isinstance(op, ast.Token) and len(valid) == len(values):
809
+ return ast.BoolExpr(op=op, values=valid, kid=[op, valid_values])
810
+ else:
811
+ raise self.ice()
507
812
 
508
- def proc_compare(self, node: py_ast.Compare) -> None:
813
+ def proc_break(self, node: py_ast.Break) -> ast.CtrlStmt:
509
814
  """Process python node."""
815
+ break_tok = ast.Token(
816
+ file_path=self.mod_path,
817
+ name=Tok.KW_BREAK,
818
+ value="break",
819
+ line=0,
820
+ col_start=0,
821
+ col_end=0,
822
+ pos_start=0,
823
+ pos_end=0,
824
+ )
825
+ return ast.CtrlStmt(ctrl=break_tok, kid=[break_tok])
826
+
827
+ def proc_call(self, node: py_ast.Call) -> ast.FuncCall:
828
+ """Process python node.
829
+
830
+ class Call(expr):
831
+ if sys.version_info >= (3, 10):
832
+ __match_args__ = ("func", "args", "keywords")
833
+ func: expr
834
+ args: list[expr]
835
+ keywords: list[keyword]
836
+ """
837
+ func = self.convert(node.func)
838
+ params_in: list[ast.Expr | ast.KWPair] = []
839
+ args = [self.convert(arg) for arg in node.args]
840
+ keywords = [self.convert(keyword) for keyword in node.keywords]
841
+
842
+ for i in args:
843
+ if isinstance(i, ast.Expr):
844
+ params_in.append(i)
845
+ for i in keywords:
846
+ if isinstance(i, ast.KWPair):
847
+ params_in.append(i)
848
+ if len(params_in) != 0:
849
+ params_in2 = ast.SubNodeList[ast.Expr | ast.KWPair](
850
+ items=params_in, delim=Tok.COMMA, kid=params_in
851
+ )
852
+ else:
853
+ params_in2 = None
854
+ if isinstance(func, ast.Expr):
855
+ return ast.FuncCall(
856
+ target=func,
857
+ params=params_in2,
858
+ kid=[func, params_in2] if params_in2 else [func],
859
+ )
860
+ else:
861
+ raise self.ice()
862
+
863
+ def proc_compare(self, node: py_ast.Compare) -> ast.CompareExpr:
864
+ """Process python node.
865
+
866
+ class Compare(expr):
867
+ if sys.version_info >= (3, 10):
868
+ __match_args__ = ("left", "ops", "comparators")
869
+ left: expr
870
+ ops: list[cmpop]
871
+ comparators: list[expr]
872
+ """
873
+ left = self.convert(node.left)
874
+ comparators = [self.convert(comparator) for comparator in node.comparators]
875
+ valid_comparators = [
876
+ comparator for comparator in comparators if isinstance(comparator, ast.Expr)
877
+ ]
878
+ comparators2 = ast.SubNodeList[ast.Expr](
879
+ items=valid_comparators, delim=Tok.COMMA, kid=comparators
880
+ )
881
+ ops = [self.convert(op) for op in node.ops]
882
+ valid_ops = [op for op in ops if isinstance(op, ast.Token)]
883
+ ops2 = ast.SubNodeList[ast.Token](items=valid_ops, delim=Tok.COMMA, kid=ops)
884
+
885
+ kids = [left, ops2, comparators2]
886
+ if (
887
+ isinstance(left, ast.Expr)
888
+ and len(ops) == len(valid_ops)
889
+ and len(comparators) == len(valid_comparators)
890
+ ):
891
+ return ast.CompareExpr(
892
+ left=left, rights=valid_comparators, ops=valid_ops, kid=kids
893
+ )
894
+ else:
895
+ raise self.ice()
510
896
 
511
897
  def proc_constant(self, node: py_ast.Constant) -> ast.Literal:
512
898
  """Process python node.
@@ -518,37 +904,155 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
518
904
  s: Any
519
905
  n: int | float | complex
520
906
  """
521
- if isinstance(node.value, str):
522
- return ast.String(
907
+ type_mapping = {
908
+ int: ast.Int,
909
+ float: ast.Float,
910
+ str: ast.String,
911
+ bytes: ast.String,
912
+ bool: ast.Bool,
913
+ type(None): ast.Null,
914
+ }
915
+ value_type = type(node.value)
916
+ if value_type in type_mapping:
917
+ if value_type is None:
918
+ token_type = "NULL"
919
+ elif value_type == str:
920
+ token_type = "STRING"
921
+ else:
922
+ token_type = f"{value_type.__name__.upper()}"
923
+
924
+ return type_mapping[value_type](
523
925
  file_path=self.mod_path,
524
- name=Tok.STRING,
525
- value=node.value,
926
+ name=token_type,
927
+ value=str(node.value),
526
928
  line=node.lineno,
527
929
  col_start=node.col_offset,
528
- col_end=node.col_offset + len(node.value),
930
+ col_end=node.col_offset + len(str(node.value)),
529
931
  pos_start=0,
530
932
  pos_end=0,
531
- kid=[],
532
933
  )
533
934
  else:
534
- raise self.ice()
935
+ raise self.ice("Invalid type for constant")
535
936
 
536
- def proc_continue(self, node: py_ast.Continue) -> None:
937
+ def proc_continue(self, node: py_ast.Continue) -> ast.CtrlStmt:
537
938
  """Process python node."""
939
+ continue_tok = ast.Token(
940
+ file_path=self.mod_path,
941
+ name=Tok.KW_CONTINUE,
942
+ value="continue",
943
+ line=0,
944
+ col_start=0,
945
+ col_end=0,
946
+ pos_start=0,
947
+ pos_end=0,
948
+ )
949
+ return ast.CtrlStmt(ctrl=continue_tok, kid=[continue_tok])
538
950
 
539
- def proc_dict(self, node: py_ast.Dict) -> None:
540
- """Process python node."""
951
+ def proc_dict(self, node: py_ast.Dict) -> ast.DictVal:
952
+ """Process python node.
541
953
 
542
- def proc_dict_comp(self, node: py_ast.DictComp) -> None:
543
- """Process python node."""
954
+ class Dict(expr):
955
+ keys: list[expr | None]
956
+ values: list[expr]
957
+ """
958
+ keys = [self.convert(i) if i else None for i in node.keys]
959
+ values = [self.convert(i) for i in node.values]
960
+ valid_keys = [i for i in keys if isinstance(i, ast.Expr) or i is None]
961
+ valid_values = [i for i in values if isinstance(i, ast.Expr)]
962
+ kvpair: list[ast.KVPair] = []
963
+ for i in range(len(values)):
964
+ key_pluck = valid_keys[i]
965
+ kvp = ast.KVPair(
966
+ key=key_pluck,
967
+ value=valid_values[i],
968
+ kid=(
969
+ [key_pluck, valid_values[i]]
970
+ if key_pluck is not None
971
+ else [valid_values[i]]
972
+ ),
973
+ )
974
+ kvpair.append(kvp)
975
+ return ast.DictVal(
976
+ kv_pairs=kvpair,
977
+ kid=(
978
+ [*kvpair]
979
+ if len(kvpair)
980
+ else [self.operator(Tok.LBRACE, "{"), self.operator(Tok.RBRACE, "}")]
981
+ ),
982
+ )
983
+
984
+ def proc_dict_comp(self, node: py_ast.DictComp) -> ast.DictCompr:
985
+ """Process python node.
986
+
987
+ class DictComp(expr):
988
+ key: expr
989
+ value: expr
990
+ generators: list[comprehension]
991
+ """
992
+ key = self.convert(node.key)
993
+ value = self.convert(node.value)
994
+ if isinstance(key, ast.Expr) and isinstance(value, ast.Expr):
995
+ kv_pair = ast.KVPair(key=key, value=value, kid=[key, value])
996
+ else:
997
+ raise self.ice()
998
+ generators = [self.convert(i) for i in node.generators]
999
+ valid = [i for i in generators if isinstance(i, (ast.InnerCompr))]
1000
+ if len(valid) != len(generators):
1001
+ raise self.ice("Length mismatch in dict compr generators")
1002
+ compr = ast.SubNodeList[ast.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1003
+ return ast.DictCompr(kv_pair=kv_pair, compr=valid, kid=[kv_pair, compr])
544
1004
 
545
1005
  def proc_ellipsis(self, node: py_ast.Ellipsis) -> None:
546
1006
  """Process python node."""
547
1007
 
548
- def proc_except_handler(self, node: py_ast.ExceptHandler) -> None:
549
- """Process python node."""
1008
+ def proc_except_handler(self, node: py_ast.ExceptHandler) -> ast.Except:
1009
+ """Process python node.
1010
+
1011
+ class ExceptHandler(excepthandler):
1012
+ type: expr | None
1013
+ name: _Identifier | None
1014
+ body: list[stmt]
1015
+ """
1016
+ type = self.convert(node.type) if node.type is not None else None
1017
+ name = (
1018
+ ast.Name(
1019
+ file_path=self.mod_path,
1020
+ name=Tok.NAME,
1021
+ value=node.name,
1022
+ line=node.lineno,
1023
+ col_start=node.col_offset,
1024
+ col_end=node.col_offset + len(node.name),
1025
+ pos_start=0,
1026
+ pos_end=0,
1027
+ )
1028
+ if node.name is not None
1029
+ else None
1030
+ )
1031
+
1032
+ body = [self.convert(i) for i in node.body]
1033
+ valid = [i for i in body if isinstance(i, (ast.CodeBlockStmt))]
1034
+ if len(valid) != len(body):
1035
+ raise self.ice("Length mismatch in except handler body")
1036
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
1037
+ items=valid, delim=Tok.WS, kid=valid
1038
+ )
1039
+ kid = []
1040
+ if type:
1041
+ kid.append(type)
1042
+ if name:
1043
+ kid.append(name)
1044
+ kid.append(valid_body)
1045
+ if isinstance(type, ast.Expr) and (isinstance(name, ast.Name) or not name):
1046
+ return ast.Except(
1047
+ ex_type=type,
1048
+ name=name,
1049
+ body=valid_body,
1050
+ kid=kid,
1051
+ )
1052
+ else:
1053
+ raise self.ice()
550
1054
 
551
- def proc_expr(self, node: py_ast.Expr) -> ast.Expr:
1055
+ def proc_expr(self, node: py_ast.Expr) -> ast.ExprStmt:
552
1056
  """Process python node.
553
1057
 
554
1058
  class Expr(stmt):
@@ -556,135 +1060,1204 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
556
1060
  """
557
1061
  value = self.convert(node.value)
558
1062
  if isinstance(value, ast.Expr):
559
- return value
1063
+ return ast.ExprStmt(expr=value, in_fstring=False, kid=[value])
560
1064
  else:
561
1065
  raise self.ice()
562
1066
 
563
- def proc_formatted_value(self, node: py_ast.FormattedValue) -> None:
564
- """Process python node."""
1067
+ def proc_formatted_value(self, node: py_ast.FormattedValue) -> ast.ExprStmt:
1068
+ """Process python node.
1069
+
1070
+ class FormattedValue(expr):
1071
+ if sys.version_info >= (3, 10):
1072
+ __match_args__ = ("value", "conversion", "format_spec")
1073
+ value: expr
1074
+ conversion: int
1075
+ format_spec: expr | None
1076
+ """
1077
+ value = self.convert(node.value)
1078
+ if isinstance(value, ast.Expr):
1079
+ ret = ast.ExprStmt(
1080
+ expr=value,
1081
+ in_fstring=True,
1082
+ kid=[value],
1083
+ )
1084
+ else:
1085
+ raise self.ice()
1086
+ return ret
565
1087
 
566
1088
  def proc_function_type(self, node: py_ast.FunctionType) -> None:
567
1089
  """Process python node."""
568
1090
 
569
- def proc_generator_exp(self, node: py_ast.GeneratorExp) -> None:
570
- """Process python node."""
1091
+ def proc_generator_exp(self, node: py_ast.GeneratorExp) -> ast.GenCompr:
1092
+ """Process python node..
571
1093
 
572
- def proc_global(self, node: py_ast.Global) -> None:
573
- """Process python node."""
1094
+ class SetComp(expr):
1095
+ elt: expr
1096
+ generators: list[comprehension]
1097
+ """
1098
+ elt = self.convert(node.elt)
1099
+ generators = [self.convert(gen) for gen in node.generators]
1100
+ valid = [gen for gen in generators if isinstance(gen, ast.InnerCompr)]
1101
+ if len(generators) != len(valid):
1102
+ raise self.ice("Length mismatch in list comp generators")
1103
+ compr = ast.SubNodeList[ast.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1104
+ if isinstance(elt, ast.Expr):
1105
+ return ast.GenCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1106
+ else:
1107
+ raise self.ice()
574
1108
 
575
- def proc_if_exp(self, node: py_ast.IfExp) -> None:
576
- """Process python node."""
1109
+ def proc_global(self, node: py_ast.Global) -> ast.GlobalStmt:
1110
+ """Process python node.
577
1111
 
578
- def proc_import(self, node: py_ast.Import) -> None:
579
- """Process python node."""
1112
+ class Global(stmt):
1113
+ names: list[_Identifier]
1114
+ """
1115
+ names: list[ast.NameSpec] = []
1116
+ for id in node.names:
1117
+ names.append(
1118
+ ast.Name(
1119
+ file_path=self.mod_path,
1120
+ name=Tok.NAME,
1121
+ value=id,
1122
+ line=node.lineno,
1123
+ col_start=node.col_offset,
1124
+ col_end=node.col_offset + len(id),
1125
+ pos_start=0,
1126
+ pos_end=0,
1127
+ )
1128
+ )
1129
+ target = ast.SubNodeList[ast.NameSpec](items=names, delim=Tok.COMMA, kid=names)
1130
+ return ast.GlobalStmt(target=target, kid=[target])
580
1131
 
581
- def proc_import_from(self, node: py_ast.ImportFrom) -> None:
582
- """Process python node."""
1132
+ def proc_if_exp(self, node: py_ast.IfExp) -> ast.IfElseExpr:
1133
+ """Process python node.
583
1134
 
584
- def proc_joined_str(self, node: py_ast.JoinedStr) -> None:
585
- """Process python node."""
1135
+ class IfExp(expr):
1136
+ test: expr
1137
+ body: expr
1138
+ orelse: expr
1139
+ """
1140
+ test = self.convert(node.test)
1141
+ body = self.convert(node.body)
1142
+ orelse = self.convert(node.orelse)
1143
+ if (
1144
+ isinstance(test, ast.Expr)
1145
+ and isinstance(body, ast.Expr)
1146
+ and isinstance(orelse, ast.Expr)
1147
+ ):
1148
+ return ast.IfElseExpr(
1149
+ value=body, condition=test, else_value=orelse, kid=[body, test, orelse]
1150
+ )
1151
+ else:
1152
+ raise self.ice()
586
1153
 
587
- def proc_lambda(self, node: py_ast.Lambda) -> None:
588
- """Process python node."""
1154
+ def proc_import(self, node: py_ast.Import) -> ast.Import:
1155
+ """Process python node.
589
1156
 
590
- def proc_list(self, node: py_ast.List) -> None:
591
- """Process python node."""
1157
+ class Import(stmt):
1158
+ names: list[alias]
1159
+ """
1160
+ names = [self.convert(name) for name in node.names]
1161
+ valid_names = [name for name in names if isinstance(name, ast.ExprAsItem)]
1162
+ if len(valid_names) != len(names):
1163
+ self.error("Length mismatch in import names")
1164
+ paths = []
1165
+ for name in valid_names:
1166
+ if isinstance(name.expr, ast.Name) and (
1167
+ isinstance(name.alias, ast.Name) or name.alias is None
1168
+ ):
1169
+ paths.append(
1170
+ ast.ModulePath(
1171
+ path=[name.expr],
1172
+ level=0,
1173
+ alias=name.alias,
1174
+ kid=[i for i in name.kid if i],
1175
+ )
1176
+ )
1177
+ # Need to unravel atom trailers
1178
+ else:
1179
+ raise self.ice()
1180
+ lang = ast.Name(
1181
+ file_path=self.mod_path,
1182
+ name=Tok.NAME,
1183
+ value="py",
1184
+ line=node.lineno,
1185
+ col_start=node.col_offset,
1186
+ col_end=0,
1187
+ pos_start=0,
1188
+ pos_end=0,
1189
+ )
1190
+ pytag = ast.SubTag[ast.Name](tag=lang, kid=[lang])
1191
+ ret = ast.Import(
1192
+ lang=pytag,
1193
+ paths=paths,
1194
+ items=None,
1195
+ is_absorb=False,
1196
+ kid=[pytag, *paths],
1197
+ )
1198
+ return ret
592
1199
 
593
- def proc_list_comp(self, node: py_ast.ListComp) -> None:
594
- """Process python node."""
1200
+ def proc_import_from(self, node: py_ast.ImportFrom) -> ast.Import:
1201
+ """Process python node.
1202
+
1203
+ class ImportFrom(stmt):
1204
+ module: str | None
1205
+ names: list[alias]
1206
+ level: int
1207
+ """
1208
+ lang = ast.Name(
1209
+ file_path=self.mod_path,
1210
+ name=Tok.NAME,
1211
+ value="py",
1212
+ line=node.lineno,
1213
+ col_start=node.col_offset,
1214
+ col_end=0,
1215
+ pos_start=0,
1216
+ pos_end=0,
1217
+ )
1218
+ modpaths: list[ast.Name] = []
1219
+ if node.module:
1220
+ for i in node.module.split("."):
1221
+ modpaths.append(
1222
+ ast.Name(
1223
+ file_path=self.mod_path,
1224
+ name=Tok.NAME,
1225
+ value=i,
1226
+ line=node.lineno,
1227
+ col_start=0,
1228
+ col_end=0,
1229
+ pos_start=0,
1230
+ pos_end=0,
1231
+ )
1232
+ )
1233
+ path = ast.ModulePath(
1234
+ path=modpaths,
1235
+ level=node.level,
1236
+ alias=None,
1237
+ kid=modpaths,
1238
+ )
1239
+ names = [self.convert(name) for name in node.names]
1240
+ valid_names = []
1241
+ for name in names:
1242
+ if (
1243
+ isinstance(name, ast.ExprAsItem)
1244
+ and isinstance(name.expr, ast.Name)
1245
+ and (isinstance(name.alias, ast.Name) or name.alias is None)
1246
+ ):
1247
+ valid_names.append(
1248
+ ast.ModuleItem(
1249
+ name=name.expr,
1250
+ alias=name.alias if name.alias is not None else None,
1251
+ kid=[i for i in name.kid if i],
1252
+ )
1253
+ )
1254
+ else:
1255
+ raise self.ice()
1256
+ items = (
1257
+ ast.SubNodeList[ast.ModuleItem](
1258
+ items=valid_names, delim=Tok.COMMA, kid=valid_names
1259
+ )
1260
+ if valid_names
1261
+ else None
1262
+ )
1263
+ if not items:
1264
+ raise self.ice("No valid names in import from")
1265
+ pytag = ast.SubTag[ast.Name](tag=lang, kid=[lang])
1266
+ ret = ast.Import(
1267
+ lang=pytag,
1268
+ paths=[path],
1269
+ items=items,
1270
+ is_absorb=False,
1271
+ kid=[pytag, path, items],
1272
+ )
1273
+ return ret
1274
+
1275
+ def proc_joined_str(self, node: py_ast.JoinedStr) -> ast.FString:
1276
+ """Process python node.
1277
+
1278
+ class JoinedStr(expr):
1279
+ if sys.version_info >= (3, 10):
1280
+ __match_args__ = ("values",)
1281
+ values: list[expr]
1282
+ """
1283
+ values = [self.convert(value) for value in node.values]
1284
+ valid = [
1285
+ value for value in values if isinstance(value, (ast.String, ast.ExprStmt))
1286
+ ]
1287
+ valid_values = ast.SubNodeList[ast.String | ast.ExprStmt](
1288
+ items=valid, delim=None, kid=valid
1289
+ )
1290
+ return ast.FString(parts=valid_values, kid=[valid_values])
1291
+
1292
+ def proc_lambda(self, node: py_ast.Lambda) -> ast.LambdaExpr:
1293
+ """Process python node.
1294
+
1295
+ class Lambda(expr):
1296
+ args: arguments
1297
+ body: expr
1298
+ """
1299
+ args = self.convert(node.args)
1300
+ body = self.convert(node.body)
1301
+ if isinstance(args, ast.FuncSignature) and isinstance(body, ast.Expr):
1302
+ return ast.LambdaExpr(signature=args, body=body, kid=[args, body])
1303
+ else:
1304
+ raise self.ice()
1305
+
1306
+ def proc_list(self, node: py_ast.List) -> ast.ListVal:
1307
+ """Process python node.
1308
+
1309
+ class List(expr):
1310
+ elts: list[expr]
1311
+ ctx: expr_context
1312
+ """
1313
+ elts = [self.convert(elt) for elt in node.elts]
1314
+ valid_elts = [elt for elt in elts if isinstance(elt, ast.Expr)]
1315
+ if len(valid_elts) != len(elts):
1316
+ raise self.ice("Length mismatch in list elements")
1317
+ l_square = self.operator(Tok.LSQUARE, "[")
1318
+ r_square = self.operator(Tok.RSQUARE, "]")
1319
+ return ast.ListVal(
1320
+ values=(
1321
+ ast.SubNodeList[ast.Expr](
1322
+ items=valid_elts, delim=Tok.COMMA, kid=valid_elts
1323
+ )
1324
+ if valid_elts
1325
+ else None
1326
+ ),
1327
+ kid=[*valid_elts] if valid_elts else [l_square, r_square],
1328
+ )
1329
+
1330
+ def proc_list_comp(self, node: py_ast.ListComp) -> ast.ListCompr:
1331
+ """Process python node.
1332
+
1333
+ class ListComp(expr):
1334
+ elt: expr
1335
+ generators: list[comprehension]
1336
+ """
1337
+ elt = self.convert(node.elt)
1338
+ generators = [self.convert(gen) for gen in node.generators]
1339
+ valid = [gen for gen in generators if isinstance(gen, ast.InnerCompr)]
1340
+ if len(generators) != len(valid):
1341
+ raise self.ice("Length mismatch in list comp generators")
1342
+ compr = ast.SubNodeList[ast.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1343
+ if isinstance(elt, ast.Expr):
1344
+ return ast.ListCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1345
+ else:
1346
+ raise self.ice()
1347
+
1348
+ def proc_match(self, node: py_ast.Match) -> ast.MatchStmt:
1349
+ """Process python node.
1350
+
1351
+ class Match(stmt):
1352
+ subject: expr
1353
+ cases: list[match_case]
1354
+ """
1355
+ subject = self.convert(node.subject)
1356
+ cases = [self.convert(i) for i in node.cases]
1357
+ valid = [case for case in cases if isinstance(case, ast.MatchCase)]
1358
+ if isinstance(subject, ast.Expr):
1359
+ return ast.MatchStmt(target=subject, cases=valid, kid=[subject, *valid])
1360
+ else:
1361
+ raise self.ice()
1362
+
1363
+ def proc_match_as(self, node: py_ast.MatchAs) -> ast.MatchAs:
1364
+ """Process python node.
1365
+
1366
+ class MatchAs(pattern):
1367
+ pattern: _Pattern | None
1368
+ name: _Identifier | None
1369
+ """
1370
+ pattern = self.convert(node.pattern) if node.pattern is not None else None
1371
+ name = ast.Name(
1372
+ file_path=self.mod_path,
1373
+ name=Tok.NAME,
1374
+ value=node.name if node.name is not None else "_",
1375
+ line=node.lineno,
1376
+ col_start=node.col_offset,
1377
+ col_end=node.col_offset + len(node.name if node.name is not None else "_"),
1378
+ pos_start=0,
1379
+ pos_end=0,
1380
+ )
1381
+ if isinstance(pattern, ast.MatchPattern) or pattern is None:
1382
+ return ast.MatchAs(
1383
+ name=name,
1384
+ pattern=pattern,
1385
+ kid=[name, pattern] if pattern is not None else [name],
1386
+ )
1387
+ else:
1388
+ raise self.ice()
1389
+
1390
+ def proc_match_class(self, node: py_ast.MatchClass) -> ast.MatchArch:
1391
+ """Process python node.
1392
+
1393
+ class MatchClass(pattern):
1394
+ cls: expr
1395
+ patterns: list[pattern]
1396
+ kwd_attrs: list[_Identifier]
1397
+ kwd_patterns: list[pattern]
1398
+ """
1399
+ cls = self.convert(node.cls)
1400
+ kid = [cls]
1401
+ if len(node.patterns) != 0:
1402
+ patterns = [self.convert(i) for i in node.patterns]
1403
+ valid_patterns = [i for i in patterns if isinstance(i, ast.MatchPattern)]
1404
+ if len(patterns) == len(valid_patterns):
1405
+ patterns_sub = ast.SubNodeList[ast.MatchPattern](
1406
+ items=valid_patterns, delim=Tok.COMMA, kid=valid_patterns
1407
+ )
1408
+ kid.append(patterns_sub)
1409
+ else:
1410
+ raise self.ice()
1411
+ else:
1412
+ patterns_sub = None
1413
+
1414
+ if len(node.kwd_patterns):
1415
+ names: list[ast.Name] = []
1416
+ kv_pairs: list[ast.MatchKVPair] = []
1417
+ for kwd_attrs in node.kwd_attrs:
1418
+ names.append(
1419
+ ast.Name(
1420
+ file_path=self.mod_path,
1421
+ name=Tok.NAME,
1422
+ value=kwd_attrs,
1423
+ line=node.lineno,
1424
+ col_start=node.col_offset,
1425
+ col_end=node.col_offset + len(kwd_attrs),
1426
+ pos_start=0,
1427
+ pos_end=0,
1428
+ )
1429
+ )
1430
+ kwd_patterns = [self.convert(i) for i in node.kwd_patterns]
1431
+ valid_kwd_patterns = [
1432
+ i for i in kwd_patterns if isinstance(i, ast.MatchPattern)
1433
+ ]
1434
+ for i in range(len(kwd_patterns)):
1435
+ kv_pairs.append(
1436
+ ast.MatchKVPair(
1437
+ key=names[i],
1438
+ value=valid_kwd_patterns[i],
1439
+ kid=[names[i], valid_kwd_patterns[i]],
1440
+ )
1441
+ )
1442
+ kw_patterns = ast.SubNodeList[ast.MatchKVPair](
1443
+ items=kv_pairs, delim=Tok.COMMA, kid=kv_pairs
1444
+ )
1445
+ kid.append(kw_patterns)
1446
+ if isinstance(cls, ast.NameSpec):
1447
+ return ast.MatchArch(
1448
+ name=cls, arg_patterns=patterns_sub, kw_patterns=kw_patterns, kid=kid
1449
+ )
1450
+ else:
1451
+ raise self.ice()
1452
+
1453
+ def proc_match_mapping(self, node: py_ast.MatchMapping) -> ast.MatchMapping:
1454
+ """Process python node.
1455
+
1456
+ class MatchMapping(pattern):
1457
+ keys: list[expr]
1458
+ patterns: list[pattern]
1459
+ rest: _Identifier | None
1460
+ """
1461
+ values: list[ast.MatchKVPair | ast.MatchStar] = []
1462
+ keys = [self.convert(i) for i in node.keys]
1463
+ valid_keys = [
1464
+ i for i in keys if isinstance(i, (ast.MatchPattern, ast.NameSpec))
1465
+ ]
1466
+ patterns = [self.convert(i) for i in node.patterns]
1467
+ valid_patterns = [i for i in patterns if isinstance(i, ast.MatchPattern)]
1468
+ for i in range(len(valid_keys)):
1469
+ kv_pair = ast.MatchKVPair(
1470
+ key=valid_keys[i],
1471
+ value=valid_patterns[i],
1472
+ kid=[valid_keys[i], valid_patterns[i]],
1473
+ )
1474
+ values.append(kv_pair)
1475
+ if node.rest:
1476
+ name = ast.Name(
1477
+ file_path=self.mod_path,
1478
+ name=Tok.NAME,
1479
+ value=node.rest,
1480
+ line=node.lineno,
1481
+ col_start=node.col_offset,
1482
+ col_end=node.col_offset + len(node.rest),
1483
+ pos_start=0,
1484
+ pos_end=0,
1485
+ )
1486
+ values.append(ast.MatchStar(name=name, is_list=True, kid=[name]))
1487
+ return ast.MatchMapping(values=values, kid=values)
1488
+
1489
+ def proc_match_or(self, node: py_ast.MatchOr) -> ast.MatchOr:
1490
+ """Process python node.
1491
+
1492
+ class MatchOr(pattern):
1493
+ patterns: list[pattern]
1494
+ """
1495
+ patterns = [self.convert(i) for i in node.patterns]
1496
+ valid = [i for i in patterns if isinstance(i, ast.MatchPattern)]
1497
+ return ast.MatchOr(patterns=valid, kid=valid)
1498
+
1499
+ def proc_match_sequence(self, node: py_ast.MatchSequence) -> ast.MatchSequence:
1500
+ """Process python node.
1501
+
1502
+ class MatchSequence(pattern):
1503
+ patterns: list[pattern]
1504
+ """
1505
+ patterns = [self.convert(i) for i in node.patterns]
1506
+ valid = [i for i in patterns if isinstance(i, ast.MatchPattern)]
1507
+ if len(patterns) == len(valid):
1508
+ return ast.MatchSequence(values=valid, kid=valid)
1509
+ else:
1510
+ raise self.ice()
1511
+
1512
+ def proc_match_singleton(self, node: py_ast.MatchSingleton) -> ast.MatchSingleton:
1513
+ """Process python node.
1514
+
1515
+ class MatchSingleton(pattern):
1516
+ value: Literal[True, False] | None
1517
+ """
1518
+ type = Tok.NULL if node.value is None else Tok.BOOL
1519
+ ret_type = ast.Null if node.value is None else ast.Bool
1520
+ value = ret_type(
1521
+ file_path=self.mod_path,
1522
+ name=type,
1523
+ value=str(node.value),
1524
+ line=node.lineno,
1525
+ col_start=node.col_offset,
1526
+ col_end=node.col_offset + len(str(node.value)),
1527
+ pos_start=0,
1528
+ pos_end=0,
1529
+ )
1530
+ if isinstance(value, (ast.Bool, ast.Null)):
1531
+ return ast.MatchSingleton(value=value, kid=[value])
1532
+ else:
1533
+ raise self.ice()
1534
+
1535
+ def proc_match_star(self, node: py_ast.MatchStar) -> ast.MatchStar:
1536
+ """Process python node.
1537
+
1538
+ class MatchStar(pattern):
1539
+ name: _Identifier | None
1540
+ """
1541
+ name = ast.Name(
1542
+ file_path=self.mod_path,
1543
+ name=Tok.NAME,
1544
+ value=node.name if node.name is not None else "_",
1545
+ line=node.lineno,
1546
+ col_start=node.col_offset,
1547
+ col_end=node.col_offset + len(node.name if node.name is not None else "_"),
1548
+ pos_start=0,
1549
+ pos_end=0,
1550
+ )
1551
+ return ast.MatchStar(name=name, is_list=True, kid=[name])
1552
+
1553
+ def proc_match_value(self, node: py_ast.MatchValue) -> ast.MatchValue:
1554
+ """Process python node.
1555
+
1556
+ class MatchValue(pattern):
1557
+ value: expr
1558
+ """
1559
+ value = self.convert(node.value)
1560
+ if isinstance(value, ast.Expr):
1561
+ return ast.MatchValue(value=value, kid=[value])
1562
+ else:
1563
+ raise self.ice()
1564
+
1565
+ def proc_name(self, node: py_ast.Name) -> ast.Name:
1566
+ """Process python node.
595
1567
 
596
- def proc_match(self, node: py_ast.Match) -> None:
1568
+ class Name(expr):
1569
+ if sys.version_info >= (3, 10):
1570
+ __match_args__ = ("id", "ctx")
1571
+ id: _Identifier
1572
+ ctx: expr_context
1573
+ """
1574
+ ret = ast.Name(
1575
+ file_path=self.mod_path,
1576
+ name=Tok.NAME,
1577
+ value=node.id,
1578
+ line=node.lineno,
1579
+ col_start=node.col_offset,
1580
+ col_end=node.col_offset + len(node.id),
1581
+ pos_start=0,
1582
+ pos_end=0,
1583
+ )
1584
+ return ret
1585
+
1586
+ def proc_named_expr(self, node: py_ast.NamedExpr) -> ast.BinaryExpr:
1587
+ """Process python node.
1588
+
1589
+ class NamedExpr(expr):
1590
+ target: Name
1591
+ value: expr
1592
+ """
1593
+ target = self.convert(node.target)
1594
+ value = self.convert(node.value)
1595
+ if isinstance(value, ast.Expr) and isinstance(target, ast.Name):
1596
+ return ast.BinaryExpr(
1597
+ left=target,
1598
+ op=self.operator(Tok.WALRUS_EQ, ":="),
1599
+ right=value,
1600
+ kid=[target, value],
1601
+ )
1602
+ else:
1603
+ raise self.ice()
1604
+
1605
+ def proc_nonlocal(self, node: py_ast.Nonlocal) -> ast.NonLocalStmt:
1606
+ """Process python node.
1607
+
1608
+ class Nonlocal(stmt):
1609
+ names: list[_Identifier]
1610
+ """
1611
+ names: list[ast.NameSpec] = []
1612
+ for name in node.names:
1613
+ names.append(
1614
+ ast.Name(
1615
+ file_path=self.mod_path,
1616
+ name=Tok.NAME,
1617
+ value=name,
1618
+ line=node.lineno,
1619
+ col_start=node.col_offset,
1620
+ col_end=node.col_offset + len(name),
1621
+ pos_start=0,
1622
+ pos_end=0,
1623
+ )
1624
+ )
1625
+ target = ast.SubNodeList[ast.NameSpec](items=names, delim=Tok.COMMA, kid=names)
1626
+ return ast.NonLocalStmt(target=target, kid=names)
1627
+
1628
+ def proc_pass(self, node: py_ast.Pass) -> ast.Semi:
597
1629
  """Process python node."""
1630
+ return ast.Semi(
1631
+ file_path=self.mod_path,
1632
+ name=Tok.SEMI,
1633
+ value=";",
1634
+ line=0,
1635
+ col_start=0,
1636
+ col_end=0,
1637
+ pos_start=0,
1638
+ pos_end=0,
1639
+ )
1640
+
1641
+ def proc_set(self, node: py_ast.Set) -> ast.SetVal:
1642
+ """Process python node.
1643
+
1644
+ class Set(expr):
1645
+ elts: list[expr]
1646
+ """
1647
+ if len(node.elts) != 0:
1648
+ elts = [self.convert(i) for i in node.elts]
1649
+ valid = [i for i in elts if isinstance(i, (ast.Expr))]
1650
+ if len(valid) != len(elts):
1651
+ raise self.ice("Length mismatch in set body")
1652
+ valid_elts = ast.SubNodeList[ast.Expr](
1653
+ items=valid, delim=Tok.COMMA, kid=valid
1654
+ )
1655
+ kid: list[ast.AstNode] = [*valid]
1656
+ else:
1657
+ valid_elts = None
1658
+ l_brace = self.operator(Tok.LBRACE, "{")
1659
+ r_brace = self.operator(Tok.RBRACE, "}")
1660
+ kid = [l_brace, r_brace]
1661
+ return ast.SetVal(values=valid_elts, kid=kid)
1662
+
1663
+ def proc_set_comp(self, node: py_ast.SetComp) -> ast.ListCompr:
1664
+ """Process python node.
1665
+
1666
+ class SetComp(expr):
1667
+ elt: expr
1668
+ generators: list[comprehension]
1669
+ """
1670
+ elt = self.convert(node.elt)
1671
+ generators = [self.convert(gen) for gen in node.generators]
1672
+ valid = [gen for gen in generators if isinstance(gen, ast.InnerCompr)]
1673
+ if len(generators) != len(valid):
1674
+ raise self.ice("Length mismatch in list comp generators")
1675
+ compr = ast.SubNodeList[ast.InnerCompr](items=valid, delim=Tok.COMMA, kid=valid)
1676
+ if isinstance(elt, ast.Expr):
1677
+ return ast.SetCompr(out_expr=elt, compr=valid, kid=[elt, compr])
1678
+ else:
1679
+ raise self.ice()
1680
+
1681
+ def proc_slice(self, node: py_ast.Slice) -> ast.IndexSlice:
1682
+ """Process python node.
1683
+
1684
+ class Slice(_Slice):
1685
+ lower: expr | None
1686
+ upper: expr | None
1687
+ step: expr | None
1688
+ """
1689
+ lower = self.convert(node.lower) if node.lower else None
1690
+ upper = self.convert(node.upper) if node.upper else None
1691
+ step = self.convert(node.step) if node.step else None
1692
+ valid_kid = [i for i in [lower, upper, step] if i]
1693
+ if not valid_kid:
1694
+ valid_kid = [self.operator(Tok.COLON, ":")]
1695
+ if (
1696
+ (isinstance(lower, ast.Expr) or lower is None)
1697
+ and (isinstance(upper, ast.Expr) or upper is None)
1698
+ and (isinstance(step, ast.Expr) or step is None)
1699
+ ):
1700
+ return ast.IndexSlice(
1701
+ start=lower,
1702
+ stop=upper,
1703
+ step=step,
1704
+ is_range=True,
1705
+ kid=valid_kid,
1706
+ )
1707
+ else:
1708
+ raise self.ice()
1709
+
1710
+ def proc_starred(self, node: py_ast.Starred) -> ast.UnaryExpr:
1711
+ """Process python node.
1712
+
1713
+ class Starred(expr):
1714
+ value: expr
1715
+ ctx: expr_context
1716
+ """
1717
+ star_tok = self.operator(Tok.STAR_MUL, "*")
1718
+ value = self.convert(node.value)
1719
+ if isinstance(value, ast.Expr):
1720
+ return ast.UnaryExpr(operand=value, op=star_tok, kid=[value, star_tok])
1721
+ else:
1722
+ raise self.ice()
1723
+
1724
+ def proc_subscript(self, node: py_ast.Subscript) -> ast.AtomTrailer:
1725
+ """Process python node.
1726
+
1727
+ class Subscript(expr):
1728
+ value: expr
1729
+ slice: _Slice
1730
+ ctx: expr_context
1731
+ """
1732
+ value = self.convert(node.value)
1733
+ slice = self.convert(node.slice)
1734
+ if not isinstance(slice, ast.IndexSlice) and isinstance(slice, ast.Expr):
1735
+ slice = ast.IndexSlice(
1736
+ start=slice,
1737
+ stop=None,
1738
+ step=None,
1739
+ is_range=False,
1740
+ kid=[slice],
1741
+ )
1742
+ if isinstance(value, ast.Expr) and isinstance(slice, ast.IndexSlice):
1743
+ return ast.AtomTrailer(
1744
+ target=value,
1745
+ right=slice,
1746
+ is_attr=False,
1747
+ is_null_ok=False,
1748
+ kid=[value, slice],
1749
+ )
1750
+ else:
1751
+ raise self.ice()
1752
+
1753
+ def proc_try(self, node: py_ast.Try | py_ast.TryStar) -> ast.TryStmt:
1754
+ """Process python node.
1755
+
1756
+ class Try(stmt):
1757
+ body: list[stmt]
1758
+ handlers: list[ExceptHandler]
1759
+ orelse: list[stmt]
1760
+ finalbody: list[stmt]
1761
+ """
1762
+ body = [self.convert(i) for i in node.body]
1763
+ valid = [i for i in body if isinstance(i, (ast.CodeBlockStmt))]
1764
+ if len(valid) != len(body):
1765
+ raise self.ice("Length mismatch in try body")
1766
+ valid_body = ast.SubNodeList[ast.CodeBlockStmt](
1767
+ items=valid, delim=Tok.WS, kid=valid
1768
+ )
1769
+ kid: list[ast.AstNode] = [valid_body]
1770
+
1771
+ if len(node.handlers) != 0:
1772
+ handlers = [self.convert(i) for i in node.handlers]
1773
+ valid_handlers = [i for i in handlers if isinstance(i, (ast.Except))]
1774
+ if len(handlers) != len(valid_handlers):
1775
+ raise self.ice("Length mismatch in try handlers")
1776
+ excepts = ast.SubNodeList[ast.Except](
1777
+ items=valid_handlers, delim=Tok.WS, kid=valid_handlers
1778
+ )
1779
+ kid.append(excepts)
1780
+ else:
1781
+ excepts = None
1782
+
1783
+ if len(node.orelse) != 0:
1784
+ orelse = [self.convert(i) for i in node.orelse]
1785
+ valid_orelse = [i for i in orelse if isinstance(i, (ast.CodeBlockStmt))]
1786
+ if len(orelse) != len(valid_orelse):
1787
+ raise self.ice("Length mismatch in try orelse")
1788
+ else_body = ast.SubNodeList[ast.CodeBlockStmt](
1789
+ items=valid_orelse, delim=Tok.WS, kid=valid_orelse
1790
+ )
1791
+ kid.append(else_body)
1792
+ else:
1793
+ else_body = None
1794
+
1795
+ if len(node.finalbody) != 0:
1796
+ finalbody = [self.convert(i) for i in node.finalbody]
1797
+ valid_finalbody = [
1798
+ i for i in finalbody if isinstance(i, (ast.CodeBlockStmt))
1799
+ ]
1800
+ if len(finalbody) != len(valid_finalbody):
1801
+ raise self.ice("Length mismatch in try finalbody")
1802
+ finally_body = ast.SubNodeList[ast.CodeBlockStmt](
1803
+ items=valid_finalbody, delim=Tok.WS, kid=valid_finalbody
1804
+ )
1805
+ kid.append(finally_body)
1806
+ else:
1807
+ finally_body = None
1808
+
1809
+ return ast.TryStmt(
1810
+ body=valid_body,
1811
+ excepts=excepts,
1812
+ else_body=(
1813
+ ast.ElseStmt(body=else_body, kid=[else_body]) if else_body else None
1814
+ ),
1815
+ finally_body=(
1816
+ ast.FinallyStmt(body=finally_body, kid=[finally_body])
1817
+ if finally_body
1818
+ else None
1819
+ ),
1820
+ kid=kid,
1821
+ )
1822
+
1823
+ def proc_try_star(self, node: py_ast.TryStar) -> ast.TryStmt:
1824
+ """Process python node.
1825
+
1826
+ class Try(stmt):
1827
+ body: list[stmt]
1828
+ handlers: list[ExceptHandler]
1829
+ orelse: list[stmt]
1830
+ finalbody: list[stmt]
1831
+ """
1832
+ return self.proc_try(node)
598
1833
 
599
- def proc_match_as(self, node: py_ast.MatchAs) -> None:
1834
+ def proc_tuple(self, node: py_ast.Tuple) -> ast.TupleVal:
1835
+ """Process python node.
1836
+
1837
+ class Tuple(expr):
1838
+ elts: list[expr]
1839
+ ctx: expr_context
1840
+ """
1841
+ elts = [self.convert(elt) for elt in node.elts]
1842
+ if len(node.elts) != 0:
1843
+ valid = [i for i in elts if isinstance(i, (ast.Expr, ast.KWPair))]
1844
+ if len(elts) != len(valid):
1845
+ raise self.ice("Length mismatch in tuple elts")
1846
+ valid_elts = ast.SubNodeList[ast.Expr | ast.KWPair](
1847
+ items=valid, delim=Tok.COMMA, kid=valid
1848
+ )
1849
+ kid = elts
1850
+ else:
1851
+ l_paren = self.operator(Tok.LPAREN, "(")
1852
+ r_paren = self.operator(Tok.RPAREN, ")")
1853
+ valid_elts = None
1854
+ kid = [l_paren, r_paren]
1855
+ return ast.TupleVal(values=valid_elts, kid=kid)
1856
+
1857
+ def proc_yield(self, node: py_ast.Yield) -> ast.YieldExpr:
1858
+ """Process python node.
1859
+
1860
+ class Yield(expr):
1861
+ value: expr | None
1862
+ """
1863
+ value = self.convert(node.value) if node.value else None
1864
+ if isinstance(value, ast.Expr):
1865
+ return ast.YieldExpr(expr=value, with_from=False, kid=[value])
1866
+ else:
1867
+ raise self.ice()
1868
+
1869
+ def proc_yield_from(self, node: py_ast.YieldFrom) -> ast.YieldExpr:
600
1870
  """Process python node."""
1871
+ value = self.convert(node.value)
1872
+ if isinstance(value, ast.Expr):
1873
+ return ast.YieldExpr(expr=value, with_from=True, kid=[value])
1874
+ else:
1875
+ raise self.ice()
1876
+
1877
+ def proc_alias(self, node: py_ast.alias) -> ast.ExprAsItem:
1878
+ """Process python node.
1879
+
1880
+ class alias(AST):
1881
+ name: _Identifier
1882
+ asname: _Identifier | None
1883
+ """
1884
+ name = ast.Name(
1885
+ file_path=self.mod_path,
1886
+ name=Tok.NAME,
1887
+ value=node.name,
1888
+ line=node.lineno,
1889
+ col_start=node.col_offset,
1890
+ col_end=node.col_offset + len(node.name),
1891
+ pos_start=0,
1892
+ pos_end=0,
1893
+ )
1894
+ asname = (
1895
+ ast.Name(
1896
+ file_path=self.mod_path,
1897
+ name=Tok.NAME,
1898
+ value=node.asname,
1899
+ line=node.lineno,
1900
+ col_start=node.col_offset,
1901
+ col_end=node.col_offset + len(node.asname),
1902
+ pos_start=0,
1903
+ pos_end=0,
1904
+ )
1905
+ if node.asname
1906
+ else None
1907
+ )
1908
+ return ast.ExprAsItem(
1909
+ expr=name, alias=asname, kid=[name, asname] if asname else [name]
1910
+ )
1911
+
1912
+ def proc_arg(self, node: py_ast.arg) -> ast.ParamVar:
1913
+ """Process python node.
1914
+
1915
+ class arg(AST):
1916
+ arg: _Identifier
1917
+ annotation: expr | None
1918
+ """
1919
+ name = ast.Name(
1920
+ file_path=self.mod_path,
1921
+ name=Tok.NAME,
1922
+ value=node.arg,
1923
+ line=node.lineno,
1924
+ col_start=node.col_offset,
1925
+ col_end=node.col_offset + len(node.arg),
1926
+ pos_start=0,
1927
+ pos_end=0,
1928
+ )
1929
+ ann_expr = (
1930
+ self.convert(node.annotation)
1931
+ if node.annotation
1932
+ else ast.Name(
1933
+ file_path=self.mod_path,
1934
+ name=Tok.NAME,
1935
+ value="Any",
1936
+ line=node.lineno,
1937
+ col_start=node.col_offset,
1938
+ col_end=node.col_offset + 3,
1939
+ pos_start=0,
1940
+ pos_end=0,
1941
+ )
1942
+ )
1943
+ if not isinstance(ann_expr, ast.Expr):
1944
+ raise self.ice("Expected annotation to be an expression")
1945
+ annot = ast.SubTag[ast.Expr](tag=ann_expr, kid=[ann_expr])
1946
+ paramvar = ast.ParamVar(
1947
+ name=name, type_tag=annot, unpack=None, value=None, kid=[name, annot]
1948
+ )
1949
+ return paramvar
1950
+
1951
+ def proc_arguments(self, node: py_ast.arguments) -> ast.FuncSignature:
1952
+ """Process python node.
1953
+
1954
+ class arguments(AST):
1955
+ args: list[arg]
1956
+ vararg: arg | None
1957
+ kwonlyargs: list[arg]
1958
+ kw_defaults: list[expr | None]
1959
+ kwarg: arg | None
1960
+ defaults: list[expr]
1961
+ """
1962
+ args = [self.convert(arg) for arg in node.args]
1963
+ vararg = self.convert(node.vararg) if node.vararg else None
1964
+ if vararg and isinstance(vararg, ast.ParamVar):
1965
+ vararg.unpack = ast.Token(
1966
+ file_path=self.mod_path,
1967
+ name=Tok.STAR_MUL,
1968
+ value="*",
1969
+ line=vararg.loc.first_line,
1970
+ col_start=vararg.loc.col_start,
1971
+ col_end=vararg.loc.col_end,
1972
+ pos_start=0,
1973
+ pos_end=0,
1974
+ )
1975
+ vararg.add_kids_left([vararg.unpack])
1976
+ kwonlyargs = [self.convert(arg) for arg in node.kwonlyargs]
1977
+ for i in range(len(kwonlyargs)):
1978
+ kwa = kwonlyargs[i]
1979
+ kwd = node.kw_defaults[i]
1980
+ kwdefault = self.convert(kwd) if kwd else None
1981
+ if (
1982
+ kwdefault
1983
+ and isinstance(kwa, ast.ParamVar)
1984
+ and isinstance(kwdefault, ast.Expr)
1985
+ ):
1986
+ kwa.value = kwdefault
1987
+ kwa.add_kids_right([kwa.value])
1988
+ kwarg = self.convert(node.kwarg) if node.kwarg else None
1989
+ if kwarg and isinstance(kwarg, ast.ParamVar):
1990
+ kwarg.unpack = ast.Token(
1991
+ file_path=self.mod_path,
1992
+ name=Tok.STAR_POW,
1993
+ value="**",
1994
+ line=kwarg.loc.first_line,
1995
+ col_start=kwarg.loc.col_start,
1996
+ col_end=kwarg.loc.col_end,
1997
+ pos_start=0,
1998
+ pos_end=0,
1999
+ )
2000
+ kwarg.add_kids_left([kwarg.unpack])
2001
+ defaults = [self.convert(expr) for expr in node.defaults if type(expr) is None]
2002
+
2003
+ params = [*args]
2004
+ if vararg:
2005
+ params.append(vararg)
2006
+ params += kwonlyargs
2007
+ if kwarg:
2008
+ params.append(kwarg)
2009
+ params += defaults
2010
+
2011
+ valid_params = [param for param in params if isinstance(param, ast.ParamVar)]
2012
+ if len(valid_params) != len(params):
2013
+ raise self.ice("Length mismatch in arguments")
2014
+ if valid_params:
2015
+ fs_params = ast.SubNodeList[ast.ParamVar](
2016
+ items=valid_params, delim=Tok.COMMA, kid=valid_params
2017
+ )
2018
+ return ast.FuncSignature(
2019
+ params=fs_params,
2020
+ return_type=None,
2021
+ kid=[fs_params],
2022
+ )
2023
+ else:
2024
+ _lparen = self.operator(Tok.LPAREN, "(")
2025
+ _rparen = self.operator(Tok.RPAREN, ")")
2026
+ return ast.FuncSignature(
2027
+ params=None,
2028
+ return_type=None,
2029
+ kid=[_lparen, _rparen],
2030
+ )
2031
+
2032
+ def operator(self, tok: Tok, value: str) -> ast.Token:
2033
+ """Create an operator token."""
2034
+ return ast.Token(
2035
+ file_path=self.mod_path,
2036
+ name=tok,
2037
+ value=value,
2038
+ line=0,
2039
+ col_start=0,
2040
+ col_end=0,
2041
+ pos_start=0,
2042
+ pos_end=0,
2043
+ )
601
2044
 
602
- def proc_match_class(self, node: py_ast.MatchClass) -> None:
2045
+ def proc_and(self, node: py_ast.And) -> ast.Token:
603
2046
  """Process python node."""
2047
+ return self.operator(Tok.KW_AND, "and")
604
2048
 
605
- def proc_match_mapping(self, node: py_ast.MatchMapping) -> None:
2049
+ def proc_or(self, node: py_ast.Or) -> ast.Token:
606
2050
  """Process python node."""
2051
+ return self.operator(Tok.KW_OR, "or")
607
2052
 
608
- def proc_match_or(self, node: py_ast.MatchOr) -> None:
2053
+ def proc_add(self, node: py_ast.Add) -> ast.Token:
609
2054
  """Process python node."""
2055
+ return self.operator(Tok.PLUS, "+")
610
2056
 
611
- def proc_match_sequence(self, node: py_ast.MatchSequence) -> None:
2057
+ def proc_bit_and(self, node: py_ast.BitAnd) -> ast.Token:
612
2058
  """Process python node."""
2059
+ return self.operator(Tok.BW_AND, "&")
613
2060
 
614
- def proc_match_singleton(self, node: py_ast.MatchSingleton) -> None:
2061
+ def proc_bit_or(self, node: py_ast.BitOr) -> ast.Token:
615
2062
  """Process python node."""
2063
+ return self.operator(Tok.BW_OR, "|")
616
2064
 
617
- def proc_match_star(self, node: py_ast.MatchStar) -> None:
2065
+ def proc_bit_xor(self, node: py_ast.BitXor) -> ast.Token:
618
2066
  """Process python node."""
2067
+ return self.operator(Tok.BW_XOR, "^")
619
2068
 
620
- def proc_match_value(self, node: py_ast.MatchValue) -> None:
2069
+ def proc_div(self, node: py_ast.Div) -> ast.Token:
621
2070
  """Process python node."""
2071
+ return self.operator(Tok.DIV, "/")
622
2072
 
623
- def proc_name(self, node: py_ast.Name) -> None:
2073
+ def proc_floor_div(self, node: py_ast.FloorDiv) -> ast.Token:
624
2074
  """Process python node."""
2075
+ return self.operator(Tok.FLOOR_DIV, "//")
625
2076
 
626
- def proc_named_expr(self, node: py_ast.NamedExpr) -> None:
2077
+ def proc_l_shift(self, node: py_ast.LShift) -> ast.Token:
627
2078
  """Process python node."""
2079
+ return self.operator(Tok.LSHIFT, "<<")
628
2080
 
629
- def proc_nonlocal(self, node: py_ast.Nonlocal) -> None:
2081
+ def proc_mod(self, node: py_ast.Mod) -> ast.Token:
630
2082
  """Process python node."""
2083
+ return self.operator(Tok.MOD, "%")
631
2084
 
632
- def proc_pass(self, node: py_ast.Pass) -> None:
2085
+ def proc_mult(self, node: py_ast.Mult) -> ast.Token:
633
2086
  """Process python node."""
2087
+ return self.operator(Tok.STAR_MUL, "*")
634
2088
 
635
- def proc_set(self, node: py_ast.Set) -> None:
2089
+ def proc_mat_mult(self, node: py_ast.MatMult) -> ast.Token:
636
2090
  """Process python node."""
2091
+ return self.operator(Tok.DECOR_OP, "@")
637
2092
 
638
- def proc_set_comp(self, node: py_ast.SetComp) -> None:
2093
+ def proc_pow(self, node: py_ast.Pow) -> ast.Token:
639
2094
  """Process python node."""
2095
+ return self.operator(Tok.STAR_POW, "**")
640
2096
 
641
- def proc_slice(self, node: py_ast.Slice) -> None:
2097
+ def proc_r_shift(self, node: py_ast.RShift) -> ast.Token:
642
2098
  """Process python node."""
2099
+ return self.operator(Tok.RSHIFT, ">>")
643
2100
 
644
- def proc_starred(self, node: py_ast.Starred) -> None:
2101
+ def proc_sub(self, node: py_ast.Sub) -> ast.Token:
645
2102
  """Process python node."""
2103
+ return self.operator(Tok.MINUS, "-")
646
2104
 
647
- def proc_subscript(self, node: py_ast.Subscript) -> None:
2105
+ def proc_invert(self, node: py_ast.Invert) -> ast.Token:
648
2106
  """Process python node."""
2107
+ return self.operator(Tok.BW_NOT, "~")
649
2108
 
650
- def proc_try(self, node: py_ast.Try) -> None:
2109
+ def proc_not(self, node: py_ast.Not) -> ast.Token:
651
2110
  """Process python node."""
2111
+ return self.operator(Tok.NOT, "not")
652
2112
 
653
- def proc_try_star(self, node: py_ast.TryStar) -> None:
2113
+ def proc_u_add(self, node: py_ast.UAdd) -> ast.Token:
654
2114
  """Process python node."""
2115
+ return self.operator(Tok.PLUS, "+")
655
2116
 
656
- def proc_tuple(self, node: py_ast.Tuple) -> None:
2117
+ def proc_u_sub(self, node: py_ast.USub) -> ast.Token:
657
2118
  """Process python node."""
2119
+ return self.operator(Tok.MINUS, "-")
658
2120
 
659
- def proc_unary_op(self, node: py_ast.UnaryOp) -> None:
2121
+ def proc_eq(self, node: py_ast.Eq) -> ast.Token:
660
2122
  """Process python node."""
2123
+ return self.operator(Tok.EE, "==")
661
2124
 
662
- def proc_yield(self, node: py_ast.Yield) -> None:
2125
+ def proc_gt(self, node: py_ast.Gt) -> ast.Token:
663
2126
  """Process python node."""
2127
+ return self.operator(Tok.GT, ">")
664
2128
 
665
- def proc_yield_from(self, node: py_ast.YieldFrom) -> None:
2129
+ def proc_gt_e(self, node: py_ast.GtE) -> ast.Token:
666
2130
  """Process python node."""
2131
+ return self.operator(Tok.GTE, ">=")
667
2132
 
668
- def proc_alias(self, node: py_ast.alias) -> None:
2133
+ def proc_in(self, node: py_ast.In) -> ast.Token:
669
2134
  """Process python node."""
2135
+ return self.operator(Tok.KW_IN, "in")
670
2136
 
671
- def proc_arg(self, node: py_ast.arg) -> None:
2137
+ def proc_is(self, node: py_ast.Is) -> ast.Token:
672
2138
  """Process python node."""
2139
+ return self.operator(Tok.KW_IS, "is")
673
2140
 
674
- def proc_arguments(self, node: py_ast.arguments) -> None:
2141
+ def proc_is_not(self, node: py_ast.IsNot) -> ast.Token:
675
2142
  """Process python node."""
2143
+ return self.operator(Tok.KW_ISN, "is not")
676
2144
 
677
- def proc_comprehension(self, node: py_ast.comprehension) -> None:
2145
+ def proc_lt(self, node: py_ast.Lt) -> ast.Token:
678
2146
  """Process python node."""
2147
+ return self.operator(Tok.LT, "<")
679
2148
 
680
- def proc_keyword(self, node: py_ast.keyword) -> None:
2149
+ def proc_lt_e(self, node: py_ast.LtE) -> ast.Token:
681
2150
  """Process python node."""
2151
+ return self.operator(Tok.LTE, "<=")
682
2152
 
683
- def proc_match_case(self, node: py_ast.match_case) -> None:
2153
+ def proc_not_eq(self, node: py_ast.NotEq) -> ast.Token:
684
2154
  """Process python node."""
2155
+ return self.operator(Tok.NE, "!=")
685
2156
 
686
- def proc_withitem(self, node: py_ast.withitem) -> None:
2157
+ def proc_not_in(self, node: py_ast.NotIn) -> ast.Token:
687
2158
  """Process python node."""
2159
+ return self.operator(Tok.KW_NIN, "not in")
2160
+
2161
+ def proc_comprehension(self, node: py_ast.comprehension) -> ast.InnerCompr:
2162
+ """Process python node.
2163
+
2164
+ class comprehension(AST):
2165
+ target: expr
2166
+ iter: expr
2167
+ ifs: list[expr]
2168
+ is_async: int
2169
+ """
2170
+ target = self.convert(node.target)
2171
+ iter = self.convert(node.iter)
2172
+ if len(node.ifs) != 0:
2173
+ ifs_list = [self.convert(ifs) for ifs in node.ifs]
2174
+ valid = [ifs for ifs in ifs_list if isinstance(ifs, ast.Expr)]
2175
+ else:
2176
+ valid = None
2177
+ is_async = node.is_async > 0
2178
+ if isinstance(target, ast.Expr) and isinstance(iter, ast.Expr):
2179
+ return ast.InnerCompr(
2180
+ is_async=is_async,
2181
+ target=target,
2182
+ collection=iter,
2183
+ conditional=valid,
2184
+ kid=[target, iter, *valid] if valid else [target, iter],
2185
+ )
2186
+ else:
2187
+ raise self.ice()
2188
+
2189
+ def proc_keyword(self, node: py_ast.keyword) -> ast.KWPair:
2190
+ """Process python node.
2191
+
2192
+ class keyword(AST):
2193
+ if sys.version_info >= (3, 10):
2194
+ __match_args__ = ("arg", "value")
2195
+ arg: _Identifier | None
2196
+ value: expr
2197
+ """
2198
+ arg = ast.Name(
2199
+ file_path=self.mod_path,
2200
+ name=Tok.NAME,
2201
+ value=node.arg if node.arg is not None else "_",
2202
+ line=node.lineno,
2203
+ col_start=node.col_offset,
2204
+ col_end=node.col_offset + len(node.arg if node.arg is not None else "_"),
2205
+ pos_start=0,
2206
+ pos_end=0,
2207
+ )
2208
+ value = self.convert(node.value)
2209
+ if isinstance(value, ast.Expr):
2210
+ return ast.KWPair(key=arg, value=value, kid=[arg, value])
2211
+ else:
2212
+ raise self.ice()
2213
+
2214
+ def proc_match_case(self, node: py_ast.match_case) -> ast.MatchCase:
2215
+ """Process python node.
2216
+
2217
+ class match_case(AST):
2218
+ pattern: _Pattern
2219
+ guard: expr | None
2220
+ body: list[stmt]
2221
+ """
2222
+ pattern = self.convert(node.pattern)
2223
+ guard = self.convert(node.guard) if node.guard is not None else None
2224
+ body = [self.convert(i) for i in node.body]
2225
+ valid = [i for i in body if isinstance(i, ast.CodeBlockStmt)]
2226
+ if isinstance(pattern, ast.MatchPattern) and (
2227
+ isinstance(guard, ast.Expr) or guard is None
2228
+ ):
2229
+ return ast.MatchCase(
2230
+ pattern=pattern,
2231
+ guard=guard,
2232
+ body=valid,
2233
+ kid=(
2234
+ [pattern, guard, *valid] if guard is not None else [pattern, *valid]
2235
+ ),
2236
+ )
2237
+ else:
2238
+ raise self.ice()
2239
+
2240
+ def proc_withitem(self, node: py_ast.withitem) -> ast.ExprAsItem:
2241
+ """Process python node.
2242
+
2243
+ class withitem(AST):
2244
+ context_expr: expr
2245
+ optional_vars: expr | None
2246
+ """
2247
+ context_expr = self.convert(node.context_expr)
2248
+ optional_vars = (
2249
+ self.convert(node.optional_vars) if node.optional_vars is not None else None
2250
+ )
2251
+ if isinstance(context_expr, ast.Expr) and (
2252
+ isinstance(optional_vars, ast.Expr) or optional_vars is None
2253
+ ):
2254
+ return ast.ExprAsItem(
2255
+ expr=context_expr,
2256
+ alias=optional_vars if optional_vars else None,
2257
+ kid=[context_expr, optional_vars] if optional_vars else [context_expr],
2258
+ )
2259
+ else:
2260
+ raise self.ice()
688
2261
 
689
2262
  def proc_param_spec(self, node: py_ast.ParamSpec) -> None:
690
2263
  """Process python node."""