summaryrefslogtreecommitdiff
path: root/manual/tools/caml_tex2.ml
blob: d2f6ceaea928692f77044ff9a8db4c32d2a16412 (plain)
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
(* $Id$ *)

open StdLabels
open Printf
open Str

let camlbegin = "\\caml\n"
let camlend = "\\endcaml\n"
let camlin = "\\\\?\\1"
let camlout = "\\\\:\\1"
let camlbunderline = "\\<"
let camleunderline = "\\>"

let camllight = ref "TERM=norepeat ocaml"
let linelen = ref 72
let outfile = ref ""
let cut_at_blanks = ref false
let files = ref []

let _ =
  Arg.parse ["-n", Arg.Int (fun n -> linelen := n), "line length";
             "-o", Arg.String (fun s -> outfile := s), "output";
             "-caml", Arg.String (fun s -> camllight := s), "toplevel";
             "-w", Arg.Set cut_at_blanks, "cut at blanks"]
    (fun s -> files := s :: !files)
    "caml-tex2: "

let (~!) =
  let memo = ref [] in
  fun key ->
    try List.assq key !memo
    with Not_found ->
      let data = Str.regexp key in
      memo := (key, data) :: !memo;
      data

let caml_input, caml_output =
  let cmd = !camllight ^ " 2>&1" in
  try Unix.open_process cmd with _ -> failwith "Cannot start toplevel"
let () =
  at_exit (fun () -> ignore (Unix.close_process (caml_input, caml_output)));
  ignore (input_line caml_input);
  ignore (input_line caml_input)

let read_output () =
  let input = ref (input_line caml_input) in
  input := replace_first ~!"^# *" "" !input;
  let underline =
    if string_match ~!"Characters *\\([0-9]+\\)-\\([0-9]+\\):$" !input 0
    then
      let b = int_of_string (matched_group 1 !input)
      and e = int_of_string (matched_group 2 !input) in
      input := input_line caml_input;
      b, e
    else 0, 0
  in
  let output = Buffer.create 256 in
  while not (string_match ~!".*\"end_of_input\"$" !input 0) do
    prerr_endline !input;
    Buffer.add_string output !input;
    Buffer.add_char output '\n';
    input := input_line caml_input;
  done;
  Buffer.contents output, underline

let escape_specials s =
  let s1 = global_replace ~!"\\\\" "\\\\\\\\" s in
  let s2 = global_replace ~!"'" "\\\\textquotesingle\\\\-" s1 in
  let s3 = global_replace ~!"`" "\\\\textasciigrave\\\\-" s2 in
  s3

let process_file file =
  prerr_endline ("Processing " ^ file);
  let ic = try open_in file with _ -> failwith "Cannot read input file" in
  let oc =
    try if !outfile = "-" then
      stdout
    else if !outfile = "" then
      open_out (replace_first ~!"\\.tex$" "" file ^ ".ml.tex")
    else
      open_out_gen [Open_wronly; Open_creat; Open_append; Open_text]
        0x666 !outfile
    with _ -> failwith "Cannot open output file" in
  try while true do
    let input = ref (input_line ic) in
    if string_match ~!"\\\\begin{caml_example\\(\\*?\\)}[ \t]*$"
        !input 0
    then begin
      let omit_answer = matched_group 1 !input = "*" in
      output_string oc camlbegin;
      let first = ref true in
      let read_phrase () =
        let phrase = Buffer.create 256 in
        while
          let input = input_line ic in
          if string_match ~!"\\\\end{caml_example\\*?}[ \t]*$"
              input 0
          then raise End_of_file;
          if Buffer.length phrase > 0 then Buffer.add_char phrase '\n';
          Buffer.add_string phrase input;
          not (string_match ~!".*;;[ \t]*$" input 0)
        do
          ()
        done;
        Buffer.contents phrase
      in
      try while true do
        let phrase = read_phrase () in
        fprintf caml_output "%s\n" phrase;
        flush caml_output;
        output_string caml_output "\"end_of_input\";;\n";
        flush caml_output;
        let output, (b, e) = read_output () in
        let phrase =
          if b < e then begin
            let start = String.sub phrase ~pos:0 ~len:b
            and underlined = String.sub phrase ~pos:b ~len:(e-b)
            and rest =
              String.sub phrase ~pos:e ~len:(String.length phrase - e)
            in
            String.concat ""
              [escape_specials start; "\\<";
               escape_specials underlined; "\\>";
               escape_specials rest]
          end else
            escape_specials phrase in
        (* Special characters may also appear in output strings -Didier *)
        let output = escape_specials output in
        let phrase = global_replace ~!"^\\(.\\)" camlin phrase
        and output = global_replace ~!"^\\(.\\)" camlout output in
        if not !first then output_string oc "\\;\n";
        fprintf oc "%s\n" phrase;
        if not omit_answer then fprintf oc "%s" output;
        flush oc;
        first := false
      done
      with End_of_file -> output_string oc camlend
    end
    else if string_match ~!"\\\\begin{caml_eval}[ \t]*$" !input 0
    then begin
      while input := input_line ic;
        not (string_match ~!"\\\\end{caml_eval}[ \t]*$" !input 0)
      do
        fprintf caml_output "%s\n" !input;
        if string_match ~!".*;;[ \t]*$" !input 0 then begin
          flush caml_output;
          output_string caml_output "\"end_of_input\";;\n";
          flush caml_output;
          ignore (read_output ())
        end
      done
    end else begin
      fprintf oc "%s\n" !input;
      flush oc
    end
  done with
    End_of_file -> close_in ic; close_out oc

let _ =
  if !outfile <> "-" && !outfile <> "" then begin
    try close_out (open_out !outfile)
    with _ -> failwith "Cannot open output file"
  end;
  List.iter process_file (List.rev !files)