~ubuntu-branches/ubuntu/trusty/sexplib310/trusty

« back to all changes in this revision

Viewing changes to lib/sexp_with_layout.ml

  • Committer: Package Import Robot
  • Author(s): Stéphane Glondu
  • Date: 2013-12-03 21:36:45 UTC
  • mfrom: (11.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20131203213645-h1if1c6hxual8p11
Tags: 109.20.00-2
* Team upload
* Upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* packaging of annotated sexp functions *)
 
2
 
 
3
module List = struct
 
4
  let iter t ~f = List.iter f t
 
5
  let map t ~f = List.rev (List.rev_map f t)
 
6
end
 
7
 
 
8
include Type_with_layout
 
9
 
 
10
type pos = Src_pos.Relative.t = { row : int; col : int }
 
11
let sexp_of_pos = Src_pos.Relative.sexp_of_t
 
12
 
 
13
module Lexer = struct
 
14
  let main = Lexer.main_with_layout
 
15
end
 
16
 
 
17
module Parser = Parser_with_layout
 
18
 
 
19
module Render = struct
 
20
 
 
21
  module Rel_pos = Src_pos.Relative
 
22
  module Abs_pos = Src_pos.Absolute
 
23
 
 
24
  type last_atom = {
 
25
    immed_after : Abs_pos.t;
 
26
    unescaped : bool;
 
27
  }
 
28
 
 
29
  type state = {
 
30
    mutable row_shift : Rel_pos.t;
 
31
    mutable current : Abs_pos.t;
 
32
    mutable last_atom : last_atom option;
 
33
  }
 
34
 
 
35
  (* the point of [immed_after_last_atom] is to prevent
 
36
      (A B C) from rendering as (A BBC) after we replace B with BB *)
 
37
 
 
38
  type 'a t = (char -> unit) -> state -> 'a
 
39
 
 
40
  let return a _putc _st = a
 
41
 
 
42
  let bind m f putc st = f (m putc st) putc st
 
43
 
 
44
  let run putc m =
 
45
    m putc {
 
46
      row_shift = Rel_pos.zero;
 
47
      current = Abs_pos.origin;
 
48
      last_atom = None;
 
49
    }
 
50
 
 
51
  let emit_char putc st c =
 
52
    let {Abs_pos.col; row} = st.current in
 
53
    putc c;
 
54
    if c = '\n' then
 
55
      st.current <- {Abs_pos.row = 1 + row; col = 1}
 
56
    else
 
57
      st.current <- {Abs_pos.row; col = 1 + col}
 
58
 
 
59
  let emit_string putc st str =
 
60
    let n = String.length str in
 
61
    for i = 0 to n - 1 do
 
62
      emit_char putc st str.[i]
 
63
    done
 
64
 
 
65
  let emit_chars putc st c ~n =
 
66
    emit_string putc st (String.make n c)
 
67
 
 
68
  let advance putc ~anchor st ~by:delta ~unescaped_atom =
 
69
    let new_pos = Abs_pos.add (Abs_pos.add anchor delta) st.row_shift in
 
70
    let need_to_leave_room_between_two_unescaped_atoms_lest_they_become_one =
 
71
      unescaped_atom && begin
 
72
        match st.last_atom with
 
73
        | Some {immed_after; unescaped = prev_unescaped} ->
 
74
          new_pos = immed_after && prev_unescaped
 
75
        | None -> false
 
76
      end
 
77
    in
 
78
    let need_to_reposition =
 
79
      not (Abs_pos.geq new_pos st.current)
 
80
        || need_to_leave_room_between_two_unescaped_atoms_lest_they_become_one
 
81
    in
 
82
    let (row_delta, new_pos) =
 
83
      if need_to_reposition then begin
 
84
        (* repositioning heuristic: just move to the next fresh row *)
 
85
        let new_row = 1 + st.current.Abs_pos.row in
 
86
        let row_delta = new_row - new_pos.Abs_pos.row in
 
87
        (row_delta, {Abs_pos.row = new_row; col = new_pos.Abs_pos.col})
 
88
      end else
 
89
        (0, new_pos)
 
90
    in
 
91
    begin (* advance to new_pos by emitting whitespace *)
 
92
      if new_pos.Abs_pos.row > st.current.Abs_pos.row then begin
 
93
        let n = (new_pos.Abs_pos.row - st.current.Abs_pos.row) in
 
