jaclang 0.8.7__py3-none-any.whl → 0.8.9__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 (99) hide show
  1. jaclang/cli/cli.py +77 -29
  2. jaclang/cli/cmdreg.py +44 -0
  3. jaclang/compiler/constant.py +6 -2
  4. jaclang/compiler/jac.lark +37 -47
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +356 -61
  7. jaclang/compiler/passes/main/__init__.py +2 -4
  8. jaclang/compiler/passes/main/def_use_pass.py +1 -4
  9. jaclang/compiler/passes/main/predynamo_pass.py +221 -0
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +221 -135
  11. jaclang/compiler/passes/main/pyast_load_pass.py +54 -20
  12. jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
  13. jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
  14. jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
  15. jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
  16. jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
  17. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
  18. jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
  19. jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
  20. jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
  21. jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
  22. jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
  23. jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
  24. jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
  25. jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
  26. jaclang/compiler/passes/main/tests/test_checker_pass.py +190 -0
  27. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +56 -0
  28. jaclang/compiler/passes/main/type_checker_pass.py +29 -73
  29. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +302 -58
  30. jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
  31. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
  32. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
  33. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  34. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +276 -10
  35. jaclang/compiler/passes/transform.py +12 -8
  36. jaclang/compiler/program.py +19 -7
  37. jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
  38. jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
  39. jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
  40. jaclang/compiler/tests/fixtures/python_module.py +1 -0
  41. jaclang/compiler/tests/test_importer.py +39 -0
  42. jaclang/compiler/tests/test_parser.py +49 -0
  43. jaclang/compiler/type_system/type_evaluator.jac +959 -0
  44. jaclang/compiler/type_system/type_utils.py +246 -0
  45. jaclang/compiler/type_system/types.py +58 -2
  46. jaclang/compiler/unitree.py +102 -107
  47. jaclang/langserve/engine.jac +138 -159
  48. jaclang/langserve/server.jac +25 -1
  49. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  50. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  51. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  52. jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
  53. jaclang/langserve/tests/server_test/circle_template.jac +80 -0
  54. jaclang/langserve/tests/server_test/glob_template.jac +4 -0
  55. jaclang/langserve/tests/server_test/test_lang_serve.py +154 -309
  56. jaclang/langserve/tests/server_test/utils.py +153 -116
  57. jaclang/langserve/tests/test_server.py +21 -84
  58. jaclang/langserve/utils.jac +12 -15
  59. jaclang/lib.py +17 -0
  60. jaclang/runtimelib/archetype.py +25 -25
  61. jaclang/runtimelib/constructs.py +2 -2
  62. jaclang/runtimelib/machine.py +63 -46
  63. jaclang/runtimelib/meta_importer.py +27 -1
  64. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  65. jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
  66. jaclang/settings.py +19 -16
  67. jaclang/tests/fixtures/abc_check.jac +3 -3
  68. jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
  69. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  70. jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
  71. jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
  72. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  73. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  74. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  75. jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
  76. jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
  77. jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
  78. jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
  79. jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
  80. jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
  81. jaclang/tests/fixtures/py2jac_params.py +8 -0
  82. jaclang/tests/fixtures/run_test.jac +4 -4
  83. jaclang/tests/test_cli.py +159 -7
  84. jaclang/tests/test_language.py +213 -38
  85. jaclang/tests/test_reference.py +3 -1
  86. jaclang/utils/helpers.py +67 -6
  87. jaclang/utils/module_resolver.py +10 -0
  88. jaclang/utils/test.py +8 -0
  89. jaclang/utils/tests/test_lang_tools.py +4 -15
  90. jaclang/utils/treeprinter.py +0 -18
  91. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -2
  92. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/RECORD +95 -65
  93. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/WHEEL +1 -1
  94. jaclang/compiler/passes/main/inheritance_pass.py +0 -131
  95. jaclang/compiler/type_system/type_evaluator.py +0 -560
  96. jaclang/langserve/dev_engine.jac +0 -645
  97. jaclang/langserve/dev_server.jac +0 -201
  98. /jaclang/{langserve/tests/server_test/code_test.py → tests/fixtures/py2jac_empty.py} +0 -0
  99. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/entry_points.txt +0 -0
