src/Pure/Tools/isabelle_process.ML
author wenzelm
Fri Jan 02 23:28:47 2009 +0100 (2009-01-02 ago)
changeset 29328 eba7f9f3b06d
parent 29327 e41274f6cc9d
child 29522 793766d4c1b5
permissions -rw-r--r--
tuned message_text;
wenzelm@25528
     1
(*  Title:      Pure/Tools/isabelle_process.ML
wenzelm@25528
     2
    Author:     Makarius
wenzelm@25528
     3
wenzelm@25528
     4
Isabelle process wrapper -- interaction via external program.
wenzelm@25631
     5
wenzelm@25631
     6
General format of process output:
wenzelm@25631
     7
wenzelm@29327
     8
  (1) unmarked stdout/stderr, no line structure (output should be
wenzelm@25842
     9
  processed immediately as it arrives);
wenzelm@25631
    10
wenzelm@29327
    11
  (2) properly marked-up messages, e.g. for writeln channel
wenzelm@25810
    12
wenzelm@25810
    13
  "\002A" ^ props ^ "\002,\n" ^ text ^ "\002.\n"
wenzelm@25810
    14
wenzelm@25841
    15
  where the props consist of name=value lines terminated by "\002,\n"
wenzelm@25810
    16
  each, and the remaining text is any number of lines (output is
wenzelm@25842
    17
  supposed to be processed in one piece);
wenzelm@25841
    18
wenzelm@29327
    19
  (3) special init message holds "pid" and "session" property;
wenzelm@29327
    20
wenzelm@29327
    21
  (4) message content is encoded in YXML format.
wenzelm@25528
    22
*)
wenzelm@25528
    23
wenzelm@25528
    24
signature ISABELLE_PROCESS =
wenzelm@25528
    25
sig
wenzelm@26574
    26
  val isabelle_processN: string
wenzelm@28044
    27
  val init: string -> unit
wenzelm@25528
    28
end;
wenzelm@25528
    29
wenzelm@25528
    30
structure IsabelleProcess: ISABELLE_PROCESS =
wenzelm@25528
    31
struct
wenzelm@25528
    32
wenzelm@26550
    33
(* print modes *)
wenzelm@25554
    34
wenzelm@25748
    35
val isabelle_processN = "isabelle_process";
wenzelm@25748
    36
wenzelm@26550
    37
val _ = Output.add_mode isabelle_processN Output.default_output Output.default_escape;
wenzelm@28036
    38
val _ = Markup.add_mode isabelle_processN YXML.output_markup;
wenzelm@25841
    39
wenzelm@25841
    40
wenzelm@25841
    41
(* message markup *)
wenzelm@25841
    42
wenzelm@26527
    43
fun special ch = Symbol.STX ^ ch;
wenzelm@25810
    44
val special_sep = special ",";
wenzelm@25576
    45
val special_end = special ".";
wenzelm@25554
    46
wenzelm@25841
    47
local
wenzelm@25810
    48
wenzelm@26574
    49
fun clean_string bad str =
wenzelm@26574
    50
  if exists_string (member (op =) bad) str then
wenzelm@26574
    51
    translate_string (fn c => if member (op =) bad c then Symbol.DEL else c) str
wenzelm@26574
    52
  else str;
wenzelm@26574
    53
wenzelm@26550
    54
fun message_props props =
wenzelm@26574
    55
  let val clean = clean_string [Symbol.STX, "\n", "\r"]
wenzelm@25841
    56
  in implode (map (fn (x, y) => clean x ^ "=" ^ clean y ^ special_sep ^ "\n") props) end;
wenzelm@25841
    57
wenzelm@29328
    58
fun message_text class body =
wenzelm@29328
    59
  YXML.element Markup.messageN [(Markup.classN, class)] [clean_string [Symbol.STX] body];
wenzelm@26550
    60
wenzelm@29327
    61
fun message_pos trees = trees |> get_first
wenzelm@29327
    62
  (fn XML.Elem (name, atts, ts) =>
wenzelm@29327
    63
        if name = Markup.positionN then SOME (Position.of_properties atts)
wenzelm@29327
    64
        else message_pos ts
wenzelm@29327
    65
    | _ => NONE);
wenzelm@25554
    66
wenzelm@28044
    67
fun output out_stream s = NAMED_CRITICAL "IO" (fn () =>
wenzelm@28182
    68
  (TextIO.output (out_stream, s); TextIO.output (out_stream, "\n")));
wenzelm@28044
    69
wenzelm@25554
    70
in
wenzelm@25554
    71
wenzelm@28044
    72
