Module of module types for OrderedType,ComparableType,Printable,Serializable,Discrete...
[ocaml.git] / parsing / location.ml
blob9921053e6a0bb4096fffbd7e899cbb892e290997
1 (***********************************************************************)
2 (* *)
3 (* Objective Caml *)
4 (* *)
5 (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
6 (* *)
7 (* Copyright 1996 Institut National de Recherche en Informatique et *)
8 (* en Automatique. All rights reserved. This file is distributed *)
9 (* under the terms of the Q Public License version 1.0. *)
10 (* *)
11 (***********************************************************************)
13 (* $Id$ *)
15 open Lexing
17 type t = { loc_start: position; loc_end: position; loc_ghost: bool };;
19 let none = { loc_start = dummy_pos; loc_end = dummy_pos; loc_ghost = true };;
21 let in_file name =
22 let loc = {
23 pos_fname = name;
24 pos_lnum = 1;
25 pos_bol = 0;
26 pos_cnum = -1;
27 } in
28 { loc_start = loc; loc_end = loc; loc_ghost = true }
31 let curr lexbuf = {
32 loc_start = lexbuf.lex_start_p;
33 loc_end = lexbuf.lex_curr_p;
34 loc_ghost = false
35 };;
37 let init lexbuf fname =
38 lexbuf.lex_curr_p <- {
39 pos_fname = fname;
40 pos_lnum = 1;
41 pos_bol = 0;
42 pos_cnum = 0;
46 let symbol_rloc () = {
47 loc_start = Parsing.symbol_start_pos ();
48 loc_end = Parsing.symbol_end_pos ();
49 loc_ghost = false;
50 };;
52 let symbol_gloc () = {
53 loc_start = Parsing.symbol_start_pos ();
54 loc_end = Parsing.symbol_end_pos ();
55 loc_ghost = true;
56 };;
58 let rhs_loc n = {
59 loc_start = Parsing.rhs_start_pos n;
60 loc_end = Parsing.rhs_end_pos n;
61 loc_ghost = false;
62 };;
64 let input_name = ref ""
65 let input_lexbuf = ref (None : lexbuf option)
67 (* Terminal info *)
69 let status = ref Terminfo.Uninitialised
71 let num_loc_lines = ref 0 (* number of lines already printed after input *)
73 (* Highlight the locations using standout mode. *)
75 let highlight_terminfo ppf num_lines lb loc1 loc2 =
76 Format.pp_print_flush ppf (); (* avoid mixing Format and normal output *)
77 (* Char 0 is at offset -lb.lex_abs_pos in lb.lex_buffer. *)
78 let pos0 = -lb.lex_abs_pos in
79 (* Do nothing if the buffer does not contain the whole phrase. *)
80 if pos0 < 0 then raise Exit;
81 (* Count number of lines in phrase *)
82 let lines = ref !num_loc_lines in
83 for i = pos0 to lb.lex_buffer_len - 1 do
84 if lb.lex_buffer.[i] = '\n' then incr lines
85 done;
86 (* If too many lines, give up *)
87 if !lines >= num_lines - 2 then raise Exit;
88 (* Move cursor up that number of lines *)
89 flush stdout; Terminfo.backup !lines;
90 (* Print the input, switching to standout for the location *)
91 let bol = ref false in
92 print_string "# ";
93 for pos = 0 to lb.lex_buffer_len - pos0 - 1 do
94 if !bol then (print_string " "; bol := false);
95 if pos = loc1.loc_start.pos_cnum || pos = loc2.loc_start.pos_cnum then
96 Terminfo.standout true;
97 if pos = loc1.loc_end.pos_cnum || pos = loc2.loc_end.pos_cnum then
98 Terminfo.standout false;
99 let c = lb.lex_buffer.[pos + pos0] in
100 print_char c;
101 bol := (c = '\n')
102 done;
103 (* Make sure standout mode is over *)
104 Terminfo.standout false;
105 (* Position cursor back to original location *)
106 Terminfo.resume !num_loc_lines;
107 flush stdout
109 (* Highlight the location by printing it again. *)
111 let highlight_dumb ppf lb loc =
112 (* Char 0 is at offset -lb.lex_abs_pos in lb.lex_buffer. *)
113 let pos0 = -lb.lex_abs_pos in
114 (* Do nothing if the buffer does not contain the whole phrase. *)
115 if pos0 < 0 then raise Exit;
116 let end_pos = lb.lex_buffer_len - pos0 - 1 in
117 (* Determine line numbers for the start and end points *)
118 let line_start = ref 0 and line_end = ref 0 in
119 for pos = 0 to end_pos do
120 if lb.lex_buffer.[pos + pos0] = '\n' then begin
121 if loc.loc_start.pos_cnum > pos then incr line_start;
122 if loc.loc_end.pos_cnum > pos then incr line_end;
124 done;
125 (* Print character location (useful for Emacs) *)
126 Format.fprintf ppf "Characters %i-%i:@."
127 loc.loc_start.pos_cnum loc.loc_end.pos_cnum;
128 (* Print the input, underlining the location *)
129 Format.pp_print_string ppf " ";
130 let line = ref 0 in
131 let pos_at_bol = ref 0 in
132 for pos = 0 to end_pos do
133 let c = lb.lex_buffer.[pos + pos0] in
134 if c <> '\n' then begin
135 if !line = !line_start && !line = !line_end then
136 (* loc is on one line: print whole line *)
137 Format.pp_print_char ppf c
138 else if !line = !line_start then
139 (* first line of multiline loc: print ... before loc_start *)
140 if pos < loc.loc_start.pos_cnum
141 then Format.pp_print_char ppf '.'
142 else Format.pp_print_char ppf c
143 else if !line = !line_end then
144 (* last line of multiline loc: print ... after loc_end *)
145 if pos < loc.loc_end.pos_cnum
146 then Format.pp_print_char ppf c
147 else Format.pp_print_char ppf '.'
148 else if !line > !line_start && !line < !line_end then
149 (* intermediate line of multiline loc: print whole line *)
150 Format.pp_print_char ppf c
151 end else begin
152 if !line = !line_start && !line = !line_end then begin
153 (* loc is on one line: underline location *)
154 Format.fprintf ppf "@. ";
155 for i = !pos_at_bol to loc.loc_start.pos_cnum - 1 do
156 Format.pp_print_char ppf ' '
157 done;
158 for i = loc.loc_start.pos_cnum to loc.loc_end.pos_cnum - 1 do
159 Format.pp_print_char ppf '^'
160 done
161 end;
162 if !line >= !line_start && !line <= !line_end then begin
163 Format.fprintf ppf "@.";
164 if pos < loc.loc_end.pos_cnum then Format.pp_print_string ppf " "
165 end;
166 incr line;
167 pos_at_bol := pos + 1;
169 done
171 (* Highlight the location using one of the supported modes. *)
173 let rec highlight_locations ppf loc1 loc2 =
174 match !status with
175 Terminfo.Uninitialised ->
176 status := Terminfo.setup stdout; highlight_locations ppf loc1 loc2
177 | Terminfo.Bad_term ->
178 begin match !input_lexbuf with
179 None -> false
180 | Some lb ->
181 let norepeat =
182 try Sys.getenv "TERM" = "norepeat" with Not_found -> false in
183 if norepeat then false else
184 try highlight_dumb ppf lb loc1; true
185 with Exit -> false
187 | Terminfo.Good_term num_lines ->
188 begin match !input_lexbuf with
189 None -> false
190 | Some lb ->
191 try highlight_terminfo ppf num_lines lb loc1 loc2; true
192 with Exit -> false
195 (* Print the location in some way or another *)
197 open Format
199 let reset () =
200 num_loc_lines := 0
202 let (msg_file, msg_line, msg_chars, msg_to, msg_colon, msg_head) =
203 ("File \"", "\", line ", ", characters ", "-", ":", "")
205 (* return file, line, char from the given position *)
206 let get_pos_info pos =
207 let (filename, linenum, linebeg) =
208 if pos.pos_fname = "" && !input_name = "" then
209 ("", -1, 0)
210 else if pos.pos_fname = "" then
211 Linenum.for_position !input_name pos.pos_cnum
212 else
213 (pos.pos_fname, pos.pos_lnum, pos.pos_bol)
215 (filename, linenum, pos.pos_cnum - linebeg)
218 let print ppf loc =
219 let (file, line, startchar) = get_pos_info loc.loc_start in
220 let endchar = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum + startchar in
221 let (startchar, endchar) =
222 if startchar < 0 then (0, 1) else (startchar, endchar)
224 if file = "" then begin
225 if highlight_locations ppf loc none then () else
226 fprintf ppf "Characters %i-%i:@."
227 loc.loc_start.pos_cnum loc.loc_end.pos_cnum
228 end else begin
229 fprintf ppf "%s%s%s%i" msg_file file msg_line line;
230 fprintf ppf "%s%i" msg_chars startchar;
231 fprintf ppf "%s%i%s@.%s" msg_to endchar msg_colon msg_head;
234 let print_warning loc ppf w =
235 if Warnings.is_active w then begin
236 let printw ppf w =
237 let n = Warnings.print ppf w in
238 num_loc_lines := !num_loc_lines + n
240 fprintf ppf "%a" print loc;
241 fprintf ppf "Warning %a@." printw w;
242 pp_print_flush ppf ();
243 incr num_loc_lines;
247 let prerr_warning loc w = print_warning loc err_formatter w;;
249 let echo_eof () =
250 print_newline ();
251 incr num_loc_lines