package ppx_sexp_conv

  1. Overview
  2. Docs

Source file attrs.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
open! Base
open! Ppxlib

let default =
  Attribute.declare "sexp.default"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (pstr_eval __ nil ^:: nil))
    (fun x -> x)

let drop_default =
  Attribute.declare "sexp.sexp_drop_default"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (alt_option (pstr_eval __ nil ^:: nil) nil))
    (fun x -> x)

let drop_default_equal =
  Attribute.declare "sexp.@sexp_drop_default.equal"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let drop_default_compare =
  Attribute.declare "sexp.@sexp_drop_default.compare"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let drop_default_sexp =
  Attribute.declare "sexp.@sexp_drop_default.sexp"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let drop_if =
  Attribute.declare "sexp.sexp_drop_if"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr (pstr_eval __ nil ^:: nil))
    (fun x -> x)

let opaque =
  Attribute.declare "sexp.opaque"
    Attribute.Context.core_type
    Ast_pattern.(pstr nil)
    ()

let omit_nil =
  Attribute.declare "sexp.omit_nil"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let option =
  Attribute.declare "sexp.option"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let list =
  Attribute.declare "sexp.list"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let array =
  Attribute.declare "sexp.array"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let bool =
  Attribute.declare "sexp.bool"
    Attribute.Context.label_declaration
    Ast_pattern.(pstr nil)
    ()

let list_variant =
  Attribute.declare "sexp.list"
    Attribute.Context.constructor_declaration
    Ast_pattern.(pstr nil)
    ()

let list_exception =
  Attribute.declare "sexp.list"
    Attribute.Context.type_exception
    Ast_pattern.(pstr nil)
    ()

let list_poly =
  Attribute.declare "sexp.list"
    Attribute.Context.rtag
    Ast_pattern.(pstr nil)
    ()

let allow_extra_fields_td =
  Attribute.declare "sexp.allow_extra_fields"
    Attribute.Context.type_declaration
    Ast_pattern.(pstr nil)
    ()

let allow_extra_fields_cd =
  Attribute.declare "sexp.allow_extra_fields"
    Attribute.Context.constructor_declaration
    Ast_pattern.(pstr nil)
    ()


let invalid_attribute ~loc attr description =
  Location.raise_errorf ~loc
    "ppx_sexp_conv: [@%s] is only allowed on type [%s]."
    (Attribute.name attr)
    description

let fail_if_allow_extra_field_cd ~loc x =
  if Option.is_some (Attribute.get allow_extra_fields_cd x)
  then
    Location.raise_errorf ~loc
      "ppx_sexp_conv: [@@allow_extra_fields] is only allowed on \
       inline records."

let fail_if_allow_extra_field_td ~loc x =
  if Option.is_some (Attribute.get allow_extra_fields_td x)
  then
    match x.ptype_kind with
    | Ptype_variant cds
      when List.exists cds
             ~f:(fun cd -> match cd.pcd_args with Pcstr_record _ -> true | _ -> false)
      ->
      Location.raise_errorf ~loc
        "ppx_sexp_conv: [@@@@allow_extra_fields] only works on records. \
         For inline records, do: type t = A of { a : int } [@@allow_extra_fields] | B \
         [@@@@deriving sexp]"
    | _ ->
      Location.raise_errorf ~loc
        "ppx_sexp_conv: [@@@@allow_extra_fields] is only allowed on \
         records."

module Record_field_handler = struct

  type common =
    [ `omit_nil
    | `sexp_array of core_type
    | `sexp_bool
    | `sexp_list of core_type
    | `sexp_option of core_type
    ]

  let get_attribute attr ld ~f =
    Option.map (Attribute.get attr ld) ~f:(fun x -> f x, Attribute.name attr)
  ;;

  let create ~loc getters ld =
    let common_getters =
      [ get_attribute omit_nil ~f:(fun () -> `omit_nil)
      ; (fun ld ->
           match ld.pld_type with
           | [%type: sexp_bool ] -> Some (`sexp_bool, "sexp_bool")
           | [%type: [%t? ty] sexp_option ] -> Some (`sexp_option ty, "sexp_option")
           | [%type: [%t? ty] sexp_list ] -> Some (`sexp_list ty, "sexp_list")
           | [%type: [%t? ty] sexp_array ] -> Some (`sexp_array ty, "sexp_array")
           | ty when Option.is_some (Attribute.get bool ld) ->
             (match ty with
              | [%type: bool] -> Some (`sexp_bool, "[@sexp.bool]")
              | _ -> invalid_attribute ~loc bool "bool")
           | ty when Option.is_some (Attribute.get option ld) ->
             (match ty with
              | [%type: [%t? ty] option] -> Some (`sexp_option ty, "[@sexp.option]")
              | _ -> invalid_attribute ~loc option "_ option")
           | ty when Option.is_some (Attribute.get list ld) ->
             (match ty with
              | [%type: [%t? ty] list] -> Some (`sexp_list ty, "[@sexp.list]")
              | _ -> invalid_attribute ~loc list "_ list")
           | ty when Option.is_some (Attribute.get array ld) ->
             (match ty with
              | [%type: [%t? ty] array] -> Some (`sexp_array ty, "[@sexp.array]")
              | _ -> invalid_attribute ~loc array "_ array")
           | _ -> None)
      ]
    in
    match List.filter_map (getters @ common_getters) ~f:(fun f -> f ld) with
    | [] -> None
    | [ (v, _) ] -> Some v
    | _ :: _ :: _ as attributes ->
      Location.raise_errorf ~loc "The following elements are mutually exclusive: %s"
        (String.concat ~sep:" " (List.map attributes ~f:snd))
  ;;

  module Of_sexp = struct
    type t =
      [ common
      | `default of expression
      ]

    let create ~loc ld =
      create ~loc [ get_attribute default ~f:(fun default -> `default default) ] ld
  end

  module Sexp_of = struct
    type t =
      [ common
      | `drop_default of [ `no_arg | `compare | `equal | `sexp | `func of expression ]
      | `drop_if of expression
      | `keep
      ]

    let create ~loc ld =
      create
        ~loc
        [ get_attribute drop_default ~f:(function
            | None -> `drop_default `no_arg
            | Some e -> `drop_default (`func e))
        ; get_attribute drop_default_equal ~f:(fun () -> `drop_default `equal)
        ; get_attribute drop_default_compare
            ~f:(fun () -> `drop_default `compare)
        ; get_attribute drop_default_sexp
            ~f:(fun () -> `drop_default `sexp)
        ; get_attribute drop_if ~f:(fun x -> `drop_if x)
        ]
        ld
    |> Option.value ~default:`keep

  end
end
OCaml

Innovation. Community. Security.