jaclang 0.8.6__py3-none-any.whl → 0.8.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.
- jaclang/cli/cli.md +3 -3
- jaclang/cli/cli.py +37 -37
- jaclang/cli/cmdreg.py +45 -140
- jaclang/compiler/constant.py +0 -1
- jaclang/compiler/jac.lark +3 -6
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +213 -34
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +0 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +83 -55
- jaclang/compiler/passes/main/pyast_load_pass.py +66 -40
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +265 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +57 -0
- jaclang/compiler/passes/main/type_checker_pass.py +36 -61
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +204 -44
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +171 -11
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +14 -6
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/operations.py +104 -0
- jaclang/compiler/type_system/type_evaluator.py +470 -47
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +79 -94
- jaclang/langserve/engine.jac +253 -230
- jaclang/langserve/server.jac +46 -15
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -312
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_dev_server.py +1 -1
- jaclang/langserve/tests/test_server.py +30 -86
- jaclang/langserve/utils.jac +56 -63
- jaclang/runtimelib/machine.py +7 -0
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +18 -14
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
- jaclang/tests/fixtures/jac_run_py_import.py +13 -0
- jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
- jaclang/tests/fixtures/lambda_self.jac +18 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +103 -18
- jaclang/tests/test_language.py +74 -16
- jaclang/utils/helpers.py +47 -2
- jaclang/utils/module_resolver.py +11 -1
- jaclang/utils/test.py +8 -0
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/METADATA +3 -3
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/RECORD +99 -62
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- jaclang/langserve/tests/server_test/code_test.py +0 -0
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/entry_points.txt +0 -0
|
@@ -220,6 +220,8 @@ class DocIRGenPass(UniPass):
|
|
|
220
220
|
parts.pop()
|
|
221
221
|
parts.append(i.gen.doc_ir)
|
|
222
222
|
parts.append(self.space())
|
|
223
|
+
elif not in_body and isinstance(i, uni.Token) and i.name == Tok.DECOR_OP:
|
|
224
|
+
parts.append(i.gen.doc_ir)
|
|
223
225
|
else:
|
|
224
226
|
parts.append(i.gen.doc_ir)
|
|
225
227
|
parts.append(self.space())
|
|
@@ -257,6 +259,8 @@ class DocIRGenPass(UniPass):
|
|
|
257
259
|
parts.pop()
|
|
258
260
|
parts.append(i.gen.doc_ir)
|
|
259
261
|
parts.append(self.space())
|
|
262
|
+
elif not in_body and isinstance(i, uni.Token) and i.name == Tok.DECOR_OP:
|
|
263
|
+
parts.append(i.gen.doc_ir)
|
|
260
264
|
else:
|
|
261
265
|
parts.append(i.gen.doc_ir)
|
|
262
266
|
parts.append(self.space())
|
|
@@ -275,8 +279,17 @@ class DocIRGenPass(UniPass):
|
|
|
275
279
|
elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
|
|
276
280
|
in_params = False
|
|
277
281
|
has_parens = True
|
|
282
|
+
if isinstance(indent_parts[-1], doc.Line):
|
|
283
|
+
indent_parts.pop()
|
|
278
284
|
parts.append(
|
|
279
|
-
self.indent(
|
|
285
|
+
self.indent(
|
|
286
|
+
self.concat(
|
|
287
|
+
[
|
|
288
|
+
self.tight_line(),
|
|
289
|
+
self.group(self.concat([*indent_parts])),
|
|
290
|
+
]
|
|
291
|
+
)
|
|
292
|
+
)
|
|
280
293
|
)
|
|
281
294
|
parts.append(self.tight_line())
|
|
282
295
|
parts.append(i.gen.doc_ir)
|
|
@@ -346,14 +359,14 @@ class DocIRGenPass(UniPass):
|
|
|
346
359
|
lhs_parts.append(self.space())
|
|
347
360
|
|
|
348
361
|
if eq_tok is not None:
|
|
349
|
-
rhs_concat = self.concat(rhs_parts)
|
|
362
|
+
rhs_concat = self.group(self.concat(rhs_parts))
|
|
350
363
|
node.gen.doc_ir = self.group(
|
|
351
364
|
self.concat(
|
|
352
365
|
[
|
|
353
366
|
*lhs_parts,
|
|
354
367
|
self.space(),
|
|
355
368
|
eq_tok,
|
|
356
|
-
self.
|
|
369
|
+
self.concat([self.space(), rhs_concat]),
|
|
357
370
|
]
|
|
358
371
|
)
|
|
359
372
|
)
|
|
@@ -375,6 +388,20 @@ class DocIRGenPass(UniPass):
|
|
|
375
388
|
parts.pop()
|
|
376
389
|
parts.append(i.gen.doc_ir)
|
|
377
390
|
parts.append(self.space())
|
|
391
|
+
elif i == node.condition and isinstance(i, uni.BoolExpr):
|
|
392
|
+
cond_str = i.gen.doc_ir
|
|
393
|
+
flat = self.concat([cond_str, self.space()])
|
|
394
|
+
broken = self.group(
|
|
395
|
+
self.concat(
|
|
396
|
+
[
|
|
397
|
+
self.text("("),
|
|
398
|
+
self.indent(self.concat([self.line(), cond_str])),
|
|
399
|
+
self.line(),
|
|
400
|
+
self.text(")"),
|
|
401
|
+
]
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
parts.append(self.if_break(broken, flat))
|
|
378
405
|
else:
|
|
379
406
|
parts.append(i.gen.doc_ir)
|
|
380
407
|
parts.append(self.space())
|
|
@@ -397,6 +424,21 @@ class DocIRGenPass(UniPass):
|
|
|
397
424
|
parts.pop()
|
|
398
425
|
parts.append(i.gen.doc_ir)
|
|
399
426
|
parts.append(self.space())
|
|
427
|
+
elif i == node.condition and isinstance(i, uni.BoolExpr):
|
|
428
|
+
cond_str = i.gen.doc_ir
|
|
429
|
+
flat = self.concat([cond_str, self.space()])
|
|
430
|
+
broken = self.group(
|
|
431
|
+
self.concat(
|
|
432
|
+
[
|
|
433
|
+
self.text("("),
|
|
434
|
+
self.indent(self.concat([self.line(), cond_str])),
|
|
435
|
+
self.line(),
|
|
436
|
+
self.text(")"),
|
|
437
|
+
self.space(),
|
|
438
|
+
]
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
parts.append(self.if_break(broken, flat))
|
|
400
442
|
else:
|
|
401
443
|
parts.append(i.gen.doc_ir)
|
|
402
444
|
parts.append(self.space())
|
|
@@ -471,8 +513,18 @@ class DocIRGenPass(UniPass):
|
|
|
471
513
|
parts.append(i.gen.doc_ir)
|
|
472
514
|
elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
|
|
473
515
|
in_params = False
|
|
516
|
+
|
|
517
|
+
if isinstance(indent_parts[-1], doc.Line):
|
|
518
|
+
indent_parts.pop()
|
|
474
519
|
parts.append(
|
|
475
|
-
self.indent(
|
|
520
|
+
self.indent(
|
|
521
|
+
self.concat(
|
|
522
|
+
[
|
|
523
|
+
self.tight_line(),
|
|
524
|
+
self.group(self.concat([*indent_parts])),
|
|
525
|
+
]
|
|
526
|
+
)
|
|
527
|
+
)
|
|
476
528
|
)
|
|
477
529
|
parts.append(self.tight_line())
|
|
478
530
|
parts.append(i.gen.doc_ir)
|
|
@@ -510,12 +562,16 @@ class DocIRGenPass(UniPass):
|
|
|
510
562
|
if isinstance(i, uni.Token) and i.name == Tok.COMMA:
|
|
511
563
|
parts.append(i.gen.doc_ir)
|
|
512
564
|
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
565
|
else:
|
|
517
566
|
parts.append(i.gen.doc_ir)
|
|
518
|
-
broke = self.concat(
|
|
567
|
+
broke = self.concat(
|
|
568
|
+
[
|
|
569
|
+
parts[0],
|
|
570
|
+
self.indent(self.concat([self.hard_line(), *parts[1:-1]])),
|
|
571
|
+
self.hard_line(),
|
|
572
|
+
parts[-1],
|
|
573
|
+
]
|
|
574
|
+
)
|
|
519
575
|
node.gen.doc_ir = self.group(self.if_break(broke, not_broke))
|
|
520
576
|
|
|
521
577
|
def exit_dict_val(self, node: uni.DictVal) -> None:
|
|
@@ -699,12 +755,16 @@ class DocIRGenPass(UniPass):
|
|
|
699
755
|
if isinstance(i, uni.Token) and i.name == Tok.COMMA:
|
|
700
756
|
parts.append(i.gen.doc_ir)
|
|
701
757
|
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
758
|
else:
|
|
706
759
|
parts.append(i.gen.doc_ir)
|
|
707
|
-
broke = self.concat(
|
|
760
|
+
broke = self.concat(
|
|
761
|
+
[
|
|
762
|
+
parts[0],
|
|
763
|
+
self.indent(self.concat([self.hard_line(), *parts[1:-1]])),
|
|
764
|
+
self.hard_line(),
|
|
765
|
+
parts[-1],
|
|
766
|
+
]
|
|
767
|
+
)
|
|
708
768
|
node.gen.doc_ir = self.group(self.if_break(broke, not_broke))
|
|
709
769
|
|
|
710
770
|
def exit_multi_string(self, node: uni.MultiString) -> None:
|
|
@@ -748,17 +808,34 @@ class DocIRGenPass(UniPass):
|
|
|
748
808
|
"""Generate DocIR for list comprehensions."""
|
|
749
809
|
parts: list[doc.DocType] = []
|
|
750
810
|
for i in node.kid:
|
|
751
|
-
|
|
811
|
+
if isinstance(i, uni.InnerCompr):
|
|
812
|
+
parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
|
|
813
|
+
else:
|
|
814
|
+
parts.append(i.gen.doc_ir)
|
|
752
815
|
parts.append(self.space())
|
|
753
816
|
parts.pop()
|
|
754
|
-
node.gen.doc_ir = self.group(
|
|
817
|
+
node.gen.doc_ir = self.group(
|
|
818
|
+
self.concat(
|
|
819
|
+
[
|
|
820
|
+
parts[0],
|
|
821
|
+
self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
|
|
822
|
+
self.tight_line(),
|
|
823
|
+
parts[-1],
|
|
824
|
+
]
|
|
825
|
+
)
|
|
826
|
+
)
|
|
755
827
|
|
|
756
828
|
def exit_inner_compr(self, node: uni.InnerCompr) -> None:
|
|
757
829
|
"""Generate DocIR for inner comprehension clauses."""
|
|
758
830
|
parts: list[doc.DocType] = []
|
|
759
831
|
for i in node.kid:
|
|
760
|
-
|
|
761
|
-
|
|
832
|
+
if isinstance(i, uni.Token) and i.name == Tok.KW_IF:
|
|
833
|
+
parts.append(self.hard_line())
|
|
834
|
+
parts.append(i.gen.doc_ir)
|
|
835
|
+
parts.append(self.space())
|
|
836
|
+
else:
|
|
837
|
+
parts.append(i.gen.doc_ir)
|
|
838
|
+
parts.append(self.space())
|
|
762
839
|
parts.pop()
|
|
763
840
|
node.gen.doc_ir = self.group(self.concat(parts))
|
|
764
841
|
|
|
@@ -772,6 +849,7 @@ class DocIRGenPass(UniPass):
|
|
|
772
849
|
def exit_if_else_expr(self, node: uni.IfElseExpr) -> None:
|
|
773
850
|
"""Generate DocIR for conditional expressions."""
|
|
774
851
|
parts: list[doc.DocType] = []
|
|
852
|
+
need_parens = not isinstance(node.parent, uni.AtomUnit)
|
|
775
853
|
|
|
776
854
|
for i in node.kid:
|
|
777
855
|
if isinstance(i, uni.Expr):
|
|
@@ -782,21 +860,60 @@ class DocIRGenPass(UniPass):
|
|
|
782
860
|
parts.append(self.space())
|
|
783
861
|
else:
|
|
784
862
|
parts.append(i.gen.doc_ir)
|
|
785
|
-
parts.append(self.
|
|
863
|
+
parts.append(self.line())
|
|
786
864
|
parts.pop()
|
|
787
|
-
|
|
865
|
+
|
|
866
|
+
flat = self.group(self.concat(parts))
|
|
867
|
+
parens = self.group(
|
|
868
|
+
self.concat(
|
|
869
|
+
[
|
|
870
|
+
self.text("("),
|
|
871
|
+
self.indent(self.concat([self.tight_line(), flat])),
|
|
872
|
+
self.tight_line(),
|
|
873
|
+
self.text(")"),
|
|
874
|
+
]
|
|
875
|
+
)
|
|
876
|
+
)
|
|
877
|
+
node.gen.doc_ir = flat
|
|
878
|
+
|
|
879
|
+
if need_parens:
|
|
880
|
+
if isinstance(node.parent, uni.Assignment):
|
|
881
|
+
node.gen.doc_ir = self.if_break(
|
|
882
|
+
break_contents=parens, flat_contents=flat
|
|
883
|
+
)
|
|
884
|
+
else:
|
|
885
|
+
node.gen.doc_ir = parens
|
|
788
886
|
|
|
789
887
|
def exit_bool_expr(self, node: uni.BoolExpr) -> None:
|
|
790
888
|
"""Generate DocIR for boolean expressions (and/or)."""
|
|
889
|
+
exprs: list[uni.UniNode] = []
|
|
791
890
|
parts: list[doc.DocType] = []
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
891
|
+
|
|
892
|
+
def __flatten_bool_expr(expr: uni.Expr) -> list[uni.UniNode]:
|
|
893
|
+
if isinstance(expr, uni.BoolExpr):
|
|
894
|
+
out: list[uni.UniNode] = []
|
|
895
|
+
for val in expr.values:
|
|
896
|
+
out += __flatten_bool_expr(val)
|
|
897
|
+
out.append(expr.op)
|
|
898
|
+
out.pop()
|
|
899
|
+
return out
|
|
796
900
|
else:
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
901
|
+
return [expr]
|
|
902
|
+
|
|
903
|
+
exprs = __flatten_bool_expr(node)
|
|
904
|
+
for i in range(0, len(exprs) - 1, 2):
|
|
905
|
+
(
|
|
906
|
+
expr,
|
|
907
|
+
op,
|
|
908
|
+
) = (
|
|
909
|
+
exprs[i],
|
|
910
|
+
exprs[i + 1],
|
|
911
|
+
)
|
|
912
|
+
parts += [expr.gen.doc_ir, self.space(), op.gen.doc_ir, self.line()]
|
|
913
|
+
parts += [exprs[-1].gen.doc_ir, self.line()]
|
|
914
|
+
parts.pop()
|
|
915
|
+
flat = self.concat(parts)
|
|
916
|
+
node.gen.doc_ir = self.group(flat)
|
|
800
917
|
|
|
801
918
|
def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
|
|
802
919
|
"""Generate DocIR for unary expressions."""
|
|
@@ -860,27 +977,49 @@ class DocIRGenPass(UniPass):
|
|
|
860
977
|
parts: list[doc.DocType] = []
|
|
861
978
|
for i in node.kid:
|
|
862
979
|
parts.append(i.gen.doc_ir)
|
|
863
|
-
parts.append(self.space())
|
|
864
|
-
parts.pop()
|
|
865
980
|
node.gen.doc_ir = self.group(self.concat(parts))
|
|
866
981
|
|
|
867
982
|
def exit_gen_compr(self, node: uni.GenCompr) -> None:
|
|
868
983
|
"""Generate DocIR for generator comprehensions."""
|
|
869
984
|
parts: list[doc.DocType] = []
|
|
870
985
|
for i in node.kid:
|
|
871
|
-
|
|
986
|
+
if isinstance(i, uni.InnerCompr):
|
|
987
|
+
parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
|
|
988
|
+
else:
|
|
989
|
+
parts.append(i.gen.doc_ir)
|
|
872
990
|
parts.append(self.space())
|
|
873
991
|
parts.pop()
|
|
874
|
-
node.gen.doc_ir = self.group(
|
|
992
|
+
node.gen.doc_ir = self.group(
|
|
993
|
+
self.concat(
|
|
994
|
+
[
|
|
995
|
+
parts[0],
|
|
996
|
+
self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
|
|
997
|
+
self.tight_line(),
|
|
998
|
+
parts[-1],
|
|
999
|
+
]
|
|
1000
|
+
)
|
|
1001
|
+
)
|
|
875
1002
|
|
|
876
1003
|
def exit_set_compr(self, node: uni.SetCompr) -> None:
|
|
877
1004
|
"""Generate DocIR for set comprehensions."""
|
|
878
1005
|
parts: list[doc.DocType] = []
|
|
879
1006
|
for i in node.kid:
|
|
880
|
-
|
|
1007
|
+
if isinstance(i, uni.InnerCompr):
|
|
1008
|
+
parts.append(self.group(self.concat([self.tight_line(), i.gen.doc_ir])))
|
|
1009
|
+
else:
|
|
1010
|
+
parts.append(i.gen.doc_ir)
|
|
881
1011
|
parts.append(self.space())
|
|
882
1012
|
parts.pop()
|
|
883
|
-
node.gen.doc_ir = self.group(
|
|
1013
|
+
node.gen.doc_ir = self.group(
|
|
1014
|
+
self.concat(
|
|
1015
|
+
[
|
|
1016
|
+
parts[0],
|
|
1017
|
+
self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
|
|
1018
|
+
self.tight_line(),
|
|
1019
|
+
parts[-1],
|
|
1020
|
+
]
|
|
1021
|
+
)
|
|
1022
|
+
)
|
|
884
1023
|
|
|
885
1024
|
def exit_dict_compr(self, node: uni.DictCompr) -> None:
|
|
886
1025
|
"""Generate DocIR for dictionary comprehensions."""
|
|
@@ -889,10 +1028,24 @@ class DocIRGenPass(UniPass):
|
|
|
889
1028
|
if isinstance(i, uni.Token) and i.name in [Tok.STAR_POW, Tok.STAR_MUL]:
|
|
890
1029
|
parts.append(i.gen.doc_ir)
|
|
891
1030
|
else:
|
|
892
|
-
|
|
1031
|
+
if isinstance(i, uni.InnerCompr):
|
|
1032
|
+
parts.append(
|
|
1033
|
+
self.group(self.concat([self.tight_line(), i.gen.doc_ir]))
|
|
1034
|
+
)
|
|
1035
|
+
else:
|
|
1036
|
+
parts.append(i.gen.doc_ir)
|
|
893
1037
|
parts.append(self.space())
|
|
894
1038
|
parts.pop()
|
|
895
|
-
node.gen.doc_ir = self.group(
|
|
1039
|
+
node.gen.doc_ir = self.group(
|
|
1040
|
+
self.concat(
|
|
1041
|
+
[
|
|
1042
|
+
parts[0],
|
|
1043
|
+
self.indent(self.concat([self.tight_line(), *parts[2:-2]])),
|
|
1044
|
+
self.tight_line(),
|
|
1045
|
+
parts[-1],
|
|
1046
|
+
]
|
|
1047
|
+
)
|
|
1048
|
+
)
|
|
896
1049
|
|
|
897
1050
|
def exit_k_w_pair(self, node: uni.KWPair) -> None:
|
|
898
1051
|
"""Generate DocIR for keyword arguments."""
|
|
@@ -1101,7 +1254,23 @@ class DocIRGenPass(UniPass):
|
|
|
1101
1254
|
parts.append(self.space())
|
|
1102
1255
|
prev_item = i
|
|
1103
1256
|
parts.pop()
|
|
1104
|
-
|
|
1257
|
+
broken = self.group(
|
|
1258
|
+
self.concat(
|
|
1259
|
+
[
|
|
1260
|
+
parts[0],
|
|
1261
|
+
self.indent(self.concat([self.tight_line(), *parts[1:-1]])),
|
|
1262
|
+
self.tight_line(),
|
|
1263
|
+
parts[-1],
|
|
1264
|
+
]
|
|
1265
|
+
)
|
|
1266
|
+
)
|
|
1267
|
+
if isinstance(node.parent, uni.Assignment):
|
|
1268
|
+
node.gen.doc_ir = self.if_break(
|
|
1269
|
+
flat_contents=self.group(self.concat(parts[1:-1])),
|
|
1270
|
+
break_contents=broken,
|
|
1271
|
+
)
|
|
1272
|
+
else:
|
|
1273
|
+
node.gen.doc_ir = broken
|
|
1105
1274
|
|
|
1106
1275
|
def exit_expr_as_item(self, node: uni.ExprAsItem) -> None:
|
|
1107
1276
|
"""Generate DocIR for expression as item nodes."""
|
|
@@ -1109,6 +1278,7 @@ class DocIRGenPass(UniPass):
|
|
|
1109
1278
|
for i in node.kid:
|
|
1110
1279
|
parts.append(i.gen.doc_ir)
|
|
1111
1280
|
parts.append(self.space())
|
|
1281
|
+
parts.pop()
|
|
1112
1282
|
node.gen.doc_ir = self.group(self.concat(parts))
|
|
1113
1283
|
|
|
1114
1284
|
def exit_filter_compr(self, node: uni.FilterCompr) -> None:
|
|
@@ -1160,16 +1330,6 @@ class DocIRGenPass(UniPass):
|
|
|
1160
1330
|
parts.append(self.space())
|
|
1161
1331
|
node.gen.doc_ir = self.group(self.concat(parts))
|
|
1162
1332
|
|
|
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))
|
|
1172
|
-
|
|
1173
1333
|
def exit_match_stmt(self, node: uni.MatchStmt) -> None:
|
|
1174
1334
|
"""Generate DocIR for match statements."""
|
|
1175
1335
|
parts: list[doc.DocType] = []
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
This is a pass for formatting Jac code.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from collections import deque
|
|
7
|
+
from typing import Deque, Optional, Tuple
|
|
7
8
|
|
|
8
9
|
import jaclang.compiler.passes.tool.doc_ir as doc
|
|
9
10
|
import jaclang.compiler.unitree as uni
|
|
@@ -19,6 +20,83 @@ class JacFormatPass(Transform[uni.Module, uni.Module]):
|
|
|
19
20
|
self.indent_size = 4
|
|
20
21
|
self.MAX_LINE_LENGTH = settings.max_line_length
|
|
21
22
|
|
|
23
|
+
def _probe_fits(
|
|
24
|
+
self,
|
|
25
|
+
node: doc.DocType,
|
|
26
|
+
indent_level: int,
|
|
27
|
+
width_remaining: int,
|
|
28
|
+
*,
|
|
29
|
+
max_steps: int = 2000,
|
|
30
|
+
) -> bool:
|
|
31
|
+
"""
|
|
32
|
+
Check if flat can be used early.
|
|
33
|
+
|
|
34
|
+
returns True if `node` could be printed *flat* on the current line within
|
|
35
|
+
`width_remaining` columns at `indent_level`.
|
|
36
|
+
Stops early on overflow or hard/literal lines.
|
|
37
|
+
"""
|
|
38
|
+
# Worklist holds (node, indent_level). We only ever push FLAT in a probe.
|
|
39
|
+
work: Deque[Tuple[object, int]] = deque()
|
|
40
|
+
work.append((node, indent_level))
|
|
41
|
+
steps = 0
|
|
42
|
+
remaining = width_remaining
|
|
43
|
+
|
|
44
|
+
while work:
|
|
45
|
+
if steps >= max_steps:
|
|
46
|
+
# Safety cutoff: if it's *that* complex, assume it doesn't fit.
|
|
47
|
+
return False
|
|
48
|
+
steps += 1
|
|
49
|
+
|
|
50
|
+
cur, lvl = work.pop()
|
|
51
|
+
|
|
52
|
+
if isinstance(cur, doc.Text):
|
|
53
|
+
remaining -= len(cur.text)
|
|
54
|
+
if remaining < 0:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
elif isinstance(cur, doc.Line):
|
|
58
|
+
if cur.hard or cur.literal:
|
|
59
|
+
# Any *real* newline (hard or literal) in FLAT means "doesn't fit"
|
|
60
|
+
return False
|
|
61
|
+
if cur.tight:
|
|
62
|
+
# tight softline disappears in flat mode
|
|
63
|
+
continue
|
|
64
|
+
# regular soft line becomes a single space in flat mode
|
|
65
|
+
remaining -= 1
|
|
66
|
+
if remaining < 0:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
# --- Structural nodes (walk children in LIFO) ---
|
|
70
|
+
elif isinstance(cur, doc.Concat):
|
|
71
|
+
# push reversed so we process left-to-right as work is a stack
|
|
72
|
+
for p in reversed(cur.parts):
|
|
73
|
+
work.append((p, lvl))
|
|
74
|
+
|
|
75
|
+
elif isinstance(cur, doc.Group):
|
|
76
|
+
# Probe is always FLAT for groups.
|
|
77
|
+
work.append((cur.contents, lvl))
|
|
78
|
+
|
|
79
|
+
elif isinstance(cur, doc.Indent):
|
|
80
|
+
# In flat mode, indentation has no effect until a newline; keep lvl in case
|
|
81
|
+
# children contain Lines (which would have already returned False).
|
|
82
|
+
work.append((cur.contents, lvl + 1))
|
|
83
|
+
|
|
84
|
+
elif isinstance(cur, doc.Align):
|
|
85
|
+
# In flat mode, alignment doesn’t change width immediately (no newline),
|
|
86
|
+
# but we carry its virtual indent so nested (illegal) Line would be caught.
|
|
87
|
+
align_spaces = cur.n if cur.n is not None else self.indent_size
|
|
88
|
+
extra_levels = align_spaces // self.indent_size
|
|
89
|
+
work.append((cur.contents, lvl + extra_levels))
|
|
90
|
+
|
|
91
|
+
elif isinstance(cur, doc.IfBreak):
|
|
92
|
+
# Flat branch while probing
|
|
93
|
+
work.append((cur.flat_contents, lvl))
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
raise ValueError(f"Unknown DocType in probe: {type(cur)}")
|
|
97
|
+
|
|
98
|
+
return True
|
|
99
|
+
|
|
22
100
|
def transform(self, ir_in: uni.Module) -> uni.Module:
|
|
23
101
|
"""After pass."""
|
|
24
102
|
ir_in.gen.jac = self.format_doc_ir()
|
|
@@ -52,106 +130,78 @@ class JacFormatPass(Transform[uni.Module, uni.Module]):
|
|
|
52
130
|
return " "
|
|
53
131
|
|
|
54
132
|
elif isinstance(doc_node, doc.Group):
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
133
|
+
fits_flat = self._probe_fits(
|
|
134
|
+
doc_node.contents,
|
|
135
|
+
indent_level=indent_level,
|
|
136
|
+
width_remaining=width_remaining,
|
|
137
|
+
)
|
|
138
|
+
return self.format_doc_ir(
|
|
139
|
+
doc_node.contents,
|
|
140
|
+
indent_level,
|
|
141
|
+
width_remaining,
|
|
142
|
+
is_broken=not fits_flat,
|
|
58
143
|
)
|
|
59
|
-
if (
|
|
60
|
-
"\n" not in flat_contents_str
|
|
61
|
-
and len(flat_contents_str) <= width_remaining
|
|
62
|
-
):
|
|
63
|
-
return flat_contents_str
|
|
64
|
-
else:
|
|
65
|
-
full_width_for_broken_content = self.MAX_LINE_LENGTH - (
|
|
66
|
-
indent_level * self.indent_size
|
|
67
|
-
)
|
|
68
|
-
return self.format_doc_ir(
|
|
69
|
-
doc_node.contents,
|
|
70
|
-
indent_level,
|
|
71
|
-
full_width_for_broken_content,
|
|
72
|
-
is_broken=True,
|
|
73
|
-
)
|
|
74
144
|
|
|
75
145
|
elif isinstance(doc_node, doc.Indent):
|
|
76
146
|
new_indent_level = indent_level + 1
|
|
77
|
-
|
|
78
|
-
width_for_indented_content = self.MAX_LINE_LENGTH - (
|
|
79
|
-
new_indent_level * self.indent_size
|
|
80
|
-
)
|
|
81
147
|
return self.format_doc_ir(
|
|
82
148
|
doc_node.contents,
|
|
83
149
|
new_indent_level,
|
|
84
|
-
|
|
150
|
+
width_remaining, # width_for_indented_content # Budget for lines within indent
|
|
85
151
|
is_broken, # is_broken state propagates
|
|
86
152
|
)
|
|
87
153
|
|
|
88
154
|
elif isinstance(doc_node, doc.Concat):
|
|
89
|
-
result =
|
|
90
|
-
# current_line_budget is the space left on the current line for the current part.
|
|
155
|
+
result: list[str] = []
|
|
91
156
|
current_line_budget = width_remaining
|
|
92
157
|
|
|
93
158
|
for part in doc_node.parts:
|
|
94
159
|
part_str = self.format_doc_ir(
|
|
95
160
|
part, indent_level, current_line_budget, is_broken
|
|
96
161
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
result
|
|
162
|
+
|
|
163
|
+
# Trim trailing spaces when a newline begins next
|
|
164
|
+
if part_str.startswith("\n") and result and result[-1].endswith(" "):
|
|
165
|
+
result[-1] = result[-1].rstrip(" ")
|
|
166
|
+
|
|
167
|
+
result.append(part_str)
|
|
100
168
|
|
|
101
169
|
if "\n" in part_str:
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
indent_level * self.indent_size
|
|
170
|
+
# After a newline, reset budget to full width at this indent.
|
|
171
|
+
last_line = part_str.splitlines()[-1]
|
|
172
|
+
full_budget = max(
|
|
173
|
+
0, self.MAX_LINE_LENGTH - indent_level * self.indent_size
|
|
106
174
|
)
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
content_on_last_line = len(last_line_of_part) - indent_str_len
|
|
115
|
-
else: # It was a line not starting with the full indent (e.g. literal \n)
|
|
116
|
-
content_on_last_line = len(last_line_of_part)
|
|
117
|
-
|
|
118
|
-
current_line_budget -= content_on_last_line
|
|
175
|
+
# Compute how many chars are already on the last line (after indent).
|
|
176
|
+
indent_spaces = " " * (indent_level * self.indent_size)
|
|
177
|
+
if last_line.startswith(indent_spaces):
|
|
178
|
+
used = len(last_line) - len(indent_spaces)
|
|
179
|
+
else:
|
|
180
|
+
used = len(last_line)
|
|
181
|
+
current_line_budget = max(0, full_budget - used)
|
|
119
182
|
else:
|
|
120
|
-
|
|
121
|
-
current_line_budget -= len(part_str)
|
|
183
|
+
current_line_budget = max(0, current_line_budget - len(part_str))
|
|
122
184
|
|
|
123
|
-
|
|
124
|
-
current_line_budget = 0
|
|
125
|
-
return result
|
|
185
|
+
return "".join(result)
|
|
126
186
|
|
|
127
187
|
elif isinstance(doc_node, doc.IfBreak):
|
|
128
|
-
if is_broken
|
|
129
|
-
|
|
130
|
-
doc_node.break_contents, indent_level, width_remaining, is_broken
|
|
131
|
-
)
|
|
132
|
-
else:
|
|
133
|
-
return self.format_doc_ir(
|
|
134
|
-
doc_node.flat_contents, indent_level, width_remaining, is_broken
|
|
135
|
-
)
|
|
188
|
+
branch = doc_node.break_contents if is_broken else doc_node.flat_contents
|
|
189
|
+
return self.format_doc_ir(branch, indent_level, width_remaining, is_broken)
|
|
136
190
|
|
|
137
191
|
elif isinstance(doc_node, doc.Align):
|
|
138
192
|
align_spaces = doc_node.n if doc_node.n is not None else self.indent_size
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# ) + align_spaces
|
|
142
|
-
child_indent_level_for_align = indent_level + (
|
|
143
|
-
align_spaces // self.indent_size
|
|
144
|
-
)
|
|
193
|
+
extra_levels = align_spaces // self.indent_size
|
|
194
|
+
child_indent_level = indent_level + extra_levels
|
|
145
195
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
child_width_budget = 0
|
|
196
|
+
# On the same line, alignment "consumes" part of the current budget.
|
|
197
|
+
child_width_budget = max(0, width_remaining - align_spaces)
|
|
149
198
|
|
|
150
199
|
return self.format_doc_ir(
|
|
151
200
|
doc_node.contents,
|
|
152
|
-
|
|
153
|
-
child_width_budget,
|
|
201
|
+
child_indent_level,
|
|
202
|
+
child_width_budget,
|
|
154
203
|
is_broken,
|
|
155
204
|
)
|
|
205
|
+
|
|
156
206
|
else:
|
|
157
207
|
raise ValueError(f"Unknown DocType: {type(doc_node)}")
|
|
@@ -474,15 +474,15 @@ impl JacPlugin.visit_node
|
|
|
474
474
|
glob expected_area = 78.53981633974483;
|
|
475
475
|
|
|
476
476
|
test a1 {
|
|
477
|
-
|
|
477
|
+
assert assertAlmostEqual(calculate_area(RAD), expected_area);
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
test a2 {
|
|
481
481
|
c = Circle(RAD);
|
|
482
|
-
|
|
482
|
+
assert assertAlmostEqual(c.area(), expected_area);
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
test a3 {
|
|
486
486
|
c = Circle(RAD);
|
|
487
|
-
|
|
487
|
+
assert assertEqual(c.shape_type, ShapeType.CIRCLE);
|
|
488
488
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
with entry {
|
|
2
|
-
triple_quoted_string =
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
""";
|
|
2
|
+
triple_quoted_string = """This is a triple quoted string.
|
|
3
|
+
It can span multiple lines.
|
|
4
|
+
It can contain any number of quotes or apostrophes.
|
|
5
|
+
""";
|
|
7
6
|
}
|