@greenlabs/ppx-spice 0.1.8 → 0.1.9-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +4 -0
- package/docs/GUIDE.md +139 -0
- package/package.json +1 -1
- package/ppx-windows.exe +0 -0
- package/.github/workflows/build_linux.yml +0 -41
- package/.github/workflows/build_macos.yml +0 -41
- package/.github/workflows/build_windows.yml +0 -41
- package/.github/workflows/print_esy_cache.js +0 -13
- package/.github/workflows/publish.yml +0 -161
- package/.github/workflows/publish_prerelease.yml +0 -177
- package/.vscode/settings.json +0 -6
- package/src/.ocamlformat +0 -0
- package/src/bin/bin.ml +0 -1
- package/src/bin/dune +0 -5
- package/src/dune +0 -1
- package/src/dune-project +0 -2
- package/src/dune-workspace +0 -3
- package/src/esy.lock/.gitattributes +0 -3
- package/src/esy.lock/.gitignore +0 -3
- package/src/esy.lock/index.json +0 -1196
- package/src/esy.lock/opam/astring.0.8.5/opam +0 -37
- package/src/esy.lock/opam/base-bytes.base/opam +0 -9
- package/src/esy.lock/opam/base-threads.base/opam +0 -6
- package/src/esy.lock/opam/base-unix.base/opam +0 -6
- package/src/esy.lock/opam/base.v0.14.1/opam +0 -36
- package/src/esy.lock/opam/biniou.1.2.1/opam +0 -45
- package/src/esy.lock/opam/cmdliner.1.0.4/opam +0 -36
- package/src/esy.lock/opam/cppo.1.6.8/opam +0 -37
- package/src/esy.lock/opam/csexp.1.5.1/opam +0 -60
- package/src/esy.lock/opam/dot-merlin-reader.4.1/opam +0 -30
- package/src/esy.lock/opam/dune-build-info.2.9.1/opam +0 -42
- package/src/esy.lock/opam/dune-configurator.2.9.1/opam +0 -47
- package/src/esy.lock/opam/dune.2.9.1/opam +0 -58
- package/src/esy.lock/opam/easy-format.1.3.2/opam +0 -46
- package/src/esy.lock/opam/fix.20201120/opam +0 -24
- package/src/esy.lock/opam/fpath.0.7.3/opam +0 -36
- package/src/esy.lock/opam/menhir.20211012/opam +0 -28
- package/src/esy.lock/opam/menhirLib.20211012/opam +0 -29
- package/src/esy.lock/opam/menhirSdk.20211012/opam +0 -29
- package/src/esy.lock/opam/ocaml-compiler-libs.v0.12.4/opam +0 -39
- package/src/esy.lock/opam/ocaml-lsp-server.1.8.3/opam +0 -54
- package/src/esy.lock/opam/ocamlbuild.0.14.0/opam +0 -36
- package/src/esy.lock/opam/ocamlfind.1.9.1/opam +0 -44
- package/src/esy.lock/opam/ocamlformat-rpc-lib.0.18.0/opam +0 -40
- package/src/esy.lock/opam/ocamlformat.0.19.0/opam +0 -55
- package/src/esy.lock/opam/ocp-indent.1.8.1/opam +0 -57
- package/src/esy.lock/opam/odoc-parser.0.9.0/opam +0 -45
- package/src/esy.lock/opam/pp.1.1.2/opam +0 -58
- package/src/esy.lock/opam/ppx_derivers.1.2.1/opam +0 -23
- package/src/esy.lock/opam/ppx_yojson_conv_lib.v0.14.0/opam +0 -24
- package/src/esy.lock/opam/ppxlib.0.23.0/opam +0 -62
- package/src/esy.lock/opam/re.1.10.3/opam +0 -46
- package/src/esy.lock/opam/result.1.5/opam +0 -22
- package/src/esy.lock/opam/seq.base/files/META.seq +0 -4
- package/src/esy.lock/opam/seq.base/files/seq.install +0 -3
- package/src/esy.lock/opam/seq.base/opam +0 -15
- package/src/esy.lock/opam/sexplib0.v0.14.0/opam +0 -26
- package/src/esy.lock/opam/stdio.v0.14.0/opam +0 -27
- package/src/esy.lock/opam/stdlib-shims.0.3.0/opam +0 -31
- package/src/esy.lock/opam/topkg.1.0.4/opam +0 -44
- package/src/esy.lock/opam/uchar.0.0.2/opam +0 -36
- package/src/esy.lock/opam/uucp.14.0.0/opam +0 -41
- package/src/esy.lock/opam/uuseg.14.0.0/opam +0 -43
- package/src/esy.lock/opam/uutf.1.0.2/opam +0 -40
- package/src/esy.lock/opam/yojson.1.7.0/opam +0 -38
- package/src/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/files/ocamlbuild-0.14.0.patch +0 -463
- package/src/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/package.json +0 -27
- package/src/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.9.1_opam_override/files/findlib-1.9.1.patch +0 -471
- package/src/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.9.1_opam_override/package.json +0 -61
- package/src/ppx/codecs.ml +0 -120
- package/src/ppx/decode_cases.ml +0 -18
- package/src/ppx/dune +0 -9
- package/src/ppx/polyvariants.ml +0 -288
- package/src/ppx/ppx_spice.ml +0 -19
- package/src/ppx/records.ml +0 -163
- package/src/ppx/signature.ml +0 -86
- package/src/ppx/structure.ml +0 -109
- package/src/ppx/tuple.ml +0 -68
- package/src/ppx/utils.ml +0 -117
- package/src/ppx/variants.ml +0 -257
- package/src/ppx_spice.opam +0 -21
- package/test/__tests__/test_optional_field_records.js +0 -91
- package/test/__tests__/test_optional_field_records.res +0 -98
- package/test/__tests__/test_polyvariants.js +0 -57
- package/test/__tests__/test_polyvariants.res +0 -41
- package/test/__tests__/test_records.js +0 -100
- package/test/__tests__/test_records.res +0 -111
- package/test/__tests__/test_variants.js +0 -85
- package/test/__tests__/test_variants.res +0 -63
- package/test/bsconfig.json +0 -27
- package/test/package.json +0 -18
- package/test/src/OptionalFieldRecords.js +0 -237
- package/test/src/OptionalFieldRecords.res +0 -23
- package/test/src/Polyvariants.js +0 -94
- package/test/src/Polyvariants.res +0 -5
- package/test/src/Polyvariants.resi +0 -5
- package/test/src/Records.js +0 -186
- package/test/src/Records.res +0 -17
- package/test/src/Records.resi +0 -17
- package/test/src/Spice.js +0 -448
- package/test/src/Spice_Codecs.js +0 -57
- package/test/src/Variants.js +0 -115
- package/test/src/Variants.res +0 -11
- package/test/src/Variants.resi +0 -11
- package/test/yarn.lock +0 -4194
package/src/ppx/polyvariants.ml
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
open Parsetree
|
|
2
|
-
open Ast_helper
|
|
3
|
-
open Utils
|
|
4
|
-
|
|
5
|
-
(* Polyvariants arguments are wrapped inside a Tuple, meaning that if there's only
|
|
6
|
-
one arg it's the coreType, but if there's more than one arg it's a tuple of one tuple with those args.
|
|
7
|
-
This function abstract this particuliarity from polyvariants (It's different from Variants). *)
|
|
8
|
-
|
|
9
|
-
type parsed_field = {
|
|
10
|
-
name : string;
|
|
11
|
-
alias : expression;
|
|
12
|
-
has_attr_as : bool;
|
|
13
|
-
row_field : Parsetree.row_field;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let get_args_from_polyvars ~loc coreTypes =
|
|
17
|
-
match coreTypes with
|
|
18
|
-
| [] -> []
|
|
19
|
-
| [ coreType ] -> (
|
|
20
|
-
match coreType.ptyp_desc with
|
|
21
|
-
(* If it's a tuple, return the args *)
|
|
22
|
-
| Ptyp_tuple coreTypes -> coreTypes
|
|
23
|
-
(* If it's any other coreType, return it *)
|
|
24
|
-
| _ -> [ coreType ])
|
|
25
|
-
| _ ->
|
|
26
|
-
fail loc
|
|
27
|
-
"This error shoudn't happen, means that the AST of your polyvariant is \
|
|
28
|
-
wrong"
|
|
29
|
-
|
|
30
|
-
let generate_encoder_case generator_settings unboxed has_attr_as row =
|
|
31
|
-
let { name; alias; row_field = { prf_desc } } = row in
|
|
32
|
-
match prf_desc with
|
|
33
|
-
| Rtag (_, _attributes, core_types) ->
|
|
34
|
-
let alias_name, _, delimit = get_string_from_expression alias in
|
|
35
|
-
let constructor_expr =
|
|
36
|
-
Exp.constant (Pconst_string (alias_name, Location.none, delimit))
|
|
37
|
-
in
|
|
38
|
-
let args = get_args_from_polyvars ~loc core_types in
|
|
39
|
-
|
|
40
|
-
let lhs_vars =
|
|
41
|
-
match args with
|
|
42
|
-
| [] -> None
|
|
43
|
-
| [ _ ] -> Some (Pat.var (mknoloc "v0"))
|
|
44
|
-
| _ ->
|
|
45
|
-
args
|
|
46
|
-
|> List.mapi (fun i _ ->
|
|
47
|
-
mkloc ("v" ^ string_of_int i) loc |> Pat.var)
|
|
48
|
-
|> Pat.tuple
|
|
49
|
-
|> fun v -> Some v
|
|
50
|
-
in
|
|
51
|
-
|
|
52
|
-
let rhs_list =
|
|
53
|
-
args
|
|
54
|
-
|> List.map (Codecs.generate_codecs generator_settings)
|
|
55
|
-
|> List.map (fun (encoder, _) -> Option.get encoder)
|
|
56
|
-
|> List.mapi (fun i e ->
|
|
57
|
-
Exp.apply ~loc e
|
|
58
|
-
[ (Asttypes.Nolabel, make_ident_expr ("v" ^ string_of_int i)) ])
|
|
59
|
-
|> List.append [ [%expr Js.Json.string [%e constructor_expr]] ]
|
|
60
|
-
in
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
pc_lhs = Pat.variant name lhs_vars;
|
|
64
|
-
pc_guard = None;
|
|
65
|
-
pc_rhs =
|
|
66
|
-
(if unboxed then List.tl rhs_list |> List.hd (* diff *)
|
|
67
|
-
else if has_attr_as then [%expr Js.Json.string [%e constructor_expr]]
|
|
68
|
-
else [%expr Js.Json.array [%e rhs_list |> Exp.array]]);
|
|
69
|
-
}
|
|
70
|
-
(* We don't have enough information to generate a encoder *)
|
|
71
|
-
| Rinherit arg ->
|
|
72
|
-
fail arg.ptyp_loc "This syntax is not yet implemented by spice"
|
|
73
|
-
|
|
74
|
-
let generate_decode_success_case num_args constructor_name =
|
|
75
|
-
{
|
|
76
|
-
pc_lhs =
|
|
77
|
-
Array.init num_args (fun i ->
|
|
78
|
-
mknoloc ("v" ^ string_of_int i) |> Pat.var |> fun p ->
|
|
79
|
-
[%pat? Belt.Result.Ok [%p p]])
|
|
80
|
-
|> Array.to_list
|
|
81
|
-
|> tuple_or_singleton Pat.tuple;
|
|
82
|
-
pc_guard = None;
|
|
83
|
-
pc_rhs =
|
|
84
|
-
( Array.init num_args (fun i -> make_ident_expr ("v" ^ string_of_int i))
|
|
85
|
-
|> Array.to_list
|
|
86
|
-
|> tuple_or_singleton Exp.tuple
|
|
87
|
-
|> fun v ->
|
|
88
|
-
Some v |> Exp.variant constructor_name |> fun e ->
|
|
89
|
-
[%expr Belt.Result.Ok [%e e]] );
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
let generate_arg_decoder generator_settings args constructor_name =
|
|
93
|
-
let num_args = List.length args in
|
|
94
|
-
args
|
|
95
|
-
|> List.mapi (Decode_cases.generate_error_case num_args)
|
|
96
|
-
|> List.append [ generate_decode_success_case num_args constructor_name ]
|
|
97
|
-
|> Exp.match_
|
|
98
|
-
(args
|
|
99
|
-
|> List.map (Codecs.generate_codecs generator_settings)
|
|
100
|
-
|> List.mapi (fun i (_, decoder) ->
|
|
101
|
-
Exp.apply (Option.get decoder)
|
|
102
|
-
[
|
|
103
|
-
( Asttypes.Nolabel,
|
|
104
|
-
(* +1 because index 0 is the constructor *)
|
|
105
|
-
let idx =
|
|
106
|
-
Pconst_integer (string_of_int (i + 1), None)
|
|
107
|
-
|> Exp.constant
|
|
108
|
-
in
|
|
109
|
-
[%expr Belt.Array.getExn json_arr [%e idx]] );
|
|
110
|
-
])
|
|
111
|
-
|> tuple_or_singleton Exp.tuple)
|
|
112
|
-
|
|
113
|
-
let generate_decoder_case generator_settings { prf_desc } =
|
|
114
|
-
match prf_desc with
|
|
115
|
-
| Rtag ({ txt }, _, core_types) ->
|
|
116
|
-
let args = get_args_from_polyvars ~loc core_types in
|
|
117
|
-
let arg_len =
|
|
118
|
-
Pconst_integer (string_of_int (List.length args + 1), None)
|
|
119
|
-
|> Exp.constant
|
|
120
|
-
in
|
|
121
|
-
let decoded =
|
|
122
|
-
match args with
|
|
123
|
-
| [] ->
|
|
124
|
-
let resultant_exp = Exp.variant txt None in
|
|
125
|
-
[%expr Belt.Result.Ok [%e resultant_exp]]
|
|
126
|
-
| _ -> generate_arg_decoder generator_settings args txt
|
|
127
|
-
in
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
pc_lhs =
|
|
131
|
-
( Pconst_string (txt, Location.none, None) |> Pat.constant |> fun v ->
|
|
132
|
-
Some v |> Pat.construct (lid "Js.Json.JSONString") );
|
|
133
|
-
pc_guard = None;
|
|
134
|
-
pc_rhs =
|
|
135
|
-
[%expr
|
|
136
|
-
if Js.Array.length tagged != [%e arg_len] then
|
|
137
|
-
Spice.error
|
|
138
|
-
"Invalid number of arguments to polyvariant constructor" v
|
|
139
|
-
else [%e decoded]];
|
|
140
|
-
}
|
|
141
|
-
| Rinherit core_type ->
|
|
142
|
-
fail core_type.ptyp_loc "This syntax is not yet implemented by spice"
|
|
143
|
-
|
|
144
|
-
let generate_decoder_case_attr generator_settings row =
|
|
145
|
-
let { alias; row_field = { prf_desc } } = row in
|
|
146
|
-
match prf_desc with
|
|
147
|
-
| Rtag ({ txt }, _, core_types) ->
|
|
148
|
-
let args = get_args_from_polyvars ~loc core_types in
|
|
149
|
-
let alias_name, loc, delimit = get_string_from_expression alias in
|
|
150
|
-
let decoded =
|
|
151
|
-
match args with
|
|
152
|
-
| [] ->
|
|
153
|
-
let resultant_exp = Exp.variant txt None in
|
|
154
|
-
[%expr Belt.Result.Ok [%e resultant_exp]]
|
|
155
|
-
| _ -> generate_arg_decoder generator_settings args txt
|
|
156
|
-
in
|
|
157
|
-
|
|
158
|
-
let if' =
|
|
159
|
-
Exp.apply (make_ident_expr "=")
|
|
160
|
-
[
|
|
161
|
-
( Asttypes.Nolabel,
|
|
162
|
-
Pconst_string (alias_name, Location.none, delimit) |> Exp.constant
|
|
163
|
-
);
|
|
164
|
-
(Asttypes.Nolabel, [%expr str]);
|
|
165
|
-
]
|
|
166
|
-
in
|
|
167
|
-
let then' = [%expr [%e decoded]] in
|
|
168
|
-
|
|
169
|
-
(if', then')
|
|
170
|
-
| Rinherit core_type ->
|
|
171
|
-
fail core_type.ptyp_loc "This syntax is not yet implemented by spice"
|
|
172
|
-
|
|
173
|
-
let generate_unboxed_decode generator_settings { prf_desc } =
|
|
174
|
-
match prf_desc with
|
|
175
|
-
| Rtag ({ txt; loc }, _, args) -> (
|
|
176
|
-
match args with
|
|
177
|
-
| [ a ] -> (
|
|
178
|
-
let _, d = Codecs.generate_codecs generator_settings a in
|
|
179
|
-
match d with
|
|
180
|
-
| Some d ->
|
|
181
|
-
let constructor = Exp.construct (lid txt) (Some [%expr v]) in
|
|
182
|
-
|
|
183
|
-
Some
|
|
184
|
-
[%expr
|
|
185
|
-
fun v ->
|
|
186
|
-
Belt.Result.map ([%e d] v) (fun v -> [%e constructor])]
|
|
187
|
-
| None -> None)
|
|
188
|
-
| _ -> fail loc "Expected exactly one type argument")
|
|
189
|
-
| Rinherit coreType ->
|
|
190
|
-
fail coreType.ptyp_loc "This syntax is not yet implemented by spice"
|
|
191
|
-
|
|
192
|
-
let parse_decl ({ prf_desc; prf_loc; prf_attributes } as row_field) =
|
|
193
|
-
let txt =
|
|
194
|
-
match prf_desc with
|
|
195
|
-
| Rtag ({ txt }, _, _) -> txt
|
|
196
|
-
| _ -> failwith "cannot get polymorphic variant constructor"
|
|
197
|
-
in
|
|
198
|
-
|
|
199
|
-
let alias, has_attr_as =
|
|
200
|
-
match get_attribute_by_name prf_attributes "spice.as" with
|
|
201
|
-
| Ok (Some attribute) -> (get_expression_from_payload attribute, true)
|
|
202
|
-
| Ok None -> (Exp.constant (Pconst_string (txt, Location.none, None)), false)
|
|
203
|
-
| Error s -> (fail prf_loc s, false)
|
|
204
|
-
in
|
|
205
|
-
|
|
206
|
-
{ name = txt; alias; has_attr_as; row_field }
|
|
207
|
-
|
|
208
|
-
let generate_codecs ({ do_encode; do_decode } as generator_settings) row_fields
|
|
209
|
-
unboxed =
|
|
210
|
-
let parsed_fields = List.map parse_decl row_fields in
|
|
211
|
-
let count_has_attr =
|
|
212
|
-
parsed_fields |> List.filter (fun v -> v.has_attr_as) |> List.length
|
|
213
|
-
in
|
|
214
|
-
let has_attr_as =
|
|
215
|
-
if count_has_attr > 0 then
|
|
216
|
-
if count_has_attr = List.length parsed_fields then true
|
|
217
|
-
else failwith "Partial @spice.as usage is not allowed"
|
|
218
|
-
else false
|
|
219
|
-
in
|
|
220
|
-
|
|
221
|
-
let encoder =
|
|
222
|
-
some_if_true do_encode
|
|
223
|
-
(List.map
|
|
224
|
-
(generate_encoder_case generator_settings unboxed has_attr_as)
|
|
225
|
-
parsed_fields
|
|
226
|
-
|> Exp.match_ [%expr v]
|
|
227
|
-
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
|
|
228
|
-
in
|
|
229
|
-
|
|
230
|
-
let decoder =
|
|
231
|
-
match not do_decode with
|
|
232
|
-
| true -> None
|
|
233
|
-
| false ->
|
|
234
|
-
if unboxed then
|
|
235
|
-
generate_unboxed_decode generator_settings (List.hd row_fields)
|
|
236
|
-
else if has_attr_as then
|
|
237
|
-
let rec make_ifthenelse cases =
|
|
238
|
-
match cases with
|
|
239
|
-
| [] -> [%expr Spice.error "Not matched" v]
|
|
240
|
-
| hd :: tl ->
|
|
241
|
-
let if_, then_ = hd in
|
|
242
|
-
Exp.ifthenelse if_ then_ (Some (make_ifthenelse tl))
|
|
243
|
-
in
|
|
244
|
-
|
|
245
|
-
let decoder_switch =
|
|
246
|
-
parsed_fields
|
|
247
|
-
|> List.map (generate_decoder_case_attr generator_settings)
|
|
248
|
-
|> make_ifthenelse
|
|
249
|
-
in
|
|
250
|
-
|
|
251
|
-
Some
|
|
252
|
-
[%expr
|
|
253
|
-
fun v ->
|
|
254
|
-
match Js.Json.classify v with
|
|
255
|
-
| Js.Json.JSONString str -> [%e decoder_switch]
|
|
256
|
-
| _ -> Spice.error "Not a JSONString" v]
|
|
257
|
-
else
|
|
258
|
-
let decoder_default_case =
|
|
259
|
-
{
|
|
260
|
-
pc_lhs = [%pat? _];
|
|
261
|
-
pc_guard = None;
|
|
262
|
-
pc_rhs =
|
|
263
|
-
[%expr
|
|
264
|
-
Spice.error "Invalid polymorphic constructor"
|
|
265
|
-
(Belt.Array.getExn json_arr 0)];
|
|
266
|
-
}
|
|
267
|
-
in
|
|
268
|
-
|
|
269
|
-
let decoder_switch =
|
|
270
|
-
row_fields |> List.map (generate_decoder_case generator_settings)
|
|
271
|
-
|> fun l ->
|
|
272
|
-
l @ [ decoder_default_case ]
|
|
273
|
-
|> Exp.match_ [%expr Belt.Array.getExn tagged 0]
|
|
274
|
-
in
|
|
275
|
-
|
|
276
|
-
Some
|
|
277
|
-
[%expr
|
|
278
|
-
fun v ->
|
|
279
|
-
match Js.Json.classify v with
|
|
280
|
-
| Js.Json.JSONArray [||] ->
|
|
281
|
-
Spice.error "Expected polyvariant, found empty array" v
|
|
282
|
-
| Js.Json.JSONArray json_arr ->
|
|
283
|
-
let tagged = Js.Array.map Js.Json.classify json_arr in
|
|
284
|
-
[%e decoder_switch]
|
|
285
|
-
| _ -> Spice.error "Not a polyvariant" v]
|
|
286
|
-
in
|
|
287
|
-
|
|
288
|
-
(encoder, decoder)
|
package/src/ppx/ppx_spice.ml
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
|
|
3
|
-
class mapper =
|
|
4
|
-
object (self)
|
|
5
|
-
inherit Ast_traverse.map
|
|
6
|
-
|
|
7
|
-
method! signature sign =
|
|
8
|
-
sign |> List.map (Signature.map_signature_item self) |> List.concat
|
|
9
|
-
|
|
10
|
-
method! structure strt =
|
|
11
|
-
strt |> List.map (Structure.map_structure_item self) |> List.concat
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
let signature_mapper = (new mapper)#signature
|
|
15
|
-
|
|
16
|
-
let structure_mapper = (new mapper)#structure;;
|
|
17
|
-
|
|
18
|
-
Ppxlib.Driver.register_transformation ~preprocess_impl:structure_mapper
|
|
19
|
-
~preprocess_intf:signature_mapper "spice"
|
package/src/ppx/records.ml
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Ast_helper
|
|
4
|
-
open Utils
|
|
5
|
-
|
|
6
|
-
type parsed_decl = {
|
|
7
|
-
name : string;
|
|
8
|
-
(* "NAME" *)
|
|
9
|
-
key : expression;
|
|
10
|
-
(* v.NAME *)
|
|
11
|
-
field : expression;
|
|
12
|
-
codecs : expression option * expression option;
|
|
13
|
-
default : expression option;
|
|
14
|
-
is_optional : bool;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let optional_attr : Ppxlib.Parsetree.attribute =
|
|
18
|
-
{
|
|
19
|
-
attr_name = { txt = "ns.optional"; loc = Location.none };
|
|
20
|
-
attr_payload = PStr [];
|
|
21
|
-
attr_loc = Location.none;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let generate_encoder decls unboxed =
|
|
25
|
-
match unboxed with
|
|
26
|
-
| true ->
|
|
27
|
-
let { codecs; field } = List.hd decls in
|
|
28
|
-
let e, _ = codecs in
|
|
29
|
-
[%expr fun v -> [%e Option.get e] [%e field]]
|
|
30
|
-
| false ->
|
|
31
|
-
let arrExpr =
|
|
32
|
-
decls
|
|
33
|
-
|> List.map (fun { key; field; codecs = encoder, _; is_optional } ->
|
|
34
|
-
let is_optional =
|
|
35
|
-
if is_optional then [%expr true] else [%expr false]
|
|
36
|
-
in
|
|
37
|
-
[%expr
|
|
38
|
-
[%e key], [%e is_optional], [%e Option.get encoder] [%e field]])
|
|
39
|
-
|> Exp.array
|
|
40
|
-
in
|
|
41
|
-
[%expr
|
|
42
|
-
[%e arrExpr] |> Spice.filterOptional |> Js.Dict.fromArray
|
|
43
|
-
|> Js.Json.object_]
|
|
44
|
-
|> Exp.fun_ Asttypes.Nolabel None [%pat? v]
|
|
45
|
-
|
|
46
|
-
let generate_dict_get { key; codecs = _, decoder; default } =
|
|
47
|
-
let decoder = Option.get decoder in
|
|
48
|
-
match default with
|
|
49
|
-
| Some default ->
|
|
50
|
-
[%expr
|
|
51
|
-
Belt.Option.getWithDefault
|
|
52
|
-
(Belt.Option.map (Js.Dict.get dict [%e key]) [%e decoder])
|
|
53
|
-
(Belt.Result.Ok [%e default])]
|
|
54
|
-
| None ->
|
|
55
|
-
[%expr
|
|
56
|
-
Belt.Option.getWithDefault (Js.Dict.get dict [%e key]) Js.Json.null
|
|
57
|
-
|> [%e decoder]]
|
|
58
|
-
|
|
59
|
-
let generate_dict_gets decls =
|
|
60
|
-
decls |> List.map generate_dict_get |> tuple_or_singleton Exp.tuple
|
|
61
|
-
|
|
62
|
-
let generate_error_case { key } =
|
|
63
|
-
{
|
|
64
|
-
pc_lhs = [%pat? Belt.Result.Error (e : Spice.decodeError)];
|
|
65
|
-
pc_guard = None;
|
|
66
|
-
pc_rhs = [%expr Belt.Result.Error { e with path = "." ^ [%e key] ^ e.path }];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let generate_final_record_expr decls =
|
|
70
|
-
decls
|
|
71
|
-
|> List.map (fun { name; is_optional } ->
|
|
72
|
-
let attrs = if is_optional then [ optional_attr ] else [] in
|
|
73
|
-
(lid name, make_ident_expr ~attrs name))
|
|
74
|
-
|> fun l -> [%expr Belt.Result.Ok [%e Exp.record l None]]
|
|
75
|
-
|
|
76
|
-
let generate_success_case { name } success_expr =
|
|
77
|
-
{
|
|
78
|
-
pc_lhs = (mknoloc name |> Pat.var |> fun p -> [%pat? Belt.Result.Ok [%p p]]);
|
|
79
|
-
pc_guard = None;
|
|
80
|
-
pc_rhs = success_expr;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let rec generate_nested_switches_recurse all_decls remaining_decls =
|
|
84
|
-
let current, success_expr =
|
|
85
|
-
match remaining_decls with
|
|
86
|
-
| [] -> failwith "Spice internal error: [] not expected"
|
|
87
|
-
| [ last ] -> (last, generate_final_record_expr all_decls)
|
|
88
|
-
| first :: tail -> (first, generate_nested_switches_recurse all_decls tail)
|
|
89
|
-
in
|
|
90
|
-
[ generate_error_case current ]
|
|
91
|
-
|> List.append [ generate_success_case current success_expr ]
|
|
92
|
-
|> Exp.match_ (generate_dict_get current)
|
|
93
|
-
[@@ocaml.doc
|
|
94
|
-
" Recursively generates an expression containing nested switches, first\n\
|
|
95
|
-
\ * decoding the first record items, then (if successful) the second, \
|
|
96
|
-
etc. "]
|
|
97
|
-
|
|
98
|
-
let generate_nested_switches decls =
|
|
99
|
-
generate_nested_switches_recurse decls decls
|
|
100
|
-
|
|
101
|
-
let generate_decoder decls unboxed =
|
|
102
|
-
match unboxed with
|
|
103
|
-
| true ->
|
|
104
|
-
let { codecs; name } = List.hd decls in
|
|
105
|
-
let _, d = codecs in
|
|
106
|
-
|
|
107
|
-
let record_expr = Exp.record [ (lid name, make_ident_expr "v") ] None in
|
|
108
|
-
|
|
109
|
-
[%expr
|
|
110
|
-
fun v ->
|
|
111
|
-
Belt.Result.map ([%e Option.get d] v) (fun v -> [%e record_expr])]
|
|
112
|
-
| false ->
|
|
113
|
-
[%expr
|
|
114
|
-
fun v ->
|
|
115
|
-
match Js.Json.classify v with
|
|
116
|
-
| Js.Json.JSONObject dict -> [%e generate_nested_switches decls]
|
|
117
|
-
| _ -> Spice.error "Not an object" v]
|
|
118
|
-
|
|
119
|
-
let parse_decl generator_settings
|
|
120
|
-
{ pld_name = { txt }; pld_loc; pld_type; pld_attributes } =
|
|
121
|
-
let default =
|
|
122
|
-
match get_attribute_by_name pld_attributes "spice.default" with
|
|
123
|
-
| Ok (Some attribute) -> Some (get_expression_from_payload attribute)
|
|
124
|
-
| Ok None -> None
|
|
125
|
-
| Error s -> fail pld_loc s
|
|
126
|
-
in
|
|
127
|
-
let key =
|
|
128
|
-
match get_attribute_by_name pld_attributes "spice.key" with
|
|
129
|
-
| Ok (Some attribute) -> get_expression_from_payload attribute
|
|
130
|
-
| Ok None -> Exp.constant (Pconst_string (txt, Location.none, None))
|
|
131
|
-
| Error s -> fail pld_loc s
|
|
132
|
-
in
|
|
133
|
-
let optional_attrs = [ "ns.optional"; "res.optional" ] in
|
|
134
|
-
let is_optional =
|
|
135
|
-
optional_attrs
|
|
136
|
-
|> List.map (fun attr -> get_attribute_by_name pld_attributes attr)
|
|
137
|
-
|> List.exists (function Ok (Some _) -> true | _ -> false)
|
|
138
|
-
in
|
|
139
|
-
let codecs = Codecs.generate_codecs generator_settings pld_type in
|
|
140
|
-
let codecs =
|
|
141
|
-
if is_optional then
|
|
142
|
-
match codecs with
|
|
143
|
-
| Some encode, Some decode ->
|
|
144
|
-
( Some [%expr Spice.optionToJson [%e encode]],
|
|
145
|
-
Some [%expr Spice.optionFromJson [%e decode]] )
|
|
146
|
-
| _ -> codecs
|
|
147
|
-
else codecs
|
|
148
|
-
in
|
|
149
|
-
|
|
150
|
-
{
|
|
151
|
-
name = txt;
|
|
152
|
-
key;
|
|
153
|
-
field = Exp.field [%expr v] (lid txt);
|
|
154
|
-
codecs;
|
|
155
|
-
default;
|
|
156
|
-
is_optional;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
let generate_codecs ({ do_encode; do_decode } as generator_settings) decls
|
|
160
|
-
unboxed =
|
|
161
|
-
let parsed_decls = List.map (parse_decl generator_settings) decls in
|
|
162
|
-
( some_if_true do_encode (generate_encoder parsed_decls unboxed),
|
|
163
|
-
some_if_true do_decode (generate_decoder parsed_decls unboxed) )
|
package/src/ppx/signature.ml
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Utils
|
|
4
|
-
|
|
5
|
-
let rec add_encoder_params param_names result_type =
|
|
6
|
-
match param_names with
|
|
7
|
-
| [] -> result_type
|
|
8
|
-
| hd :: tl ->
|
|
9
|
-
[%type: ([%t Ast_helper.Typ.var hd] -> Js.Json.t) -> [%t result_type]]
|
|
10
|
-
|> add_encoder_params tl
|
|
11
|
-
|
|
12
|
-
let make_result_type value_type =
|
|
13
|
-
[%type: ([%t value_type], Spice.decodeError) Belt.Result.t]
|
|
14
|
-
|
|
15
|
-
let rec add_decoder_params param_names result_type =
|
|
16
|
-
match param_names with
|
|
17
|
-
| [] -> result_type
|
|
18
|
-
| hd :: tl ->
|
|
19
|
-
let decoder_param =
|
|
20
|
-
[%type: Js.Json.t -> [%t make_result_type (Ast_helper.Typ.var hd)]]
|
|
21
|
-
in
|
|
22
|
-
[%type: [%t decoder_param] -> [%t result_type]] |> add_decoder_params tl
|
|
23
|
-
|
|
24
|
-
let generate_sig_decls { do_encode; do_decode } type_name param_names =
|
|
25
|
-
let encoder_pat = type_name ^ Utils.encoder_func_suffix in
|
|
26
|
-
let decoder_pat = type_name ^ Utils.decoder_func_suffix in
|
|
27
|
-
let value_type =
|
|
28
|
-
param_names
|
|
29
|
-
|> List.map Ast_helper.Typ.var
|
|
30
|
-
|> Ast_helper.Typ.constr (lid type_name)
|
|
31
|
-
in
|
|
32
|
-
|
|
33
|
-
let decls = [] in
|
|
34
|
-
|
|
35
|
-
let decls =
|
|
36
|
-
match do_encode with
|
|
37
|
-
| true ->
|
|
38
|
-
decls
|
|
39
|
-
@ [
|
|
40
|
-
[%type: [%t value_type] -> Js.Json.t]
|
|
41
|
-
|> add_encoder_params (List.rev param_names)
|
|
42
|
-
|> Ast_helper.Val.mk (mknoloc encoder_pat)
|
|
43
|
-
|> Ast_helper.Sig.value;
|
|
44
|
-
]
|
|
45
|
-
| false -> decls
|
|
46
|
-
in
|
|
47
|
-
let decls =
|
|
48
|
-
match do_decode with
|
|
49
|
-
| true ->
|
|
50
|
-
decls
|
|
51
|
-
@ [
|
|
52
|
-
[%type: Js.Json.t -> [%t make_result_type value_type]]
|
|
53
|
-
|> add_decoder_params (List.rev param_names)
|
|
54
|
-
|> Ast_helper.Val.mk (mknoloc decoder_pat)
|
|
55
|
-
|> Ast_helper.Sig.value;
|
|
56
|
-
]
|
|
57
|
-
| false -> decls
|
|
58
|
-
in
|
|
59
|
-
|
|
60
|
-
decls
|
|
61
|
-
|
|
62
|
-
let map_type_decl decl =
|
|
63
|
-
let {
|
|
64
|
-
ptype_attributes;
|
|
65
|
-
ptype_name = { txt = type_name };
|
|
66
|
-
ptype_params;
|
|
67
|
-
ptype_loc;
|
|
68
|
-
} =
|
|
69
|
-
decl
|
|
70
|
-
in
|
|
71
|
-
|
|
72
|
-
match get_generator_settings_from_attributes ptype_attributes with
|
|
73
|
-
| Error s -> fail ptype_loc s
|
|
74
|
-
| Ok None -> []
|
|
75
|
-
| Ok (Some generator_settings) ->
|
|
76
|
-
generate_sig_decls generator_settings type_name
|
|
77
|
-
(get_param_names ptype_params)
|
|
78
|
-
|
|
79
|
-
let map_signature_item mapper ({ psig_desc } as signature_item) =
|
|
80
|
-
match psig_desc with
|
|
81
|
-
| Psig_type (_, decls) ->
|
|
82
|
-
let generated_sig_items =
|
|
83
|
-
decls |> List.map map_type_decl |> List.concat
|
|
84
|
-
in
|
|
85
|
-
mapper#signature_item signature_item :: generated_sig_items
|
|
86
|
-
| _ -> [ mapper#signature_item signature_item ]
|
package/src/ppx/structure.ml
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Ast_helper
|
|
4
|
-
open Codecs
|
|
5
|
-
open Utils
|
|
6
|
-
|
|
7
|
-
let add_params param_names expr =
|
|
8
|
-
List.fold_right
|
|
9
|
-
(fun s acc ->
|
|
10
|
-
let pat = Pat.var (mknoloc s) in
|
|
11
|
-
Exp.fun_ Asttypes.Nolabel None pat acc)
|
|
12
|
-
param_names
|
|
13
|
-
[%expr fun v -> [%e expr] v]
|
|
14
|
-
|
|
15
|
-
let generate_codec_decls type_name param_names (encoder, decoder) =
|
|
16
|
-
let encoder_pat = Pat.var (mknoloc (type_name ^ Utils.encoder_func_suffix)) in
|
|
17
|
-
let encoder_param_names =
|
|
18
|
-
List.map (fun s -> encoder_var_prefix ^ s) param_names
|
|
19
|
-
in
|
|
20
|
-
|
|
21
|
-
let decoder_pat = Pat.var (mknoloc (type_name ^ Utils.decoder_func_suffix)) in
|
|
22
|
-
let decoder_param_names =
|
|
23
|
-
List.map (fun s -> decoder_var_prefix ^ s) param_names
|
|
24
|
-
in
|
|
25
|
-
|
|
26
|
-
let vbs = [] in
|
|
27
|
-
|
|
28
|
-
let vbs =
|
|
29
|
-
match encoder with
|
|
30
|
-
| None -> vbs
|
|
31
|
-
| Some encoder ->
|
|
32
|
-
vbs
|
|
33
|
-
@ [
|
|
34
|
-
Vb.mk
|
|
35
|
-
~attrs:[ attr_warning [%expr "-39"] ]
|
|
36
|
-
encoder_pat
|
|
37
|
-
(add_params encoder_param_names encoder);
|
|
38
|
-
]
|
|
39
|
-
in
|
|
40
|
-
|
|
41
|
-
let vbs =
|
|
42
|
-
match decoder with
|
|
43
|
-
| None -> vbs
|
|
44
|
-
| Some decoder ->
|
|
45
|
-
vbs
|
|
46
|
-
@ [
|
|
47
|
-
Vb.mk
|
|
48
|
-
~attrs:[ attr_warning [%expr "-4"]; attr_warning [%expr "-39"] ]
|
|
49
|
-
decoder_pat
|
|
50
|
-
(add_params decoder_param_names decoder);
|
|
51
|
-
]
|
|
52
|
-
in
|
|
53
|
-
|
|
54
|
-
vbs
|
|
55
|
-
|
|
56
|
-
let map_type_decl decl =
|
|
57
|
-
let {
|
|
58
|
-
ptype_attributes;
|
|
59
|
-
ptype_name = { txt = type_name };
|
|
60
|
-
ptype_manifest;
|
|
61
|
-
ptype_params;
|
|
62
|
-
ptype_loc;
|
|
63
|
-
ptype_kind;
|
|
64
|
-
} =
|
|
65
|
-
decl
|
|
66
|
-
in
|
|
67
|
-
|
|
68
|
-
let is_unboxed =
|
|
69
|
-
match Utils.get_attribute_by_name ptype_attributes "unboxed" with
|
|
70
|
-
| Ok (Some _) -> true
|
|
71
|
-
| _ -> false
|
|
72
|
-
in
|
|
73
|
-
|
|
74
|
-
match get_generator_settings_from_attributes ptype_attributes with
|
|
75
|
-
| Ok None -> []
|
|
76
|
-
| Ok (Some generator_settings) -> (
|
|
77
|
-
match (ptype_manifest, ptype_kind) with
|
|
78
|
-
| None, Ptype_abstract ->
|
|
79
|
-
fail ptype_loc "Can't generate codecs for unspecified type"
|
|
80
|
-
| Some { ptyp_desc = Ptyp_variant (row_fields, _, _) }, Ptype_abstract ->
|
|
81
|
-
generate_codec_decls type_name
|
|
82
|
-
(get_param_names ptype_params)
|
|
83
|
-
(Polyvariants.generate_codecs generator_settings row_fields
|
|
84
|
-
is_unboxed)
|
|
85
|
-
| Some manifest, _ ->
|
|
86
|
-
generate_codec_decls type_name
|
|
87
|
-
(get_param_names ptype_params)
|
|
88
|
-
(generate_codecs generator_settings manifest)
|
|
89
|
-
| None, Ptype_variant decls ->
|
|
90
|
-
generate_codec_decls type_name
|
|
91
|
-
(get_param_names ptype_params)
|
|
92
|
-
(Variants.generate_codecs generator_settings decls is_unboxed)
|
|
93
|
-
| None, Ptype_record decls ->
|
|
94
|
-
generate_codec_decls type_name
|
|
95
|
-
(get_param_names ptype_params)
|
|
96
|
-
(Records.generate_codecs generator_settings decls is_unboxed)
|
|
97
|
-
| _ -> fail ptype_loc "This type is not handled by spice")
|
|
98
|
-
| Error s -> fail ptype_loc s
|
|
99
|
-
|
|
100
|
-
let map_structure_item mapper ({ pstr_desc } as structure_item) =
|
|
101
|
-
match pstr_desc with
|
|
102
|
-
| Pstr_type (rec_flag, decls) -> (
|
|
103
|
-
let value_bindings = decls |> List.map map_type_decl |> List.concat in
|
|
104
|
-
[ mapper#structure_item structure_item ]
|
|
105
|
-
@
|
|
106
|
-
match List.length value_bindings > 0 with
|
|
107
|
-
| true -> [ Str.value rec_flag value_bindings ]
|
|
108
|
-
| false -> [])
|
|
109
|
-
| _ -> [ mapper#structure_item structure_item ]
|