src/Pure/General/pretty.ML
author wenzelm
Wed, 06 Jul 2011 20:46:06 +0200
changeset 44563 85388f5570c4
parent 43258 0ae4ad40d7b5
child 46537 d83797ef0d2d
permissions -rw-r--r--
prefer Synchronized.var;
wenzelm@6118
     1
(*  Title:      Pure/General/pretty.ML
wenzelm@8806
     2
    Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
wenzelm@10952
     3
    Author:     Markus Wenzel, TU Munich
wenzelm@6116
     4
wenzelm@6116
     5
Generic pretty printing module.
wenzelm@6116
     6
wenzelm@6116
     7
Loosely based on
wenzelm@6116
     8
  D. C. Oppen, "Pretty Printing",
wenzelm@6116
     9
  ACM Transactions on Programming Languages and Systems (1980), 465-483.
wenzelm@6116
    10
wenzelm@6116
    11
The object to be printed is given as a tree with indentation and line
wenzelm@6116
    12
breaking information.  A "break" inserts a newline if the text until
wenzelm@6116
    13
the next break is too long to fit on the current line.  After the newline,
wenzelm@6116
    14
text is indented to the level of the enclosing block.  Normally, if a block
wenzelm@6116
    15
is broken then all enclosing blocks will also be broken.  Only "inconsistent
wenzelm@6116
    16
breaks" are provided.
wenzelm@6116
    17
wenzelm@6116
    18
The stored length of a block is used in breakdist (to treat each inner block as
wenzelm@6116
    19
a unit for breaking).
wenzelm@6116
    20
*)
wenzelm@6116
    21
wenzelm@6116
    22
signature PRETTY =
wenzelm@14832
    23
sig
wenzelm@40391
    24
  val default_indent: string -> int -> Output.output
wenzelm@40391
    25
  val add_mode: string -> (string -> int -> Output.output) -> unit
wenzelm@6116
    26
  type T
wenzelm@6116
    27
  val str: string -> T
wenzelm@6116
    28
  val brk: int -> T
wenzelm@6116
    29
  val fbrk: T
wenzelm@6116
    30
  val breaks: T list -> T list
wenzelm@6116
    31
  val fbreaks: T list -> T list
wenzelm@23645
    32
  val blk: int * T list -> T
wenzelm@6116
    33
  val block: T list -> T
wenzelm@23645
    34
  val strs: string list -> T
wenzelm@23617
    35
  val markup: Markup.T -> T list -> T
wenzelm@26703
    36
  val mark: Markup.T -> T -> T
wenzelm@43144
    37
  val mark_str: Markup.T * string -> T
wenzelm@43144
    38
  val marks_str: Markup.T list * string -> T
wenzelm@23617
    39
  val keyword: string -> T
wenzelm@23617
    40
  val command: string -> T
wenzelm@23638
    41
  val markup_chunks: Markup.T -> T list -> T
wenzelm@18802
    42
  val chunks: T list -> T
wenzelm@19266
    43
  val chunks2: T list -> T
wenzelm@23617
    44
  val block_enclose: T * T -> T list -> T
wenzelm@18802
    45
  val quote: T -> T
wenzelm@18802
    46
  val backquote: T -> T
wenzelm@18802
    47
  val separate: string -> T list -> T list
wenzelm@18802
    48
  val commas: T list -> T list
wenzelm@6116
    49
  val enclose: string -> string -> T list -> T
wenzelm@18802
    50
  val enum: string -> string -> string -> T list -> T
wenzelm@30623
    51
  val position: Position.T -> T
wenzelm@6116
    52
  val list: string -> string -> T list -> T
wenzelm@6116
    53
  val str_list: string -> string -> string list -> T
wenzelm@6116
    54
  val big_list: string -> T list -> T
wenzelm@9730
    55
  val indent: int -> T -> T
wenzelm@23645
    56
  val unbreakable: T -> T
wenzelm@36754
    57
  val margin_default: int Unsynchronized.ref
wenzelm@23645
    58
  val symbolicN: string
wenzelm@36754
    59
  val output_buffer: int option -> T -> Buffer.T
wenzelm@40391
    60
  val output: int option -> T -> Output.output
wenzelm@36754
    61
  val string_of_margin: int -> T -> string