94
        emit_chars putc st '\n' ~n
 
95
      end;
 
96
      if new_pos.Abs_pos.col > st.current.Abs_pos.col then begin
 
97
        let n = (new_pos.Abs_pos.col - st.current.Abs_pos.col) in
 
98
        emit_chars putc st ' ' ~n
 
99
      end;
 
100
    end;
 
101
    assert (new_pos = st.current);
 
102
    st.row_shift <- {
 
103
      st.row_shift with Rel_pos.
 
104
      row = st.row_shift.Rel_pos.row + row_delta;
 
105
    }
 
106
 
 
107
  let rec render_t putc ~anchor (st : state) t =
 
108
    match t with
 
109
    | Atom (delta, text, fmt_text) ->
 
110
      let fmt_text =
 
111
        match fmt_text with
 
112
        | None | Some "" -> Pre_sexp.maybe_esc_str text
 
113
        | Some text -> text
 
114
      in
 
115
      let unescaped = fmt_text.[0] <> '"' in
 
116
      advance putc st ~by:delta ~anchor ~unescaped_atom:unescaped;
 
117
      emit_string putc st fmt_text;
 
118
      st.last_atom <- Some { immed_after = st.current; unescaped; };
 
119
    | List (start_delta, tocs, end_delta) ->
 
120
      advance putc st ~by:start_delta ~anchor ~unescaped_atom:false;
 
121
      let child_anchor = Abs_pos.sub st.current st.row_shift in
 
122
      emit_char putc st '(';
 
123
      List.iter tocs ~f:(fun toc -> render_toc putc ~anchor:child_anchor st toc);
 
124
      advance putc st ~by:end_delta ~anchor ~unescaped_atom:false;
 
125
      emit_char putc st ')';
 
126
      ()
 
127
 
 
128
  and render_toc putc ~anchor st = function
 
129
    | Sexp t -> render_t putc ~anchor st t
 
130
    | Comment c -> render_c putc ~anchor st c
 
131
 
 
132
  and render_c putc ~anchor st = function
 
133
    | Plain_comment (delta, text) ->
 
134
      advance putc st ~by:delta ~anchor ~unescaped_atom:false;
 
135
      emit_string putc st text
 
136
    | Sexp_comment (delta, cs, t) ->
 
137
      advance putc st ~by:delta ~anchor ~unescaped_atom:false;
 
138
      emit_string putc st "#;";
 
139
      List.iter cs ~f:(render_c putc ~anchor st);
 
140
      render_t putc ~anchor st t
 
141
 
 
142
  let render asexp putc st = render_toc putc ~anchor:Abs_pos.origin st asexp
 
143
 
 
144
  let sexp = render
 
145
 
 
146
end
 
147
 
 
148
module Forget = struct
 
149
 
 
150
  (* In cps to prevent non-tail recursion.
 
151
     The polymorphism in the signature ensures that each function returns
 
152
     only through the continuation. *)
 
153
  module Cps : sig
 
154
    val forget_t    : t                 -> (Type.t        -> 'r) -> 'r
 
155
    val forget_toc  : t_or_comment      -> (Type.t option -> 'r) -> 'r
 
156
    val forget_tocs : t_or_comment list -> (Type.t list   -> 'r) -> 'r
 
157
  end = struct
 
158
 
 
159
    let rec forget_t t k =
 
160
      match t with
 
161
      | Atom (_, x, _) -> k (Type.Atom x)
 
162
      | List (_, tocs, _) -> forget_tocs tocs (fun xs -> k (Type.List xs))
 
163
 
 
164
    and forget_tocs tocs k =
 
165
      match tocs with
 
166
      | [] -> k []
 
167
      | toc :: tocs ->
 
168
        forget_toc toc (function
 
169
        | None -> forget_tocs tocs k
 
170
        | Some x -> forget_tocs tocs (fun xs -> k (x :: xs)))
 
171
 
 
172
    and forget_toc toc k =
 
173
      match toc with
 
174
      | Comment _ -> k None
 
175
      | Sexp t -> forget_t t (fun x -> k (Some x))
 
176
  end
 
177
 
 
178
  let t             x = Cps.forget_t    x (fun y -> y)
 
179
  let t_or_comment  x = Cps.forget_toc  x (fun y -> y)
 
180
  let t_or_comments x = Cps.forget_tocs x (fun y -> y)
 
181
end
 
182