@@ -135,20 +135,53 @@ class DocIRGenPass(UniPass):
135
135
  def exit_import(self, node: uni.Import) -> None:
136
136
  """Exit import node."""
137
137
  parts: list[doc.DocType] = []
138
+ mod_items: list[doc.DocType] = []
139
+ has_comment: Optional[doc.DocType] = None
140
+ is_in_items: bool = False
138
141
  for i in node.kid:
139
142
  if isinstance(i, uni.Token) and i.name == Tok.COMMA:
140
- parts.pop()
141
- parts.append(i.gen.doc_ir)
142
- parts.append(self.space())
143
+ if is_in_items:
144
+ mod_items.pop()
145
+ mod_items.append(i.gen.doc_ir)
146
+ mod_items.append(self.line())
147
+ else:
148
+ parts.pop()
149
+ parts.append(i.gen.doc_ir)
150
+ parts.append(self.line())
151
+ elif (
152
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
153
+ ):
154
+ has_comment = i.gen.doc_ir
143
155
  elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
144
156
  parts.pop()
145
157
  parts.append(i.gen.doc_ir)
146
158
  elif isinstance(i, uni.Token) and i.name == Tok.RBRACE:
159
+ is_in_items = False
160
+ mod_items.pop()
161
+ parts.append(
162
+ self.group(
163
+ self.concat(
164
+ [
165
+ self.indent(self.concat([self.line(), *mod_items])),
166
+ self.line(),
167
+ ]
168
+ )
169
+ )
170
+ )
147
171
  parts.append(i.gen.doc_ir)
148
- else:
172
+ elif isinstance(i, uni.Token) and i.name == Tok.LBRACE:
173
+ is_in_items = True
149
174
  parts.append(i.gen.doc_ir)
150
- parts.append(self.space())
175
+ else:
176
+ if is_in_items:
177
+ mod_items.append(i.gen.doc_ir)
178
+ mod_items.append(self.space())
179
+ else:
180
+ parts.append(i.gen.doc_ir)
181
+ parts.append(self.space())
151
182
  node.gen.doc_ir = self.group(self.concat(parts))
183
+ if has_comment:
184
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
152
185
 
153
186
  def exit_module_item(self, node: uni.ModuleItem) -> None:
154
187
  """Generate DocIR for module items."""
@@ -178,6 +211,7 @@ class DocIRGenPass(UniPass):
178
211
  """Generate DocIR for archetypes."""
179
212
  parts: list[doc.DocType] = []
180
213
  body_parts: list[doc.DocType] = []
214
+ has_comment: Optional[doc.DocType] = None
181
215
  prev_item = None
182
216
  in_body = False
183
217
  for i in node.kid:
@@ -198,6 +232,10 @@ class DocIRGenPass(UniPass):
198
232
  parts.pop()
199
233
  parts.append(i.gen.doc_ir)
200
234
  parts.append(self.space())
235
+ elif (
236
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
237
+ ):
238
+ has_comment = i.gen.doc_ir
201
239
  elif isinstance(node.body, Sequence) and i in node.body:
202
240
  if not in_body:
203
241
  body_parts.append(self.hard_line())
@@ -220,16 +258,20 @@ class DocIRGenPass(UniPass):
220
258
  parts.pop()
221
259
  parts.append(i.gen.doc_ir)
222
260
  parts.append(self.space())
261
+ elif not in_body and isinstance(i, uni.Token) and i.name == Tok.DECOR_OP:
262
+ parts.append(i.gen.doc_ir)
223
263
  else:
224
264
  parts.append(i.gen.doc_ir)
225
265
  parts.append(self.space())
226
-
227
266
  node.gen.doc_ir = self.group(self.concat(parts))
267
+ if has_comment:
268
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
228
269
 
229
270
  def exit_ability(self, node: uni.Ability) -> None:
230
271
  """Generate DocIR for abilities."""
231
272
  parts: list[doc.DocType] = []
232
273
  body_parts: list[doc.DocType] = []
274
+ has_comment: Optional[doc.DocType] = None
233
275
  in_body = False
234
276
  for i in node.kid:
235
277
  if i == node.doc or (node.decorators and i in node.decorators):
@@ -257,10 +299,18 @@ class DocIRGenPass(UniPass):
257
299
  parts.pop()