wenzelm@23645
    62
  val string_of: T -> string
wenzelm@23645
    63
  val str_of: T -> string
wenzelm@6116
    64
  val writeln: T -> unit
wenzelm@36757
    65
  val to_ML: T -> ML_Pretty.pretty
wenzelm@36757
    66
  val from_ML: ML_Pretty.pretty -> T
wenzelm@14832
    67
end;
wenzelm@6116
    68
wenzelm@23617
    69
structure Pretty: PRETTY =
wenzelm@6116
    70
struct
wenzelm@6116
    71
wenzelm@23617
    72
(** print mode operations **)
wenzelm@23617
    73
wenzelm@23617
    74
fun default_indent (_: string) = Symbol.spaces;
wenzelm@23617
    75
wenzelm@23617
    76
local
wenzelm@23698
    77
  val default = {indent = default_indent};
wenzelm@44563
    78
  val modes = Synchronized.var "Pretty.modes" (Symtab.make [("", default)]);
wenzelm@23617
    79
in
wenzelm@44563
    80
  fun add_mode name indent =
wenzelm@44563
    81
    Synchronized.change modes (Symtab.update_new (name, {indent = indent}));
wenzelm@23617
    82
  fun get_mode () =
wenzelm@44563
    83
    the_default default
wenzelm@44563
    84
      (Library.get_first (Symtab.lookup (Synchronized.value modes)) (print_mode_value ()));
wenzelm@23617
    85
end;
wenzelm@23617
    86
wenzelm@23617
    87
fun mode_indent x y = #indent (get_mode ()) x y;
wenzelm@23645
    88
wenzelm@23645
    89
val output_spaces = Output.output o Symbol.spaces;
wenzelm@23645
    90
val add_indent = Buffer.add o output_spaces;
wenzelm@23617
    91
wenzelm@23617
    92
wenzelm@10952
    93
wenzelm@10952
    94
(** printing items: compound phrases, strings, and breaks **)
wenzelm@10952
    95
wenzelm@37530
    96
abstype T =
wenzelm@40391
    97
    Block of (Output.output * Output.output) * T list * int * int
wenzelm@40391
    98
      (*markup output, body, indentation, length*)
wenzelm@40391
    99
  | String of Output.output * int  (*text, length*)
wenzelm@40391
   100
  | Break of bool * int  (*mandatory flag, width if not taken*)
wenzelm@37530
   101
with
wenzelm@6116
   102
wenzelm@23645
   103
fun length (Block (_, _, _, len)) = len
wenzelm@23645
   104
  | length (String (_, len)) = len
wenzelm@23645
   105
  | length (Break (_, wd)) = wd;
wenzelm@6116
   106
wenzelm@9730
   107
wenzelm@9730
   108
wenzelm@23645
   109
(** derived operations to create formatting expressions **)
wenzelm@23645
   110
wenzelm@23645
   111
val str = String o Output.output_width;
wenzelm@23645
   112
wenzelm@23645
   113
fun brk wd = Break (false, wd);
wenzelm@36728
   114
val fbrk = Break (true, 1);
wenzelm@23645
   115
wenzelm@23645
   116
fun breaks prts = Library.separate (brk 1) prts;
wenzelm@23645
   117
fun fbreaks prts = Library.separate fbrk prts;
wenzelm@23645
   118
wenzelm@30667
   119
fun block_markup m (indent, es) =
wenzelm@23645
   120
  let
wenzelm@23645
   121
    fun sum [] k = k
wenzelm@23645
   122
      | sum (e :: es) k = sum es (length e + k);
wenzelm@23645
   123
  in Block (m, es, indent, sum es 0) end;
wenzelm@23645
   124
wenzelm@30667
   125
fun markup_block m arg = block_markup (Markup.output m) arg;
wenzelm@30667
   126
wenzelm@38792
   127
val blk = markup_block Markup.empty;
wenzelm@23645
   128
fun block prts = blk (2, prts);
wenzelm@23645
   129
val strs = block o breaks o map str;
wenzelm@23645
   130
wenzelm@23645
   131
fun markup m prts = markup_block m (0, prts);
wenzelm@43144
   132
fun mark m prt = if m = Markup.empty then prt else markup m [prt];
wenzelm@43144
   133
