-
Notifications
You must be signed in to change notification settings - Fork 0
/
print_source.ml
3125 lines (2871 loc) · 103 KB
/
print_source.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Document
open Custom_combinators
open Import
open Asttypes
open Source_tree
let rec list_last = function
| [] -> None
| [ x ] -> Some x
| _ :: xs -> list_last xs
let under_app =
List.exists (function
| Printing_stack.Expression Pexp_apply _ -> true
| _ -> false
)
let module_name { txt; loc } =
match txt with
| None -> underscore ~loc
| Some name -> string ~loc name
let rec_token ~recursive_by_default rf : Source_parsing.Parser.token option =
match rf, recursive_by_default with
| Recursive, false -> Some REC
| Nonrecursive, true -> Some NONREC
| _, _ -> None
module Constant : sig
val pp : loc:Location.t -> constant -> document
(* Helpers. *)
val pp_string_lit : loc:Location.t -> string -> document
(* val pp_quoted_string : loc:Location.t -> delim:string -> string -> document
*)
end = struct
let pp_string_lit ~loc _s =
let s =
Source_parsing.Source.source_between loc.Location.loc_start loc.loc_end
in
quoted_string ~loc s
(*
let pp_quoted_string ~loc ~delim s =
let delim = PPrint.string delim in
braces
(enclose ~before:PPrint.(delim ^^ bar) ~after:PPrint.(bar ^^ delim)
(quoted_string ~loc s))
*)
let pp ~loc = function
| Pconst_float (nb, suffix_opt) | Pconst_integer (nb, suffix_opt) ->
let nb =
match suffix_opt with
| None -> nb
| Some s -> nb ^ (String.make 1 s)
in
string ~loc nb
| Pconst_char c -> char ~loc c
| Pconst_string (_, None) ->
let s = Source_parsing.Source.source_between loc.loc_start loc.loc_end in
quoted_string ~adjust_indent:true ~loc s
| Pconst_string (_, Some _) ->
let s = Source_parsing.Source.source_between loc.loc_start loc.loc_end in
quoted_string ~loc s
end
module Polymorphic_variant_tag : sig
val pp : label loc -> document
end = struct
let pp tag =
string ~loc:tag.loc ("`" ^ tag.txt)
end
module rec Empty_delimited : sig
val pp
: loc:Location.t
-> ?extension:string loc
-> attributes
-> Parser.token
-> Parser.token
-> document
end = struct
let pp ~(loc:Location.t) ?extension attrs start_tok end_tok =
let fst =
let token = Token.pp ~inside:loc start_tok in
Keyword.decorate token ~extension attrs
in
let snd = Token.pp ~inside:loc end_tok in
group (fst ^/^ snd)
end
and Pattern : sig
val pp : ?indent:int -> pattern -> document
end = struct
let rec pp
?indent { ppat_desc; ppat_attributes; ppat_loc; ppat_ext_attributes; _ }
=
let desc =
pp_desc ?indent ~loc:ppat_loc ~ext_attrs:ppat_ext_attributes ppat_desc
in
Attribute.attach_to_item desc ppat_attributes
and pp_alias pat alias =
let pat = pp pat in
let alias = str alias in
let as_ = Token.pp ~after:pat ~before:alias AS in
let aliaser = group (as_ ^/^ alias) in
let doc = pat ^^ group (nest 2 (break 1 ^^ aliaser)) in
doc
and pp_interval c1 c2 =
let c1 = Constant.pp ~loc:c1.loc c1.txt in
let c2 = Constant.pp ~loc:c2.loc c2.txt in
let dotdot = Token.pp ~after:c1 ~before:c2 DOTDOT in
c1 ^/^ dotdot ^/^ c2
(* FIXME? nest on the outside, not in each of them. *)
and pp_tuple lst =
group (tuple_fields pp (List.hd lst) (List.tl lst))
and pp_list_literal ~loc elts =
List_like.pp ~loc ~formatting:Wrap (* TODO: add an option *) ~left:LBRACKET
~right:RBRACKET (List.map pp elts)
and pp_cons hd tl =
let hd = pp hd in
let tl = pp tl in
let cons = Token.pp ~after:hd ~before:tl COLONCOLON in
let doc = infix ~indent:2 ~spaces:1 cons hd tl in
doc
and pp_construct name arg_opt =
let name = Longident.pp name in
match arg_opt with
| None -> name
| Some (vars, p) ->
let p = pp p in
let p =
match vars with
| [] -> p
| v :: vs ->
let vars = flow (break 1) (str v) (List.map str vs) in
let lpar = Token.pp ~after:name ~before:vars LPAREN in
let typ = Token.pp ~after:lpar ~before:vars TYPE in
let rpar = Token.pp ~after:vars ~before:p TYPE in
prefix ~indent:0 ~spaces:1 (group (lpar ^^ typ ^/^ vars ^^ rpar)) p
in
prefix ~indent:2 ~spaces:1 name p
and pp_variant tag arg_opt =
let tag = Polymorphic_variant_tag.pp tag in
match arg_opt with
| None -> tag
| Some p ->
let arg = pp p in
(tag ^/^ arg)
and pp_record_field (lid, ctyo, pato) =
let binding : Binding.t =
{
lhs = Longident.pp lid;
params = [];
constr = Option.map Core_type.pp ctyo;
coerce = None;
rhs = Binding.Rhs.of_opt pp pato
}
in
group (Binding.pp binding)
and pp_record ~loc pats closed =
let fields = List.map pp_record_field pats in
let fields =
match closed with
| OClosed -> fields
| OOpen loc -> fields @ [ underscore ~loc ]
in
List_like.pp ~loc ~formatting:!Options.Record.pattern ~left:LBRACE
~right:RBRACE fields
and pp_array ~loc pats =
let pats = List.map pp pats in
(* TODO: add an option *)
List_like.pp ~loc ~formatting:Wrap ~left:LBRACKETBAR ~right:BARRBRACKET pats
and pp_or ~indent p1 p2 =
let p1 = pp ~indent p1 in
let p2 = pp ~indent p2 in
let pipe = Token.pp ~after:p1 ~before:p2 BAR in
let or_ = p1 ^/^ group (pipe ^/^ p2) in
or_
and pp_constraint ~loc p ct =
let p = pp p in
let ct = Core_type.pp ct in
let colon = Token.pp ~after:p ~before:ct COLON in
parens ~loc (p ^/^ colon ^/^ ct)
and pp_type ~loc typ =
let typ = Longident.pp typ in
let hash = Token.pp ~inside:loc ~before:typ HASH in
hash ^^ typ
and pp_lazy ~loc ~ext_attrs:(extension, attrs) p =
let p = pp p in
let kw =
let tok = Token.pp ~inside:loc ~before:p LAZY in
Keyword.decorate tok ~extension attrs
in
kw ^/^ p
and pp_unpack ~loc ~ext_attrs:(extension, attrs) mod_name ct =
let mod_name = module_name mod_name in
let with_constraint =
match ct with
| None -> mod_name
| Some pkg ->
let constr = Core_type.Package_type.pp pkg in
let colon = Token.pp ~after:mod_name ~before:constr COLON in
mod_name ^/^ colon ^/^ constr
in
let module_ =
let lpar = Token.pp ~inside:loc ~before:with_constraint LPAREN in
let mod_ = Token.pp ~after:lpar ~before:with_constraint MODULE in
Keyword.decorate (lpar ^^ mod_) ~extension attrs
in
let rparen = Token.pp ~inside:loc ~after:with_constraint RPAREN in
prefix ~indent:2 ~spaces:1 module_ with_constraint ^/^ rparen
and pp_exception ~loc ~ext_attrs:(extension, attrs) p =
let p = pp p in
let kw =
let tok = Token.pp ~inside:loc ~before:p EXCEPTION in
Keyword.decorate tok ~extension attrs
in
kw ^/^ p
and pp_open lid p =
let lid = Longident.pp lid in
let pat = pp p in
let dot = Token.pp ~after:lid ~before:pat DOT in
lid ^^ dot ^^ break 0 ^^ pat
and pp_var v =
Longident.pp_ident v
and pp_desc ?(indent=0) ~loc ~ext_attrs = function
| Ppat_or (p1, p2) -> pp_or ~indent p1 p2
| otherwise ->
nest indent @@
group
(match otherwise with
| Ppat_or _ -> assert false
| Ppat_any -> underscore ~loc
| Ppat_var v -> pp_var v
| Ppat_parens p -> parens ~loc (pp p)
| Ppat_alias (pat, alias) -> pp_alias pat alias
| Ppat_constant c -> Constant.pp ~loc c
| Ppat_interval (c1, c2) -> pp_interval c1 c2
| Ppat_tuple pats -> pp_tuple pats
| Ppat_construct (name, arg) -> pp_construct name arg
| Ppat_list_lit pats -> pp_list_literal ~loc pats
| Ppat_cons (hd, tl) -> pp_cons hd tl
| Ppat_variant (tag, arg) -> pp_variant tag arg
| Ppat_record (pats, closed) -> pp_record ~loc pats closed
| Ppat_array pats -> pp_array ~loc pats
| Ppat_constraint (p, ct) -> pp_constraint ~loc p ct
| Ppat_type pt -> pp_type ~loc pt
| Ppat_lazy p -> pp_lazy ~loc ~ext_attrs p
| Ppat_unpack (name, typ) -> pp_unpack ~loc ~ext_attrs name typ
| Ppat_exception p -> pp_exception ~loc ~ext_attrs p
| Ppat_extension ext -> Attribute.Extension.pp ~loc Item ext
| Ppat_open (lid, p) -> pp_open lid p)
let () =
Attribute.Payload.pp_pattern := pp
end
and Application : sig
val pp_simple
: document
-> (arg_label * expression)
-> (arg_label * expression) list
-> document
val pp : expression -> (arg_label * expression) list -> document
val pp_infix : string loc -> expression -> expression -> document
val pp_prefix : string loc -> expression -> document
end = struct
let arg_label ~later = function
| Nolabel -> empty
| Labelled { name; extra_info = `Single_token }
| Optional { name; extra_info = `Single_token } -> str name
| Labelled { name; extra_info = `Previous_token loc } ->
let tilde = string ~loc "~" in
let name = str name in
let colon = Token.pp ~after:name ~before:later COLON in
group (tilde ^^ name ^^ colon)
| Optional { name; extra_info = `Previous_token loc } ->
let tilde = string ~loc "?" in
let name = str name in
let colon = Token.pp ~after:name ~before:later COLON in
group (tilde ^^ name ^^ colon)
let perhaps_pun id = function
| Labelled { name; extra_info = `Previous_token loc }
when name.txt = id.txt ->
let tilde = string ~loc "~" in
tilde ^^ str name
| Optional { name; extra_info = `Previous_token loc }
when name.txt = id.txt ->
let question = string ~loc "?" in
question ^^ str name
| lbl ->
let id = Longident.pp_ident id in
let lbl = arg_label ~later:id lbl in
lbl ^^ id
(* Only called when a literal function is inside the parentheses
So we don't have to handle cases like ~(foo:x) which are [Pexp_constraint]
*)
let get_delims ~ext_attrs:(extension, attrs) ~begin_end ~loc lbl kw body =
let opening =
if begin_end then (
let begin_ = Token.pp ~inside:loc ~before:kw BEGIN in
Keyword.decorate begin_ ~extension attrs ^^ break 1
) else (
(* Can't put [%foo[@bar]] on '(' *)
Token.pp ~inside:loc ~before:kw LPAREN ^^ break 0
)
in
let lbl = arg_label ~later:opening lbl in
let closing =
if begin_end then
break 1 ^^ Token.pp ~after:body ~inside:loc END
else
break 0 ^^ Token.pp ~after:body ~inside:loc RPAREN
in
lbl, opening, closing
let argument (lbl, exp) =
match exp with
| { pexp_desc = Pexp_ident Lident id; _ } -> perhaps_pun id lbl
| { pexp_desc =
Pexp_parens
{ exp = { pexp_desc = Pexp_fun (params, tycstr, body); _ } as exp;
begin_end };
pexp_attributes = []; pexp_loc = loc;
pexp_ext_attributes = ext_attrs;
_ }
->
let fun_, args, arrow, body =
Expression.fun_chunks ~loc:exp.pexp_loc
~ext_attrs:exp.pexp_ext_attributes params tycstr body
in
let lbl, opening, closing =
get_delims ~ext_attrs ~begin_end ~loc lbl fun_ body
in
let open_ = lbl ^^ opening ^^ fun_ in
let unclosed =
prefix ~indent:2 ~spaces:1
(group ((prefix ~indent:2 ~spaces:1 open_ args) ^/^ arrow)) body
in
unclosed ^^ closing
| { pexp_desc =
Pexp_parens
{ exp = { pexp_desc = Pexp_function (c :: cs); _ } as exp;
begin_end };
pexp_attributes = []; pexp_loc = loc;
pexp_ext_attributes = ext_attrs;
_ }
->
let compact =
match !Options.Match.compact with
| Multi -> false
| _ -> true
in
let function_, cases =
Expression.function_chunks ~compact ~loc:exp.pexp_loc
~ext_attrs:exp.pexp_ext_attributes c cs
in
let lbl, opening, closing =
get_delims ~ext_attrs ~begin_end ~loc lbl function_ cases
in
let open_ = lbl ^^ opening ^^ function_ in
open_ ^^ cases ^^ closing
| _ ->
let exp = Expression.pp exp in
let lbl = arg_label ~later:exp lbl in
if lbl == empty then
exp
else
group (lbl ^^ break 0 ^^ exp)
type argument =
| Function of
{
fst_chunk : document;
break : bool;
snd_chunk : document;
closing : document
}
| Fully_built of document
let rec combine_app_chunks acc = function
| [] -> acc
| Function { fst_chunk; break = b; snd_chunk; closing } :: rest ->
let d1 = break 1 ^^ fst_chunk in
let d2 = if b then break 1 ^^ snd_chunk else snd_chunk in
let fn = (nest 2 @@ (ifflat d1 (group d1)) ^^ d2) ^^ closing in
combine_app_chunks (acc ^^ group fn) rest
| Fully_built doc :: rest ->
combine_app_chunks (acc ^^ nest 2 @@ group (break 1 ^^ doc)) rest
let smart_arg (lbl, exp) =
match exp with
| { pexp_desc =
Pexp_parens
{ exp =
{ pexp_desc = Pexp_fun (params, ty, body);
pexp_attributes = [];
_ } as exp;
begin_end };
pexp_attributes = []; pexp_loc = loc; pexp_ext_attributes = ext_attrs; _
}
->
let fun_, args, arrow, body =
Expression.fun_chunks ~loc:exp.pexp_loc
~ext_attrs:exp.pexp_ext_attributes params ty body
in
let lbl, opening, closing =
get_delims ~ext_attrs ~begin_end ~loc lbl fun_ body
in
let first_chunk =
group ((prefix ~indent:2 ~spaces:1 fun_ args) ^/^ arrow)
in
let fst_chunk = lbl ^^ group (opening ^^ first_chunk) in
let snd_chunk = group body in
Function { fst_chunk; snd_chunk; break = true; closing }
| { pexp_desc =
Pexp_parens
{ exp =
{ pexp_desc = Pexp_function (c :: cs); pexp_attributes = []; _ }
as exp;
begin_end };
pexp_attributes = []; pexp_loc = loc; pexp_ext_attributes = ext_attrs; _
}
->
let compact =
match !Options.Match.compact with
| Multi -> false
| _ -> true
in
let function_, cases =
Expression.function_chunks ~compact ~loc:exp.pexp_loc
~ext_attrs:exp.pexp_ext_attributes c cs
in
let lbl, opening, closing =
get_delims ~ext_attrs ~begin_end ~loc lbl function_ cases
in
let fst_chunk = lbl ^^ group (opening ^^ function_) in
let snd_chunk = group cases in
Function { fst_chunk; snd_chunk; break = false; closing }
| arg -> Fully_built (argument (lbl, arg))
let pp_simple applied arg args =
let fit_or_vertical () =
let args = separate_map (break 1) ~f:argument arg args in
prefix ~indent:2 ~spaces:1 applied args
in
let doc =
match !Options.Applications.layout with
| Fit_or_vertical -> fit_or_vertical ()
| Wrap ->
let args = List.map argument (arg :: args) in
nest 2 @@ left_assoc_map ~f:Fun.id applied args
| Smart ->
let nb_labels, len_labels =
List.fold_left (fun (nb, len) (lbl, _) ->
match lbl with
| Nolabel -> nb, len
| Labelled lbl | Optional lbl ->
nb + 1, len + String.length lbl.name.txt
) (0, 0) args
in
(* It would be nice if I could have "current indent + len_labels" ...
maybe. *)
(* HACKISH *)
if nb_labels > 4 && len_labels > 8 then
fit_or_vertical ()
else
let args = List.map smart_arg (arg :: args) in
combine_app_chunks applied args
in
doc
let simple_apply exp arg args =
let exp = Expression.pp exp in
pp_simple exp arg args
let _classify_fun exp =
match exp.pexp_desc with
| Pexp_ident Lident s when s.txt <> "" -> Ident_class.classify s
| _ -> Normal
let pp_prefix op arg =
let sep =
(* FIXME: this is most likely incomplete. *)
match arg.pexp_desc with
| Pexp_prefix_apply _
| Pexp_field ({ pexp_desc = Pexp_prefix_apply _; _ }, _)
->
break 1
| _ -> empty
in
let op = str op in
let arg = Expression.pp arg in
nest 2 (op ^^ sep ^^ arg)
let pp exp = function
| [] ->
(* An application node without arguments? That can't happen. *)
assert false
| arg :: args -> simple_apply exp arg args
let pp_infix op arg1 arg2 =
let fst = Expression.pp arg1 in
let snd = Expression.pp arg2 in
infix ~indent:2 ~spaces:1 (str op) fst snd
end
and Expression : sig
val pp : expression -> document
val function_chunks
: compact:bool
-> loc:Location.t
-> ext_attrs:string loc option * attributes
-> case
-> case list
-> document * document
val fun_chunks
: loc:Location.t
-> ext_attrs:string loc option * attributes
-> fun_param list
-> core_type option
-> expression
-> document * document * document * document
end = struct
let rec pp { pexp_desc; pexp_attributes; pexp_ext_attributes; pexp_loc; _ } =
let desc =
group (pp_desc ~ext_attrs:pexp_ext_attributes ~loc:pexp_loc pexp_desc)
in
Attribute.attach_to_item desc pexp_attributes
and pp_desc ~loc ~ext_attrs = function
| Pexp_parens { exp; begin_end } -> pp_parens ~loc ~ext_attrs ~begin_end exp
| Pexp_ident id -> pp_ident id
| Pexp_constant c -> Constant.pp ~loc c
| Pexp_let (rf, vbs, body) -> pp_let ~loc ~ext_attrs rf vbs body
| Pexp_function cases -> pp_function ~loc ~ext_attrs cases
| Pexp_fun (params, ty, exp) -> pp_fun ~loc ~ext_attrs params ty exp
| Pexp_apply (expr, args) -> Application.pp expr args
| Pexp_infix_apply (op, (arg1, arg2)) -> Application.pp_infix op arg1 arg2
| Pexp_prefix_apply (op, arg) -> Application.pp_prefix op arg
| Pexp_match (arg, cases) -> pp_match ~loc ~ext_attrs arg cases
| Pexp_try (arg, cases) -> pp_try ~loc ~ext_attrs arg cases
| Pexp_tuple exps -> pp_tuple exps
| Pexp_list_lit exps -> pp_list_literal ~loc exps
| Pexp_cons (hd, tl) -> pp_cons hd tl
| Pexp_construct (lid, arg) -> pp_construct lid arg
| Pexp_variant (tag, arg) -> pp_variant tag arg
| Pexp_record (fields, exp) -> pp_record ~loc fields exp
| Pexp_field (exp, fld) -> pp_field exp fld
| Pexp_setfield (exp, fld, val_) -> pp_setfield exp fld val_
| Pexp_array elts -> pp_array ~loc elts
| Pexp_ifthen branches -> pp_if_then branches
| Pexp_ifthenelse (branches, else_) -> pp_if_then_else branches else_
| Pexp_sequence (e1, e2) -> pp_sequence ~ext_attrs e1 e2
| Pexp_while (cond, body) -> pp_while ~loc ~ext_attrs cond body
| Pexp_for (it, start, stop, dir, body) ->
pp_for ~loc ~ext_attrs it start stop dir body
| Pexp_constraint (e, ct) -> pp_constraint ~loc e ct
| Pexp_coerce (e, ct_start, ct) -> pp_coerce ~loc e ct_start ct
| Pexp_send (e, meth) -> pp_send e meth
| Pexp_new lid -> pp_new ~loc ~ext_attrs lid
| Pexp_setinstvar (lbl, exp) -> pp_setinstvar lbl exp
| Pexp_override fields -> pp_override ~loc fields
| Pexp_letmodule (name, mb, body) ->
pp_letmodule ~loc ~ext_attrs name mb body
| Pexp_letexception (exn, exp) -> pp_letexception ~loc ~ext_attrs exn exp
| Pexp_assert exp -> pp_assert ~loc ~ext_attrs exp
| Pexp_lazy exp -> pp_lazy ~loc ~ext_attrs exp
| Pexp_object cl -> pp_object ~loc ~ext_attrs cl
| Pexp_pack (me, pkg) -> pp_pack ~loc ~ext_attrs me pkg
| Pexp_open (lid, exp) -> pp_open ~loc lid exp
| Pexp_letopen (od, exp) -> pp_letopen ~loc ~ext_attrs od exp
| Pexp_letop letop -> pp_letop letop
| Pexp_extension ext -> Attribute.Extension.pp ~loc Item ext
| Pexp_unreachable -> string ~loc "."
| Pexp_access { accessed; paren; indices; set_expr } ->
pp_access ~loc accessed paren indices set_expr
| Pexp_dotop_access { accessed; path; op; paren; indices; set_expr } ->
pp_dotop_access ~loc accessed path op paren indices set_expr
and pp_ident =
Longident.pp
and pp_parens ~loc ~ext_attrs:(extension, attrs) ~begin_end e =
if not begin_end then (
assert (extension = None && attrs = []);
parens ~loc (pp e)
) else (
let end_ =
Token.pp ~inside:{ loc with loc_start = e.pexp_loc.loc_end } END
in
let begin_ =
let tok =
Token.pp ~inside:{ loc with loc_end = e.pexp_loc.loc_start } BEGIN
in
Keyword.decorate tok ~extension attrs
in
match e.pexp_desc with
| Pexp_match (arg, cases) when e.pexp_attributes = [] ->
group
(pp_match ~parens:(begin_, end_) ~loc:e.pexp_loc
~ext_attrs:e.pexp_ext_attributes arg cases)
| _ -> (prefix ~indent:2 ~spaces:1 begin_ (pp e)) ^/^ end_
)
and pp_let ~ext_attrs:(extension, attrs) ~loc rf vbs body =
assert (attrs = []);
let vbs =
let previous_vb = ref None in
List.concat_map (fun vb ->
let text, vb =
let text, attrs =
Attribute.extract_text vb.pvb_attributes
~item_start_pos:vb.pvb_loc.loc_start
in
text, { vb with pvb_attributes = attrs }
in
let binding = Value_binding.pp Attached_to_structure_item vb in
let keyword =
let lhs = binding.lhs in
let attrs =
match vb.pvb_ext_attributes with
| Some _, _ -> assert false
| None, attrs -> attrs
in
let token, extension, modifier =
match !previous_vb with
| None ->
Token.pp ~inside:loc ~before:lhs LET,
extension,
rec_token ~recursive_by_default:false rf
| Some prev_vb ->
Token.pp ~after:prev_vb ~before:lhs AND, None, None
in
let kw = Keyword.decorate token ~extension attrs in
match modifier with
| None -> kw
| Some tok ->
let modif = Token.pp ~after:kw ~before:lhs tok in
kw ^/^ modif
in
let binding = Binding.pp ~keyword binding in
previous_vb := Some binding;
Attribute.prepend_text text binding
) vbs
in
let vbs = separate hardline (List.hd vbs) (List.tl vbs) in
let body = pp body in
let in_ = Token.pp ~after:vbs ~before:body IN in
group (vbs ^/^ in_) ^//^ body
and case_chunks { pc_lhs; pc_guard; pc_rhs } =
let lhs = Pattern.pp ~indent:2 pc_lhs in
let rhs = pp pc_rhs in
let lhs =
match pc_guard with
| None ->
let arrow = Token.pp ~after:lhs ~before:rhs MINUSGREATER in
prefix ~indent:2 ~spaces:1 lhs arrow
| Some guard ->
let guarded =
let guard = pp guard in
let when_ = Token.pp ~after:lhs ~before:guard WHEN in
prefix ~indent:2 ~spaces:1 when_ guard
in
let with_arrow =
let arrow = Token.pp ~after:guarded ~before:rhs MINUSGREATER in
group (guarded ^/^ arrow)
in
prefix ~spaces:1 ~indent:2 lhs with_arrow
in
lhs, rhs
and case case =
let lhs, rhs = case_chunks case in
match !Options.Cases.body_on_separate_line with
| Always -> lhs ^^ nest !Options.Cases.body_indent (hardline ^^ rhs)
| When_needed -> prefix ~indent:!Options.Cases.body_indent ~spaces:1 lhs rhs
and cases ~compact:compact_layout c cs =
let fmt acc elt =
let elt = case elt in
let bar = Token.pp ~after:acc ~before:elt BAR in
acc ^/^ group (bar ^^ space ^^ elt)
in
let rec iterator acc = function
| [] -> acc
| [ x ] -> fmt acc x
| x :: xs -> iterator (fmt acc x) xs
in
let cases = iterator (case c) cs in
(* FIXME: reuse bar from source if present *)
let prefix =
let open PPrint in
let multi = hardline ^^ bar in
(if compact_layout then ifflat empty multi else multi) ^^ space
in
prefix ++ cases
and function_chunks ~compact ~loc ~ext_attrs:(extension, attrs) c cs =
let cases = cases ~compact c cs in
let keyword =
let kw = Token.pp ~inside:loc ~before:cases FUNCTION in
Keyword.decorate kw ~extension attrs
in
keyword, cases
and pp_function ~loc ~ext_attrs = function
| [] -> assert false (* always at least one case *)
| c :: cs ->
let compact =
match !Options.Match.compact with
| Multi -> false
| Compact -> true
| Compact_under_app -> false (* FIXME *)
(* under_app ps *)
in
let keyword, cases = function_chunks ~compact ~loc ~ext_attrs c cs in
(keyword ^^ cases)
and fun_syntactic_elts ~loc ~ext_attrs:(extension, attrs) ~lhs ~rhs =
let kw =
let kw = Token.pp ~inside:loc ~before:lhs FUN in
Keyword.decorate kw ~extension attrs
in
let arrow = Token.pp ~after:lhs ~before:rhs MINUSGREATER in
kw, arrow
and fun_chunks ~loc ~ext_attrs params tycstr exp =
match params with
| [] -> assert false
| param :: params ->
let args = left_assoc_map ~f:Fun_param.pp param params in
let body = pp exp in
let with_annot =
match tycstr with
| None -> args
| Some cty ->
let ty = Core_type.pp cty in
let colon = Token.pp ~after:args ~before:ty COLON in
args ^/^ group (colon ^/^ ty)
in
let kw, arrow =
fun_syntactic_elts ~loc ~ext_attrs ~lhs:with_annot ~rhs:body
in
kw, with_annot, arrow, body
and pp_fun ~loc ~ext_attrs params ty exp =
let fun_, args, arrow, body = fun_chunks ~loc ~ext_attrs params ty exp in
let doc =
prefix ~indent:2 ~spaces:1
(group ((prefix ~indent:2 ~spaces:1 fun_ args) ^/^ arrow)) body
in
doc
and pp_match ?parens ~loc ~ext_attrs:(extension, attrs) arg = function
| [] -> assert false (* always at least one case *)
| c :: cs ->
let arg = pp arg in
let compact =
match !Options.Match.compact with
| Multi -> false
| Compact -> true
| Compact_under_app -> false (* FIXME: under_app ps *)
in
let cases = cases ~compact c cs in
let match_ =
let token = Token.pp ~inside:loc ~before:arg MATCH in
Keyword.decorate token ~extension attrs
in
let with_ = Token.pp ~after:arg ~before:cases WITH in
match parens with
| None -> group (match_ ^^ nest 2 (break 1 ^^ arg) ^/^ with_) ^^ cases
| Some (before, after) ->
group (group (before ^/^ match_) ^^ nest 2 (break 1 ^^ arg) ^/^ with_)
^^ cases ^/^ after
and pp_try ~loc ~ext_attrs:(extension, attrs) arg cs =
let arg = pp arg in
let try_ =
let token = Token.pp ~inside:loc ~before:arg TRY in
Keyword.decorate token ~extension attrs
in
let pprefix = prefix ~indent:2 ~spaces:1 in
match cs with
| [] -> assert false
| [ c ] (* TODO: guard this layout under an option *) ->
let lhs, rhs = case_chunks c in
let with_ = Token.pp ~after:arg ~before:lhs WITH in
pprefix try_ arg ^/^
pprefix with_ lhs ^^ nest 2 ((ifflat (break 1) hardline) ^^ rhs)
| c :: cs ->
let compact =
match !Options.Match.compact with
| Multi -> false
| Compact -> true
| Compact_under_app -> false (* FIXME: under_app ps *)
in
let cases = cases ~compact c cs in
let with_ = Token.pp ~after:arg ~before:cases WITH in
pprefix try_ arg ^/^ with_ ^^ cases
and pp_tuple = function
| [] -> assert false
| exp :: exps -> group (tuple_fields pp exp exps)
and pp_construct lid arg_opt =
let name = Longident.pp lid in
match arg_opt with
| None -> name
| Some arg ->
let arg = pp arg in
let doc = prefix ~indent:2 ~spaces:1 name arg in
doc
and pp_cons hd tl =
let hd = pp hd in
let tl = pp tl in
let cons = Token.pp ~after:hd ~before:tl COLONCOLON in
let doc = infix ~indent:2 ~spaces:1 cons hd tl in
doc
and pp_list_literal ~loc elts =
let elts = List.map pp elts in
List_like.pp ~loc ~formatting:Wrap (* TODO: add an option *) ~left:LBRACKET
~right:RBRACKET elts
and pp_variant tag arg_opt =
let tag = Polymorphic_variant_tag.pp tag in
match arg_opt with
| None -> tag
| Some arg ->
let arg = pp arg in
let doc = prefix ~indent:2 ~spaces:1 tag arg in
doc
and record_field (lid, (oct1, oct2), exp) =
let binding : Binding.t =
{
lhs = Longident.pp lid;
params = [];
constr = Option.map Core_type.pp oct1;
coerce = Option.map Core_type.pp oct2;
rhs = Binding.Rhs.of_opt pp exp
}
in
Binding.pp binding
and pp_record ~loc fields updated_record =
let fields = List.map record_field fields in
match updated_record with
| None ->
List_like.pp ~loc ~formatting:!Options.Record.expression ~left:LBRACE
~right:RBRACE fields
| Some e ->
let update = pp e in
let fields =
List_like.pp_fields ~formatting:!Options.Record.expression
(List.hd fields) (List.tl fields)
in
let with_ = Token.pp ~after:update ~before:fields WITH in
braces ~loc
((group (group (break 1 ^^ update) ^/^ with_) ^/^ fields) ^^ break 1)
and pp_field re fld =
let record = pp re in
let field = Longident.pp fld in
let dot = Token.pp ~after:record ~before:field DOT in
flow (break 0) record [ dot; field ]
and pp_setfield re fld val_ =
let field = pp_field re fld in
let value = pp val_ in
let larrow = Token.pp ~after:field ~before:value LESSMINUS in
let doc = prefix ~indent:2 ~spaces:1 (group (field ^/^ larrow)) value in
doc
and pp_array ~loc elts =
let elts = List.map pp elts in
(* TODO: add an option *)
List_like.pp ~loc ~formatting:Wrap ~left:LBRACKETBAR ~right:BARRBRACKET elts
and pp_gen_access ~loc ?path ?dot paren_kind arr idx val_ =
let arr = pp arr in
let dot =
match path, dot with
| None, None -> Token.pp ~after:arr ~before:idx DOT
| Some path, Some op ->
let fstdot = Token.pp ~after:arr ~before:path DOT in
group (fstdot ^^ path ^^ break 0 ^^ op)
| None, Some op -> op
| Some _, None -> assert false
in
let (left_tok, right_tok) : (Source_parsing.Parser.token as 't) * 't =
match paren_kind with
| Paren -> LPAREN, RPAREN
| Brace -> LBRACE, RBRACE
| Bracket -> LBRACKET, RBRACKET
in
let left = Token.pp ~after:dot ~before:idx left_tok in
match val_ with
| None ->
let right = Token.pp ~inside:loc ~after:idx right_tok in
flow (break 0) arr [ dot; left ^^ idx ^^ right ]
| Some val_ ->
let value = pp val_ in
let right = Token.pp ~after:idx ~before:value right_tok in
let larrow = Token.pp ~after:right ~before:value LESSMINUS in
let access = flow (break 0) arr [ dot; left ^^ idx ^^ right ] in
prefix ~indent:2 ~spaces:1 (group (access ^/^ larrow)) value
and pp_access ~loc arr paren idx val_ =
pp_gen_access ~loc paren arr (pp idx) val_
and pp_dotop_access ~loc accessed path op paren indices val_ =
let indices =
match indices with
| [] -> assert false (* I think *)
| idx :: ids ->
List_like.pp_fields ~formatting:Wrap
(pp idx) (List.map pp ids)
in
let path = Option.map Longident.pp path in
pp_gen_access ~loc ?path ~dot:(str op) paren accessed indices val_
(* TODO: add formating options *)
and pp_if_then =
If_then_else.knr_if_then
and pp_if_then_else =
If_then_else.knr_if_then_else
and pp_sequence ~ext_attrs:(extension, attrs) e1 e2 =
let compact =
match !Options.Sequences.compact with
| Multi -> false
| Compact -> true
| Compact_under_app -> false (* FIXME: under_app ps *)
in
let e1 = pp e1 in
let e2 = pp e2 in
let semi =
Keyword.decorate (Token.pp ~after:e1 ~before:e2 SEMI)
~extension attrs
in
let doc =
if compact then
e1 ^^ semi ^/^ e2
else
e1 ^^ semi ^//^ e2
in
doc
and pp_while ~(loc:Location.t) ~ext_attrs:(extension, attrs) cond body =
let cond = pp cond in
let body = pp body in
let do_ = Token.pp ~after:cond ~before:body DO in
let while_ =
let token = Token.pp ~inside:loc ~before:cond WHILE in
Keyword.decorate token ~extension attrs
in
let done_ = Token.pp ~inside:loc ~after:body DONE in
let doc =
group
(group (while_ ^^ nest 2 (break 1 ^^ cond) ^/^ do_) ^^
nest 2 (break 1 ^^ body) ^/^ done_)
in
doc