258
300
  parts.append(i.gen.doc_ir)
259
301
  parts.append(self.space())
302
+ elif (
303
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
304
+ ):
305
+ has_comment = i.gen.doc_ir
306
+ elif not in_body and isinstance(i, uni.Token) and i.name == Tok.DECOR_OP:
307
+ parts.append(i.gen.doc_ir)
260
308
  else:
261
309
  parts.append(i.gen.doc_ir)
262
310
  parts.append(self.space())
263
311
  node.gen.doc_ir = self.group(self.concat(parts))
312
+ if has_comment:
313
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
264
314
 
265
315
  def exit_func_signature(self, node: uni.FuncSignature) -> None:
266
316
  """Generate DocIR for function signatures."""
@@ -275,8 +325,17 @@ class DocIRGenPass(UniPass):
275
325
  elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
276
326
  in_params = False
277
327
  has_parens = True
328
+ if isinstance(indent_parts[-1], doc.Line):
329
+ indent_parts.pop()
278
330
  parts.append(
279
- self.indent(self.concat([self.tight_line(), *indent_parts]))
331
+ self.indent(
332
+ self.concat(
333
+ [
334
+ self.tight_line(),
335
+ self.group(self.concat([*indent_parts])),
336
+ ]
337
+ )
338
+ )
280
339
  )
281
340
  parts.append(self.tight_line())
282
341
  parts.append(i.gen.doc_ir)
@@ -346,14 +405,14 @@ class DocIRGenPass(UniPass):
346
405
  lhs_parts.append(self.space())
347
406
 
348
407
  if eq_tok is not None:
349
- rhs_concat = self.concat(rhs_parts)
408
+ rhs_concat = self.group(self.concat(rhs_parts))
350
409
  node.gen.doc_ir = self.group(
351
410
  self.concat(
352
411
  [
353
412
  *lhs_parts,
354
413
  self.space(),
355
414
  eq_tok,
356
- self.indent(self.concat([self.line(), rhs_concat])),
415
+ self.concat([self.space(), rhs_concat]),
357
416
  ]
358
417
  )
359
418
  )
@@ -375,6 +434,20 @@ class DocIRGenPass(UniPass):
375
434
  parts.pop()
376
435
  parts.append(i.gen.doc_ir)
377
436
  parts.append(self.space())
437
+ elif i == node.condition and isinstance(i, uni.BoolExpr):
438
+ cond_str = i.gen.doc_ir
439
+ flat = self.concat([cond_str, self.space()])
440
+ broken = self.group(
441
+ self.concat(
442
+ [
443
+ self.text("("),
444
+ self.indent(self.concat([self.line(), cond_str])),
445
+ self.line(),
446
+ self.text(")"),
447
+ ]
448
+ )
449
+ )
450
+ parts.append(self.group(self.if_break(broken, flat)))
378
451
  else:
379
452
  parts.append(i.gen.doc_ir)
380
453
  parts.append(self.space())
@@ -397,6 +470,21 @@ class DocIRGenPass(UniPass):
397
470
  parts.pop()
398
471
  parts.append(i.gen.doc_ir)
399
472
  parts.append(self.space())
473
+ elif i == node.condition and isinstance(i, uni.BoolExpr):
474
+ cond_str = i.gen.doc_ir
475
+ flat = self.concat([cond_str, self.space()])
476
+ broken = self.group(
477
+ self.concat(
478
+ [
479
+ self.text("("),
480
+ self.indent(self.concat([self.line(), cond_str])),
481
+ self.line(),
482
+ self.text(")"),
483
+ self.space(),
484
+ ]
485
+ )
486
+ )
487
+ parts.append(self.if_break(broken, flat))
400
488
  else:
401
489
  parts.append(i.gen.doc_ir)
402
490
  parts.append(self.space())
@@ -471,8 +559,18 @@ class DocIRGenPass(UniPass):
471
559
  parts.append(i.gen.doc_ir)
472
560
  elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
473
561
  in_params = False
562
+
563
+ if isinstance(indent_parts[-1], doc.Line):
564
+ indent_parts.pop()
474
565
  parts.append(
475
- self.indent(self.concat([self.tight_line(), *indent_parts]))
566
+ self.indent(
567
+ self.concat(
568
+ [
569
+ self.tight_line(),
570
+ self.group(self.concat([*indent_parts])),
571
+ ]
572
+ )
573
+ )
476
574
  )