fun mark_str (m, s) = mark m (str s);
wenzelm@43144
   134
fun marks_str (ms, s) = fold_rev mark ms (str s);
wenzelm@43144
   135
wenzelm@43144
   136
fun keyword name = mark_str (Markup.keyword, name);
wenzelm@43144
   137
fun command name = mark_str (Markup.command, name);
wenzelm@23645
   138
wenzelm@23645
   139
fun markup_chunks m prts = markup m (fbreaks prts);
wenzelm@38792
   140
val chunks = markup_chunks Markup.empty;
wenzelm@23645
   141
fun chunks2 prts = blk (0, flat (Library.separate [fbrk, fbrk] (map single prts)));
wenzelm@23645
   142
wenzelm@36733
   143
fun block_enclose (prt1, prt2) prts = chunks [block (fbreaks (prt1 :: prts)), prt2];
wenzelm@23645
   144
wenzelm@23645
   145
fun quote prt = blk (1, [str "\"", prt, str "\""]);
wenzelm@23645
   146
fun backquote prt = blk (1, [str "`", prt, str "`"]);
wenzelm@23645
   147
wenzelm@23645
   148
fun separate sep prts =
wenzelm@23645
   149
  flat (Library.separate [str sep, brk 1] (map single prts));
wenzelm@23645
   150
wenzelm@23645
   151
val commas = separate ",";
wenzelm@23645
   152
wenzelm@23645
   153
fun enclose lpar rpar prts =
wenzelm@23645
   154
  block (str lpar :: (prts @ [str rpar]));
wenzelm@23645
   155
wenzelm@23645
   156
fun enum sep lpar rpar prts = enclose lpar rpar (separate sep prts);
wenzelm@23645
   157
wenzelm@30623
   158
val position =
wenzelm@30623
   159
  enum "," "{" "}" o map (fn (x, y) => str (x ^ "=" ^ y)) o Position.properties_of;
wenzelm@30623
   160
wenzelm@23645
   161
val list = enum ",";
wenzelm@23645
   162
fun str_list lpar rpar strs = list lpar rpar (map str strs);
wenzelm@23645
   163
wenzelm@23645
   164
fun big_list name prts = block (fbreaks (str name :: prts));
wenzelm@23645
   165
wenzelm@23645
   166
fun indent 0 prt = prt
wenzelm@23645
   167
  | indent n prt = blk (0, [str (Symbol.spaces n), prt]);
wenzelm@23645
   168
wenzelm@23645
   169
fun unbreakable (Break (_, wd)) = String (output_spaces wd, wd)
wenzelm@23645
   170
  | unbreakable (Block (m, es, indent, wd)) = Block (m, map unbreakable es, indent, wd)
wenzelm@23645
   171
  | unbreakable (e as String _) = e;
wenzelm@23645
   172
wenzelm@23645
   173
wenzelm@23645
   174
wenzelm@23645
   175
(** formatting **)
wenzelm@23645
   176
wenzelm@23645
   177
(* formatted output *)
wenzelm@23645
   178
wenzelm@23645
   179
local
wenzelm@6116
   180
wenzelm@17756
   181
type text = {tx: Buffer.T, ind: Buffer.T, pos: int, nl: int};
wenzelm@17756
   182
wenzelm@17756
   183
val empty: text =
wenzelm@10952
   184
 {tx = Buffer.empty,
wenzelm@10952
   185
  ind = Buffer.empty,
wenzelm@10952
   186
  pos = 0,
wenzelm@10952
   187
  nl = 0};
wenzelm@6116
   188
wenzelm@32791
   189
fun newline {tx, ind = _, pos = _, nl} : text =
wenzelm@14832
   190
 {tx = Buffer.add (Output.output "\n") tx,
wenzelm@10952
   191
  ind = Buffer.empty,
wenzelm@10952
   192
  pos = 0,
wenzelm@10952
   193
  nl = nl + 1};
wenzelm@6116
   194
wenzelm@23628
   195
fun control s {tx, ind, pos: int, nl} : text =
wenzelm@23628
   196
 {tx = Buffer.add s tx,
wenzelm@23628
   197
  ind = ind,
wenzelm@23628
   198
  pos = pos,
wenzelm@23628
   199
  nl = nl};
