Source file sexp_with_layout.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
module List = struct
let iter t ~f = List.iter f t
let map t ~f = List.rev (List.rev_map f t)
end
include Type_with_layout
type pos = Src_pos.Relative.t =
{ row : int
; col : int
}
let sexp_of_pos = Src_pos.Relative.sexp_of_t
module Lexer = struct
let main = Lexer.main_with_layout
end
module Parser = Parser_with_layout
module Render = struct
module Rel_pos = Src_pos.Relative
module Abs_pos = Src_pos.Abs
type last =
{ immed_after : Abs_pos.t
; un : bool
}
type state =
{ mutable row_shift : Rel_p.t
; mutable current : Abs_pos.t
; mutable last_atom : last_atom option
; mutable last_comment_row : int
}
type 'a t = (char -> unit) -> state -> 'a
let return a _putc _st = a
let bind m ~f putc st = f (m putc st) putc st
let run putc m =
m
putc
{ row_shift = Rel_pos.zero
; current = Abs_pos.origin
; last_atom = None
; last_comment_row = 0
}
;;
let emit_char putc st c =
let { Abs_pos.col; row } = st.current in
putc c;
if c = '\n'
then st.current <- { Abs_pos.row = 1 + row; col = 1 }
else st.current <- { Abs_pos.row; col = 1 + col }
;;
let emit_string putc st str =
let n = String.length str in
for i = 0 to n - 1 do
emit_char putc st str.[i]
done
;;
let emit_chars putc st c ~n = emit_string putc st (String.make n c)
let advance putc ~anchor st ~by:delta ~unescaped_atom ~ =
let new_pos = Abs_pos.add (Abs_pos.add anchor delta) st.row_shift in
let need_to_leave_room_between_two_unescaped_atoms_lest_they_become_one =
unescaped_atom
&&
match st.last_atom with
| Some { immed_after; unescaped = prev_unescaped } ->
new_pos = immed_after && prev_unescaped
| None -> false
in
let = new_pos.row = st.last_comment_row in
let need_to_reposition =
(not (Abs_pos.geq new_pos st.current))
|| need_to_clear_line_comment
|| need_to_leave_room_between_two_unescaped_atoms_lest_they_become_one
in
let row_delta, new_pos =
if need_to_reposition
then (
let new_row = 1 + st.current.Abs_pos.row in
let row_delta = new_row - new_pos.Abs_pos.row in
row_delta, { Abs_pos.row = new_row; col = new_pos.Abs_pos.col })
else 0, new_pos
in
if new_pos.Abs_pos.row > st.current.Abs_pos.row
then (
let n = new_pos.Abs_pos.row - st.current.Abs_pos.row in
emit_chars putc st '\n' ~n);
if new_pos.Abs_pos.col > st.current.Abs_pos.col
then (
let n = new_pos.Abs_pos.col - st.current.Abs_pos.col in
emit_chars putc st ' ' ~n);
assert (new_pos = st.current);
if line_comment then st.last_comment_row <- st.current.row;
st.row_shift
<- { st.row_shift with Rel_pos.row = st.row_shift.Rel_pos.row + row_delta }
;;
let rec render_t putc ~anchor (st : state) t =
match t with
| Atom (delta, text, fmt_text) ->
let fmt_text =
match fmt_text with
| None | Some "" -> Pre_sexp.mach_maybe_esc_str text
| Some text -> text
in
let unescaped = fmt_text.[0] <> '"' in
advance putc st ~by:delta ~anchor ~unescaped_atom:unescaped ~line_comment:false;
emit_string putc st fmt_text;
st.last_atom <- Some { immed_after = st.current; unescaped }
| List (start_delta, tocs, end_delta) ->
advance putc st ~by:start_delta ~anchor ~unescaped_atom:false ~line_comment:false;
let child_anchor = Abs_pos.sub st.current st.row_shift in
emit_char putc st '(';
List.iter tocs ~f:(fun toc -> render_toc putc ~anchor:child_anchor st toc);
advance putc st ~by:end_delta ~anchor ~unescaped_atom:false ~line_comment:false;
emit_char putc st ')';
()
and render_toc putc ~anchor st = function
| Sexp t -> render_t putc ~anchor st t
| Comment c -> render_c putc ~anchor st c
and render_c putc ~anchor st = function
| Plain_comment (delta, text) ->
let = String.length text > 0 && text.[0] = ';' in
advance putc st ~by:delta ~anchor ~unescaped_atom:false ~line_comment;
emit_string putc st text
| Sexp_comment (delta, cs, t) ->
advance putc st ~by:delta ~anchor ~unescaped_atom:false ~line_comment:false;
emit_string putc st "#;";
List.iter cs ~f:(render_c putc ~anchor st);
render_t putc ~anchor st t
;;
let render asexp putc st = render_toc putc ~anchor:Abs_pos.origin st asexp
let sexp = render
end
module Forget = struct
module Cps : sig
val forget_t : t -> (Type.t -> 'r) -> 'r
val forget_toc : t_or_comment -> (Type.t option -> 'r) -> 'r
val forget_tocs : t_or_comment list -> (Type.t list -> 'r) -> 'r
end = struct
let rec forget_t t k =
match t with
| Atom (_, x, _) -> k (Type.Atom x)
| List (_, tocs, _) -> forget_tocs tocs (fun xs -> k (Type.List xs))
and forget_tocs tocs k =
match tocs with
| [] -> k []
| toc :: tocs ->
forget_toc toc (function
| None -> forget_tocs tocs k
| Some x -> forget_tocs tocs (fun xs -> k (x :: xs)))
and forget_toc toc k =
match toc with
| Comment _ -> k None
| Sexp t -> forget_t t (fun x -> k (Some x))
;;
end
let t x = Cps.forget_t x (fun y -> y)
let x = Cps.forget_toc x (fun y -> y)
let x = Cps.forget_tocs x (fun y -> y)
end