477
575
  parts.append(self.tight_line())
478
576
  parts.append(i.gen.doc_ir)
@@ -510,12 +608,16 @@ class DocIRGenPass(UniPass):
510
608
  if isinstance(i, uni.Token) and i.name == Tok.COMMA:
511
609
  parts.append(i.gen.doc_ir)
512
610
  parts.append(self.hard_line())
513
- elif isinstance(i, uni.Token) and i.name == Tok.LSQUARE:
514
- parts.append(self.hard_line())
515
- parts.append(i.gen.doc_ir)
516
611
  else:
517
612
  parts.append(i.gen.doc_ir)
518
- broke = self.concat(parts)
613
+ broke = self.concat(
614
+ [
615
+ parts[0],
616
+ self.indent(self.concat([self.hard_line(), *parts[1:-1]])),
617
+ self.hard_line(),
618
+ parts[-1],
619
+ ]
620
+ )
519
621
  node.gen.doc_ir = self.group(self.if_break(broke, not_broke))
520
622
 
521
623
  def exit_dict_val(self, node: uni.DictVal) -> None:
@@ -699,12 +801,16 @@ class DocIRGenPass(UniPass):
699
801
  if isinstance(i, uni.Token) and i.name == Tok.COMMA:
700
802
  parts.append(i.gen.doc_ir)
701
803
  parts.append(self.hard_line())
702
- elif isinstance(i, uni.Token) and i.name == Tok.LPAREN:
703
- parts.append(self.hard_line())
704
- parts.append(i.gen.doc_ir)
705
804
  else:
706
805
  parts.append(i.gen.doc_ir)
707
- broke = self.concat(parts)
806
+ broke = self.concat(
807
+ [
808
+ parts[0],
809
+ self.indent(self.concat([self.hard_line(), *parts[1:-1]])),
810
+ self.hard_line(),
811
+ parts[-1],
812
+ ]
813
+ )
708
814
  node.gen.doc_ir = self.group(self.if_break(broke, not_broke))
709
815
 
710
816
  def exit_multi_string(self, node: uni.MultiString) -> None:
@@ -748,17 +854,34 @@ class DocIRGenPass(UniPass):
748
854
  """Generate DocIR for list comprehensions."""
749
855
  parts: list[doc.DocType] = []
750
856
  for i in node.kid:
751
- parts.append(i.gen.doc_ir)
857
+ if isinstance(i, uni.InnerCompr):
858
+ parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
859
+ else:
860
+ parts.append(i.gen.doc_ir)
752
861
  parts.append(self.space())
753
862
  parts.pop()
754
- node.gen.doc_ir = self.group(self.concat(parts))
863
+ node.gen.doc_ir = self.group(
864
+ self.concat(
865
+ [
866
+ parts[0],
867
+ self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
868
+ self.tight_line(),
869
+ parts[-1],
870
+ ]
871
+ )
872
+ )
755
873
 
756
874
  def exit_inner_compr(self, node: uni.InnerCompr) -> None:
757
875
  """Generate DocIR for inner comprehension clauses."""
758
876
  parts: list[doc.DocType] = []
759
877
  for i in node.kid:
760
- parts.append(i.gen.doc_ir)
761
- parts.append(self.space())
878
+ if isinstance(i, uni.Token) and i.name == Tok.KW_IF:
879
+ parts.append(self.hard_line())
880
+ parts.append(i.gen.doc_ir)
881
+ parts.append(self.space())
882
+ else:
883
+ parts.append(i.gen.doc_ir)
884
+ parts.append(self.space())
762
885
  parts.pop()
763
886
  node.gen.doc_ir = self.group(self.concat(parts))
764
887
 
@@ -772,6 +895,7 @@ class DocIRGenPass(UniPass):
772
895
  def exit_if_else_expr(self, node: uni.IfElseExpr) -> None:
773
896
  """Generate DocIR for conditional expressions."""
774
897
  parts: list[doc.DocType] = []
898
+ need_parens = not isinstance(node.parent, uni.AtomUnit)
775
899
 
776
900
  for i in node.kid:
