1 package parser
2
3 import (
4
5 . "github.com/vektah/gqlparser/v2/ast"
6 "github.com/vektah/gqlparser/v2/lexer"
7 )
8
9 func ParseSchemas(inputs ...*Source) (*SchemaDocument, error) {
10 sd := &SchemaDocument{}
11 for _, input := range inputs {
12 inputAst, err := ParseSchema(input)
13 if err != nil {
14 return nil, err
15 }
16 sd.Merge(inputAst)
17 }
18 return sd, nil
19 }
20
21 func ParseSchema(source *Source) (*SchemaDocument, error) {
22 p := parser{
23 lexer: lexer.New(source),
24 }
25 sd, err := p.parseSchemaDocument(), p.err
26 if err != nil {
27 return nil, err
28 }
29
30 for _, def := range sd.Definitions {
31 def.BuiltIn = source.BuiltIn
32 }
33 for _, def := range sd.Extensions {
34 def.BuiltIn = source.BuiltIn
35 }
36
37 return sd, nil
38 }
39
40 func (p *parser) parseSchemaDocument() *SchemaDocument {
41 var doc SchemaDocument
42 doc.Position = p.peekPos()
43 for p.peek().Kind != lexer.EOF {
44 if p.err != nil {
45 return nil
46 }
47
48 var description descriptionWithComment
49 if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String {
50 description = p.parseDescription()
51 }
52
53 if p.peek().Kind != lexer.Name {
54 p.unexpectedError()
55 break
56 }
57
58 switch p.peek().Value {
59 case "scalar", "type", "interface", "union", "enum", "input":
60 doc.Definitions = append(doc.Definitions, p.parseTypeSystemDefinition(description))
61 case "schema":
62 doc.Schema = append(doc.Schema, p.parseSchemaDefinition(description))
63 case "directive":
64 doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description))
65 case "extend":
66 if description.text != "" {
67 p.unexpectedToken(p.prev)
68 }
69 p.parseTypeSystemExtension(&doc)
70 default:
71 p.unexpectedError()
72 return nil
73 }
74 }
75
76
77 doc.Comment = p.comment
78
79 return &doc
80 }
81
82 func (p *parser) parseDescription() descriptionWithComment {
83 token := p.peek()
84
85 var desc descriptionWithComment
86 if token.Kind != lexer.BlockString && token.Kind != lexer.String {
87 return desc
88 }
89
90 desc.comment = p.comment
91 desc.text = p.next().Value
92 return desc
93 }
94
95 func (p *parser) parseTypeSystemDefinition(description descriptionWithComment) *Definition {
96 tok := p.peek()
97 if tok.Kind != lexer.Name {
98 p.unexpectedError()
99 return nil
100 }
101
102 switch tok.Value {
103 case "scalar":
104 return p.parseScalarTypeDefinition(description)
105 case "type":
106 return p.parseObjectTypeDefinition(description)
107 case "interface":
108 return p.parseInterfaceTypeDefinition(description)
109 case "union":
110 return p.parseUnionTypeDefinition(description)
111 case "enum":
112 return p.parseEnumTypeDefinition(description)
113 case "input":
114 return p.parseInputObjectTypeDefinition(description)
115 default:
116 p.unexpectedError()
117 return nil
118 }
119 }
120
121 func (p *parser) parseSchemaDefinition(description descriptionWithComment) *SchemaDefinition {
122 _, comment := p.expectKeyword("schema")
123
124 def := SchemaDefinition{}
125 def.Position = p.peekPos()
126 def.BeforeDescriptionComment = description.comment
127 def.Description = description.text
128 def.AfterDescriptionComment = comment
129 def.Directives = p.parseDirectives(true)
130
131 def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() {
132 def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
133 })
134 return &def
135 }
136
137 func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition {
138 var op OperationTypeDefinition
139 op.Position = p.peekPos()
140 op.Comment = p.comment
141 op.Operation = p.parseOperationType()
142 p.expect(lexer.Colon)
143 op.Type = p.parseName()
144 return &op
145 }
146
147 func (p *parser) parseScalarTypeDefinition(description descriptionWithComment) *Definition {
148 _, comment := p.expectKeyword("scalar")
149
150 var def Definition
151 def.Position = p.peekPos()
152 def.BeforeDescriptionComment = description.comment
153 def.Description = description.text
154 def.AfterDescriptionComment = comment
155 def.Kind = Scalar
156 def.Name = p.parseName()
157 def.Directives = p.parseDirectives(true)
158 return &def
159 }
160
161 func (p *parser) parseObjectTypeDefinition(description descriptionWithComment) *Definition {
162 _, comment := p.expectKeyword("type")
163
164 var def Definition
165 def.Position = p.peekPos()
166 def.Kind = Object
167 def.BeforeDescriptionComment = description.comment
168 def.Description = description.text
169 def.AfterDescriptionComment = comment
170 def.Name = p.parseName()
171 def.Interfaces = p.parseImplementsInterfaces()
172 def.Directives = p.parseDirectives(true)
173 def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
174 return &def
175 }
176
177 func (p *parser) parseImplementsInterfaces() []string {
178 var types []string
179 if p.peek().Value == "implements" {
180 p.next()
181
182 p.skip(lexer.Amp)
183
184 types = append(types, p.parseName())
185 for p.skip(lexer.Amp) && p.err == nil {
186 types = append(types, p.parseName())
187 }
188 }
189 return types
190 }
191
192 func (p *parser) parseFieldsDefinition() (FieldList, *CommentGroup) {
193 var defs FieldList
194 comment := p.some(lexer.BraceL, lexer.BraceR, func() {
195 defs = append(defs, p.parseFieldDefinition())
196 })
197 return defs, comment
198 }
199
200 func (p *parser) parseFieldDefinition() *FieldDefinition {
201 var def FieldDefinition
202 def.Position = p.peekPos()
203
204 desc := p.parseDescription()
205 if desc.text != "" {
206 def.BeforeDescriptionComment = desc.comment
207 def.Description = desc.text
208 }
209
210 p.peek()
211 def.AfterDescriptionComment = p.comment
212 def.Name = p.parseName()
213 def.Arguments = p.parseArgumentDefs()
214 p.expect(lexer.Colon)
215 def.Type = p.parseTypeReference()
216 def.Directives = p.parseDirectives(true)
217
218 return &def
219 }
220
221 func (p *parser) parseArgumentDefs() ArgumentDefinitionList {
222 var args ArgumentDefinitionList
223 p.some(lexer.ParenL, lexer.ParenR, func() {
224 args = append(args, p.parseArgumentDef())
225 })
226 return args
227 }
228
229 func (p *parser) parseArgumentDef() *ArgumentDefinition {
230 var def ArgumentDefinition
231 def.Position = p.peekPos()
232
233 desc := p.parseDescription()
234 if desc.text != "" {
235 def.BeforeDescriptionComment = desc.comment
236 def.Description = desc.text
237 }
238
239 p.peek()
240 def.AfterDescriptionComment = p.comment
241 def.Name = p.parseName()
242 p.expect(lexer.Colon)
243 def.Type = p.parseTypeReference()
244 if p.skip(lexer.Equals) {
245 def.DefaultValue = p.parseValueLiteral(true)
246 }
247 def.Directives = p.parseDirectives(true)
248 return &def
249 }
250
251 func (p *parser) parseInputValueDef() *FieldDefinition {
252 var def FieldDefinition
253 def.Position = p.peekPos()
254
255 desc := p.parseDescription()
256 if desc.text != "" {
257 def.BeforeDescriptionComment = desc.comment
258 def.Description = desc.text
259 }
260
261 p.peek()
262 def.AfterDescriptionComment = p.comment
263 def.Name = p.parseName()
264 p.expect(lexer.Colon)
265 def.Type = p.parseTypeReference()
266 if p.skip(lexer.Equals) {
267 def.DefaultValue = p.parseValueLiteral(true)
268 }
269 def.Directives = p.parseDirectives(true)
270 return &def
271 }
272
273 func (p *parser) parseInterfaceTypeDefinition(description descriptionWithComment) *Definition {
274 _, comment := p.expectKeyword("interface")
275
276 var def Definition
277 def.Position = p.peekPos()
278 def.Kind = Interface
279 def.BeforeDescriptionComment = description.comment
280 def.Description = description.text
281 def.AfterDescriptionComment = comment
282 def.Name = p.parseName()
283 def.Interfaces = p.parseImplementsInterfaces()
284 def.Directives = p.parseDirectives(true)
285 def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
286 return &def
287 }
288
289 func (p *parser) parseUnionTypeDefinition(description descriptionWithComment) *Definition {
290 _, comment := p.expectKeyword("union")
291
292 var def Definition
293 def.Position = p.peekPos()
294 def.Kind = Union
295 def.BeforeDescriptionComment = description.comment
296 def.Description = description.text
297 def.AfterDescriptionComment = comment
298 def.Name = p.parseName()
299 def.Directives = p.parseDirectives(true)
300 def.Types = p.parseUnionMemberTypes()
301 return &def
302 }
303
304 func (p *parser) parseUnionMemberTypes() []string {
305 var types []string
306 if p.skip(lexer.Equals) {
307
308 p.skip(lexer.Pipe)
309
310 types = append(types, p.parseName())
311 for p.skip(lexer.Pipe) && p.err == nil {
312 types = append(types, p.parseName())
313 }
314 }
315 return types
316 }
317
318 func (p *parser) parseEnumTypeDefinition(description descriptionWithComment) *Definition {
319 _, comment := p.expectKeyword("enum")
320
321 var def Definition
322 def.Position = p.peekPos()
323 def.Kind = Enum
324 def.BeforeDescriptionComment = description.comment
325 def.Description = description.text
326 def.AfterDescriptionComment = comment
327 def.Name = p.parseName()
328 def.Directives = p.parseDirectives(true)
329 def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition()
330 return &def
331 }
332
333 func (p *parser) parseEnumValuesDefinition() (EnumValueList, *CommentGroup) {
334 var values EnumValueList
335 comment := p.some(lexer.BraceL, lexer.BraceR, func() {
336 values = append(values, p.parseEnumValueDefinition())
337 })
338 return values, comment
339 }
340
341 func (p *parser) parseEnumValueDefinition() *EnumValueDefinition {
342 var def EnumValueDefinition
343 def.Position = p.peekPos()
344 desc := p.parseDescription()
345 if desc.text != "" {
346 def.BeforeDescriptionComment = desc.comment
347 def.Description = desc.text
348 }
349
350 p.peek()
351 def.AfterDescriptionComment = p.comment
352
353 def.Name = p.parseName()
354 def.Directives = p.parseDirectives(true)
355
356 return &def
357 }
358
359 func (p *parser) parseInputObjectTypeDefinition(description descriptionWithComment) *Definition {
360 _, comment := p.expectKeyword("input")
361
362 var def Definition
363 def.Position = p.peekPos()
364 def.Kind = InputObject
365 def.BeforeDescriptionComment = description.comment
366 def.Description = description.text
367 def.AfterDescriptionComment = comment
368 def.Name = p.parseName()
369 def.Directives = p.parseDirectives(true)
370 def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition()
371 return &def
372 }
373
374 func (p *parser) parseInputFieldsDefinition() (FieldList, *CommentGroup) {
375 var values FieldList
376 comment := p.some(lexer.BraceL, lexer.BraceR, func() {
377 values = append(values, p.parseInputValueDef())
378 })
379 return values, comment
380 }
381
382 func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) {
383 _, comment := p.expectKeyword("extend")
384
385 switch p.peek().Value {
386 case "schema":
387 doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension(comment))
388 case "scalar":
389 doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension(comment))
390 case "type":
391 doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension(comment))
392 case "interface":
393 doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension(comment))
394 case "union":
395 doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension(comment))
396 case "enum":
397 doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension(comment))
398 case "input":
399 doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension(comment))
400 default:
401 p.unexpectedError()
402 }
403 }
404
405 func (p *parser) parseSchemaExtension(comment *CommentGroup) *SchemaDefinition {
406 p.expectKeyword("schema")
407
408 var def SchemaDefinition
409 def.Position = p.peekPos()
410 def.AfterDescriptionComment = comment
411 def.Directives = p.parseDirectives(true)
412 def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() {
413 def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
414 })
415 if len(def.Directives) == 0 && len(def.OperationTypes) == 0 {
416 p.unexpectedError()
417 }
418 return &def
419 }
420
421 func (p *parser) parseScalarTypeExtension(comment *CommentGroup) *Definition {
422 p.expectKeyword("scalar")
423
424 var def Definition
425 def.Position = p.peekPos()
426 def.AfterDescriptionComment = comment
427 def.Kind = Scalar
428 def.Name = p.parseName()
429 def.Directives = p.parseDirectives(true)
430 if len(def.Directives) == 0 {
431 p.unexpectedError()
432 }
433 return &def
434 }
435
436 func (p *parser) parseObjectTypeExtension(comment *CommentGroup) *Definition {
437 p.expectKeyword("type")
438
439 var def Definition
440 def.Position = p.peekPos()
441 def.AfterDescriptionComment = comment
442 def.Kind = Object
443 def.Name = p.parseName()
444 def.Interfaces = p.parseImplementsInterfaces()
445 def.Directives = p.parseDirectives(true)
446 def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
447 if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 {
448 p.unexpectedError()
449 }
450 return &def
451 }
452
453 func (p *parser) parseInterfaceTypeExtension(comment *CommentGroup) *Definition {
454 p.expectKeyword("interface")
455
456 var def Definition
457 def.Position = p.peekPos()
458 def.AfterDescriptionComment = comment
459 def.Kind = Interface
460 def.Name = p.parseName()
461 def.Directives = p.parseDirectives(true)
462 def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
463 if len(def.Directives) == 0 && len(def.Fields) == 0 {
464 p.unexpectedError()
465 }
466 return &def
467 }
468
469 func (p *parser) parseUnionTypeExtension(comment *CommentGroup) *Definition {
470 p.expectKeyword("union")
471
472 var def Definition
473 def.Position = p.peekPos()
474 def.AfterDescriptionComment = comment
475 def.Kind = Union
476 def.Name = p.parseName()
477 def.Directives = p.parseDirectives(true)
478 def.Types = p.parseUnionMemberTypes()
479
480 if len(def.Directives) == 0 && len(def.Types) == 0 {
481 p.unexpectedError()
482 }
483 return &def
484 }
485
486 func (p *parser) parseEnumTypeExtension(comment *CommentGroup) *Definition {
487 p.expectKeyword("enum")
488
489 var def Definition
490 def.Position = p.peekPos()
491 def.AfterDescriptionComment = comment
492 def.Kind = Enum
493 def.Name = p.parseName()
494 def.Directives = p.parseDirectives(true)
495 def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition()
496 if len(def.Directives) == 0 && len(def.EnumValues) == 0 {
497 p.unexpectedError()
498 }
499 return &def
500 }
501
502 func (p *parser) parseInputObjectTypeExtension(comment *CommentGroup) *Definition {
503 p.expectKeyword("input")
504
505 var def Definition
506 def.Position = p.peekPos()
507 def.AfterDescriptionComment = comment
508 def.Kind = InputObject
509 def.Name = p.parseName()
510 def.Directives = p.parseDirectives(false)
511 def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition()
512 if len(def.Directives) == 0 && len(def.Fields) == 0 {
513 p.unexpectedError()
514 }
515 return &def
516 }
517
518 func (p *parser) parseDirectiveDefinition(description descriptionWithComment) *DirectiveDefinition {
519 _, comment := p.expectKeyword("directive")
520 p.expect(lexer.At)
521
522 var def DirectiveDefinition
523 def.Position = p.peekPos()
524 def.BeforeDescriptionComment = description.comment
525 def.Description = description.text
526 def.AfterDescriptionComment = comment
527 def.Name = p.parseName()
528 def.Arguments = p.parseArgumentDefs()
529
530 if peek := p.peek(); peek.Kind == lexer.Name && peek.Value == "repeatable" {
531 def.IsRepeatable = true
532 p.skip(lexer.Name)
533 }
534
535 p.expectKeyword("on")
536 def.Locations = p.parseDirectiveLocations()
537 return &def
538 }
539
540 func (p *parser) parseDirectiveLocations() []DirectiveLocation {
541 p.skip(lexer.Pipe)
542
543 locations := []DirectiveLocation{p.parseDirectiveLocation()}
544
545 for p.skip(lexer.Pipe) && p.err == nil {
546 locations = append(locations, p.parseDirectiveLocation())
547 }
548
549 return locations
550 }
551
552 func (p *parser) parseDirectiveLocation() DirectiveLocation {
553 name, _ := p.expect(lexer.Name)
554
555 switch name.Value {
556 case `QUERY`:
557 return LocationQuery
558 case `MUTATION`:
559 return LocationMutation
560 case `SUBSCRIPTION`:
561 return LocationSubscription
562 case `FIELD`:
563 return LocationField
564 case `FRAGMENT_DEFINITION`:
565 return LocationFragmentDefinition
566 case `FRAGMENT_SPREAD`:
567 return LocationFragmentSpread
568 case `INLINE_FRAGMENT`:
569 return LocationInlineFragment
570 case `VARIABLE_DEFINITION`:
571 return LocationVariableDefinition
572 case `SCHEMA`:
573 return LocationSchema
574 case `SCALAR`:
575 return LocationScalar
576 case `OBJECT`:
577 return LocationObject
578 case `FIELD_DEFINITION`:
579 return LocationFieldDefinition
580 case `ARGUMENT_DEFINITION`:
581 return LocationArgumentDefinition
582 case `INTERFACE`:
583 return LocationInterface
584 case `UNION`:
585 return LocationUnion
586 case `ENUM`:
587 return LocationEnum
588 case `ENUM_VALUE`:
589 return LocationEnumValue
590 case `INPUT_OBJECT`:
591 return LocationInputObject
592 case `INPUT_FIELD_DEFINITION`:
593 return LocationInputFieldDefinition
594 }
595
596 p.unexpectedToken(name)
597 return ""
598 }
599
600 type descriptionWithComment struct {
601 text string
602 comment *CommentGroup
603 }
604
View as plain text