Source file polymorphic_variant.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
open! Base
open! Import
open Common

module Constructor = struct
  type t =
    { name : Name.t
    ; loc : Location.t
    ; fields : Tuple.t
    }

  let of_rtag lloc possibly_empty cts =
    let { txt = name; loc } = lloc in
    let name = Name.of_string name in
    let fields =
      match possibly_empty, cts with
      | true, [] -> Tuple.empty
      | false, [ _ ] -> Tuple.singleton
      | false, _ | true, _ :: _ -> unsupported ~loc "intersection type"
    in
    { name; loc; fields }
  ;;

  let wildcard_pattern { name; loc; fields } =
    let fields = Tuple.Polymorphic_variant.wildcard_pattern fields ~loc in
    ppat_variant ~loc (Name.to_constructor_string name) fields
  ;;

  let to_str { name; loc; fields } ~wildcard =
    Tuple.Polymorphic_variant.to_str fields ~loc ~name ~wildcard
  ;;
end

module Inherit = struct
  type t =
    { type_constructor : longident_loc
    ; loc : Location.t
    }

  let of_core_type ct =
    let loc = ct.ptyp_loc in
    match ct.ptyp_desc with
    | Ptyp_constr (type_constructor, []) -> { type_constructor; loc }
    | _ -> unsupported ~loc "non-simple type constructor in polymorphic variant"
  ;;

  let wildcard_pattern { type_constructor; loc } = ppat_type ~loc type_constructor

  let to_construct_expr { type_constructor; loc } =
    [%expr fun ([%p ppat_type ~loc type_constructor] as bt) -> bt]
  ;;

  let to_match_expr { type_constructor; loc } ~wildcard =
    [%expr
      function
      | [%p ppat_type ~loc type_constructor] as a -> First a
      | [%p wildcard] as bt -> Second bt]
  ;;

  let to_str ({ type_constructor; loc } as t) ~wildcard =
    let name =
      Longident.flatten_exn type_constructor.txt
      |> String.concat ~sep:"_"
      |> Name.of_string
      |> Name.to_lowercase_string
    in
    Polymorphize.binding
      ~loc
      ~name
      ~expr:
        [%expr
          Accessor.variant
            ~match_:[%e to_match_expr t ~wildcard]
            ~construct:[%e to_construct_expr t]]
  ;;
end

module Row = struct
  type t =
    | Constructor of Constructor.t
    | Inherit of Inherit.t

  let of_row_field rf =
    match rf.prf_desc with
    | Rtag (lloc, possibly_empty, cts) ->
      Constructor (Constructor.of_rtag lloc possibly_empty cts)
    | Rinherit ct -> Inherit (Inherit.of_core_type ct)
  ;;

  let wildcard_pattern = function
    | Constructor constructor -> Constructor.wildcard_pattern constructor
    | Inherit inherit_ -> Inherit.wildcard_pattern inherit_
  ;;

  let to_str t ~wildcard =
    match t with
    | Constructor constructor -> Constructor.to_str constructor ~wildcard
    | Inherit inherit_ ->
      (match wildcard with
       | None ->
         Location.raise_errorf
           ~loc:inherit_.loc
           "Bug in ppx_accessor: unexpectedly lonely inherited polymorphic variant"
       | Some wildcard -> Inherit.to_str inherit_ ~wildcard)
  ;;
end

type t = Row.t list

let of_row_fields = List.map ~f:Row.of_row_field

let of_core_type_desc t ~loc =
  match t with
  | Ptyp_variant (rfs, Closed, None) -> of_row_fields rfs
  | Ptyp_variant _ -> unsupported ~loc "non-simple polymorphic variant"
  | _ -> unsupported ~loc "manifest type that is not a polymorphic variant"
;;

let of_core_type ct = of_core_type_desc ct.ptyp_desc ~loc:ct.ptyp_loc

let wildcard_patterns t ~loc =
  List.reduce (List.map t ~f:Row.wildcard_pattern) ~f:(ppat_or ~loc)
;;

let to_strs t ~loc =
  Common.map_with_context t ~f:(fun constructor ~context:other_constructors ->
    let wildcard = wildcard_patterns other_constructors ~loc in
    Row.to_str constructor ~wildcard)
;;