wenzelm@23628
   200
wenzelm@17756
   201
fun string (s, len) {tx, ind, pos: int, nl} : text =
wenzelm@10952
   202
 {tx = Buffer.add s tx,
wenzelm@10952
   203
  ind = Buffer.add s ind,
wenzelm@10952
   204
  pos = pos + len,
wenzelm@10952
   205
  nl = nl};
wenzelm@6116
   206
wenzelm@10952
   207
fun blanks wd = string (output_spaces wd, wd);
wenzelm@6116
   208
wenzelm@17756
   209
fun indentation (buf, len) {tx, ind, pos, nl} : text =
wenzelm@10952
   210
  let val s = Buffer.content buf in
wenzelm@23617
   211
   {tx = Buffer.add (mode_indent s len) tx,
wenzelm@10952
   212
    ind = Buffer.add s ind,
wenzelm@10952
   213
    pos = pos + len,
wenzelm@10952
   214
    nl = nl}
wenzelm@10952
   215
  end;
wenzelm@6116
   216
wenzelm@10952
   217
(*Add the lengths of the expressions until the next Break; if no Break then
wenzelm@10952
   218
  include "after", to account for text following this block.*)
wenzelm@36725
   219
fun breakdist (Break _ :: _, _) = 0
wenzelm@36725
   220
  | breakdist (Block (_, _, _, len) :: es, after) = len + breakdist (es, after)
wenzelm@32791
   221
  | breakdist (String (_, len) :: es, after) = len + breakdist (es, after)
wenzelm@10952
   222
  | breakdist ([], after) = after;
wenzelm@10952
   223
wenzelm@10952
   224
(*Search for the next break (at this or higher levels) and force it to occur.*)
wenzelm@6116
   225
fun forcenext [] = []
wenzelm@36728
   226
  | forcenext (Break _ :: es) = fbrk :: es
wenzelm@6116
   227
  | forcenext (e :: es) = e :: forcenext es;
wenzelm@6116
   228
wenzelm@23645
   229
in
wenzelm@6116
   230
wenzelm@36754
   231
fun formatted margin input =
wenzelm@36754
   232
  let
wenzelm@36754
   233
    val breakgain = margin div 20;     (*minimum added space required of a break*)
wenzelm@36754
   234
    val emergencypos = margin div 2;   (*position too far to right*)
wenzelm@36754
   235
wenzelm@36754
   236
    (*es is list of expressions to print;
wenzelm@36754
   237
      blockin is the indentation of the current block;
wenzelm@36754
   238
      after is the width of the following context until next break.*)
wenzelm@36754
   239
    fun format ([], _, _) text = text
wenzelm@36754
   240
      | format (e :: es, block as (_, blockin), after) (text as {ind, pos, nl, ...}) =
wenzelm@36754
   241
          (case e of
wenzelm@36754
   242
            Block ((bg, en), bes, indent, _) =>
wenzelm@36754
   243
              let
wenzelm@36754
   244
                val pos' = pos + indent;
wenzelm@36754
   245
                val pos'' = pos' mod emergencypos;
wenzelm@36754
   246
                val block' =
wenzelm@36754
   247
                  if pos' < emergencypos then (ind |> add_indent indent, pos')
wenzelm@36754
   248
                  else (add_indent pos'' Buffer.empty, pos'');
wenzelm@36754
   249
                val btext: text = text
wenzelm@36754
   250
                  |> control bg
