package pfff

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file pretty_print_code.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
(* Julien Verlaguet, Yoann Padioleau
 *
 * Copyright (C) 2011 Facebook
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation, with the
 * special exception on linking described in file license.txt.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
 * license.txt for more details.
 *)

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* julien: this is a copy/paste of the original pp.ml.
 * It is slightly modified, and I don't know how much these modifications
 * affect xhpizer. I hope to be able to merge these two files back together.
 *)

(*****************************************************************************)
(* Types *)
(*****************************************************************************)

(* we use a backtracking model *)
exception Fail

type env = {
  (* the actual printing hook, sometimes temporarily set to do_nothing()
   * when trying something before actually printing it. *)
  print: (string -> unit);

  (* stack of margin, push'ed and pop'ed when processing {} *)
  mutable margin: int list;

  (* current column *)
  mutable cmargin: int;
  (* current line *)
  mutable line: int;

  (* depth in the tree of try_ *)
  mutable level: int;

  (* for the parenthesis automatic insertion *)
  mutable priority: int;

  (* pad: ?? *)
  mutable last_nl: bool;
  mutable emptyl: bool;
  mutable failed: bool;
  mutable pushed: bool;
}

(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)

let empty o = {
  print = o;
  margin = [0];
  priority = 0;
  cmargin = 0;
  level = 0;
  pushed = false;
  last_nl = false;
  emptyl = false;
  failed = false;
  line = 0;
}