777
901
  if isinstance(i, uni.Expr):
@@ -782,21 +906,60 @@ class DocIRGenPass(UniPass):
782
906
  parts.append(self.space())
783
907
  else:
784
908
  parts.append(i.gen.doc_ir)
785
- parts.append(self.space())
909
+ parts.append(self.line())
786
910
  parts.pop()
787
- node.gen.doc_ir = self.group(self.concat(parts))
911
+
912
+ flat = self.group(self.concat(parts))
913
+ parens = self.group(
914
+ self.concat(
915
+ [
916
+ self.text("("),
917
+ self.indent(self.concat([self.tight_line(), flat])),
918
+ self.tight_line(),
919
+ self.text(")"),
920
+ ]
921
+ )
922
+ )
923
+ node.gen.doc_ir = flat
924
+
925
+ if need_parens:
926
+ if isinstance(node.parent, uni.Assignment):
927
+ node.gen.doc_ir = self.if_break(
928
+ break_contents=parens, flat_contents=flat
929
+ )
930
+ else:
931
+ node.gen.doc_ir = parens
788
932
 
789
933
  def exit_bool_expr(self, node: uni.BoolExpr) -> None:
790
934
  """Generate DocIR for boolean expressions (and/or)."""
935
+ exprs: list[uni.UniNode] = []
791
936
  parts: list[doc.DocType] = []
792
- for i in node.kid:
793
- if isinstance(i, uni.Token):
794
- parts.append(i.gen.doc_ir)
795
- parts.append(self.space())
937
+
938
+ def __flatten_bool_expr(expr: uni.Expr) -> list[uni.UniNode]:
939
+ if isinstance(expr, uni.BoolExpr):
940
+ out: list[uni.UniNode] = []
941
+ for val in expr.values:
942
+ out += __flatten_bool_expr(val)
943
+ out.append(expr.op)
944
+ out.pop()
945
+ return out
796
946
  else:
797
- parts.append(i.gen.doc_ir)
798
- parts.append(self.line()) # Potential break
799
- node.gen.doc_ir = self.group(self.concat(parts))
947
+ return [expr]
948
+
949
+ exprs = __flatten_bool_expr(node)
950
+ parts += [exprs[0].gen.doc_ir, self.line()]
951
+ for i in range(1, len(exprs), 2):
952
+ (
953
+ op,
954
+ expr,
955
+ ) = (
956
+ exprs[i + 1],
957
+ exprs[i],
958
+ )
959
+ parts += [expr.gen.doc_ir, self.space(), op.gen.doc_ir, self.line()]
960
+ parts.pop()
961
+ flat = self.concat(parts)
962
+ node.gen.doc_ir = self.group(flat)
800
963
 
801
964
  def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
802
965
  """Generate DocIR for unary expressions."""
@@ -860,27 +1023,49 @@ class DocIRGenPass(UniPass):
860
1023
  parts: list[doc.DocType] = []
861
1024
  for i in node.kid:
862
1025
  parts.append(i.gen.doc_ir)
863
- parts.append(self.space())
864
- parts.pop()
865
1026
  node.gen.doc_ir = self.group(self.concat(parts))
866
1027
 
867
1028
  def exit_gen_compr(self, node: uni.GenCompr) -> None:
868
1029
  """Generate DocIR for generator comprehensions."""
869
1030
  parts: list[doc.DocType] = []
870
1031
  for i in node.kid:
871
- parts.append(i.gen.doc_ir)
1032
+ if isinstance(i, uni.InnerCompr):
1033
+ parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
1034
+ else:
1035
+ parts.append(i.gen.doc_ir)
872
1036
  parts.append(self.space())
873
1037
  parts.pop()
874
- node.gen.doc_ir = self.group(self.concat(parts))
1038
+ node.gen.doc_ir = self.group(
1039
+ self.concat(
1040
+ [
1041
+ parts[0],
1042
+ self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
1043
+ self.tight_line(),
1044
+ parts[-1],
1045
+ ]
1046
+ )
1047
+ )
875
1048
 
876
1049
  def exit_set_compr(self, node: uni.SetCompr) -> None:
877
1050
  """Generate DocIR for set comprehensions."""
878
1051
  parts: list[doc.DocType] = []