wenzelm@36754
   251
                  |> format (bes, block', breakdist (es, after))
wenzelm@36754
   252
                  |> control en;
wenzelm@36754
   253
                (*if this block was broken then force the next break*)
wenzelm@36754
   254
                val es' = if nl < #nl btext then forcenext es else es;
wenzelm@36754
   255
              in format (es', block, after) btext end
wenzelm@36754
   256
          | Break (force, wd) =>
wenzelm@36754
   257
              (*no break if text to next break fits on this line
wenzelm@36754
   258
                or if breaking would add only breakgain to space*)
wenzelm@36754
   259
              format (es, block, after)
wenzelm@36754
   260
                (if not force andalso
wenzelm@36754
   261
                    pos + wd <= Int.max (margin - breakdist (es, after), blockin + breakgain)
wenzelm@36754
   262
                  then text |> blanks wd  (*just insert wd blanks*)
wenzelm@36754
   263
                  else text |> newline |> indentation block)
wenzelm@36754
   264
          | String str => format (es, block, after) (string str text));
wenzelm@36754
   265
  in
wenzelm@36756
   266
    #tx (format ([input], (Buffer.empty, 0), 0) empty)
wenzelm@36754
   267
  end;
wenzelm@6116
   268
wenzelm@23645
   269
end;
wenzelm@6116
   270
wenzelm@19266
   271
wenzelm@23645
   272
(* special output *)
wenzelm@6116
   273
wenzelm@23645
   274
(*symbolic markup -- no formatting*)
wenzelm@23645
   275
fun symbolic prt =
wenzelm@6116
   276
  let
wenzelm@30667
   277
    fun out (Block ((bg, en), [], _, _)) = Buffer.add bg #> Buffer.add en
wenzelm@30667
   278
      | out (Block ((bg, en), prts, indent, _)) =
wenzelm@30667
   279
          Buffer.add bg #> Buffer.markup (Markup.block indent) (fold out prts) #> Buffer.add en
wenzelm@23645
   280
      | out (String (s, _)) = Buffer.add s
wenzelm@23787
   281
      | out (Break (false, wd)) = Buffer.markup (Markup.break wd) (Buffer.add (output_spaces wd))
wenzelm@36727
   282
      | out (Break (true, _)) = Buffer.add (Output.output "\n");
wenzelm@23645
   283
  in out prt Buffer.empty end;
wenzelm@23617
   284
wenzelm@23645
   285
(*unformatted output*)
wenzelm@23645
   286
fun unformatted prt =
wenzelm@6116
   287
  let
wenzelm@30667
   288
    fun fmt (Block ((bg, en), prts, _, _)) = Buffer.add bg #> fold fmt prts #> Buffer.add en
wenzelm@23617
   289
      | fmt (String (s, _)) = Buffer.add s
wenzelm@36728
   290
      | fmt (Break (_, wd)) = Buffer.add (output_spaces wd);
wenzelm@36756
   291
  in fmt prt Buffer.empty end;
wenzelm@6116
   292
wenzelm@23645
   293
wenzelm@36757
   294
(* output interfaces *)
wenzelm@30623
   295
wenzelm@36757
   296
val margin_default = Unsynchronized.ref 76;  (*right margin, or page width*)
wenzelm@23645
   297
wenzelm@23645
   298
val symbolicN = "pretty_symbolic";
wenzelm@23645
   299
wenzelm@36754
   300
fun output_buffer margin prt =
wenzelm@23645
   301
  if print_mode_active symbolicN then symbolic prt
wenzelm@36754
   302
  else formatted (the_default (! margin_default) margin) prt;
wenzelm@23645
   303
wenzelm@36754
   304
val output = Buffer.content oo output_buffer;
wenzelm@36754
   305
fun string_of_margin margin = Output.escape o output (SOME margin);
wenzelm@36754
   306
val string_of = Output.escape o output NONE;
wenzelm@23645
   307
val str_of = Output.escape o Buffer.content o unformatted;
wenzelm@23645
   308
val writeln = Output.writeln o string_of;
wenzelm@6116
   309
wenzelm@6116
   310
wenzelm@14832
   311
wenzelm@36757
   312
(** ML toplevel pretty printing **)
wenzelm@36757
   313
wenzelm@36757
   314
fun to_ML (Block (m, prts, ind, _)) = ML_Pretty.Block (m, map to_ML prts, ind)
wenzelm@36757
   315
  | to_ML (String s) = ML_Pretty.String s
wenzelm@36757
   316
  | to_ML (Break b) = ML_Pretty.Break b;
wenzelm@36757
   317
wenzelm@36757
   318
fun from_ML (ML_Pretty.Block (m, prts, ind)) = block_markup m (ind, map from_ML prts)
wenzelm@36757
   319
  | from_ML (ML_Pretty.String s) = String s
wenzelm@36757
   320
  | from_ML (ML_Pretty.Break b) = Break b;
wenzelm@36757
   321
wenzelm@37530
   322
end;
wenzelm@37530
   323
wenzelm@6116
   324
end;