blob: 6bfa1ea7e032e56148539aaa780af845660b1642 [file] [log] [blame] [edit]
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)