-
Notifications
You must be signed in to change notification settings - Fork 36
/
piqobj_to_piq.ml
264 lines (212 loc) · 6.96 KB
/
piqobj_to_piq.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2017, 2018 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)
module C = Piqi_common
open C
open Piqobj_common
(* whether to generate piqi-any as :typed or leave it as piqi-any (this depends
* on whether we are going to pretty-print the ast or not)
*)
let is_external_mode = ref false
(* NOTE, XXX: losing precision here, in future we will support encoding floats
* as string literals containing binary representation of 64-bit IEEE float *)
let gen_float x = `float (x, "")
let gen_string ?piq_format s =
match piq_format with
| Some `text ->
(* TODO: check if we can actually represent it as verbatim text; make
* sure there are no non-printable characters *)
`text s
| Some `word when Piq_lexer.is_valid_word s ->
`word s
| _ ->
`string (s, "")
let gen_binary s =
if Piq_lexer.is_ascii_string s
then
`string (s, "")
else
`binary (s, "")
let make_named name value =
`named Piq_ast.Named.({name = name; value = value})
let make_name name =
`name name
let make_typed typename ast :piq_ast =
let res = Piq_ast.Typed.({typename = typename; value = ast}) in
Piqloc.addref ast res;
`typed res
(* (re-)order fields according to their positions in the original piqi spec *)
let order_record_fields t piqobj_fields =
let find_fields ft l =
List.partition (fun x -> x.F.t == ft) l
in
let res, _rem =
List.fold_left
(fun (accu, rem) x -> (* folder *)
let res, rem' = find_fields x rem in
(List.rev_append res accu, rem'))
([], piqobj_fields) (* accu *)
t.T.Record.field (* list to fold *)
in
List.rev res
let rec gen_obj0 ?(piq_format: T.piq_format option) (x:Piqobj.obj) :piq_ast =
match x with
(* built-in types *)
| `int x -> `int (x, "")
| `uint x -> `uint (x, "")
| `float x -> gen_float x
| `bool x -> `bool x
| `string x -> gen_string x ?piq_format
| `binary x -> gen_binary x
| `any x -> gen_any x
(* custom types *)
| `record x -> gen_record x
| `variant x -> gen_variant x
| `enum x -> gen_enum x
| `list x -> gen_list x
| `alias x -> gen_alias x ?piq_format
(* TODO: provide more precise locations for fields, options, etc *)
and gen_obj ?piq_format x =
let res = gen_obj0 x ?piq_format in
match res with
| `any any ->
Piqloc.addrefret x res
| _ ->
Piq_parser.piq_addrefret x res
and gen_typed_obj x =
let name = Piqobj_common.full_typename x in
`typed Piq_ast.Typed.({typename = name; value = gen_obj x})
and gen_any x =
let open Any in
if not !is_external_mode
then
(* in internal mode, passing a reference to intermediate Any prepresentation
* registered using Piqi_objstore *)
`any (Piqobj.put_any x)
else (
let ast = Piqobj.piq_of_any x in
match x.typename, ast with
| Some typename, Some ast ->
make_typed typename ast
| None, Some ast ->
ast
| Some _, None ->
assert false (* this is an impossible case *)
| None, None -> (
(* support for untyped JSON and XML *)
match Piqobj.json_of_any x, Piqobj.xml_of_any x with
| None, None ->
(* this is not supposed to happen as any should always be
* represented in one of pb, xml, json or piq formats *)
assert false
| Some json_ast, _ ->
let s = !Piqobj.string_of_json json_ast in
`form (`word "json", [`text s]) (* (json ...) form *)
| None, Some xml_elems ->
let s = !Piqobj.string_of_xml (`Elem ("value", xml_elems)) in
`form (`word "xml", [`text s]) (* (xml ...) form *)
)
)
and gen_record x =
let open R in
(* TODO, XXX: doing ordering at every generation step is inefficient *)
let fields = order_record_fields x.t x.field in
let encoded_fields = U.flatmap gen_field fields in
let encoded_fields =
match x.unparsed_piq_fields_ref with
| None -> encoded_fields
| Some ref ->
let unparsed_fields = Piqi_objstore.get ref in
encoded_fields @ unparsed_fields
in
`list encoded_fields
and gen_field x =
let open F in
let name = name_of_field x.t in
let is_bool_default default const =
match default with
| None ->
false
| Some piqi_any ->
let any = Piqobj.any_of_piqi_any piqi_any in
(match any.Piqobj.Any.obj with
| None -> false
| Some obj -> (Piqobj.unalias obj = `bool const)
)
in
let bool_value =
match x.obj with
| None -> None
| Some obj ->
(match Piqobj.unalias obj with
| `bool x -> Some x
| _ -> None
)
in
if bool_value = Some false && is_bool_default x.t.T.Field.default false
then
(* FIXME, XXX: excluding explicit .foo false from output makes it
* non-reversable
*
* TODO, XXX: should there be expicit option to skip defaults, including
* flag defaults *)
[]
else
let res =
match x.obj with
| None -> (* flag *)
make_name name
| Some obj ->
if bool_value = Some true && is_bool_default x.t.T.Field.piq_flag_default true
then
(* FIXME, XXX: converting explicit .foo true to .foo makes it
* non-reversable *)
make_name name
else
make_named name (gen_obj obj ?piq_format:x.t.T.Field.piq_format)
in
let res = Piq_parser.piq_addrefret x res in
[res]
and gen_variant x =
let open V in
gen_option x.option
and gen_option x =
let open O in
let name = name_of_option x.t in
let res =
match x.obj with
| None -> make_name name
| Some obj -> make_named name (gen_obj obj ?piq_format:x.t.T.Option.piq_format)
in Piq_parser.piq_addrefret x res
and gen_enum x =
let open E in
gen_option x.option
and gen_list x =
let open L in
`list (List.map (fun obj -> gen_obj obj ?piq_format:x.t.T.Piqi_list.piq_format) x.obj)
and gen_alias ?(piq_format: T.piq_format option) x =
let open A in
(* upper-level setting overrides lower-level setting *)
let this_piq_format = x.t.T.Alias.piq_format in
let piq_format =
if this_piq_format <> None
then this_piq_format
else piq_format
in
match x.obj with
| `alias x ->
gen_alias x ?piq_format
| x ->
gen_obj x ?piq_format
let gen_obj obj = gen_obj obj
let _ =
Piqobj.to_piq := gen_obj