Skip to content

Commit ea7aec6

Browse files
vouillonOlivierNicole
authored andcommitted
Build_info/Unit_info: support for sexp serialization
1 parent 3204c33 commit ea7aec6

File tree

6 files changed

+244
-0
lines changed

6 files changed

+244
-0
lines changed

compiler/lib/build_info.ml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ let parse s =
9090
in
9191
Some t
9292

93+
let to_sexp info =
94+
Sexp.List
95+
(info
96+
|> StringMap.bindings
97+
|> List.map ~f:(fun (k, v) -> Sexp.List [ Atom k; Atom v ]))
98+
99+
let from_sexp info =
100+
let open Sexp.Util in
101+
info
102+
|> assoc
103+
|> List.fold_left
104+
~f:(fun m (k, v) -> StringMap.add k (single string v) m)
105+
~init:StringMap.empty
106+
93107
exception
94108
Incompatible_build_info of
95109
{ key : string

compiler/lib/build_info.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ val to_string : t -> string
3434

3535
val parse : string -> t option
3636

37+
val to_sexp : t -> Sexp.t
38+
39+
val from_sexp : Sexp.t -> t
40+
3741
val with_kind : t -> kind -> t
3842

3943
exception

compiler/lib/sexp.ml

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
(* ()#;"" space <-- reserved *)
2+
open Stdlib
3+
4+
type t =
5+
| Atom of string
6+
| List of t list
7+
8+
let reserved_char c =
9+
match c with
10+
| '\x00' .. ' ' | '(' | ')' | '#' | ';' | '"' | '\x7f' .. '\xff' -> true
11+
| _ -> false
12+
13+
let need_escaping s =
14+
let len = String.length s in
15+
len = 0
16+
||
17+
let res = ref false in
18+
for i = 0 to len - 1 do
19+
res := !res || reserved_char s.[i]
20+
done;
21+
!res
22+
23+
let should_quote c =
24+
match c with
25+
| '\x00' .. '\x1F' | '"' | '\\' | '\x7f' .. '\xff' -> true
26+
| _ -> false
27+
28+
let escape_to_buffer buf s =
29+
let start = ref 0 in
30+
let len = String.length s in
31+
Buffer.add_char buf '"';
32+
for i = 0 to len - 1 do
33+
let c = s.[i] in
34+
if should_quote c
35+
then (
36+
if !start < i then Buffer.add_substring buf s !start (i - !start);
37+
Buffer.add_char buf '\\';
38+
let c = Char.code c in
39+
Buffer.add_uint8 buf ((c / 100) + 48);
40+
Buffer.add_uint8 buf ((c / 10 mod 10) + 48);
41+
Buffer.add_uint8 buf ((c mod 10) + 48);
42+
start := i + 1)
43+
done;
44+
if !start < len then Buffer.add_substring buf s !start (len - !start);
45+
Buffer.add_char buf '"'
46+
47+
let rec add_to_buffer buf v =
48+
match v with
49+
| Atom s -> if need_escaping s then escape_to_buffer buf s else Buffer.add_string buf s
50+
| List l ->
51+
Buffer.add_char buf '(';
52+
List.iteri
53+
~f:(fun i v' ->
54+
if i > 0 then Buffer.add_char buf ' ';
55+
add_to_buffer buf v')
56+
l;
57+
Buffer.add_char buf ')'
58+
59+
let to_string v =
60+
let b = Buffer.create 128 in
61+
add_to_buffer b v;
62+
Buffer.contents b
63+
64+
let parse_error () = failwith "parse error"
65+
66+
let rec parse buf s pos : t * int =
67+
match s.[pos] with
68+
| '(' -> parse_list buf s [] (pos + 1)
69+
| '\"' ->
70+
Buffer.clear buf;
71+
parse_quoted_atom buf s (pos + 1) (pos + 1)
72+
| _ -> parse_atom buf s pos pos
73+
74+
and parse_list buf s acc pos =
75+
match s.[pos] with
76+
| ' ' -> parse_list buf s acc (pos + 1)
77+
| ')' -> List (List.rev acc), pos + 1
78+
| _ ->
79+
let v, pos' = parse buf s pos in
80+
parse_list buf s (v :: acc) pos'
81+
82+
and parse_atom buf s pos0 pos =
83+
if reserved_char s.[pos]
84+
then (
85+
if pos0 = pos then parse_error ();
86+
Atom (String.sub s ~pos:pos0 ~len:(pos - pos0)), pos)
87+
else parse_atom buf s pos0 (pos + 1)
88+
89+
and parse_quoted_atom buf s pos0 pos =
90+
match s.[pos] with
91+
| '\"' ->
92+
if pos0 < pos then Buffer.add_substring buf s pos0 (pos - pos0);
93+
Atom (Buffer.contents buf), pos + 1
94+
| '\\' ->
95+
if pos0 < pos then Buffer.add_substring buf s pos0 (pos - pos0);
96+
Buffer.add_uint8
97+
buf
98+
(((Char.code s.[pos + 1] - 48) * 100)
99+
+ ((Char.code s.[pos + 2] - 48) * 10)
100+
+ Char.code s.[pos + 3]
101+
- 48);
102+
parse_quoted_atom buf s (pos + 4) (pos + 4)
103+
| _ -> parse_quoted_atom buf s pos0 (pos + 1)
104+
105+
let from_string s =
106+
let v, pos = parse (Buffer.create 16) s 0 in
107+
if pos < String.length s then parse_error ();
108+
v
109+
110+
module Util = struct
111+
let single f v =
112+
match v with
113+
| [ v ] -> f v
114+
| _ -> assert false
115+
116+
let string v =
117+
match v with
118+
| Atom s -> s
119+
| _ -> assert false
120+
121+
let assoc v =
122+
match v with
123+
| List l ->
124+
List.map
125+
~f:(fun p ->
126+
match p with
127+
| List (Atom k :: v) -> k, v
128+
| _ -> assert false)
129+
l
130+
| Atom _ -> assert false
131+
132+
let member nm v =
133+
match v with
134+
| Atom _ -> assert false
135+
| List l ->
136+
List.find_map
137+
~f:(fun p ->
138+
match p with
139+
| List (Atom nm' :: v) when String.equal nm nm' -> Some v
140+
| _ -> None)
141+
l
142+
143+
let bool v =
144+
match v with
145+
| Atom "true" -> true
146+
| Atom "false" -> false
147+
| _ -> assert false
148+
149+
let mandatory f v =
150+
match v with
151+
| Some v -> f v
152+
| None -> assert false
153+
end
154+
(*
155+
parse
156+
(to_string
157+
(List
158+
[ List [ Atom "provides"; Atom "toto" ]
159+
; List [ Atom "requires"; Atom "foo"; Atom "bar"; Atom "foo\n bar" ]
160+
]))
161+
*)

compiler/lib/sexp.mli

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
type t =
2+
| Atom of string
3+
| List of t list
4+
5+
val to_string : t -> string
6+
7+
val from_string : string -> t
8+
9+
module Util : sig
10+
val single : (t -> 'a) -> t list -> 'a
11+
12+
val mandatory : (t list -> 'a) -> t list option -> 'a
13+
14+
val string : t -> string
15+
16+
val bool : t -> bool
17+
18+
val assoc : t -> (string * t list) list
19+
20+
val member : string -> t -> t list option
21+
end

compiler/lib/unit_info.ml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,43 @@ let parse acc s =
150150
| Some ("Effects_without_cps", b) ->
151151
Some { acc with effects_without_cps = bool_of_string (String.trim b) }
152152
| Some (_, _) -> None)
153+
154+
let to_sexp t =
155+
let add nm skip v rem = if skip then rem else Sexp.List (Atom nm :: v) :: rem in
156+
let set nm f rem =
157+
add
158+
nm
159+
(List.equal ~eq:String.equal (f empty) (f t))
160+
(List.map ~f:(fun x -> Sexp.Atom x) (f t))
161+
rem
162+
in
163+
let bool nm f rem =
164+
add
165+
nm
166+
(Bool.equal (f empty) (f t))
167+
(if f t then [ Atom "true" ] else [ Atom "false" ])
168+
rem
169+
in
170+
[]
171+
|> bool "effects_without_cps" (fun t -> t.effects_without_cps)
172+
|> set "primitives" (fun t -> t.primitives)
173+
|> bool "force_link" (fun t -> t.force_link)
174+
|> set "requires" (fun t -> StringSet.elements t.requires)
175+
|> add "provides" false [ Atom (StringSet.choose t.provides) ]
176+
177+
let from_sexp t =
178+
let open Sexp.Util in
179+
let opt_list l = l |> Option.map ~f:(List.map ~f:string) in
180+
let list default l = Option.value ~default (opt_list l) in
181+
let set default l =
182+
Option.value ~default (Option.map ~f:StringSet.of_list (opt_list l))
183+
in
184+
let bool default v = Option.value ~default (Option.map ~f:(single bool) v) in
185+
{ provides = t |> member "provides" |> mandatory (single string) |> StringSet.singleton
186+
; requires = t |> member "requires" |> set empty.requires
187+
; primitives = t |> member "primitives" |> list empty.primitives
188+
; force_link = t |> member "force_link" |> bool empty.force_link
189+
; effects_without_cps =
190+
t |> member "effects_without_cps" |> bool empty.effects_without_cps
191+
; crcs = StringMap.empty
192+
}

compiler/lib/unit_info.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ val prefix : string
4141
val to_string : t -> string
4242

4343
val parse : t -> string -> t option
44+
45+
val to_sexp : t -> Sexp.t list
46+
47+
val from_sexp : Sexp.t -> t

0 commit comments

Comments
 (0)