package graphql_parser

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file graphql_parser.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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
open Sexplib.Std
open Angstrom

(* Language type definitions *)

type primitive_value = [
  | `Null
  | `Int of int
  | `Float of float
  | `String of string
  | `Bool of bool
  | `Enum of string
] [@@deriving sexp]

type const_value = [
  | primitive_value
  | `List of const_value list
  | `Assoc of (string * const_value) list
] [@@deriving sexp]

type value = [
  | primitive_value
  | `Variable of string
  | `List of value list
  | `Assoc of (string * value) list
] [@@deriving sexp]

type directive =
  {
    name : string;
    arguments : (string * value) list;
  }
  [@@deriving sexp]

type fragment_spread =
  {
    name : string;
    directives : directive list;
  }
  [@@deriving sexp]

type selection =
  | Field          of field
  | FragmentSpread of fragment_spread
  | InlineFragment of inline_fragment
  [@@deriving sexp]

and field =
  {
    alias : string option;
    name : string;
    arguments : (string * value) list;
    directives : directive list;
    selection_set : selection list;
  }
  [@@deriving sexp]

and inline_fragment =
  {
    type_condition : string option;
    directives : directive list;
    selection_set : selection list;
  }
  [@@deriving sexp]

type fragment =
  {
    name : string;
    type_condition : string;
    directives : directive list;
    selection_set : selection list;
  }
  [@@deriving sexp]

type typ =
  | NamedType   of string
  | ListType    of typ
  | NonNullType of typ
  [@@deriving sexp]

type variable_definition =
  {
    name : string;
    typ : typ;
    default_value : const_value option;
  }
  [@@deriving sexp]

type optype =
  | Query
  | Mutation
  | Subscription
  [@@deriving sexp]

type operation =
  {
    optype : optype;
    name   : string option;
    variable_definitions : variable_definition list;
    directives : directive list;
    selection_set : selection list;
  }
  [@@deriving sexp]

type definition =
  | Operation of operation
  | Fragment of fragment
  [@@deriving sexp]

type document =
  definition list
  [@@deriving sexp]

(* Parser combinators *)

let optional p = option None (lift (fun x -> Some x) p)
let optional_list p = option [] p
let lift5 f a b c d e = lift4 f a b c d <*> e

let ignored = scan_state `Whitespace (fun state c ->
  match state with
  | `Comment ->
      if c = '\n' then Some `Whitespace else Some `Comment
  | `Whitespace ->
      match c with
      | ' ' | ',' | '\t' | '\n' -> Some `Whitespace
      | '#' -> Some `Comment
      | _ -> None
) >>| fun _ -> ()

let ( *~>) a b = (a *> ignored) *> b
let ( <~*) a b = (a <* ignored) <* b

let lift2' f a b = lift2 f (a <* ignored) b
let lift3' f a b c = lift3 f (a <* ignored) (b <* ignored) c
let lift4' f a b c d = lift4 f (a <* ignored) (b <* ignored) (c <* ignored) d
let lift5' f a b c d e = lift5 f (a <* ignored) (b <* ignored) (c <* ignored) (d <* ignored) e

let ellipsis = string "..."
let lparen = char '('
let rparen = char ')'
let lbrace = char '{'
let rbrace = char '}'
let lbrack = char '['
let rbrack = char ']'
let bang   = char '!'
let colon  = char ':'
let equal  = char '='
let dollar = char '$'
let at     = char '@'
let dot    = char '.'
let dash   = char '-'
let quote  = char '"'

let is_name_char =
  function | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '_'  -> true | _ -> false
let name = take_while1 is_name_char

let is_number_char =
  function | '0' .. '9' | 'e' | 'E' | '.' | '-' | '+' -> true | _ -> false
let number_chars = take_while1 is_number_char

let string_buf = Buffer.create 8

let string_chars = scan_state `Unescaped (fun state c ->
    match state with
    | `Escaped ->
        Buffer.add_char string_buf c;
        Some `Unescaped
    | `Unescaped ->
        match c with
        | '\\' -> Some `Escaped
        | '"' -> None
        | _ ->
            Buffer.add_char string_buf c;
            Some `Unescaped
  ) >>= fun _ ->
  let s = Buffer.contents string_buf in
  Buffer.clear string_buf;
  return s