879
1052
  for i in node.kid:
880
- parts.append(i.gen.doc_ir)
1053
+ if isinstance(i, uni.InnerCompr):
1054
+ parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
1055
+ else:
1056
+ parts.append(i.gen.doc_ir)
881
1057
  parts.append(self.space())
882
1058
  parts.pop()
883
- node.gen.doc_ir = self.group(self.concat(parts))
1059
+ node.gen.doc_ir = self.group(
1060
+ self.concat(
1061
+ [
1062
+ parts[0],
1063
+ self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
1064
+ self.tight_line(),
1065
+ parts[-1],
1066
+ ]
1067
+ )
1068
+ )
884
1069
 
885
1070
  def exit_dict_compr(self, node: uni.DictCompr) -> None:
886
1071
  """Generate DocIR for dictionary comprehensions."""
@@ -889,10 +1074,24 @@ class DocIRGenPass(UniPass):
889
1074
  if isinstance(i, uni.Token) and i.name in [Tok.STAR_POW, Tok.STAR_MUL]:
890
1075
  parts.append(i.gen.doc_ir)
891
1076
  else:
892
- parts.append(i.gen.doc_ir)
1077
+ if isinstance(i, uni.InnerCompr):
1078
+ parts.append(
1079
+ self.group(self.concat([self.tight_line(), i.gen.doc_ir]))
1080
+ )
1081
+ else:
1082
+ parts.append(i.gen.doc_ir)
893
1083
  parts.append(self.space())
894
1084
  parts.pop()
895
- node.gen.doc_ir = self.group(self.concat(parts))
1085
+ node.gen.doc_ir = self.group(
1086
+ self.concat(
1087
+ [
1088
+ parts[0],
1089
+ self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
1090
+ self.tight_line(),
1091
+ parts[-1],
1092
+ ]
1093
+ )
1094
+ )
896
1095
 
897
1096
  def exit_k_w_pair(self, node: uni.KWPair) -> None:
898
1097
  """Generate DocIR for keyword arguments."""
@@ -955,6 +1154,8 @@ class DocIRGenPass(UniPass):
955
1154
  """Generate DocIR for assert statements."""
956
1155
  parts: list[doc.DocType] = []
957
1156
  for i in node.kid:
1157
+ if isinstance(i, uni.Token) and i.name == Tok.SEMI and len(parts):
1158
+ parts.pop()
958
1159
  parts.append(i.gen.doc_ir)
959
1160
  parts.append(self.space())
960
1161
  parts.pop()
@@ -972,6 +1173,7 @@ class DocIRGenPass(UniPass):
972
1173
  def exit_global_vars(self, node: uni.GlobalVars) -> None:
973
1174
  """Generate DocIR for global variables."""
974
1175
  parts: list[doc.DocType] = []
1176
+ has_comment: Optional[doc.DocType] = None
975
1177
  for i in node.kid:
976
1178
  if i == node.doc:
977
1179
  parts.append(i.gen.doc_ir)
@@ -980,15 +1182,22 @@ class DocIRGenPass(UniPass):
980
1182
  parts.pop()
981
1183
  parts.append(i.gen.doc_ir)
982
1184
  parts.append(self.space())
1185
+ elif (
1186
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1187
+ ):
1188
+ has_comment = i.gen.doc_ir
983
1189
  else:
984
1190
  parts.append(i.gen.doc_ir)
985
1191
  parts.append(self.space())
986
1192
  node.gen.doc_ir = self.group(self.concat(parts))
1193
+ if has_comment:
1194
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
987
1195
 
988
1196
  def exit_module_code(self, node: uni.ModuleCode) -> None:
989
1197
  """Generate DocIR for module code."""
990
1198
  parts: list[doc.DocType] = []
991
1199
  body_parts: list[doc.DocType] = []
1200
+ has_comment: Optional[doc.DocType] = None
992
1201
  in_body = False
993
1202
  for i in node.kid:
994
1203
  if node.doc and i is node.doc:
@@ -1000,6 +1209,10 @@ class DocIRGenPass(UniPass):
1000
1209
  elif isinstance(i, uni.Token) and i.name == Tok.COLON:
1001
1210
  parts.pop()
1002
1211
  parts.append(i.gen.doc_ir)
