-
Notifications
You must be signed in to change notification settings - Fork 10
/
range.ml
50 lines (39 loc) · 2.11 KB
/
range.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
open Fmlib_parse
open Asai.Range
open Core.Reporter
(* To convert Fmlib positions to Asai ranges, we need to know the string or file they're being run on. We also store its length so that we can test for EOF. *)
module Data = struct
type t = { source : source; length : int64 }
end
module S = Algaeff.Reader.Make (Data)
let () = S.register_printer (function `Read -> Some "unhandled Range read effect")
let convert_pos (src : source) pos =
let start_of_line = Position.byte_offset_bol pos in
(* It appears that fmlib lines are 0-indexed and Asai lines are 1-indexed *)
let line_num = Position.line pos + 1 in
let offset = Position.byte_offset pos in
{ source = src; offset; start_of_line; line_num }
let convert (pos1, pos2) =
(* In case of a lexing or parsing error at end of input, Fmlib returns a 0-width range, which we convert into Asai's special "eof" position. *)
let src = S.read () in
if pos1 = pos2 then
if Int64.of_int (Position.byte_offset pos2) = src.length then eof (convert_pos src.source pos1)
(* Fmlib also reports a 0-width range in mid-parse if we fail directly (i.e. with "fail" or "unexpected" rather than during a lookahead such as "step"). But our calls to "fail" all include an explicit range, and we never call "unexpected". Thus I don't think this should happen, so we flag it as an Anomaly. *)
else fatal (Anomaly "zero-width range during parse failure before EOF")
else make (convert_pos src.source pos1, convert_pos src.source pos2)
let merge loc1 loc2 =
match (view loc1, view loc2) with
| `Range (p1, _), `Range (_, p2) -> make (p1, p2)
| _, `End_of_file p -> eof p
| `End_of_file p, _ -> eof p
let merge_opt loc1 loc2 =
match (loc1, loc2) with
| Some loc1, Some loc2 -> Some (merge loc1 loc2)
| Some loc1, None -> Some loc1
| None, Some loc2 -> Some loc2
| None, None -> None
let merge_opt3 loc1 loc2 loc3 = merge_opt (merge_opt loc1 loc2) loc3
let run = S.run
type 'a located = 'a Asai.Range.located
let locate : type a. a -> Asai.Range.t option -> a located = fun value loc -> { value; loc }
let unlocated (value : 'a) : 'a located = { value; loc = None }