Source file nice_parser.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
let reraise exn =
  Printexc.(raise_with_backtrace exn (get_raw_backtrace ()))


module type RAW_PARSER = sig
  type token
  type result
  exception LexError of string
  exception ParseError
  val next_token : Lexing.lexbuf -> token
  val parse : (Lexing.lexbuf -> token) -> Lexing.lexbuf -> result
end

module type NICE_PARSER = sig
  type token
  type result
  exception LexError of { msg: string; loc: Location.t }
  exception ParseError of { token: token; loc: Location.t }
  val pp_exceptions : unit -> unit
  val parse_string : ?pos:Lexing.position -> string -> result
  val parse_chan : ?pos:Lexing.position -> in_channel -> result
  val parse_file : string -> result
end


module Make (P : RAW_PARSER) : NICE_PARSER with 
  type token = P.token and
  type result = P.result
= struct
  type token = P.token
  type result = P.result
  exception LexError of { msg: string; loc: Location.t }
  exception ParseError of { token: token; loc:Location.t }

  let pp_exceptions () = begin
    Location.register_error_of_exn (function
      | LexError {msg; loc} ->
        Some (Location.error ~loc msg)
      | ParseError {loc; _} ->
        Some (Location.error ~loc "[parser] unexpected token")
      | _ ->
        None
    );
    Printexc.register_printer (function exn ->
      try
        ignore (Format.flush_str_formatter ());
        Location.report_exception Format.str_formatter exn;
        Some (Format.flush_str_formatter ());
      with _ ->
        None
    );
  end

  let curr_token : token option ref =
    ref None

  let next_token lexbuf =
    let token = P.next_token lexbuf in
    curr_token := Some token;
    token

  let parse ?(file="") lexbuf =
    Location.input_name := file;
    Location.input_lexbuf := Some lexbuf;
    try 
      P.parse next_token lexbuf
    with
    | P.LexError msg ->
      reraise (LexError { msg; loc = Location.curr lexbuf })
    | P.ParseError ->
      let[@warning "-8"] (Some token) = !curr_token in
      reraise (ParseError { token; loc = Location.curr lexbuf })

  let parse_string ?(pos : Lexing.position option) s =
    match pos with
    | None ->
      parse (Lexing.from_string s)
    | Some ({pos_fname=file; _} as p) ->
      parse ~file Lexing.{(from_string s) with lex_start_p=p; lex_curr_p=p}

  let parse_chan ?(pos : Lexing.position option) chan =
    match pos with
    | None ->
      parse (Lexing.from_channel chan)
    | Some ({pos_fname=file; _} as p) ->
      parse ~file Lexing.{(from_channel chan) with lex_start_p=p; lex_curr_p=p}

  let parse_file file =
    Stdio.In_channel.with_file file ~f:(fun chan ->
      let lexbuf = Lexing.from_channel chan in
      Location.init lexbuf file;
      parse ~file lexbuf
    )

end