Source file candidate.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
module Stable = struct
  open! Core.Core_stable

  module Case_sensitivity = struct
    module V1 = struct
      type t = Sexp_grammar.case_sensitivity =
        | Case_insensitive
        | Case_sensitive
        | Case_sensitive_except_first_character
      [@@deriving bin_io, compare, sexp]
    end

    module Model = V1
  end

  module Atom_to_add = struct
    module V1 = struct
      type t =
        { ignore_capitalization : bool
        ; signified : string
        }
      [@@deriving bin_io, compare, sexp]

      include Comparator.V1.Make (struct
          type nonrec t = t [@@deriving compare, sexp_of]
        end)
    end

    module V2 = struct
      type t =
        { atom_signified : string
        ; case_sensitivity : Case_sensitivity.V1.t
        ; documentation : string list
        }
      [@@deriving
        bin_io
        , compare
        , sexp
        , stable_record
            ~version:V1.t
            ~add:[ ignore_capitalization; signified ]
            ~remove:[ atom_signified; case_sensitivity; documentation ]]

      include Comparator.V1.Make (struct
          type nonrec t = t [@@deriving compare, sexp_of]
        end)

      let to_prev ({ atom_signified; case_sensitivity; _ } as v2) =
        to_V1_t
          v2
          ~ignore_capitalization:
            (match case_sensitivity with
             | Case_sensitive -> false
             | Case_sensitive_except_first_character -> true
             | Case_insensitive ->
               Base.Error.raise_s
                 [%sexp "Cannot convert fully case-insensitive grammar to older version."])
          ~signified:atom_signified
      ;;

      let from_prev ({ ignore_capitalization; signified } as v1 : V1.t) =
        of_V1_t
          v1
          ~atom_signified:signified
          ~case_sensitivity:
            (match ignore_capitalization with
             | true -> Case_sensitive_except_first_character
             | false -> Case_sensitive)
          ~documentation:[]
      ;;
    end

    module Model = V2
  end

  module V1 = struct
    type t =
      | Add_atom of Atom_to_add.V1.t
      | Enter_list
      | Enter_list_and_add_atom of Atom_to_add.V1.t
    [@@deriving bin_io, compare, sexp, stable_variant]

    let%expect_test _ =
      print_endline [%bin_digest: t];
      [%expect {| a6c0d6e720ab153a1148854a6a8372e9 |}]
    ;;

    include Comparator.V1.Make (struct
        type nonrec t = t [@@deriving compare, sexp_of]
      end)
  end

  module V2 = struct
    type t =
      | Add_atom of Atom_to_add.V2.t
      | Enter_list
      | Enter_list_and_add_atom of Atom_to_add.V2.t
    [@@deriving
      bin_io
      , compare
      , sexp
      , stable_variant ~version:V1.t ~modify:[ Add_atom; Enter_list_and_add_atom ]]

    let%expect_test _ =
      print_endline [%bin_digest: t];
      [%expect {| 61cd329fbe790fa35afb07dbc60eb43d |}]
    ;;

    include Comparator.V1.Make (struct
        type nonrec t = t [@@deriving compare, sexp_of]
      end)

    let to_prev v2 =
      to_V1_t
        v2
        ~modify_Add_atom:(fun x -> Add_atom (Atom_to_add.V2.to_prev x))
        ~modify_Enter_list_and_add_atom:(fun x ->
          Enter_list_and_add_atom (Atom_to_add.V2.to_prev x))
    ;;

    let from_prev v1 =
      of_V1_t
        v1
        ~modify_Add_atom:(fun x -> Add_atom (Atom_to_add.V2.from_prev x))
        ~modify_Enter_list_and_add_atom:(fun x ->
          Enter_list_and_add_atom (Atom_to_add.V2.from_prev x))
    ;;
  end

  module Model = V2
end

open! Core
open! Import

module Case_sensitivity = struct
  type t = Stable.Case_sensitivity.Model.t =
    | Case_insensitive
    | Case_sensitive
    | Case_sensitive_except_first_character
  [@@deriving compare, enumerate, sexp_of]
end

module Atom_to_add = struct
  type t = Stable.Atom_to_add.Model.t =
    { atom_signified : string
    ; case_sensitivity : Case_sensitivity.t
    ; documentation : string list
    }
  [@@deriving compare, fields ~getters, sexp_of]

  include (
    Stable.Atom_to_add.Model :
      Comparator.S
      with type t := t
      with type comparator_witness = Stable.Atom_to_add.Model.comparator_witness)

  let matches_atom_prefix { atom_signified; case_sensitivity; _ } = function
    | None -> true
    | Some atom_prefix ->
      let prefix =
        match Parsexp_prefix.Atom_prefix.get_signified atom_prefix with
        | Complete { prefix } | Incomplete { prefix_of_prefix = prefix } -> prefix
      in
      let normalize =
        match case_sensitivity with
        | Case_sensitive_except_first_character -> String.uncapitalize
        | Case_sensitive -> Fn.id
        | Case_insensitive -> String.uppercase
      in
      String.is_prefix (normalize atom_signified) ~prefix:(normalize prefix)
  ;;

  let signifier { atom_signified; case_sensitivity = _; _ } =
    Sexp.to_string [%sexp (atom_signified : string)]
  ;;
end

include Stable.Model
module Unstable = Stable.Model

let insert_to_left = function
  | Add_atom atom_to_add -> Atom_to_add.signifier atom_to_add
  | Enter_list -> "("
  | Enter_list_and_add_atom atom_to_add -> "(" ^ Atom_to_add.signifier atom_to_add ^ " "
;;

let insert_to_right = function
  | Add_atom _ -> ""
  | Enter_list | Enter_list_and_add_atom _ -> ")"
;;

let matches_atom_prefix t atom_prefix_option =
  match t with
  | Enter_list | Enter_list_and_add_atom _ -> Option.is_none atom_prefix_option
  | Add_atom atom_to_add -> Atom_to_add.matches_atom_prefix atom_to_add atom_prefix_option
;;

let documentation = function
  | Enter_list -> []
  | Enter_list_and_add_atom atom_to_add | Add_atom atom_to_add ->
    Atom_to_add.documentation atom_to_add
;;