package acgtk

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file error.ml

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
type pos = Lexing.position * Lexing.position
let dummy_pos = (Lexing.dummy_pos, Lexing.dummy_pos)

exception AcgtkError of string * (Format.formatter -> unit) * (pos option)

let base_prompt = "ACGtk> "
let cont_prompt = "... "

module type ERROR_MANAGER =
  sig
    type t
    val kind : string
    val pp : Format.formatter -> t -> unit
  end

module type ERROR_HANDLER =
sig
  type manager
  val emit : ?loc:pos -> manager -> 'a
end


module ErrorManager (E : ERROR_MANAGER) =
struct
  type manager = E.t
    let emit ?loc e = raise (AcgtkError (E.kind, (fun fmt -> E.pp fmt e), loc))
  end

let set_position lexbuf loc =
  let () = Sedlexing.set_position lexbuf loc in
  let () = Sedlexing.set_filename lexbuf loc.Lexing.pos_fname in ()

let quote_position (p1, p2) =
  ({ p1 with Lexing.pos_cnum = p1.Lexing.pos_cnum + 1 }, { p2 with Lexing.pos_cnum = p2.Lexing.pos_cnum - 1 })

let underlined_red_pp s = Fmt.(styled `Red (styled `Underline string)) s

let unicode_sub_str str s_byte uni_chars =
  let buff = Buffer.create 128 in
  let rec unicode_sub_str_rec s_byte uni_chars =
    if uni_chars = 0
      then (Buffer.contents buff, s_byte)
      else
        let u = (Uchar.utf_decode_uchar ((String.get_utf_8_uchar str s_byte))) in
        let () = Buffer.add_utf_8_uchar buff u in
        let (str, k) =
          (unicode_sub_str_rec (s_byte + (Uchar.utf_8_byte_length u)) (uni_chars - 1)) in
        (str, k) in
   unicode_sub_str_rec s_byte uni_chars

let color_error_pp start_c end_c fmt str =
  let (s1, s2_start_byte) = unicode_sub_str str 0 start_c in
  let (s2, s3_start_byte) = unicode_sub_str str s2_start_byte (end_c - start_c) in
  let s3 = String.sub str s3_start_byte (String.length str - s3_start_byte) in
  Format.fprintf fmt "%s%s%a%s" base_prompt s1 underlined_red_pp s2 s3

let underline_error str (l1, l2) =
  let line_num = String.fold_left (fun i c -> if c = '\n' then i + 1 else i) 1 str in
  let () = ANSITerminal.move_cursor 0 (-line_num) in
  Logs.app (fun m -> m "%a" (color_error_pp l1.Lexing.pos_cnum l2.Lexing.pos_cnum) str)

let compute_comment_for_location (pos1, pos2) =
  let line2 = pos2.Lexing.pos_lnum in
  let col2 = pos2.Lexing.pos_cnum - pos2.Lexing.pos_bol in
  let pos1 = pos1 in
  let line1 = pos1.Lexing.pos_lnum in
  let col1 = pos1.Lexing.pos_cnum - pos1.Lexing.pos_bol in
  let msg =
    if line1 = line2 then
      if col1 = col2 then
        Printf.sprintf "line %d, character %d" line2 col2
      else
        Printf.sprintf "line %d, characters %d-%d" line2 col1 col2
    else
      Printf.sprintf "line %d, character %d to line %d, character %d" line1 col1
        line2 col2 in
  match pos1.Lexing.pos_fname with
  | "" -> msg
  | "<stdin>" -> Printf.sprintf "file <stdin>, %s" msg
  | fname -> Printf.sprintf "file \"%s\", %s" fname msg

let pp_text fmt text =
  let words = String.split_on_char ' ' text in
  match words with
  | w :: words ->
      let () = Format.fprintf fmt "%s" w in
      List.iter (fun w -> Format.fprintf fmt "@ %s" w) words
  | _ -> ()

let print_error e str =
  match e with
  | AcgtkError (kind, ppf, Some (l1, l2)) ->
    let () = match str with
      | Some str -> if l1.Lexing.pos_fname = "" && l2.Lexing.pos_fname = "" then underline_error str (l1, l2)
      | None -> () in
    let loc_string = compute_comment_for_location (l1, l2) in
    Logs.err (fun m -> m "@[@[<h>%s error:@;@[%a:@]@]@;@[%t.@]@]" kind pp_text loc_string ppf)
  | AcgtkError (kind, ppf, None) ->
      Logs.err (fun m -> m "%s error:@;@[<4>@[%t.@]@]" kind ppf)
  | Failure s -> Logs.err (fun m -> m "Bug,@ please@ report:@ failure:@ %a" pp_text s)
  | Assert_failure (file, line, col) -> Logs.err (fun m -> m "Bug,@ please@ report:@ assertion@ failed@ at@ %s:%i:%i" file line col)
  | e ->
    let bt = (Printexc.get_backtrace ()) in
      if bt = "" then
        Logs.err (fun m -> m "Bug,@ please@ report:@ uncaught@ exception@ \"%s\"@ (run@ with@ \"-d\"@ to@ see@ backtrace)" (Printexc.to_string e))
      else
        Logs.err (fun m -> m "Bug,@ please@ report:@ uncaught@ exception@ \"%s\"\nBACKTRACE:\n%a" (Printexc.to_string e) pp_text bt)

let print_error_fatal e str =
  let () = print_error e str in exit 1

let rec list_to_string l =
  match l with
  | [] -> "nothing"
  | [ s ] -> Printf.sprintf "\"%s\"" s
  | [ s1 ; s2 ] -> Printf.sprintf "\"%s\" or \"%s\"" s1 s2
  | s :: l -> Printf.sprintf "\"%s\", %s" s (list_to_string l)
OCaml

Innovation. Community. Security.