let null = string "null" *> return `Null

let variable = lift (fun n -> `Variable n) (dollar *> name)

let string_value = lift (fun s -> `String s) (quote *> string_chars <* quote)

let boolean_value = string "true"  *> return (`Bool true) <|>
                    string "false" *> return (`Bool false)

let number_value = number_chars >>= fun n ->
  try
    return (`Int (int_of_string n))
  with Failure _ ->
    try
      return (`Float (float_of_string n))
    with Failure _ ->
      fail (Format.sprintf "Invalid number value: %s" n)

let enum_value = name >>= function
  | "true"
  | "false"
  | "null" as n -> fail (Format.sprintf "Invalid enum value: %s" n)
  | n -> return (`Enum n)

let value_parser value_types = fix (fun value' ->
  let list_value = lbrack *~> rbrack *> return (`List []) <|>
                   lift (fun l -> `List l) (lbrack *~> sep_by1 ignored value' <~* rbrack)
  and object_field = lift2' (fun name value -> name, value) (name <~* colon) value'
  in
  let object_value = lbrace *~> rbrace *> return (`Assoc []) <|>
                     lift (fun p -> `Assoc p) (lbrace *~> sep_by1 ignored object_field <~* rbrace)
  in
    List.fold_left (<|>) (list_value <|> object_value) value_types
  )

let value : value Angstrom.t = value_parser [
  null;
  number_value;
  string_value;
  boolean_value;
  enum_value;
  variable
]

let const_value : const_value Angstrom.t = value_parser [
  null;
  number_value;
  string_value;
  boolean_value;
  enum_value
]

let argument = lift2' (fun name value -> name, value)
                 (name <~* colon) value

let arguments = lparen *~> sep_by ignored argument <~* rparen

let directive = lift2' (fun name arguments -> {name; arguments})
                  (at *> name) (optional_list arguments)

let directives = sep_by ignored directive

let typ = fix (fun typ' ->
  let named_type = lift (fun n -> NamedType n) name
  and list_type = lift (fun t -> ListType t) (lbrack *~> typ' <~* rbrack)
  and non_null = option false (bang *> return true)
  in
    lift2' (fun t non_null -> if non_null then NonNullType t else t)
      (named_type <|> list_type) non_null
)

let variable_definition = lift3' (fun name typ default_value -> {
  name;
  typ;
  default_value;
}) (dollar *~> name <~* colon) typ (optional (equal *~> const_value))

let variable_definitions = lparen *~> many variable_definition <~* rparen

let alias = name <~* colon

let fragment_name = name >>= function
  | "on" -> fail "Invalid fragment name `on`"
  | n    -> return n

let type_condition = string "on" *~> name

let selection_set = fix (fun selection_set' ->
  let field =
    lift5' (fun alias name arguments directives selection_set -> Field {
      alias;
      name;
      arguments;
      directives;
      selection_set;
    }) (optional alias) name (optional_list arguments) (optional_list directives) (optional_list selection_set')
  and fragment_spread =
    lift2' (fun name directives -> FragmentSpread {name; directives})
    (ellipsis *~> fragment_name) (optional_list directives)
  and inline_fragment =
    lift3' (fun type_condition directives selection_set -> InlineFragment {
      type_condition;
      directives;
      selection_set;
    })
    (ellipsis *~> optional type_condition) (optional_list directives) selection_set'
  in let selection =
    field <|>
    fragment_spread <|>
    inline_fragment
  in lbrace *~> sep_by1 ignored selection <~* rbrace
)

let optype = (string "query"        *> return Query) <|>
             (string "mutation"     *> return Mutation) <|>
             (string "subscription" *> return Subscription)

let operation_definition =
  lift5' (fun optype name variable_definitions directives selection_set -> Operation {
    optype;
    name;
    variable_definitions;
    directives;
    selection_set;
  }) (option Query optype) (optional name) (optional_list variable_definitions) (optional_list directives) selection_set

let fragment_definition =
  lift4' (fun name type_condition directives selection_set -> Fragment {
    name;
    type_condition;
    directives;
    selection_set;
  })
  (string "fragment" *~> fragment_name) type_condition (optional_list directives) selection_set

let definition = operation_definition <|> fragment_definition

let document = many1 (ignored *> definition)

let parse query = Angstrom.parse_string document query