Source file odoc_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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
module Ast = Ast
module Loc = Loc
module Warning = Warning
type t = {
ast : Ast.t;
warnings : Warning.t list;
reversed_newlines : (int * int) list;
original_pos : Lexing.position;
}
let reversed_newlines : input:string -> (int * int) list =
fun ~input ->
let rec find_newlines line_number input_index newlines_accumulator =
if input_index >= String.length input then newlines_accumulator
else if
input.[input_index] = '\n'
then
find_newlines (line_number + 1) (input_index + 1)
((line_number + 1, input_index + 1) :: newlines_accumulator)
else find_newlines line_number (input_index + 1) newlines_accumulator
in
find_newlines 1 0 [ (1, 0) ]
let offset_to_location :
reversed_newlines:(int * int) list ->
comment_location:Lexing.position ->
int ->
Loc.point =
fun ~reversed_newlines ~ byte_offset ->
let rec scan_to_last_newline reversed_newlines_prefix =
match reversed_newlines_prefix with
| [] -> assert false
| (, line_start_offset) :: prefix ->
if line_start_offset > byte_offset then scan_to_last_newline prefix
else
let column_in_comment = byte_offset - line_start_offset in
let line_in_file =
line_in_comment + comment_location.Lexing.pos_lnum - 1
in
let column_in_file =
if line_in_comment = 1 then
column_in_comment + comment_location.Lexing.pos_cnum
- comment_location.Lexing.pos_bol
else column_in_comment
in
{ Loc.line = line_in_file; column = column_in_file }
in
scan_to_last_newline reversed_newlines
let position_of_point : t -> Loc.point -> Lexing.position =
fun v point ->
let { reversed_newlines; original_pos; _ } = v in
let = point.Loc.line - original_pos.pos_lnum + 1 in
let rec find_pos_bol reversed_newlines_prefix =
match reversed_newlines_prefix with
| [] -> assert false
| [ _ ] -> original_pos.pos_bol
| (line_number, line_start_offset) :: prefix ->
if line_number > line_in_comment then find_pos_bol prefix
else line_start_offset + original_pos.pos_cnum
in
let pos_bol = find_pos_bol reversed_newlines in
let pos_lnum = point.Loc.line in
let pos_cnum = point.column + pos_bol in
let pos_fname = original_pos.pos_fname in
{ Lexing.pos_bol; pos_lnum; pos_cnum; pos_fname }
let ~location ~text =
let warnings = ref [] in
let reversed_newlines = reversed_newlines ~input:text in
let token_stream =
let lexbuf = Lexing.from_string text in
let offset_to_location =
offset_to_location ~reversed_newlines ~comment_location:location
in
let input : Lexer.input =
{ file = location.Lexing.pos_fname; offset_to_location; warnings; lexbuf }
in
Stream.from (fun _token_index -> Some (Lexer.token input lexbuf))
in
let ast, warnings = Syntax.parse warnings token_stream in
{ ast; warnings; reversed_newlines; original_pos = location }
let warnings t = t.warnings
let ast t = t.ast