fun message _ _ _ "" = ()
wenzelm@28044
    73
  | message out_stream ch class body =
wenzelm@27844
    74
      let
wenzelm@29328
    75
        val pos = the_default Position.none (message_pos (YXML.parse_body body));
wenzelm@27844
    76
        val props =
wenzelm@27844
    77
          Position.properties_of (Position.thread_data ())
wenzelm@27844
    78
          |> Position.default_properties pos;
wenzelm@29328
    79
        val txt = message_text class body;
wenzelm@28044
    80
      in output out_stream (special ch ^ message_props props ^ txt ^ special_end) end;
wenzelm@25554
    81
wenzelm@28044
    82
fun init_message out_stream =
wenzelm@25841
    83
  let
wenzelm@28491
    84
    val pid = (Markup.pidN, process_id ());
wenzelm@27972
    85
    val session = (Markup.sessionN, List.last (Session.id ()) handle List.Empty => "unknown");
wenzelm@29328
    86
    val text = message_text Markup.initN (Session.welcome ());
wenzelm@28343
    87
  in output out_stream (special "A" ^ message_props [pid, session] ^ text ^ special_end) end;
wenzelm@25748
    88
wenzelm@25554
    89
end;
wenzelm@25554
    90
wenzelm@25554
    91
wenzelm@25841
    92
(* channels *)
wenzelm@25841
    93
wenzelm@28188
    94
local
wenzelm@28188
    95
wenzelm@28188
    96
fun auto_flush stream =
wenzelm@28188
    97
  let
wenzelm@28189
    98
    val _ = TextIO.StreamIO.setBufferMode (TextIO.getOutstream stream, IO.BLOCK_BUF);
wenzelm@28188
    99
    fun loop () =
wenzelm@28188
   100
      (OS.Process.sleep (Time.fromMilliseconds 50); try TextIO.flushOut stream; loop ());
wenzelm@28188
   101
  in loop end;
wenzelm@28188
   102
wenzelm@28188
   103
in
wenzelm@28188
   104
wenzelm@28044
   105
fun setup_channels out =
wenzelm@28189
   106
  let
wenzelm@28189
   107
    val out_stream =
wenzelm@28189
   108
      if out = "-" then TextIO.stdOut
wenzelm@28189
   109
      else
wenzelm@28189
   110
        let
wenzelm@28189
   111
          val path = File.platform_path (Path.explode out);
wenzelm@28189
   112
          val out_stream = TextIO.openOut path;  (*fifo blocks until reader is ready*)
wenzelm@28189
   113
          val _ = OS.FileSys.remove path;  (*prevent alien access, indicate writer is ready*)
wenzelm@28242
   114
          val _ = SimpleThread.fork false (auto_flush TextIO.stdOut);
wenzelm@28189
   115
        in out_stream end;
wenzelm@28242
   116
    val _ = SimpleThread.fork false (auto_flush out_stream);
wenzelm@28242
   117
    val _ = SimpleThread.fork false (auto_flush TextIO.stdErr);
wenzelm@28044
   118
  in
wenzelm@28343
   119
    Output.status_fn   := message out_stream "B" Markup.statusN;
wenzelm@28343
   120
    Output.writeln_fn  := message out_stream "C" Markup.writelnN;
wenzelm@28343
   121
    Output.priority_fn := message out_stream "D" Markup.priorityN;
wenzelm@28343
   122
    Output.tracing_fn  := message out_stream "E" Markup.tracingN;
wenzelm@28343
   123
    Output.warning_fn  := message out_stream "F" Markup.warningN;
wenzelm@28343
   124
    Output.error_fn    := message out_stream "G" Markup.errorN;
wenzelm@28343
   125
    Output.debug_fn    := message out_stream "H" Markup.debugN;
wenzelm@28498
   126
    Output.prompt_fn   := ignore;
wenzelm@28044
   127
    out_stream
wenzelm@28044
   128
  end;
wenzelm@25841
   129
wenzelm@28188
   130
end;
wenzelm@28188
   131
wenzelm@25841
   132
wenzelm@25554
   133
(* init *)
wenzelm@25554
   134
wenzelm@28044
   135
fun init out =
wenzelm@25841
   136
 (change print_mode (update (op =) isabelle_processN);
wenzelm@28044
   137
  setup_channels out |> init_message;
wenzelm@28342
   138
  OuterKeyword.report ();
wenzelm@26643
   139
  Isar.toplevel_loop {init = true, welcome = false, sync = true, secure = true});
wenzelm@25528
   140
wenzelm@25528
   141
end;