~ubuntu-branches/ubuntu/wily/coq-doc/wily

« back to all changes in this revision

Viewing changes to toplevel/toplevel.ml

  • Committer: Bazaar Package Importer
  • Author(s): Stéphane Glondu, Stéphane Glondu, Samuel Mimram
  • Date: 2010-01-07 22:50:39 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100107225039-n3cq82589u0qt0s2
Tags: 8.2pl1-1
[ Stéphane Glondu ]
* New upstream release (Closes: #563669)
  - remove patches
* Packaging overhaul:
  - use git, advertise it in Vcs-* fields of debian/control
  - use debhelper 7 and dh with override
  - use source format 3.0 (quilt)
* debian/control:
  - set Maintainer to d-o-m, set Uploaders to Sam and myself
  - add Homepage field
  - bump Standards-Version to 3.8.3
* Register PDF documentation into doc-base
* Add debian/watch
* Update debian/copyright

[ Samuel Mimram ]
* Change coq-doc's description to mention that it provides documentation in
  pdf format, not postscript, closes: #543545.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(************************************************************************)
 
2
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team     *)
 
3
(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
 
4
(*   \VV/  **************************************************************)
 
5
(*    //   *      This file is distributed under the terms of the       *)
 
6
(*         *       GNU Lesser General Public License Version 2.1        *)
 
7
(************************************************************************)
 
8
 
 
9
(* $Id: toplevel.ml 11784 2009-01-14 11:36:32Z herbelin $ *)
 
10
 
 
11
open Pp
 
12
open Util
 
13
open Flags
 
14
open Cerrors
 
15
open Vernac
 
16
open Vernacexpr
 
17
open Pcoq
 
18
open Protectedtoplevel
 
19
 
 
20
(* A buffer for the character read from a channel. We store the command
 
21
 * entered to be able to report errors without pretty-printing. *)
 
22
 
 
23
type input_buffer = { 
 
24
  mutable prompt : unit -> string;
 
25
  mutable str : string; (* buffer of already read characters *)
 
26
  mutable len : int;    (* number of chars in the buffer *)
 
27
  mutable bols : int list; (* offsets in str of begining of lines *)
 
28
  mutable tokens : Gram.parsable; (* stream of tokens *)
 
29
  mutable start : int } (* stream count of the first char of the buffer *)
 
30
 
 
31
(* Double the size of the buffer. *)
 
32
 
 
33
let resize_buffer ibuf =
 
34
  let nstr = String.create (2 * String.length ibuf.str + 1) in
 
35
  String.blit ibuf.str 0 nstr 0 (String.length ibuf.str);
 
36
  ibuf.str <- nstr
 
37
 
 
38
(* Delete all irrelevent lines of the input buffer. Keep the last line
 
39
   in the buffer (useful when there are several commands on the same line. *)
 
40
 
 
41
let resynch_buffer ibuf =
 
42
  match ibuf.bols with
 
43
    | ll::_ ->
 
44
        let new_len = ibuf.len - ll in
 
45
        String.blit ibuf.str ll ibuf.str 0 new_len;
 
46
        ibuf.len <- new_len;
 
47
        ibuf.bols <- [];
 
48
        ibuf.start <- ibuf.start + ll
 
49
    | _ -> ()
 
50
 
 
51
 
 
52
(* emacs special character for prompt end (fast) detection. Prefer
 
53
   (Char.chr 6) since it does not interfere with utf8. For
 
54
    compatibility we let (Char.chr 249) as default for a while. *)
 
55
 
 
56
let emacs_prompt_startstring() = Printer.emacs_str "" "<prompt>"
 
57
 
 
58
let emacs_prompt_endstring() = Printer.emacs_str (String.make 1 (Char.chr 249)) "</prompt>"
 
59
 
 
60
(* Read a char in an input channel, displaying a prompt at every
 
61
   beginning of line. *)
 
62
let prompt_char ic ibuf count =
 
63
  let bol = match ibuf.bols with
 
64
    | ll::_ -> ibuf.len == ll
 
65
    | [] -> ibuf.len == 0
 
66
  in
 
67
  if bol && not !print_emacs then msgerr (str (ibuf.prompt()));
 
68
  try
 
69
    let c = input_char ic in
 
70
    if c == '\n' then ibuf.bols <- (ibuf.len+1) :: ibuf.bols;
 
71
    if ibuf.len == String.length ibuf.str then resize_buffer ibuf;
 
72
    ibuf.str.[ibuf.len] <- c;
 
73
    ibuf.len <- ibuf.len + 1;
 
74
    Some c
 
75
  with End_of_file -> 
 
76
    None
 
77
 
 
78
(* Reinitialize the char stream (after a Drop) *)
 
79
 
 
80
let reset_input_buffer ic ibuf =
 
81
  ibuf.str <- "";
 
82
  ibuf.len <- 0;
 
83
  ibuf.bols <- [];
 
84
  ibuf.tokens <- Gram.parsable (Stream.from (prompt_char ic ibuf));
 
85
  ibuf.start <- 0
 
86
 
 
87
(* Functions to print underlined locations from an input buffer. *)
 
88
 
 
89
(* Given a location, returns the list of locations of each line. The last
 
90
   line is returned separately. It also checks the location bounds. *)
 
91
 
 
92
let get_bols_of_loc ibuf (bp,ep) =
 
93
  let add_line (b,e) lines =
 
94
    if b < 0 or e < b then anomaly "Bad location";
 
95
    match lines with
 
96
      | ([],None) -> ([], Some (b,e))
 
97
      | (fl,oe) -> ((b,e)::fl, oe) 
 
98
  in
 
99
  let rec lines_rec ba after = function
 
100
    | []                  -> add_line (0,ba) after
 
101
    | ll::_ when ll <= bp -> add_line (ll,ba) after
 
102
    | ll::fl              ->
 
103
        let nafter = if ll < ep then add_line (ll,ba) after else after in
 
104
        lines_rec ll nafter fl 
 
105
  in
 
106
  let (fl,ll) = lines_rec ibuf.len ([],None) ibuf.bols in
 
107
  (fl,Option.get ll)
 
108
 
 
109
let dotted_location (b,e) =
 
110
  if e-b < 3 then 
 
111
    ("", String.make (e-b) ' ')
 
112
  else 
 
113
    (String.make (e-b-1) '.', " ")
 
114
 
 
115
let print_highlight_location ib loc =
 
116
  let (bp,ep) = unloc loc in
 
117
  let bp = bp - ib.start 
 
118
  and ep = ep - ib.start in
 
119
  let highlight_lines =
 
120
    match get_bols_of_loc ib (bp,ep) with
 
121
      | ([],(bl,el)) ->  
 
122
          (str"> " ++ str(String.sub ib.str bl (el-bl-1)) ++ fnl () ++
 
123
             str"> " ++ str(String.make (bp-bl) ' ') ++
 
124
             str(String.make (ep-bp) '^'))
 
125
      | ((b1,e1)::ml,(bn,en)) ->
 
126
          let (d1,s1) = dotted_location (b1,bp) in
 
127
          let (dn,sn) = dotted_location (ep,en) in
 
128
          let l1 = (str"> " ++ str d1 ++ str s1 ++
 
129
                      str(String.sub ib.str bp (e1-bp))) in
 
130
          let li =
 
131
            prlist (fun (bi,ei) ->
 
132
                      (str"> " ++ str(String.sub ib.str bi (ei-bi)))) ml in
 
133
          let ln = (str"> " ++ str(String.sub ib.str bn (ep-bn)) ++
 
134
                      str sn ++ str dn) in 
 
135
          (l1 ++ li ++ ln)
 
136
  in 
 
137
  let loc = make_loc (bp,ep) in
 
138
  (str"Toplevel input, characters " ++ Cerrors.print_loc loc ++ str":" ++ fnl () ++
 
139
     highlight_lines ++ fnl ())
 
140
 
 
141
(* Functions to report located errors in a file. *)
 
142
 
 
143
let print_location_in_file s inlibrary fname loc =
 
144
  let errstrm = str"Error while reading " ++ str s in
 
145
  if loc = dummy_loc then
 
146
    hov 1 (errstrm ++ spc() ++ str" (unknown location):") ++ fnl ()
 
147
  else
 
148
    let errstrm =
 
149
      if s = fname then mt() else errstrm ++ str":" ++ fnl() in
 
150
    if inlibrary then
 
151
      hov 0 (errstrm ++ str"Module " ++ str ("\""^fname^"\"") ++ spc() ++
 
152
             str"characters " ++ Cerrors.print_loc loc) ++ fnl ()
 
153
    else
 
154
    let (bp,ep) = unloc loc in
 
155
    let ic = open_in fname in
 
156
    let rec line_of_pos lin bol cnt =
 
157
      if cnt < bp then
 
158
        if input_char ic == '\n'
 
159
        then line_of_pos (lin + 1) (cnt +1) (cnt+1)
 
160
        else line_of_pos lin bol (cnt+1)
 
161
      else (lin, bol)
 
162
    in
 
163
    try
 
164
      let (line, bol) = line_of_pos 1 0 0 in
 
165
      close_in ic;
 
166
      hov 0
 
167
        (errstrm ++ str"File " ++ str ("\""^fname^"\"") ++ str"," ++ spc() ++
 
168
         hov 0 (str"line " ++ int line ++ str"," ++ spc() ++
 
169
                str"characters " ++
 
170
                Cerrors.print_loc (make_loc (bp-bol,ep-bol))) ++ str":") ++
 
171
      fnl ()
 
172
    with e ->
 
173
      (close_in ic;
 
174
       hov 1 (errstrm ++ spc() ++ str"(invalid location):") ++ fnl ())
 
175
        
 
176
let print_command_location ib dloc =
 
177
  match dloc with
 
178
    | Some (bp,ep) ->
 
179
        (str"Error during interpretation of command:" ++ fnl () ++
 
180
           str(String.sub ib.str (bp-ib.start) (ep-bp)) ++ fnl ())
 
181
    | None -> (mt ())
 
182
 
 
183
let valid_loc dloc loc =
 
184
  loc <> dummy_loc
 
185
  & match dloc with
 
186
    | Some dloc ->
 
187
        let (bd,ed) = unloc dloc in let (b,e) = unloc loc in bd<=b & e<=ed
 
188
    | _ -> true
 
189
          
 
190
let valid_buffer_loc ib dloc loc =
 
191
  valid_loc dloc loc & 
 
192
  let (b,e) = unloc loc in b-ib.start >= 0 & e-ib.start < ib.len & b<=e 
 
193
 
 
194
(*s The Coq prompt is the name of the focused proof, if any, and "Coq"
 
195
    otherwise. We trap all exceptions to prevent the error message printing
 
196
    from cycling. *)
 
197
let make_prompt () =
 
198
  try
 
199
    (Names.string_of_id (Pfedit.get_current_proof_name ())) ^ " < "
 
200
  with _ -> 
 
201
    "Coq < "
 
202
 
 
203
(*let build_pending_list l = 
 
204
  let pl = ref ">" in
 
205
  let l' = ref l in
 
206
  let res = 
 
207
    while List.length !l' > 1 do 
 
208
      pl := !pl ^ "|" Names.string_of_id x;
 
209
      l':=List.tl !l'
 
210
    done in
 
211
  let last = try List.hd !l' with _ ->   in
 
212
  "<"^l'
 
213
*)    
 
214
 
 
215
(* the coq prompt added to the default one when in emacs mode
 
216
   The prompt contains the current state label [n] (for global
 
217
   backtracking) and the current proof state [p] (for proof
 
218
   backtracking) plus the list of open (nested) proofs (for proof
 
219
   aborting when backtracking). It looks like:
 
220
   
 
221
   "n |lem1|lem2|lem3| p < "
 
222
*)
 
223
let make_emacs_prompt() =
 
224
  let statnum = string_of_int (Lib.current_command_label ()) in
 
225
  let dpth = Pfedit.current_proof_depth() in
 
226
  let pending = Pfedit.get_all_proof_names() in
 
227
  let pendingprompt = 
 
228
    List.fold_left 
 
229
      (fun acc x -> acc ^ (if acc <> "" then "|" else "") ^ Names.string_of_id x)
 
230
      "" pending in
 
231
  let proof_info = if dpth >= 0 then string_of_int dpth else "0" in
 
232
  if !Flags.print_emacs then statnum ^ " |" ^ pendingprompt ^ "| " ^ proof_info ^ " < "
 
233
  else ""
 
234
 
 
235
(* A buffer to store the current command read on stdin. It is
 
236
 * initialized when a vernac command is immediately followed by "\n",
 
237
 * or after a Drop. *)
 
238
let top_buffer =
 
239
  let pr() = 
 
240
    emacs_prompt_startstring() 
 
241
    ^ make_prompt() 
 
242
    ^ make_emacs_prompt()
 
243
    ^ emacs_prompt_endstring()
 
244
  in
 
245
  { prompt = pr;
 
246
    str = "";
 
247
    len = 0;
 
248
    bols = [];
 
249
    tokens = Gram.parsable (Stream.of_list []);
 
250
    start = 0 }
 
251
 
 
252
let set_prompt prompt =
 
253
  top_buffer.prompt
 
254
  <- (fun () -> 
 
255
    emacs_prompt_startstring()
 
256
    ^ prompt ()
 
257
    ^ emacs_prompt_endstring())
 
258
 
 
259
(* Removes and prints the location of the error. The following exceptions
 
260
   need not be located. *)
 
261
let rec is_pervasive_exn = function
 
262
  | Out_of_memory | Stack_overflow | Sys.Break -> true
 
263
  | Error_in_file (_,_,e) -> is_pervasive_exn e
 
264
  | Stdpp.Exc_located (_,e) -> is_pervasive_exn e
 
265
  | DuringCommandInterp (_,e) -> is_pervasive_exn e
 
266
  | DuringSyntaxChecking e -> is_pervasive_exn e
 
267
  | _ -> false
 
268
 
 
269
(* Toplevel error explanation, dealing with locations, Drop, Ctrl-D
 
270
   May raise only the following exceptions: Drop and End_of_input,
 
271
   meaning we get out of the Coq loop *)
 
272
let print_toplevel_error exc =
 
273
  let (dloc,exc) =
 
274
    match exc with
 
275
      | DuringCommandInterp (loc,ie)
 
276
      | Stdpp.Exc_located (loc, DuringSyntaxChecking ie) ->
 
277
          if loc = dummy_loc then (None,ie) else (Some loc, ie)
 
278
      | _ -> (None, exc) 
 
279
  in
 
280
  let (locstrm,exc) =
 
281
    match exc with
 
282
      | Stdpp.Exc_located (loc, ie) ->
 
283
          if valid_buffer_loc top_buffer dloc loc then
 
284
            (print_highlight_location top_buffer loc, ie)
 
285
          else 
 
286
            ((mt ()) (* print_command_location top_buffer dloc *), ie)
 
287
      | Error_in_file (s, (inlibrary, fname, loc), ie) ->
 
288
          (print_location_in_file s inlibrary fname loc, ie)
 
289
      | _ -> 
 
290
          ((mt ()) (* print_command_location top_buffer dloc *), exc)
 
291
  in
 
292
  match exc with
 
293
    | End_of_input -> 
 
294
        msgerrnl (mt ()); pp_flush(); exit 0
 
295
    | Vernacexpr.Drop ->  (* Last chance *)
 
296
        if Mltop.is_ocaml_top() then raise Vernacexpr.Drop;
 
297
        (str"Error: There is no ML toplevel." ++ fnl ())
 
298
    | Vernacexpr.ProtectedLoop ->
 
299
        raise Vernacexpr.ProtectedLoop
 
300
    | Vernacexpr.Quit -> 
 
301
        raise Vernacexpr.Quit
 
302
    | _ -> 
 
303
        (if is_pervasive_exn exc then (mt ()) else locstrm) ++
 
304
        Cerrors.explain_exn exc
 
305
 
 
306
(* Read the input stream until a dot is encountered *)
 
307
let parse_to_dot =
 
308
  let rec dot st = match Stream.next st with
 
309
    | ("", ".") -> ()
 
310
    | ("EOI", "") -> raise End_of_input
 
311
    | _ -> dot st
 
312
  in 
 
313
  Gram.Entry.of_parser "Coqtoplevel.dot" dot
 
314
    
 
315
(* We assume that when a lexer error occurs, at least one char was eaten *)
 
316
let rec discard_to_dot () =
 
317
  try 
 
318
    Gram.Entry.parse parse_to_dot top_buffer.tokens
 
319
  with Stdpp.Exc_located(_,Token.Error _) -> 
 
320
    discard_to_dot()
 
321
 
 
322
 
 
323
(* If the error occured while parsing, we read the input until a dot token
 
324
 * in encountered. *)
 
325
 
 
326
let process_error = function
 
327
  | DuringCommandInterp _ 
 
328
  | Stdpp.Exc_located (_,DuringSyntaxChecking _) as e -> e
 
329
  | e ->
 
330
      if is_pervasive_exn e then 
 
331
        e
 
332
      else 
 
333
        try 
 
334
          discard_to_dot (); e 
 
335
        with
 
336
          | End_of_input -> End_of_input
 
337
          | de -> if is_pervasive_exn de then de else e
 
338
 
 
339
(* do_vernac reads and executes a toplevel phrase, and print error
 
340
   messages when an exception is raised, except for the following:
 
341
     Drop: kill the Coq toplevel, going down to the Caml toplevel if it exists.
 
342
           Otherwise, exit.
 
343
     End_of_input: Ctrl-D was typed in, we will quit *)
 
344
let do_vernac () =
 
345
  msgerrnl (mt ());
 
346
  if !print_emacs then msgerr (str (top_buffer.prompt()));
 
347
  resynch_buffer top_buffer;
 
348
  begin 
 
349
    try 
 
350
      raw_do_vernac top_buffer.tokens
 
351
    with e -> 
 
352
      msgnl (print_toplevel_error (process_error e)) 
 
353
  end;
 
354
  flush_all()
 
355
 
 
356
(* coq and go read vernacular expressions until Drop is entered.
 
357
 * Ctrl-C will raise the exception Break instead of aborting Coq.
 
358
 * Here we catch the exceptions terminating the Coq loop, and decide
 
359
 * if we really must quit.
 
360
 * The boolean value is used to choose between a protected loop, which
 
361
 * we think is more suited for communication with other programs, or
 
362
 * plain communication. *)
 
363
 
 
364
let rec coq_switch b =
 
365
  Sys.catch_break true;
 
366
  (* ensure we have a command separator object (DOT) so that the first
 
367
     command can be reseted. *)
 
368
  Lib.mark_end_of_command();
 
369
  try
 
370
    if b then begin
 
371
      reset_input_buffer stdin top_buffer;
 
372
      while true do do_vernac() done
 
373
    end else
 
374
      protected_loop stdin
 
375
  with
 
376
    | Vernacexpr.Drop -> ()
 
377
    | Vernacexpr.ProtectedLoop -> 
 
378
        Lib.declare_initial_state();
 
379
        coq_switch false
 
380
    | End_of_input -> msgerrnl (mt ()); pp_flush(); exit 0
 
381
    | Vernacexpr.Quit -> exit 0
 
382
    | e ->
 
383
        msgerrnl (str"Anomaly. Please report.");
 
384
        coq_switch b
 
385
 
 
386
let loop () = coq_switch true