| exception Syntax = Parse_error.Syntax |
| |
| module type S = |
| sig |
| type t |
| val parse : string -> Lexing.lexbuf -> t |
| val parse_file : string -> t |
| val parse_string : ?offset:Source.region -> string -> t |
| val parse_channel : in_channel -> t |
| end |
| |
| let wrap_lexbuf lexbuf = |
| let open Lexing in |
| let inner_refill = lexbuf.refill_buff in |
| let refill_buff lexbuf = |
| let oldlen = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in |
| inner_refill lexbuf; |
| let newlen = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in |
| let start = lexbuf.lex_start_pos + oldlen in |
| let n = newlen - oldlen in |
| Annot.extend_source (Bytes.sub_string lexbuf.lex_buffer start n) |
| in |
| let n = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in |
| Annot.extend_source (Bytes.sub_string lexbuf.lex_buffer lexbuf.lex_start_pos n); |
| {lexbuf with refill_buff} |
| |
| let region_of_pos lexbuf = |
| { Source.left = Lexer.loc_of_pos lexbuf.Lexing.lex_start_p; |
| Source.right = Lexer.loc_of_pos lexbuf.Lexing.lex_curr_p |
| } |
| |
| let make (type a) (start : _ -> _ -> a) : (module S with type t = a) = |
| (module struct |
| type t = a |
| |
| let parse name lexbuf = |
| Annot.reset (); |
| Lexing.set_filename lexbuf name; |
| let lexbuf = wrap_lexbuf lexbuf in |
| let result = |
| try start Lexer.token lexbuf with Parser.Error -> |
| raise (Syntax (region_of_pos lexbuf, "unexpected token")) |
| in |
| let annots = Annot.get_all () in |
| if not (Annot.NameMap.is_empty annots) then |
| let annot = List.hd (snd (Annot.NameMap.choose annots)) in |
| raise (Custom.Syntax (annot.Source.at, "misplaced annotation")) |
| else |
| result |
| |
| let parse_string ?offset s = |
| let open Source in |
| let name, s' = |
| match offset with |
| | None -> "string", s |
| | Some at -> |
| (* Note: this is a hack that only works for singular string literals |
| * with no escapes in them. |
| * TODO: Figure out why we need to add 2 instead of 1 to column. *) |
| at.left.file, |
| String.make (max 0 (at.left.line - 1)) '\n' ^ |
| String.make (at.left.column + 2) ' ' ^ s |
| in parse name (Lexing.from_string ~with_positions:true s') |
| |
| let parse_channel oc = |
| parse "channel" (Lexing.from_channel ~with_positions:true oc) |
| |
| let parse_file name = |
| let oc = open_in name in |
| Fun.protect ~finally:(fun () -> close_in oc) (fun () -> |
| parse name (Lexing.from_channel ~with_positions:true oc) |
| ) |
| end) |
| |
| module Module = (val make Parser.module1) |
| module Script = (val make Parser.script) |
| module Script1 = (val make Parser.script1) |