1212
+ elif (
1213
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1214
+ ):
1215
+ has_comment = i.gen.doc_ir
1003
1216
  elif isinstance(node.body, Sequence) and i in node.body:
1004
1217
  if not in_body:
1005
1218
  parts.pop()
@@ -1022,6 +1235,8 @@ class DocIRGenPass(UniPass):
1022
1235
  parts.append(i.gen.doc_ir)
1023
1236
  parts.append(self.space())
1024
1237
  node.gen.doc_ir = self.group(self.concat(parts))
1238
+ if has_comment:
1239
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
1025
1240
 
1026
1241
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
1027
1242
  """Generate DocIR for global statements."""
@@ -1101,7 +1316,25 @@ class DocIRGenPass(UniPass):
1101
1316
  parts.append(self.space())
1102
1317
  prev_item = i
1103
1318
  parts.pop()
1104
- node.gen.doc_ir = self.group(self.concat(parts))
1319
+ broken = self.group(
1320
+ self.concat(
1321
+ [
1322
+ parts[0],
1323
+ self.indent(self.concat([self.tight_line(), *parts[1:-1]])),
1324
+ self.tight_line(),
1325
+ parts[-1],
1326
+ ]
1327
+ )
1328
+ )
1329
+ if isinstance(node.parent, (uni.Assignment)) or (
1330
+ isinstance(node.parent, uni.IfStmt) and isinstance(node.value, uni.BoolExpr)
1331
+ ):
1332
+ node.gen.doc_ir = self.if_break(
1333
+ flat_contents=self.group(self.concat(parts[1:-1])),
1334
+ break_contents=broken,
1335
+ )
1336
+ else:
1337
+ node.gen.doc_ir = broken
1105
1338
 
1106
1339
  def exit_expr_as_item(self, node: uni.ExprAsItem) -> None:
1107
1340
  """Generate DocIR for expression as item nodes."""
@@ -1109,6 +1342,7 @@ class DocIRGenPass(UniPass):
1109
1342
  for i in node.kid:
1110
1343
  parts.append(i.gen.doc_ir)
1111
1344
  parts.append(self.space())
1345
+ parts.pop()
1112
1346
  node.gen.doc_ir = self.group(self.concat(parts))
1113
1347
 
1114
1348
  def exit_filter_compr(self, node: uni.FilterCompr) -> None:
@@ -1130,6 +1364,7 @@ class DocIRGenPass(UniPass):
1130
1364
  def exit_py_inline_code(self, node: uni.PyInlineCode) -> None:
1131
1365
  """Generate DocIR for Python inline code blocks."""
1132
1366
  parts: list[doc.DocType] = []
1367
+ has_comment: Optional[doc.DocType] = None
1133
1368
  for i in node.kid:
1134
1369
  if i == node.doc:
1135
1370
  parts.append(i.gen.doc_ir)
@@ -1139,14 +1374,21 @@ class DocIRGenPass(UniPass):
1139
1374
  parts.append(i.gen.doc_ir)
1140
1375
  parts.append(self.text("::py::"))
1141
1376
  parts.append(self.hard_line())
1377
+ elif (
1378
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1379
+ ):
1380
+ has_comment = i.gen.doc_ir
1142
1381
  else:
1143
1382
  parts.append(i.gen.doc_ir)
1144
1383
  parts.append(self.space())
1145
1384
  node.gen.doc_ir = self.group(self.concat(parts))
1385
+ if has_comment:
1386
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
1146
1387
 
1147
1388
  def exit_test(self, node: uni.Test) -> None:
1148
1389
  """Generate DocIR for test nodes."""
1149
1390
  parts: list[doc.DocType] = []
1391
+ has_comment: Optional[doc.DocType] = None
1150
1392
  for i in node.kid:
1151
1393
  if i == node.doc:
1152
1394
  parts.append(i.gen.doc_ir)
@@ -1155,20 +1397,16 @@ class DocIRGenPass(UniPass):
1155
1397
  if not i.value.startswith("_jac_gen_"):
1156
1398
  parts.append(i.gen.doc_ir)
1157
1399
  parts.append(self.space())
1400
+ elif (
1401
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1402
+ ):
1403
+ has_comment = i.gen.doc_ir
1158
1404
  else:
