tinygql 0.2.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/Gemfile +0 -1
- data/README.md +23 -0
- data/bin/make_hash.rb +51 -0
- data/lib/tinygql/lexer.rb +112 -64
- data/lib/tinygql/nodes.rb +5 -5
- data/lib/tinygql/nodes.rb.erb +5 -5
- data/lib/tinygql/parser.rb +18 -25
- data/lib/tinygql/version.rb +1 -1
- data/lib/tinygql/visitors.rb +272 -0
- data/lib/tinygql/visitors.rb.erb +14 -0
- data/test/lexer_test.rb +24 -1
- data/test/parser_test.rb +50 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a68b5713ff3ff9e39df6e5eb89f7eec5fc74f524df33f9840f58cd8ac5027ec
|
4
|
+
data.tar.gz: f823e82df55af3ca0327889627e99f6d896242327369b698d505b921bba819a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0b4cd65e5c017f820e7198479a54dde8f676eb52fac56d9672a173ef282b21862a028f4a685d59c49c6dbfbd2d213148eff27674b071cff561e1cd6fe685251
|
7
|
+
data.tar.gz: 266db1a7ad62bae36b375403286ec1a08b774f2fad5dc1808bacbac5e86e89ea165d9dc9d1a7598e9e4d059a7e55e0a88396df786f17aba2c6c011534a201e14
|
data/.github/workflows/ci.yml
CHANGED
@@ -10,7 +10,7 @@ jobs:
|
|
10
10
|
fail-fast: false
|
11
11
|
matrix:
|
12
12
|
os: [ubuntu, macos]
|
13
|
-
ruby: [ head, 3.2 ]
|
13
|
+
ruby: [ head, 3.2, truffleruby, truffleruby-head, jruby ]
|
14
14
|
|
15
15
|
steps:
|
16
16
|
- uses: actions/checkout@v3
|
@@ -31,7 +31,7 @@ jobs:
|
|
31
31
|
fail-fast: false
|
32
32
|
matrix:
|
33
33
|
os: [ubuntu, macos]
|
34
|
-
ruby: [ head, 3.2 ]
|
34
|
+
ruby: [ head, 3.2, truffleruby, truffleruby-head ]
|
35
35
|
|
36
36
|
steps:
|
37
37
|
- uses: actions/checkout@v3
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -66,6 +66,29 @@ ast = TinyGQL.parse "{ neat { cool } }"
|
|
66
66
|
p ast.fold(Fold, []) # => ["neat", "cool"]
|
67
67
|
```
|
68
68
|
|
69
|
+
Nodes store their position in the source GraphQL document.
|
70
|
+
If you'd like to extract the line number of the node, you'll need to keep a reference to the document and pass it to the `line` method on the node:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
doc = <<-eod
|
74
|
+
mutation {
|
75
|
+
likeStory(sturyID: 12345) {
|
76
|
+
story {
|
77
|
+
likeCount
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
eod
|
83
|
+
|
84
|
+
parser = TinyGQL::Parser.new doc
|
85
|
+
ast = parser.parse
|
86
|
+
|
87
|
+
ast.find_all(&:field?).each { |node|
|
88
|
+
p node.name => node.line(doc)
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
69
92
|
## LICENSE:
|
70
93
|
|
71
94
|
I've licensed this code as Apache 2.0, but the lexer is from [GraphQL-Ruby](https://github.com/rmosolgo/graphql-ruby/blob/772734dfcc7aa0513c867259912474ef0ba799c3/lib/graphql/language/lexer.rb) and is under the MIT license.
|
data/bin/make_hash.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "tinygql"
|
2
|
+
|
3
|
+
# Calculate a perfect hash for GraphQL keywords
|
4
|
+
|
5
|
+
def bits x
|
6
|
+
count = 0
|
7
|
+
while x > 0
|
8
|
+
count += 1
|
9
|
+
x >>= 1
|
10
|
+
end
|
11
|
+
count
|
12
|
+
end
|
13
|
+
|
14
|
+
# on is too short, and subscription is the longest.
|
15
|
+
# The lexer can easily detect them by length, so lets calculate a perfect
|
16
|
+
# hash for the rest.
|
17
|
+
kws = TinyGQL::Lexer::KEYWORDS - ["on", "subscription"]
|
18
|
+
MASK = (1 << bits(kws.length)) - 1
|
19
|
+
|
20
|
+
prefixes = kws.map { |word| word[1, 2] }
|
21
|
+
|
22
|
+
# make sure they're unique
|
23
|
+
raise "Not unique" unless prefixes.uniq == prefixes
|
24
|
+
|
25
|
+
keys = prefixes.map { |prefix|
|
26
|
+
prefix.bytes.reverse.inject(0) { |c, byte|
|
27
|
+
c << 8 | byte
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
shift = 32 - bits(kws.length) # use the top bits
|
32
|
+
|
33
|
+
c = 13
|
34
|
+
loop do
|
35
|
+
z = keys.map { |k| ((k * c) >> shift) & MASK }
|
36
|
+
break if z.uniq.length == z.length
|
37
|
+
c += 1
|
38
|
+
end
|
39
|
+
|
40
|
+
table = kws.zip(keys).each_with_object([]) { |(word, k), o|
|
41
|
+
hash = ((k * c) >> shift) & MASK
|
42
|
+
o[hash] = word.upcase.to_sym
|
43
|
+
}
|
44
|
+
|
45
|
+
print "KW_LUT ="
|
46
|
+
pp table
|
47
|
+
puts <<-eomethod
|
48
|
+
def hash key
|
49
|
+
(key * #{c}) >> #{shift} & #{sprintf("%#0x", MASK)}
|
50
|
+
end
|
51
|
+
eomethod
|
data/lib/tinygql/lexer.rb
CHANGED
@@ -71,11 +71,36 @@ module TinyGQL
|
|
71
71
|
ESCAPED_QUOTE = /\\"/;
|
72
72
|
STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/
|
73
73
|
|
74
|
-
|
74
|
+
PUNCT_LUT = Literals.constants.each_with_object([]) { |n, o|
|
75
75
|
o[Literals.const_get(n).ord] = n
|
76
76
|
}
|
77
77
|
|
78
|
-
|
78
|
+
LEAD_BYTES = Array.new(255) { 0 }
|
79
|
+
|
80
|
+
module LeadBytes
|
81
|
+
INT = 1
|
82
|
+
KW = 2
|
83
|
+
ELLIPSIS = 3
|
84
|
+
STRING = 4
|
85
|
+
PUNCT = 5
|
86
|
+
IDENT = 6
|
87
|
+
end
|
88
|
+
|
89
|
+
10.times { |i| LEAD_BYTES[i.to_s.ord] = LeadBytes::INT }
|
90
|
+
|
91
|
+
("A".."Z").each { |chr| LEAD_BYTES[chr.ord] = LeadBytes::IDENT }
|
92
|
+
("a".."z").each { |chr| LEAD_BYTES[chr.ord] = LeadBytes::IDENT }
|
93
|
+
LEAD_BYTES['_'.ord] = LeadBytes::IDENT
|
94
|
+
|
95
|
+
KEYWORDS.each { |kw| LEAD_BYTES[kw.getbyte(0)] = LeadBytes::KW }
|
96
|
+
|
97
|
+
LEAD_BYTES['.'.ord] = LeadBytes::ELLIPSIS
|
98
|
+
|
99
|
+
LEAD_BYTES['"'.ord] = LeadBytes::STRING
|
100
|
+
|
101
|
+
Literals.constants.each_with_object([]) { |n, o|
|
102
|
+
LEAD_BYTES[Literals.const_get(n).ord] = LeadBytes::PUNCT
|
103
|
+
}
|
79
104
|
|
80
105
|
QUOTED_STRING = %r{#{QUOTE} ((?:#{STRING_CHAR})*) #{QUOTE}}x
|
81
106
|
BLOCK_STRING = %r{
|
@@ -95,11 +120,10 @@ module TinyGQL
|
|
95
120
|
|
96
121
|
@string = string
|
97
122
|
@scan = StringScanner.new string
|
123
|
+
@start = nil
|
98
124
|
end
|
99
125
|
|
100
|
-
|
101
|
-
@scan.pos
|
102
|
-
end
|
126
|
+
attr_reader :start
|
103
127
|
|
104
128
|
def line
|
105
129
|
@scan.string[0, @scan.pos].count("\n") + 1
|
@@ -109,66 +133,57 @@ module TinyGQL
|
|
109
133
|
@scan.eos?
|
110
134
|
end
|
111
135
|
|
112
|
-
KW_LUT = [:FRAGMENT,
|
113
|
-
:INTERFACE,
|
114
|
-
:MUTATION,
|
115
|
-
:EXTEND,
|
116
|
-
:FALSE,
|
117
|
-
:ENUM,
|
118
|
-
:TRUE,
|
119
|
-
:NULL,
|
120
|
-
nil,
|
121
|
-
nil,
|
122
|
-
nil,
|
123
|
-
nil,
|
124
|
-
nil,
|
125
|
-
nil,
|
126
|
-
:QUERY,
|
127
|
-
nil,
|
128
|
-
nil,
|
129
|
-
nil,
|
130
|
-
:REPEATABLE,
|
131
|
-
:IMPLEMENTS,
|
132
|
-
:INPUT,
|
133
|
-
:TYPE,
|
134
|
-
:SCHEMA,
|
135
|
-
nil,
|
136
|
-
nil,
|
137
|
-
nil,
|
138
|
-
:DIRECTIVE,
|
139
|
-
:UNION,
|
140
|
-
nil,
|
141
|
-
nil,
|
142
|
-
:SCALAR]
|
143
|
-
|
144
136
|
def advance
|
145
137
|
@scan.skip(IGNORE)
|
146
138
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
139
|
+
return false if @scan.eos?
|
140
|
+
|
141
|
+
@start = @scan.pos
|
142
|
+
|
143
|
+
lead_byte = @string.getbyte(@start)
|
144
|
+
lead_code = LEAD_BYTES[lead_byte]
|
145
|
+
|
146
|
+
if lead_code == LeadBytes::PUNCT
|
151
147
|
@scan.pos += 1
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
(@string.getbyte(pos +
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
148
|
+
PUNCT_LUT[lead_byte]
|
149
|
+
|
150
|
+
elsif lead_code == LeadBytes::KW
|
151
|
+
if len = @scan.skip(KW_RE)
|
152
|
+
return :ON if len == 2
|
153
|
+
return :SUBSCRIPTION if len == 12
|
154
|
+
|
155
|
+
pos = @start
|
156
|
+
|
157
|
+
# Second 2 bytes are unique, so we'll hash on those
|
158
|
+
key = (@string.getbyte(pos + 2) << 8) | @string.getbyte(pos + 1)
|
159
|
+
|
160
|
+
KW_LUT[_hash(key)]
|
161
|
+
else
|
162
|
+
@scan.skip(IDENTIFIER)
|
163
|
+
:IDENTIFIER
|
164
|
+
end
|
165
|
+
|
166
|
+
elsif lead_code == LeadBytes::IDENT
|
167
|
+
@scan.skip(IDENTIFIER)
|
168
|
+
:IDENTIFIER
|
169
|
+
|
170
|
+
elsif lead_code == LeadBytes::INT
|
171
|
+
@scan.skip(NUMERIC)
|
172
|
+
@scan[1] ? :FLOAT : :INT
|
173
|
+
|
174
|
+
elsif lead_code == LeadBytes::ELLIPSIS
|
175
|
+
2.times do |i|
|
176
|
+
raise unless @string.getbyte(@start + i + 1) == 46
|
177
|
+
end
|
178
|
+
@scan.pos += 3
|
179
|
+
:ELLIPSIS
|
180
|
+
|
181
|
+
elsif lead_code == LeadBytes::STRING
|
182
|
+
raise unless @scan.skip(BLOCK_STRING) || @scan.skip(QUOTED_STRING)
|
183
|
+
:STRING
|
184
|
+
|
170
185
|
else
|
171
|
-
@scan.
|
186
|
+
@scan.pos += 1
|
172
187
|
:UNKNOWN_CHAR
|
173
188
|
end
|
174
189
|
end
|
@@ -193,6 +208,8 @@ module TinyGQL
|
|
193
208
|
return unless tok = advance
|
194
209
|
val = case tok
|
195
210
|
when :STRING then string_value
|
211
|
+
when :ELLIPSIS then
|
212
|
+
@string.byteslice(@scan.pos - 3, 3)
|
196
213
|
when *Literals.constants
|
197
214
|
@string.byteslice(@scan.pos - 1, 1)
|
198
215
|
else
|
@@ -315,10 +332,41 @@ module TinyGQL
|
|
315
332
|
lines.size > 1 ? lines.join("\n") : (lines.first || "".dup)
|
316
333
|
end
|
317
334
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
335
|
+
KW_LUT =[:INTERFACE,
|
336
|
+
:MUTATION,
|
337
|
+
:EXTEND,
|
338
|
+
:FALSE,
|
339
|
+
:ENUM,
|
340
|
+
:TRUE,
|
341
|
+
:NULL,
|
342
|
+
nil,
|
343
|
+
nil,
|
344
|
+
nil,
|
345
|
+
nil,
|
346
|
+
nil,
|
347
|
+
nil,
|
348
|
+
nil,
|
349
|
+
:QUERY,
|
350
|
+
nil,
|
351
|
+
nil,
|
352
|
+
:REPEATABLE,
|
353
|
+
:IMPLEMENTS,
|
354
|
+
:INPUT,
|
355
|
+
:TYPE,
|
356
|
+
:SCHEMA,
|
357
|
+
nil,
|
358
|
+
nil,
|
359
|
+
nil,
|
360
|
+
:DIRECTIVE,
|
361
|
+
:UNION,
|
362
|
+
nil,
|
363
|
+
nil,
|
364
|
+
:SCALAR,
|
365
|
+
nil,
|
366
|
+
:FRAGMENT]
|
367
|
+
|
368
|
+
def _hash key
|
369
|
+
(key * 18592990) >> 27 & 0x1f
|
322
370
|
end
|
323
371
|
end
|
324
372
|
end
|
data/lib/tinygql/nodes.rb
CHANGED
@@ -3,16 +3,16 @@ module TinyGQL
|
|
3
3
|
class Node
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
-
# `
|
7
|
-
attr_reader :
|
6
|
+
# `start` is the start position of this node in the source document
|
7
|
+
attr_reader :start
|
8
8
|
|
9
|
-
def initialize
|
10
|
-
@
|
9
|
+
def initialize start
|
10
|
+
@start = start
|
11
11
|
end
|
12
12
|
|
13
13
|
# Return the line of this node given `doc`
|
14
14
|
def line doc
|
15
|
-
doc[0, @
|
15
|
+
doc[0, @start].count("\n") + 1
|
16
16
|
end
|
17
17
|
|
18
18
|
def document?; false; end
|
data/lib/tinygql/nodes.rb.erb
CHANGED
@@ -3,16 +3,16 @@ module TinyGQL
|
|
3
3
|
class Node
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
-
# `
|
7
|
-
attr_reader :
|
6
|
+
# `start` is the start position of this node in the source document
|
7
|
+
attr_reader :start
|
8
8
|
|
9
|
-
def initialize
|
10
|
-
@
|
9
|
+
def initialize start
|
10
|
+
@start = start
|
11
11
|
end
|
12
12
|
|
13
13
|
# Return the line of this node given `doc`
|
14
14
|
def line doc
|
15
|
-
doc[0, @
|
15
|
+
doc[0, @start].count("\n") + 1
|
16
16
|
end
|
17
17
|
|
18
18
|
<%- nodes.each do |node| -%>
|
data/lib/tinygql/parser.rb
CHANGED
@@ -13,7 +13,7 @@ module TinyGQL
|
|
13
13
|
|
14
14
|
def initialize doc
|
15
15
|
@lexer = Lexer.new doc
|
16
|
-
|
16
|
+
accept_token
|
17
17
|
end
|
18
18
|
|
19
19
|
def parse
|
@@ -25,7 +25,7 @@ module TinyGQL
|
|
25
25
|
attr_reader :token_name
|
26
26
|
|
27
27
|
def pos
|
28
|
-
@lexer.
|
28
|
+
@lexer.start
|
29
29
|
end
|
30
30
|
|
31
31
|
def document
|
@@ -477,7 +477,17 @@ module TinyGQL
|
|
477
477
|
end
|
478
478
|
|
479
479
|
def operation_type
|
480
|
-
|
480
|
+
val = if at?(:QUERY)
|
481
|
+
"query"
|
482
|
+
elsif at?(:MUTATION)
|
483
|
+
"mutation"
|
484
|
+
elsif at?(:SUBSCRIPTION)
|
485
|
+
"subscription"
|
486
|
+
else
|
487
|
+
expect_token(:QUERY)
|
488
|
+
end
|
489
|
+
accept_token
|
490
|
+
val
|
481
491
|
end
|
482
492
|
|
483
493
|
def directives
|
@@ -528,7 +538,7 @@ module TinyGQL
|
|
528
538
|
|
529
539
|
def variable_definition
|
530
540
|
loc = pos
|
531
|
-
var = variable
|
541
|
+
var = variable if at?(:VAR_SIGN)
|
532
542
|
expect_token(:COLON)
|
533
543
|
type = self.type
|
534
544
|
default_value = if at?(:EQUALS)
|
@@ -608,7 +618,7 @@ module TinyGQL
|
|
608
618
|
accept_token
|
609
619
|
Nodes::BooleanValue.new(pos, "false")
|
610
620
|
else
|
611
|
-
|
621
|
+
expect_token(:TRUE)
|
612
622
|
end
|
613
623
|
end
|
614
624
|
|
@@ -643,15 +653,14 @@ module TinyGQL
|
|
643
653
|
end
|
644
654
|
|
645
655
|
def variable
|
646
|
-
return unless at?(:VAR_SIGN)
|
647
656
|
loc = pos
|
648
|
-
|
657
|
+
expect_token(:VAR_SIGN)
|
649
658
|
Nodes::Variable.new loc, name
|
650
659
|
end
|
651
660
|
|
652
661
|
def name
|
653
662
|
case token_name
|
654
|
-
when :IDENTIFIER then
|
663
|
+
when :IDENTIFIER then expect_token_value(:IDENTIFIER)
|
655
664
|
when :TYPE then
|
656
665
|
accept_token
|
657
666
|
"type"
|
@@ -662,7 +671,7 @@ module TinyGQL
|
|
662
671
|
accept_token
|
663
672
|
"input"
|
664
673
|
else
|
665
|
-
|
674
|
+
expect_token(:IDENTIFIER)
|
666
675
|
end
|
667
676
|
end
|
668
677
|
|
@@ -670,13 +679,6 @@ module TinyGQL
|
|
670
679
|
@token_name = @lexer.advance
|
671
680
|
end
|
672
681
|
|
673
|
-
# Only use when we care about the accepted token's value
|
674
|
-
def accept_token_value
|
675
|
-
token_value = @lexer.token_value
|
676
|
-
accept_token
|
677
|
-
token_value
|
678
|
-
end
|
679
|
-
|
680
682
|
def expect_token tok
|
681
683
|
unless at?(tok)
|
682
684
|
raise UnexpectedToken, "Expected token #{tok}, actual: #{token_name} #{@lexer.token_value} line: #{@lexer.line}"
|
@@ -697,15 +699,6 @@ module TinyGQL
|
|
697
699
|
token_value
|
698
700
|
end
|
699
701
|
|
700
|
-
def expect_tokens toks
|
701
|
-
token_value = @lexer.token_value
|
702
|
-
unless toks.any? { |tok| at?(tok) }
|
703
|
-
raise UnexpectedToken, "Expected token #{tok}, actual: #{token_name}"
|
704
|
-
end
|
705
|
-
accept_token
|
706
|
-
token_value
|
707
|
-
end
|
708
|
-
|
709
702
|
def at? tok
|
710
703
|
token_name == tok
|
711
704
|
end
|
data/lib/tinygql/version.rb
CHANGED
data/lib/tinygql/visitors.rb
CHANGED
@@ -461,5 +461,277 @@ module TinyGQL
|
|
461
461
|
end
|
462
462
|
|
463
463
|
end
|
464
|
+
|
465
|
+
module Null
|
466
|
+
|
467
|
+
def handle_document obj
|
468
|
+
end
|
469
|
+
|
470
|
+
def handle_operation_definition obj
|
471
|
+
end
|
472
|
+
|
473
|
+
def handle_variable obj
|
474
|
+
end
|
475
|
+
|
476
|
+
def handle_named_type obj
|
477
|
+
end
|
478
|
+
|
479
|
+
def handle_not_null_type obj
|
480
|
+
end
|
481
|
+
|
482
|
+
def handle_list_type obj
|
483
|
+
end
|
484
|
+
|
485
|
+
def handle_variable_definition obj
|
486
|
+
end
|
487
|
+
|
488
|
+
def handle_value obj
|
489
|
+
end
|
490
|
+
|
491
|
+
def handle_argument obj
|
492
|
+
end
|
493
|
+
|
494
|
+
def handle_field obj
|
495
|
+
end
|
496
|
+
|
497
|
+
def handle_object_field obj
|
498
|
+
end
|
499
|
+
|
500
|
+
def handle_int_value obj
|
501
|
+
end
|
502
|
+
|
503
|
+
def handle_float_value obj
|
504
|
+
end
|
505
|
+
|
506
|
+
def handle_string_value obj
|
507
|
+
end
|
508
|
+
|
509
|
+
def handle_boolean_value obj
|
510
|
+
end
|
511
|
+
|
512
|
+
def handle_null_value obj
|
513
|
+
end
|
514
|
+
|
515
|
+
def handle_enum_value obj
|
516
|
+
end
|
517
|
+
|
518
|
+
def handle_list_value obj
|
519
|
+
end
|
520
|
+
|
521
|
+
def handle_object_value obj
|
522
|
+
end
|
523
|
+
|
524
|
+
def handle_directive obj
|
525
|
+
end
|
526
|
+
|
527
|
+
def handle_type_condition obj
|
528
|
+
end
|
529
|
+
|
530
|
+
def handle_inline_fragment obj
|
531
|
+
end
|
532
|
+
|
533
|
+
def handle_fragment_spread obj
|
534
|
+
end
|
535
|
+
|
536
|
+
def handle_fragment_definition obj
|
537
|
+
end
|
538
|
+
|
539
|
+
def handle_root_operation_type_definition obj
|
540
|
+
end
|
541
|
+
|
542
|
+
def handle_schema_definition obj
|
543
|
+
end
|
544
|
+
|
545
|
+
def handle_field_definition obj
|
546
|
+
end
|
547
|
+
|
548
|
+
def handle_input_value_definition obj
|
549
|
+
end
|
550
|
+
|
551
|
+
def handle_object_type_definition obj
|
552
|
+
end
|
553
|
+
|
554
|
+
def handle_interface_type_definition obj
|
555
|
+
end
|
556
|
+
|
557
|
+
def handle_union_type_definition obj
|
558
|
+
end
|
559
|
+
|
560
|
+
def handle_scalar_type_definition obj
|
561
|
+
end
|
562
|
+
|
563
|
+
def handle_enum_value_definition obj
|
564
|
+
end
|
565
|
+
|
566
|
+
def handle_enum_type_definition obj
|
567
|
+
end
|
568
|
+
|
569
|
+
def handle_input_object_type_definition obj
|
570
|
+
end
|
571
|
+
|
572
|
+
def handle_object_type_extension obj
|
573
|
+
end
|
574
|
+
|
575
|
+
def handle_executable_directive_location obj
|
576
|
+
end
|
577
|
+
|
578
|
+
def handle_type_system_directive_location obj
|
579
|
+
end
|
580
|
+
|
581
|
+
def handle_directive_definition obj
|
582
|
+
end
|
583
|
+
|
584
|
+
def handle_scalar_type_extension obj
|
585
|
+
end
|
586
|
+
|
587
|
+
def handle_interface_type_extension obj
|
588
|
+
end
|
589
|
+
|
590
|
+
def handle_union_type_extension obj
|
591
|
+
end
|
592
|
+
|
593
|
+
def handle_enum_type_extension obj
|
594
|
+
end
|
595
|
+
|
596
|
+
def handle_input_object_type_extension obj
|
597
|
+
end
|
598
|
+
|
599
|
+
end
|
600
|
+
|
601
|
+
module NullFold
|
602
|
+
|
603
|
+
def handle_document obj, _
|
604
|
+
end
|
605
|
+
|
606
|
+
def handle_operation_definition obj, _
|
607
|
+
end
|
608
|
+
|
609
|
+
def handle_variable obj, _
|
610
|
+
end
|
611
|
+
|
612
|
+
def handle_named_type obj, _
|
613
|
+
end
|
614
|
+
|
615
|
+
def handle_not_null_type obj, _
|
616
|
+
end
|
617
|
+
|
618
|
+
def handle_list_type obj, _
|
619
|
+
end
|
620
|
+
|
621
|
+
def handle_variable_definition obj, _
|
622
|
+
end
|
623
|
+
|
624
|
+
def handle_value obj, _
|
625
|
+
end
|
626
|
+
|
627
|
+
def handle_argument obj, _
|
628
|
+
end
|
629
|
+
|
630
|
+
def handle_field obj, _
|
631
|
+
end
|
632
|
+
|
633
|
+
def handle_object_field obj, _
|
634
|
+
end
|
635
|
+
|
636
|
+
def handle_int_value obj, _
|
637
|
+
end
|
638
|
+
|
639
|
+
def handle_float_value obj, _
|
640
|
+
end
|
641
|
+
|
642
|
+
def handle_string_value obj, _
|
643
|
+
end
|
644
|
+
|
645
|
+
def handle_boolean_value obj, _
|
646
|
+
end
|
647
|
+
|
648
|
+
def handle_null_value obj, _
|
649
|
+
end
|
650
|
+
|
651
|
+
def handle_enum_value obj, _
|
652
|
+
end
|
653
|
+
|
654
|
+
def handle_list_value obj, _
|
655
|
+
end
|
656
|
+
|
657
|
+
def handle_object_value obj, _
|
658
|
+
end
|
659
|
+
|
660
|
+
def handle_directive obj, _
|
661
|
+
end
|
662
|
+
|
663
|
+
def handle_type_condition obj, _
|
664
|
+
end
|
665
|
+
|
666
|
+
def handle_inline_fragment obj, _
|
667
|
+
end
|
668
|
+
|
669
|
+
def handle_fragment_spread obj, _
|
670
|
+
end
|
671
|
+
|
672
|
+
def handle_fragment_definition obj, _
|
673
|
+
end
|
674
|
+
|
675
|
+
def handle_root_operation_type_definition obj, _
|
676
|
+
end
|
677
|
+
|
678
|
+
def handle_schema_definition obj, _
|
679
|
+
end
|
680
|
+
|
681
|
+
def handle_field_definition obj, _
|
682
|
+
end
|
683
|
+
|
684
|
+
def handle_input_value_definition obj, _
|
685
|
+
end
|
686
|
+
|
687
|
+
def handle_object_type_definition obj, _
|
688
|
+
end
|
689
|
+
|
690
|
+
def handle_interface_type_definition obj, _
|
691
|
+
end
|
692
|
+
|
693
|
+
def handle_union_type_definition obj, _
|
694
|
+
end
|
695
|
+
|
696
|
+
def handle_scalar_type_definition obj, _
|
697
|
+
end
|
698
|
+
|
699
|
+
def handle_enum_value_definition obj, _
|
700
|
+
end
|
701
|
+
|
702
|
+
def handle_enum_type_definition obj, _
|
703
|
+
end
|
704
|
+
|
705
|
+
def handle_input_object_type_definition obj, _
|
706
|
+
end
|
707
|
+
|
708
|
+
def handle_object_type_extension obj, _
|
709
|
+
end
|
710
|
+
|
711
|
+
def handle_executable_directive_location obj, _
|
712
|
+
end
|
713
|
+
|
714
|
+
def handle_type_system_directive_location obj, _
|
715
|
+
end
|
716
|
+
|
717
|
+
def handle_directive_definition obj, _
|
718
|
+
end
|
719
|
+
|
720
|
+
def handle_scalar_type_extension obj, _
|
721
|
+
end
|
722
|
+
|
723
|
+
def handle_interface_type_extension obj, _
|
724
|
+
end
|
725
|
+
|
726
|
+
def handle_union_type_extension obj, _
|
727
|
+
end
|
728
|
+
|
729
|
+
def handle_enum_type_extension obj, _
|
730
|
+
end
|
731
|
+
|
732
|
+
def handle_input_object_type_extension obj, _
|
733
|
+
end
|
734
|
+
|
735
|
+
end
|
464
736
|
end
|
465
737
|
end
|
data/lib/tinygql/visitors.rb.erb
CHANGED
@@ -28,6 +28,20 @@ module TinyGQL
|
|
28
28
|
<%- end -%>
|
29
29
|
seed
|
30
30
|
end
|
31
|
+
<% end %>
|
32
|
+
end
|
33
|
+
|
34
|
+
module Null
|
35
|
+
<% nodes.each do |node| %>
|
36
|
+
def handle_<%= node.human_name %> obj
|
37
|
+
end
|
38
|
+
<% end %>
|
39
|
+
end
|
40
|
+
|
41
|
+
module NullFold
|
42
|
+
<% nodes.each do |node| %>
|
43
|
+
def handle_<%= node.human_name %> obj, _
|
44
|
+
end
|
31
45
|
<% end %>
|
32
46
|
end
|
33
47
|
end
|
data/test/lexer_test.rb
CHANGED
@@ -21,7 +21,7 @@ module TinyGQL
|
|
21
21
|
lexer = Lexer.new punc
|
22
22
|
token = lexer.next_token
|
23
23
|
expected = PUNC_LUT[punc]
|
24
|
-
assert_equal(expected
|
24
|
+
assert_equal(expected, token)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -179,5 +179,28 @@ eod
|
|
179
179
|
|
180
180
|
assert_equal words.map { |x| [x.upcase.to_sym, x] }, toks
|
181
181
|
end
|
182
|
+
|
183
|
+
def test_looks_like_kw
|
184
|
+
words = ["fragment", "fragments"]
|
185
|
+
doc = words.join(" ")
|
186
|
+
|
187
|
+
lexer = Lexer.new doc
|
188
|
+
toks = []
|
189
|
+
while tok = lexer.advance
|
190
|
+
toks << tok
|
191
|
+
end
|
192
|
+
|
193
|
+
assert_equal [:FRAGMENT, :IDENTIFIER], toks
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_num_with_dots
|
197
|
+
lexer = Lexer.new "1...2"
|
198
|
+
toks = []
|
199
|
+
while tok = lexer.advance
|
200
|
+
toks << tok
|
201
|
+
end
|
202
|
+
|
203
|
+
assert_equal [:INT, :ELLIPSIS, :INT], toks
|
204
|
+
end
|
182
205
|
end
|
183
206
|
end
|
data/test/parser_test.rb
CHANGED
@@ -58,8 +58,8 @@ mutation {
|
|
58
58
|
eod
|
59
59
|
parser = Parser.new doc
|
60
60
|
ast = parser.parse
|
61
|
-
expected = ["likeStory", "story", "likeCount"].map { |str| doc.index(str)
|
62
|
-
assert_equal expected, ast.find_all(&:field?).map(&:
|
61
|
+
expected = ["likeStory", "story", "likeCount"].map { |str| doc.index(str) }
|
62
|
+
assert_equal expected, ast.find_all(&:field?).map(&:start)
|
63
63
|
assert_equal [2, 3, 4], ast.find_all(&:field?).map { |n| n.line(doc) }
|
64
64
|
end
|
65
65
|
|
@@ -165,6 +165,54 @@ eod
|
|
165
165
|
assert_equal 1, node.arguments.length
|
166
166
|
end
|
167
167
|
|
168
|
+
def test_null
|
169
|
+
doc = <<-eod
|
170
|
+
mutation {
|
171
|
+
a: likeStory(storyID: 12345) {
|
172
|
+
b: story {
|
173
|
+
c: likeCount
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
eod
|
178
|
+
viz = Module.new do
|
179
|
+
extend TinyGQL::Visitors::Null
|
180
|
+
|
181
|
+
def self.handle_field obj
|
182
|
+
true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
parser = Parser.new doc
|
186
|
+
ast = parser.parse
|
187
|
+
ast.each { |node|
|
188
|
+
assert_equal(node.field?, !!node.accept(viz))
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_null_fold
|
193
|
+
doc = <<-eod
|
194
|
+
mutation {
|
195
|
+
a: likeStory(storyID: 12345) {
|
196
|
+
b: story {
|
197
|
+
c: likeCount
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}
|
201
|
+
eod
|
202
|
+
viz = Module.new do
|
203
|
+
extend TinyGQL::Visitors::NullFold
|
204
|
+
|
205
|
+
def self.handle_field obj, x
|
206
|
+
x
|
207
|
+
end
|
208
|
+
end
|
209
|
+
parser = Parser.new doc
|
210
|
+
ast = parser.parse
|
211
|
+
ast.each { |node|
|
212
|
+
assert_equal(node.field?, !!node.fold(viz, true))
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
168
216
|
def test_multiple_implements
|
169
217
|
doc = <<-eod
|
170
218
|
type SomeType implements a, b, c {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tinygql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Patterson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- Rakefile
|
54
54
|
- benchmark/fixtures/negotiate.gql
|
55
55
|
- bin/bench.rb
|
56
|
+
- bin/make_hash.rb
|
56
57
|
- bin/profile.rb
|
57
58
|
- lib/tinygql.rb
|
58
59
|
- lib/tinygql/lexer.rb
|