diff options
Diffstat (limited to 'ocaml-binary-annot/parsing/location.ml')
| -rw-r--r-- | ocaml-binary-annot/parsing/location.ml | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/ocaml-binary-annot/parsing/location.ml b/ocaml-binary-annot/parsing/location.ml new file mode 100644 index 0000000000..15b074acd4 --- /dev/null +++ b/ocaml-binary-annot/parsing/location.ml @@ -0,0 +1,259 @@ +(***********************************************************************) +(* *) +(* Objective Caml *) +(* *) +(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) +(* *) +(* Copyright 1996 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) + +open Lexing + +type t = { loc_start: position; loc_end: position; loc_ghost: bool };; + +let none = { loc_start = dummy_pos; loc_end = dummy_pos; loc_ghost = true };; + +let in_file name = + let loc = { + pos_fname = name; + pos_lnum = 1; + pos_bol = 0; + pos_cnum = -1; + } in + { loc_start = loc; loc_end = loc; loc_ghost = true } +;; + +let curr lexbuf = { + loc_start = lexbuf.lex_start_p; + loc_end = lexbuf.lex_curr_p; + loc_ghost = false +};; + +let init lexbuf fname = + lexbuf.lex_curr_p <- { + pos_fname = fname; + pos_lnum = 1; + pos_bol = 0; + pos_cnum = 0; + } +;; + +let symbol_rloc () = { + loc_start = Parsing.symbol_start_pos (); + loc_end = Parsing.symbol_end_pos (); + loc_ghost = false; +};; + +let symbol_gloc () = { + loc_start = Parsing.symbol_start_pos (); + loc_end = Parsing.symbol_end_pos (); + loc_ghost = true; +};; + +let rhs_loc n = { + loc_start = Parsing.rhs_start_pos n; + loc_end = Parsing.rhs_end_pos n; + loc_ghost = false; +};; + +let input_name = ref "_none_" +let input_lexbuf = ref (None : lexbuf option) + +(* Terminal info *) + +let status = ref Terminfo.Uninitialised + +let num_loc_lines = ref 0 (* number of lines already printed after input *) + +(* Highlight the locations using standout mode. *) + +let highlight_terminfo ppf num_lines lb loc1 loc2 = + Format.pp_print_flush ppf (); (* avoid mixing Format and normal output *) + (* Char 0 is at offset -lb.lex_abs_pos in lb.lex_buffer. *) + let pos0 = -lb.lex_abs_pos in + (* Do nothing if the buffer does not contain the whole phrase. *) + if pos0 < 0 then raise Exit; + (* Count number of lines in phrase *) + let lines = ref !num_loc_lines in + for i = pos0 to lb.lex_buffer_len - 1 do + if lb.lex_buffer.[i] = '\n' then incr lines + done; + (* If too many lines, give up *) + if !lines >= num_lines - 2 then raise Exit; + (* Move cursor up that number of lines *) + flush stdout; Terminfo.backup !lines; + (* Print the input, switching to standout for the location *) + let bol = ref false in + print_string "# "; + for pos = 0 to lb.lex_buffer_len - pos0 - 1 do + if !bol then (print_string " "; bol := false); + if pos = loc1.loc_start.pos_cnum || pos = loc2.loc_start.pos_cnum then + Terminfo.standout true; + if pos = loc1.loc_end.pos_cnum || pos = loc2.loc_end.pos_cnum then + Terminfo.standout false; + let c = lb.lex_buffer.[pos + pos0] in + print_char c; + bol := (c = '\n') + done; + (* Make sure standout mode is over *) + Terminfo.standout false; + (* Position cursor back to original location *) + Terminfo.resume !num_loc_lines; + flush stdout + +(* Highlight the location by printing it again. *) + +let highlight_dumb ppf lb loc = + (* Char 0 is at offset -lb.lex_abs_pos in lb.lex_buffer. *) + let pos0 = -lb.lex_abs_pos in + (* Do nothing if the buffer does not contain the whole phrase. *) + if pos0 < 0 then raise Exit; + let end_pos = lb.lex_buffer_len - pos0 - 1 in + (* Determine line numbers for the start and end points *) + let line_start = ref 0 and line_end = ref 0 in + for pos = 0 to end_pos do + if lb.lex_buffer.[pos + pos0] = '\n' then begin + if loc.loc_start.pos_cnum > pos then incr line_start; + if loc.loc_end.pos_cnum > pos then incr line_end; + end + done; + (* Print character location (useful for Emacs) *) + Format.fprintf ppf "Characters %i-%i:@." + loc.loc_start.pos_cnum loc.loc_end.pos_cnum; + (* Print the input, underlining the location *) + Format.pp_print_string ppf " "; + let line = ref 0 in + let pos_at_bol = ref 0 in + for pos = 0 to end_pos do + let c = lb.lex_buffer.[pos + pos0] in + if c <> '\n' then begin + if !line = !line_start && !line = !line_end then + (* loc is on one line: print whole line *) + Format.pp_print_char ppf c + else if !line = !line_start then + (* first line of multiline loc: print ... before loc_start *) + if pos < loc.loc_start.pos_cnum + then Format.pp_print_char ppf '.' + else Format.pp_print_char ppf c + else if !line = !line_end then + (* last line of multiline loc: print ... after loc_end *) + if pos < loc.loc_end.pos_cnum + then Format.pp_print_char ppf c + else Format.pp_print_char ppf '.' + else if !line > !line_start && !line < !line_end then + (* intermediate line of multiline loc: print whole line *) + Format.pp_print_char ppf c + end else begin + if !line = !line_start && !line = !line_end then begin + (* loc is on one line: underline location *) + Format.fprintf ppf "@. "; + for i = !pos_at_bol to loc.loc_start.pos_cnum - 1 do + Format.pp_print_char ppf ' ' + done; + for i = loc.loc_start.pos_cnum to loc.loc_end.pos_cnum - 1 do + Format.pp_print_char ppf '^' + done + end; + if !line >= !line_start && !line <= !line_end then begin + Format.fprintf ppf "@."; + if pos < loc.loc_end.pos_cnum then Format.pp_print_string ppf " " + end; + incr line; + pos_at_bol := pos + 1; + end + done + +(* Highlight the location using one of the supported modes. *) + +let rec highlight_locations ppf loc1 loc2 = + match !status with + Terminfo.Uninitialised -> + status := Terminfo.setup stdout; highlight_locations ppf loc1 loc2 + | Terminfo.Bad_term -> + begin match !input_lexbuf with + None -> false + | Some lb -> + let norepeat = + try Sys.getenv "TERM" = "norepeat" with Not_found -> false in + if norepeat then false else + try highlight_dumb ppf lb loc1; true + with Exit -> false + end + | Terminfo.Good_term num_lines -> + begin match !input_lexbuf with + None -> false + | Some lb -> + try highlight_terminfo ppf num_lines lb loc1 loc2; true + with Exit -> false + end + +(* Print the location in some way or another *) + +open Format + +let reset () = + num_loc_lines := 0 + +let (msg_file, msg_line, msg_chars, msg_to, msg_colon, msg_head) = + ("File \"", "\", line ", ", characters ", "-", ":", "") + +(* return file, line, char from the given position *) +let get_pos_info pos = + let (filename, linenum, linebeg) = + if pos.pos_fname = "" && !input_name = "" then + ("", -1, 0) + else if pos.pos_fname = "" then + Linenum.for_position !input_name pos.pos_cnum + else + (pos.pos_fname, pos.pos_lnum, pos.pos_bol) + in + (filename, linenum, pos.pos_cnum - linebeg) +;; + +let print ppf loc = + let (file, line, startchar) = get_pos_info loc.loc_start in + let endchar = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum + startchar in + let (startchar, endchar) = + if startchar < 0 then (0, 1) else (startchar, endchar) + in + if file = "" then begin + if highlight_locations ppf loc none then () else + fprintf ppf "Characters %i-%i:@." + loc.loc_start.pos_cnum loc.loc_end.pos_cnum + end else begin + fprintf ppf "%s%s%s%i" msg_file file msg_line line; + fprintf ppf "%s%i" msg_chars startchar; + fprintf ppf "%s%i%s@.%s" msg_to endchar msg_colon msg_head; + end +;; + +let print_error ppf loc = + print ppf loc; + fprintf ppf "Error: "; +;; + +let print_error_cur_file ppf = print_error ppf (in_file !input_name);; + +let print_warning loc ppf w = + if Warnings.is_active w then begin + let printw ppf w = + let n = Warnings.print ppf w in + num_loc_lines := !num_loc_lines + n + in + fprintf ppf "%a" print loc; + fprintf ppf "Warning %a@." printw w; + pp_print_flush ppf (); + incr num_loc_lines; + end +;; + +let prerr_warning loc w = print_warning loc err_formatter w;; + +let echo_eof () = + print_newline (); + incr num_loc_lines |