1159
1405
  parts.append(i.gen.doc_ir)
1160
1406
  parts.append(self.space())
1161
1407
  node.gen.doc_ir = self.group(self.concat(parts))
1162
-
1163
- def exit_check_stmt(self, node: uni.CheckStmt) -> None:
1164
- """Generate DocIR for check statements."""
1165
- parts: list[doc.DocType] = []
1166
- for i in node.kid:
1167
- if isinstance(i, uni.Token) and i.name == Tok.SEMI:
1168
- parts.pop()
1169
- parts.append(i.gen.doc_ir)
1170
- parts.append(self.space())
1171
- node.gen.doc_ir = self.group(self.concat(parts))
1408
+ if has_comment:
1409
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
1172
1410
 
1173
1411
  def exit_match_stmt(self, node: uni.MatchStmt) -> None:
1174
1412
  """Generate DocIR for match statements."""
@@ -1352,6 +1590,7 @@ class DocIRGenPass(UniPass):
1352
1590
  """Generate DocIR for implementation definitions."""
1353
1591
  parts: list[doc.DocType] = []
1354
1592
  body_parts: list[doc.DocType] = []
1593
+ has_comment: Optional[doc.DocType] = None
1355
1594
  in_body = False
1356
1595
  for i in node.kid:
1357
1596
  if i == node.doc or (node.decorators and i in node.decorators):
@@ -1359,6 +1598,10 @@ class DocIRGenPass(UniPass):
1359
1598
  parts.append(self.hard_line())
1360
1599
  elif self.is_within(i, node.target):
1361
1600
  parts.append(i.gen.doc_ir)
1601
+ elif (
1602
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1603
+ ):
1604
+ has_comment = i.gen.doc_ir
1362
1605
  elif (
1363
1606
  in_body
1364
1607
  or isinstance(node.body, Sequence)
@@ -1386,10 +1629,13 @@ class DocIRGenPass(UniPass):
1386
1629
  parts.append(i.gen.doc_ir)
1387
1630
  parts.append(self.space())
1388
1631
  node.gen.doc_ir = self.group(self.concat(parts))
1632
+ if has_comment:
1633
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
1389
1634
 
1390
1635
  def exit_sem_def(self, node: uni.SemDef) -> None:
1391
1636
  """Generate DocIR for semantic definitions."""
1392
1637
  parts: list[doc.DocType] = []
1638
+ has_comment: Optional[doc.DocType] = None
1393
1639
  for i in node.kid:
1394
1640
  if i in node.target:
1395
1641
  parts.append(i.gen.doc_ir)
@@ -1397,10 +1643,16 @@ class DocIRGenPass(UniPass):
1397
1643
  parts.pop()
1398
1644
  parts.append(i.gen.doc_ir)
1399
1645
  parts.append(self.space())
1646
+ elif (
1647
+ i == node.kid[0] and isinstance(i, uni.Token) and i.name == Tok.COMMENT
1648
+ ):
1649
+ has_comment = i.gen.doc_ir
1400
1650
  else:
1401
1651
  parts.append(i.gen.doc_ir)
1402
1652
  parts.append(self.space())
1403
1653
  node.gen.doc_ir = self.group(self.concat(parts))
1654
+ if has_comment:
1655
+ node.gen.doc_ir = self.concat([has_comment, node.gen.doc_ir])
1404
1656
 
1405
1657
  def exit_event_signature(self, node: uni.EventSignature) -> None:
1406
1658
  """Generate DocIR for event signatures."""
@@ -1454,14 +1706,6 @@ class DocIRGenPass(UniPass):
1454
1706
  and isinstance(node.parent, uni.FString)
1455
1707
  )
1456
1708
 
1457
- if "\n" in node.value:
1458
- lines = node.value.split("\n")
1459
- parts: list[doc.DocType] = [self.text(lines[0])]
1460
- for line in lines[1:]:
1461
- parts.append(self.hard_line())
1462
- parts.append(self.text(line.lstrip()))
1463
- node.gen.doc_ir = self.group(self.concat(parts))
1464
- return
1465
1709
  if is_escaped_curly:
1466
1710
  node.gen.doc_ir = self.concat(
1467
1711
  [self.text(node.value), self.text(node.value)]