@@ -9,17 +9,26 @@ class RecursiveDescentParser
9
9
include GraphQL ::Language ::Nodes
10
10
include EmptyObjects
11
11
12
- def self . parse ( graphql_str )
13
- self . new ( graphql_str ) . parse
12
+ def self . parse ( graphql_str , filename : nil , trace : Tracing :: NullTrace )
13
+ self . new ( graphql_str , filename : filename , trace : trace ) . parse
14
14
end
15
15
16
- def initialize ( graphql_str )
16
+ def initialize ( graphql_str , filename : nil , trace : Tracing ::NullTrace )
17
+ if graphql_str . nil?
18
+ raise GraphQL ::ParseError . new ( "No query string was present" , nil , nil , nil )
19
+ end
17
20
@lexer = Lexer . new ( graphql_str )
18
21
@graphql_str = graphql_str
22
+ @filename = filename
23
+ @trace = trace
19
24
end
20
25
21
26
def parse
22
- @document ||= document
27
+ @document ||= begin
28
+ @trace . parse ( query_string : @graphql_str ) do
29
+ document
30
+ end
31
+ end
23
32
end
24
33
25
34
private
@@ -48,8 +57,9 @@ def definition
48
57
when :FRAGMENT
49
58
loc = pos
50
59
expect_token :FRAGMENT
51
- expect_token ( :IDENTIFIER ) if at? ( :ON )
52
- f_name = parse_name
60
+ f_name = if !at? ( :ON )
61
+ parse_name
62
+ end
53
63
expect_token :ON
54
64
f_type = parse_type_name
55
65
directives = parse_directives
@@ -82,7 +92,8 @@ def definition
82
92
expect_token ( :COLON )
83
93
var_type = self . type
84
94
default_value = if at? ( :EQUALS )
85
- self . default_value
95
+ advance_token
96
+ value
86
97
end
87
98
88
99
defs << Nodes ::VariableDefinition . new ( pos : loc , name : var_name , type : var_type , default_value : default_value )
@@ -151,7 +162,7 @@ def definition
151
162
input_fields_definition = parse_input_object_field_definitions
152
163
InputObjectTypeExtension . new ( pos : loc , name : name , directives : directives , fields : input_fields_definition )
153
164
else
154
- expect_token :SOME_TYPE_EXTENSION
165
+ expect_one_of ( [ :SCALAR , :TYPE , :ENUM , :INPUT , :UNION , :INTERFACE ] )
155
166
end
156
167
else
157
168
desc = at? ( :STRING ) ? string_value : nil
@@ -177,7 +188,7 @@ def definition
177
188
expect_token ( :COLON )
178
189
subscription = parse_name
179
190
else
180
- expect_token ( :SOME_ROOT_TYPE_NAME )
191
+ expect_one_of ( [ :QUERY , :MUTATION , :SUBSCRIPTION ] )
181
192
end
182
193
end
183
194
expect_token :RCURLY
@@ -188,13 +199,18 @@ def definition
188
199
expect_token :DIR_SIGN
189
200
name = parse_name
190
201
arguments_definition = parse_argument_definitions
202
+ repeatable = if at? ( :REPEATABLE )
203
+ advance_token
204
+ true
205
+ else
206
+ fasle
207
+ end
191
208
expect_token :ON
192
209
directive_locations = [ DirectiveLocation . new ( pos : pos , name : parse_name ) ]
193
210
while at? ( :PIPE )
194
211
advance_token
195
212
directive_locations << DirectiveLocation . new ( pos : pos , name : parse_name )
196
213
end
197
- repeatable = false # TODO parse this
198
214
DirectiveDefinition . new ( pos : loc , description : desc , name : name , arguments : arguments_definition , locations : directive_locations , repeatable : repeatable )
199
215
when :TYPE
200
216
loc = pos
@@ -241,7 +257,7 @@ def definition
241
257
input_fields_definition = parse_input_object_field_definitions
242
258
InputObjectTypeDefinition . new ( pos : loc , description : desc , name : name , directives : directives , fields : input_fields_definition )
243
259
else
244
- expect_token ( :SOME_ROOT_DEFINITION )
260
+ expect_one_of ( [ :SCALAR , :TYPE , :ENUM , :INPUT , :UNION , :INTERFACE ] )
245
261
end
246
262
end
247
263
end
@@ -294,8 +310,8 @@ def parse_union_members
294
310
295
311
def parse_implements
296
312
if at? ( :IMPLEMENTS )
297
- expect_token :IMPLEMENTS
298
- list = [ parse_type_name ]
313
+ advance_token
314
+ list = [ ]
299
315
while true
300
316
advance_token if at? ( :AMP )
301
317
break unless at? ( :IDENTIFIER )
@@ -327,7 +343,7 @@ def parse_field_definitions
327
343
328
344
def parse_argument_definitions
329
345
if at? ( :LPAREN )
330
- expect_token :LPAREN
346
+ advance_token
331
347
list = [ ]
332
348
while !at? ( :RPAREN )
333
349
list << parse_input_value_definition
@@ -346,7 +362,7 @@ def parse_input_value_definition
346
362
expect_token :COLON
347
363
type = self . type
348
364
default_value = if at? ( :EQUALS )
349
- expect_token ( :EQUALS )
365
+ advance_token
350
366
value
351
367
else
352
368
nil
@@ -411,17 +427,15 @@ def selection_set
411
427
directives = parse_directives
412
428
413
429
Nodes ::InlineFragment . new ( pos : loc , type : if_type , directives : directives , selections : selection_set )
414
- when :IDENTIFIER
430
+ else
415
431
loc = pos
416
- name = parse_name
432
+ name = parse_name_without_on
417
433
directives = parse_directives
418
434
419
435
# Can this ever happen?
420
436
# expect_token(:IDENTIFIER) if at?(:ON)
421
437
422
438
FragmentSpread . new ( pos : loc , name : name , directives : directives )
423
- else
424
- expect_token ( :FRAGMENT_SPREAD )
425
439
end
426
440
else
427
441
loc = pos
@@ -431,7 +445,7 @@ def selection_set
431
445
432
446
if at? ( :COLON )
433
447
advance_token
434
- field_alias = parse_name
448
+ field_alias = name
435
449
name = parse_name
436
450
end
437
451
@@ -450,17 +464,76 @@ def parse_name
450
464
case token_name
451
465
when :IDENTIFIER
452
466
expect_token_value ( :IDENTIFIER )
467
+ when :SCHEMA
468
+ advance_token
469
+ "schema"
470
+ when :SCALAR
471
+ advance_token
472
+ "scalar"
473
+ when :IMPLEMENTS
474
+ advance_token
475
+ "implements"
476
+ when :INTERFACE
477
+ advance_token
478
+ "interface"
479
+ when :UNION
480
+ advance_token
481
+ "union"
482
+ when :ENUM
483
+ advance_token
484
+ "enum"
485
+ when :INPUT
486
+ advance_token
487
+ "input"
488
+ when :DIRECTIVE
489
+ advance_token
490
+ "directive"
453
491
when :TYPE
454
492
advance_token
455
493
"type"
456
494
when :QUERY
457
495
advance_token
458
496
"query"
459
- when :INPUT
497
+ when :MUTATION
460
498
advance_token
461
- "input"
499
+ "mutation"
500
+ when :SUBSCRIPTION
501
+ advance_token
502
+ "subscription"
503
+ when :TRUE
504
+ advance_token
505
+ "true"
506
+ when :FALSE
507
+ advance_token
508
+ "false"
509
+ when :FRAGMENT
510
+ advance_token
511
+ "fragment"
512
+ when :REPEATABLE
513
+ advance_token
514
+ "repeatable"
515
+ when :NULL
516
+ advance_token
517
+ "null"
518
+ else
519
+ expect_token ( :IDENTIFIER )
520
+ end
521
+ end
522
+
523
+ def parse_name_without_on
524
+ if at? ( :ON )
525
+ expect_token ( :IDENTIFIER )
462
526
else
527
+ parse_name
528
+ end
529
+ end
530
+
531
+ # Any identifier, but not true, false, or null
532
+ def parse_enum_name
533
+ if at? ( :TRUE ) || at? ( :FALSE ) || at? ( :NULL )
463
534
expect_token ( :IDENTIFIER )
535
+ else
536
+ parse_name
464
537
end
465
538
end
466
539
@@ -473,7 +546,7 @@ def parse_directives
473
546
dirs = [ ]
474
547
while at? ( :DIR_SIGN )
475
548
loc = pos
476
- expect_token ( :DIR_SIGN )
549
+ advance_token
477
550
name = parse_name
478
551
arguments = parse_arguments
479
552
@@ -562,11 +635,27 @@ def at?(expected_token_name)
562
635
563
636
def expect_token ( expected_token_name )
564
637
unless @token_name == expected_token_name
565
- raise "TODO nice error for Expected token #{ expected_token_name } , actual: #{ token_name . inspect } #{ @lexer . token_value } line: #{ @lexer . line } / pos: #{ @lexer . pos } "
638
+ raise_parse_error ( " Expected #{ expected_token_name } , actual: #{ token_name } ( #{ @lexer . token_value . inspect } )" )
566
639
end
567
640
advance_token
568
641
end
569
642
643
+ def expect_one_of ( token_names )
644
+ raise_parse_error ( "Expected one of #{ token_names . join ( ", " ) } , actual: #{ token_name } (#{ @lexer . token_value . inspect } )" )
645
+ end
646
+
647
+ def raise_parse_error ( message )
648
+ message += " at [#{ @lexer . line_number } , #{ @lexer . column_number } ]"
649
+ raise GraphQL ::ParseError . new (
650
+ message ,
651
+ @lexer . line_number ,
652
+ @lexer . column_number ,
653
+ @graphql_str ,
654
+ filename : @filename ,
655
+ )
656
+
657
+ end
658
+
570
659
# Only use when we care about the expected token's value
571
660
def expect_token_value ( tok )
572
661
token_value = @lexer . token_value
@@ -624,15 +713,15 @@ def advance
624
713
@scanner [ 1 ] ? :FLOAT : :INT
625
714
when ByteFor ::ELLIPSIS
626
715
if @string . getbyte ( @pos + 1 ) != 46 || @string . getbyte ( @pos + 2 ) != 46
627
- raise "TODO raise a nice error for a malformed ellipsis"
716
+ raise_parse_error ( "Expected `...`, actual: #{ @string [ @pos .. @pos + 2 ] . inspect } " )
628
717
end
629
718
@scanner . pos += 3
630
719
:ELLIPSIS
631
720
when ByteFor ::STRING
632
721
if @scanner . skip ( BLOCK_STRING_REGEXP ) || @scanner . skip ( QUOTED_STRING_REGEXP )
633
722
:STRING
634
723
else
635
- raise "TODO Raise a nice error for a badly-formatted string"
724
+ raise_parse_error ( "Expected string or block string, but it was malformed" )
636
725
end
637
726
else
638
727
@scanner . pos += 1
@@ -643,7 +732,7 @@ def advance
643
732
def token_value
644
733
@string . byteslice ( @scanner . pos - @scanner . matched_size , @scanner . matched_size )
645
734
rescue StandardError => err
646
- "(token_value failed: #{ err . class } : #{ err . message } )"
735
+ raise GraphQL :: Error , "(token_value failed: #{ err . class } : #{ err . message } )"
647
736
end
648
737
649
738
def string_value
@@ -656,23 +745,33 @@ def string_value
656
745
end
657
746
658
747
if !str . valid_encoding? || !str . match? ( Language ::Lexer ::VALID_STRING )
659
- raise "TODO Bad Unicode escape"
748
+ raise_parse_error ( " Bad unicode escape in #{ str . inspect } " )
660
749
else
661
750
GraphQL ::Language ::Lexer . replace_escaped_characters_in_place ( str )
662
751
663
752
if !str . valid_encoding?
664
- raise "TODO Bad Unicode escape"
753
+ raise_parse_error ( " Bad unicode escape in #{ str . inspect } " )
665
754
else
666
755
str
667
756
end
668
757
end
669
758
end
670
759
671
- def line
760
+ def line_number
672
761
@scanner . string [ 0 , @scanner . pos ] . count ( "\n " ) + 1
673
762
end
674
763
675
- IGNORE_REGEXP = /[, \c \r \n \t ]+/
764
+ def column_number
765
+ @scanner . string [ 0 , @scanner . pos ] . split ( "\n " ) . last . length - token_value . length + 1
766
+ end
767
+
768
+ # IGNORE_REGEXP = /[, \c\r\n\t]+/
769
+ IGNORE_REGEXP = %r{
770
+ (?:
771
+ [, \c \r \n \t ]+ |
772
+ \# .*$
773
+ )*
774
+ }x
676
775
IDENTIFIER_REGEXP = /[_A-Za-z][_0-9A-Za-z]*/
677
776
INT_REGEXP = /[-]?(?:[0]|[1-9][0-9]*)/
678
777
FLOAT_DECIMAL_REGEXP = /[.][0-9]+/
0 commit comments