let debug env f =
  let buf = Buffer.create 256 in
  let env' = { env with print = (fun s -> Buffer.add_string buf s)} in
  (try f env' with _ -> ());
  Printf.printf "Debug %s\n" (Buffer.contents buf)

let do_nothing _ = ()

(*****************************************************************************)
(* Newlines and spaces *)
(*****************************************************************************)

let print env x =
  (* todo: this is the case right now for comments.
   * we should normalize those comments?
   * if (String.contains x '\n')
   * then failwith (Printf.sprintf "%s contains a newline\n" x);
   *)

  env.last_nl <- false;
  env.emptyl <- false;

  env.cmargin <- env.cmargin + String.length x;
  if env.cmargin >= 80
  then
    (* there is nothing to backtrack on, so just print it *)
    if env.level = 0
    then begin env.print x; env.failed <- true end
    else raise Fail
  else env.print x

let spaces env =
  for _i = 1 to List.hd env.margin do
    print env " ";
  done

let newline env =
  env.pushed <- false;
  if env.last_nl
  then env.emptyl <- true;
  env.last_nl <- true;
  env.cmargin <- 0;
  env.line <- env.line + 1;
  env.print "\n"

let newline_opt env =
  if env.last_nl
  then ()
  else newline env

let space_or_nl env =
  if env.cmargin < 75
  then print env " "
  else (newline env; spaces env)

(*****************************************************************************)
(* Margins *)
(*****************************************************************************)

let margin_offset = ref 2

let push env =
  env.pushed <- true;
  env.margin <- List.hd env.margin + !margin_offset :: env.margin

let pop env =
  env.margin <- List.tl env.margin

let nest env f =
  push env;
  f env;
  pop env

let nest_opt env f =
  if env.pushed
  then f env
  else begin
    push env;
    f env;
    pop env
  end

let nestc env f =
  env.margin <- env.cmargin :: env.margin;
  f env;
  pop env

let nest_block env f =
  print env "{";
  newline env;
  nest env f;
  spaces env;
  print env "}"

let nest_block_nl env f =
  nest_block env f;
  newline env

(*****************************************************************************)
(* Lists *)
(*****************************************************************************)

let rec simpl_list env f sep = function
  | [] -> ()
  | [x] -> f env x
  | x :: rl -> f env x; print env sep; simpl_list env f sep rl

let rec list_sep env f sep = function
  | [] -> ()
  | [x] -> f env x
  | x :: rl -> f env x; sep env; list_sep env f sep rl

let flat_list env f opar l sep cpar =
  print env opar;
  list_sep env f (fun env -> print env sep; print env " ") l;
  print env cpar

(* pad: used to take a last_nl parameter, but it was not used *)
let nl_nested_list env f opar l sep cpar =
  print env opar;
  nest env (fun env ->
    newline env;
    spaces env;
    list_sep env f (fun env -> print env sep; newline env; spaces env) l;
    newline env;
  );
  if cpar <> ""
  then begin
    spaces env;
    print env cpar
  end

(*****************************************************************************)
(* Backtracking combinators *)
(*****************************************************************************)

let fail () = raise Fail

let try_ env f =
  f { env with print = do_nothing; level = env.level + 1};
  f env


let choice_left env f1 f2 =
  try try_ env f1
  with
  | Fail when env.level = 0 ->
      (try f2 env with Fail -> assert false)
  (* otherwise, just let the exception bubble up more *)


let choice_right env f1 f2 =
  try try_ env f1
  with Fail ->
    f2 env

let try_hard env f =
  try
    f { env with print = do_nothing; level = 1 };
    f env
  with Fail ->
    let env' = { env with failed = false; print = do_nothing; level = 0 } in
    f env';
    if env'.failed
    then raise Fail
    else f { env with level = 0 }

let cut_list env f l =
  List.iter (
    fun x ->
      choice_right env
        (fun env -> f env x)
        (fun env -> newline env; spaces env; f env x)
  ) l


let list env f opar l sep cpar =
  let simple = (fun env -> flat_list env f opar l sep cpar) in
  let nested = (fun env -> nl_nested_list env f opar l sep cpar) in
  choice_right env simple nested

let list_left env f opar l sep cpar =
  let simple = (fun env -> if l <> [] then print env " "; flat_list env f opar l sep cpar) in
  let nested = (fun env -> nl_nested_list env f opar l sep cpar) in
  choice_left env simple nested

let nested_arg env f opar l sep cpar =
  let rec elt = function
    | [] -> assert false
    | [x] ->
        f env x;
        newline env;
    | x :: rl ->
        f env x; print env sep; newline env; spaces env;
        elt rl
  in
  nestc env (
    fun env ->
      print env opar;
      nestc env (
        fun _env ->
          elt l;
      );
      spaces env;
      print env cpar;
  )


let fun_args env f opar l sep cpar =
  let simple = (
    fun env ->
      let line = env.line in
      flat_list env f opar l sep cpar;
      if line <> env.line then fail();
  ) in
  let nl_nested = (fun env -> nl_nested_list env f opar l sep cpar) in
  choice_right env simple nl_nested


let nested_list env f opar l sep cpar last_nl =
  env.margin <- env.cmargin :: env.margin;
  print env opar;
  env.margin <- env.cmargin :: env.margin;
  list_sep env f (fun env -> print env sep; newline env; spaces env) l;
  if last_nl
  then begin
    print env sep;
    newline env;
    pop env;
    spaces env;
    print env cpar;
  end
  else begin
    print env cpar;
    pop env;
  end;
  pop env

let fun_params env f l =
  let opar = "(" in
  let sep  = "," in
  let cpar = ")" in
  let simple = (
    fun env ->
      let line = env.line in
      flat_list env f opar l sep cpar;
      if line <> env.line then fail();
  ) in
  let nl_nested = (fun env -> nested_list env f opar l sep cpar true) in
  choice_right env simple nl_nested

(*****************************************************************************)
(* Parenthesis handling *)
(*****************************************************************************)

let paren prio env f =
  let old_prio = env.priority in
  env.priority <- prio;
  if (prio >= old_prio) || (prio = -1)
  then f env
  else begin
    print env "(";
    f env;
    print env ")";
  end;
  env.priority <- old_prio

(*****************************************************************************)
(* String helpers *)
(*****************************************************************************)
(* module PpString = struct *)

let char_is_space = function
  | ' ' | '\t' | '\n' | '\r' -> true
  | _ -> false

let is_space s i = char_is_space s.[i]

let rec is_only_space s i =
  if i >= String.length s
  then true
  else is_space s i && is_only_space s (i+1)

let strip s =
  let c1 = ref 0 in
  let c2 = ref (String.length s - 1) in
  while is_space s !c1 do
    incr c1;
  done;
  while is_space s !c2 do
    decr c2;
  done;
  let c2 = String.length s - 1 - !c2 in
  String.sub s !c1 (String.length s - !c1 - c2)

let space = function
  | ' ' | '\t' -> true
  | _ -> false

let rec find_cut x start i =
  if i < 20
  then start
  else if space x.[i]
  then i
  else find_cut x start (i-1)

let rec string quote sep env x =
  choice_left env (
    fun env ->
      print env x
  ) (
    fun env ->
      let size = 80 - env.cmargin - String.length sep - 1 in
      let size = find_cut x size size in
      let s = String.sub x 0 size in
      let rest = String.sub x size (String.length x - size) in
      print env s;
      print env quote;
      print env sep;
      newline env;
      spaces env;
      print env quote;
      string quote sep env rest
  )

let string quote sep env x =
  if env.cmargin >= 20
  then begin
    print env quote;
    print env x;
    print env quote
  end
  else
    nestc env (
      fun env ->
        print env quote;
        string quote sep env x;
        print env quote;
    )

let first_char_escape env s =
  if s = "" then 0 else
    match s.[0] with
    | 'A' .. 'Z' | 'a' .. 'z' | '&' | ' ' | '\n' | '<' | '>' -> 0
    | _c ->
        print env "{'";
        let size = ref 1 in
        while !size < String.length s && not (char_is_space s.[!size]) do incr size done;
        let size = !size in
        print env (String.sub s 0 size);
        print env "'}";
        if size < String.length s then print env " ";
        size

let print_text env s =
  let size = ref (String.length s - 1) in
  while !size >= 0 && char_is_space s.[!size] do
    decr size;
  done;
  let size = !size in
  let buf = Buffer.create 80 in
  let last_is_space = ref true in
  nestc env (
    fun env ->
      let i = first_char_escape env s in
      for i = i to size do
        if Common2.is_space s.[i]
        then
          if !last_is_space
          then ()
          else begin
            last_is_space := true;
            print env (Buffer.contents buf);
            Buffer.clear buf;
            space_or_nl env;
          end
        else (last_is_space := false; Buffer.add_char buf s.[i])
      done;
      print env (Buffer.contents buf);
      Buffer.clear buf;
  )
OCaml

Innovation. Community. Security.