signature PRETTY = sig type T val to_ML: T -> ML_Pretty.pretty val from_ML: ML_Pretty.pretty -> T val blk: int * T list -> T val block0: T list -> T val block1: T list -> T val block: T list -> T type'a block = {markup: 'a, open_block: bool, consistent: bool, indent: int} val make_block: Markup.output block -> T list -> T val markup_block: Markup.T block -> T list -> T val markup: Markup.T -> T list -> T val markup_open: Markup.T -> T list -> T val mark: Markup.T -> T -> T val mark_open: Markup.T -> T -> T val markup_blocks: Markup.T list block -> T list -> T val str: string -> T val dots: T val brk: int -> T val brk_indent: int -> int -> T val fbrk: T val breaks: T list -> T list val fbreaks: T list -> T list val strs: stringlist -> T val mark_str: Markup.T * string -> T val marks_str: Markup.T list * string -> T val mark_position: Position.T -> T -> T val mark_str_position: Position.T * string -> T val item: T list -> T val text_fold: T list -> T val keyword1: string -> T val keyword2: string -> T val text: string -> T list val paragraph: T list -> T val para: string -> T val quote: T -> T val cartouche: T -> T val separate: string -> T list -> T list val commas: T list -> T list val enclose: string -> string -> T list -> T val enum: string -> string -> string -> T list -> T val position: Position.T -> T val here: Position.T -> T list vallist: string -> string -> T list -> T val str_list: string -> string -> stringlist -> T val big_list: string -> T list -> T val indent: int -> T -> T val unbreakable: T -> T type output_ops =
{symbolic: bool,
output: string -> Output.output * int,
markup: Markup.output -> Markup.output,
indent_markup: Markup.output,
margin: int} val output_ops: int option -> output_ops val pure_output_ops: int option -> output_ops val markup_output_ops: int option -> output_ops val symbolic_output_ops: output_ops val symbolic_output: T -> Bytes.T val symbolic_string_of: T -> string val unformatted_string_of: T -> string val output: output_ops -> T -> Bytes.T val string_of_ops: output_ops -> T -> string val string_of: T -> string val strings_of: T -> stringlist val pure_string_of: T -> string val writeln: T -> unit val writeln_urgent: T -> unit val markup_chunks: Markup.T -> T list -> T val chunks: T list -> T val chunks2: T list -> T val block_enclose: T * T -> T list -> T end;
structure Pretty: PRETTY = struct
(** Pretty.T **)
(* abstype: ML_Pretty.pretty without (op =) *)
abstype T = T of ML_Pretty.pretty with fun to_ML (T prt) = prt; val from_ML = T; end;
val force_nat = Integer.max 0; val short_nat = FixedInt.fromInt o force_nat; val long_nat = force_nat o FixedInt.toInt;
fun markup m = markup_block {markup = m, open_block = false, consistent = false, indent = 0}; fun markup_open m = markup_block {markup = m, open_block = true, consistent = false, indent = 0};
fun mark m prt = if m = Markup.empty then prt else markup m [prt]; fun mark_open m prt = if m = Markup.empty then prt else markup_open m [prt];
fun markup_blocks ({markup, open_block, consistent, indent}: Markup.T list block) body = if forall Markup.is_empty markup andalso not open_block andalso not consistent then blk (indent, body) else let val markups = filter_out Markup.is_empty markup; val (ms, m) = if null markups then ([], Markup.empty) else split_last markups; in
fold_rev mark_open ms
(markup_block
{markup = m, open_block = open_block, consistent = consistent, indent = indent} body) end;
(* breaks *)
fun brk_indent wd ind = from_ML (ML_Pretty.PrettyBreak (short_nat wd, short_nat ind)); fun brk wd = brk_indent wd 0; val fbrk = from_ML ML_Pretty.PrettyLineBreak;
fun breaks prts = Library.separate (brk 1) prts; fun fbreaks prts = Library.separate fbrk prts;
(* derived operations to create formatting expressions *)
val str = from_ML o ML_Pretty.str; val dots = from_ML ML_Pretty.dots;
val strs = block o breaks o map str;
fun mark_str (m, s) = mark_open m (str s); fun marks_str (ms, s) = fold_rev mark_open ms (str s);
val mark_position = mark o Position.markup; fun mark_str_position (pos, s) = mark_str (Position.markup pos, s);
val item = markup Markup.item; val text_fold = markup Markup.text_fold;
fun keyword1 name = mark_str (Markup.keyword1, name); fun keyword2 name = mark_str (Markup.keyword2, name);
val text = breaks o map str o Symbol.explode_words; val paragraph = markup Markup.paragraph; val para = paragraph o text;
fun quote prt = block1 [str "\"", prt, str "\""]; fun cartouche prt = block1 [str Symbol.open_, prt, str Symbol.close];
fun separate sep prts =
flat (Library.separate [str sep, brk 1] (map single prts));
(* robust string output, with potential data loss *)
fun robust_string_of out prt = let val bs = out prt; val bs' = if Bytes.size bs + 8000 <= String.maxSize then bs else out (from_ML (ML_Pretty.no_markup (to_ML prt))); in Bytes.content bs' end;
val pure_output_ops = make_output_ops {symbolic = false, markup = K Markup.no_output}; val markup_output_ops = make_output_ops {symbolic = false, markup = I}; val symbolic_output_ops = make_output_ops {symbolic = true, markup = I} NONE;
fun output_ops opt_margin = if Print_Mode.PIDE_enabled () then symbolic_output_ops else pure_output_ops opt_margin;
fun output_newline (ops: output_ops) = #1 (#output ops "\n");
fun output_spaces (ops: output_ops) = #output ops o Symbol.spaces; fun output_spaces_bytes ops = Bytes.add o #1 o output_spaces ops;
(* output tree *)
datatype tree = End(*end of markup*)
| Block of
{markup: Markup.output,
open_block: bool,
consistent: bool,
indent: int,
body: tree list,
length: int}
| Break ofbool * int * int (*mandatory flag, width if not taken, extra indentation if taken*)
| Str of Output.output * int; (*string output, length*)
fun tree_length End = 0
| tree_length (Block {length = len, ...}) = len
| tree_length (Break (_, wd, _)) = wd
| tree_length (Str (_, len)) = len;
local
fun block_length indent = let fun block_len len body = let val (line, rest) = chop_prefix (fn Break (true, _, _) => false | _ => true) body; val len' = Int.max (fold (Integer.add o tree_length) line 0, len); in
(case rest of
Break (true, _, ind) :: rest' =>
block_len len' (Break (false, indent + ind, 0) :: rest')
| [] => len') end; in block_len 0end;
val fbreak = Break (true, 1, 0);
in
fun output_tree (ops: output_ops) make_length = let fun tree (ML_Pretty.PrettyBlock (ind, consistent, context, body)) = let val indent = long_nat ind; val body' = map tree body; in
Block
{markup = #markup ops (ML_Pretty.markup_get context),
open_block = ML_Pretty.open_block_detect context,
consistent = consistent,
indent = indent,
body = body',
length = if make_length then block_length indent body' else ~1} end
| tree (ML_Pretty.PrettyBreak (wd, ind)) = Break (false, long_nat wd, long_nat ind)
| tree ML_Pretty.PrettyLineBreak = fbreak
| tree (ML_Pretty.PrettyString s) = Str (#output ops s ||> force_nat)
| tree (ML_Pretty.PrettyStringWithWidth (s, n)) = Str (s, long_nat n); in tree o to_ML end;
end;
(* formatted output *)
local
type context = Markup.output list; (*stack of pending markup*)
abstype state = State of context * Bytes.T with
fun state_output (State (_, output)) = output;
val init_state = State ([], Bytes.empty);
fun add_state s (State (context, output)) =
State (context, Bytes.add s output);
fun push_state (markup as (bg, _)) (State (context, output)) =
State (markup :: context, Bytes.add bg output);
fun pop_state (State ((_, en) :: context, output)) =
State (context, Bytes.add en output);
fun close_state (State (context, output)) =
State ([], fold (Bytes.add o #2) context output);
fun reopen_state (State (old_context, _)) (State (context, output)) =
State (old_context @ context, fold_rev (Bytes.add o #1) old_context output);
end;
type text = {main: state, line: state option, pos: int, nl: int};
val init_no_line: text = {main = init_state, line = NONE, pos = 0, nl = 0}; val init_line: text = {main = init_state, line = SOME init_state, pos = 0, nl = 0};
funstring (s, len) {main, line, pos, nl} : text =
{main = add_state s main,
line = Option.map (add_state s) line,
pos = pos + len,
nl = nl};
fun push m {main, line, pos, nl} : text =
{main = push_state m main,
line = Option.map (push_state m) line,
pos = pos,
nl = nl};
fun pop {main, line, pos, nl} : text =
{main = pop_state main,
line = Option.map pop_state line,
pos = pos,
nl = nl};
fun result ({main, ...}: text) = state_output main;
(*Add the lengths of the expressions until the next Break; if no Break then
include "after", to account for text following this block.*) fun break_dist (Break _ :: _, _) = 0
| break_dist (prt :: prts, after) = tree_length prt + break_dist (prts, after)
| break_dist ([], after) = after;
(*Search for the next break (at this or higher levels) and force it to occur.*) fun force_next [] = []
| force_next ((prt as Break _) :: prts) = force_break prt :: prts
| force_next (prt :: prts) = prt :: force_next prts;
nonfix before;
in
fun format_tree (ops: output_ops) input = let val margin = #margin ops; val margin_defined = margin > 0; val margin1 = if margin_defined then margin else ML_Pretty.default_margin;
val breakgain = margin1 div 20; (*minimum added space required of a break*) val emergencypos = Int.max (margin1 div 2, 1); (*position too far to right*)
fun blanks_state n state = add_state (#1 (output_spaces ops n)) state; val blanks = string o output_spaces ops;
val indent_markup = #indent_markup ops; val no_indent_markup = indent_markup = Markup.no_output;
val break_state = add_state (output_newline ops); fun break (state, pos) ({main, nl, ...}: text) : text = let val (main', line') = if no_indent_markup then
(main |> break_state |> add_state (Symbol.spaces pos), NONE) else let val ss = Bytes.contents (state_output (the state)); val main' = if null ss then main |> break_state else
main |> close_state |> break_state
|> push_state indent_markup |> fold add_state ss |> pop_state
|> reopen_state main; val line' = init_state |> fold add_state ss |> reopen_state main; in (main', SOME line') end; in {main = main', line = line', pos = pos, nl = nl + 1} end;
(*'before' is the indentation context of the current block*) (*'after' is the width of the input context until the next break*) fun format (trees, before, after) (text as {pos, ...}) =
(case trees of
[] => text
| End :: ts => format (ts, before, after) (pop text)
| Block {markup, open_block = true, body, ...} :: ts =>
text |> push markup |> format (body @ End :: ts, before, after)
| Block {markup, consistent, indent, body, length = blen, ...} :: ts => let val pos' = pos + indent; val pos'' = pos' mod emergencypos; val before' = ifnot margin_defined orelse pos' < emergencypos then (Option.map (close_state o blanks_state indent) (#line text), pos') else (Option.map (fn _ => blanks_state pos'' init_state) (#line text), pos''); val after' = break_dist (ts, after) val body' = body
|> (margin_defined andalso consistent andalso pos + blen > margin - after') ? map force_break; val btext: text =
text |> push markup |> format (body' @ [End], before', after'); (*if this block was broken then force the next break*) val ts' = if #nl text < #nl btext then force_next ts else ts; in format (ts', before, after) btext end
| Break (force, wd, ind) :: ts => (*no break if text to next break fits on this line
or if breaking would add only breakgain to space*)
format (ts, before, after)
(ifnot force andalso
(not margin_defined orelse
pos + wd <= Int.max (margin - break_dist (ts, after), #2 before + breakgain)) then text |> blanks wd (*just insert wd blanks*) else text |> break before |> blanks ind)
| Str str :: ts => format (ts, before, after) (string str text));
val init = if no_indent_markup then init_no_line else init_line; in
result (format ([output_tree ops true input], (#line init, 0), 0) init) end;
end;
(** no formatting **)
(* symbolic output: XML markup for blocks/breaks + other markup *)
val symbolic_output = let val ops = symbolic_output_ops;
fun markup_bytes m output_body = letval (bg, en) = #markup ops (YXML.output_markup m) in Bytes.add bg #> output_body #> Bytes.add en end;
fun output End = I
| output (Block {markup = (bg, en), body = [], ...}) = Bytes.add bg #> Bytes.add en
| output (Block {markup = (bg, en), open_block = true, body, ...}) =
Bytes.add bg #> fold output body #> Bytes.add en
| output (Block {markup = (bg, en), consistent, indent, body, ...}) = letval block_markup = Markup.block {consistent = consistent, indent = indent} in Bytes.add bg #> markup_bytes block_markup (fold output body) #> Bytes.add en end
| output (Break (false, wd, ind)) =
markup_bytes (Markup.break {width = wd, indent = ind}) (output_spaces_bytes ops wd)
| output (Break (true, _, _)) = Bytes.add (output_newline ops)
| output (Str (s, _)) = Bytes.add s; in Bytes.build o output o output_tree ops falseend;
val symbolic_string_of = robust_string_of symbolic_output;
(* unformatted output: other markup only *)
fun unformatted ops = let fun output End = I
| output (Block ({markup = (bg, en), body, ...})) =
Bytes.add bg #> fold output body #> Bytes.add en
| output (Break (_, wd, _)) = output_spaces_bytes ops wd
| output (Str (s, _)) = Bytes.add s; in Bytes.build o output o output_tree ops falseend;
fun unformatted_string_of prt =
robust_string_of (unformatted (output_ops NONE)) prt;
(* output interfaces *)
fun output ops = if #symbolic ops then symbolic_output else format_tree ops;
fun string_of_ops ops = robust_string_of (output ops); fun string_of prt = string_of_ops (output_ops NONE) prt;
fun strings_of prt = Bytes.contents (output (output_ops NONE) prt);
val pure_string_of = string_of_ops (pure_output_ops NONE);
fun gen_writeln urgent prt =
(if urgent then Output.writelns_urgent else Output.writelns)
(Bytes.contents (output (output_ops NONE) prt));
val writeln = gen_writeln false; val writeln_urgent = gen_writeln true;
(* chunks *)
fun markup_chunks m prts = markup m (fbreaks (map (text_fold o single) prts)); val chunks = markup_chunks Markup.empty;
fun chunks2 prts =
(casetry split_last prts of
NONE => block0 []
| SOME (prefix, last) =>
block0 (maps (fn prt => [text_fold [prt, fbrk], fbrk]) prefix @ [text_fold [last]]));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.