tinygql 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|