gql 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,281 @@
1
+ class GQL::Parser
2
+ token STRING NUMBER TRUE FALSE NULL AS IDENT
3
+ rule
4
+ query
5
+ : variables call variables { result = QueryNode.new(val[1], convert_variables(val[0], val[2])) }
6
+ ;
7
+
8
+ call
9
+ : identifier arguments fields { result = CallNode.new(val[0], val[1], nil, val[2].presence) }
10
+ | identifier arguments sub_call { result = CallNode.new(val[0], val[1], val[2], nil) }
11
+ | identifier arguments { result = CallNode.new(val[0], val[1], nil, nil) }
12
+ ;
13
+
14
+ sub_call
15
+ : '.' call { result = val[1] }
16
+ ;
17
+
18
+ arguments
19
+ : /* empty */ { result = [] }
20
+ | '(' ')' { result = [] }
21
+ | '(' argument_list ')' { result = val[1] }
22
+ ;
23
+
24
+ argument_list
25
+ : argument_list ',' argument { result.push val[2] }
26
+ | argument { result = val }
27
+ ;
28
+
29
+ argument
30
+ : variable_identifier
31
+ | json_text
32
+ ;
33
+
34
+ fields
35
+ : '{' '}' { result = [] }
36
+ | '{' field_list '}' { result = val[1] }
37
+ ;
38
+
39
+ field_list
40
+ : field_list ',' field { result.push val[2] }
41
+ | field { result = val }
42
+ ;
43
+
44
+ field
45
+ : identifier fields alias_identifier { result = FieldNode.new(val[0], val[2], nil, val[1].presence) }
46
+ | identifier sub_call alias_identifier { result = FieldNode.new(val[0], val[2], val[1], nil) }
47
+ | identifier alias_identifier { result = FieldNode.new(val[0], val[1], nil, nil) }
48
+ | identifier fields { result = FieldNode.new(val[0], nil, nil, val[1].presence) }
49
+ | identifier sub_call { result = FieldNode.new(val[0], nil, val[1], nil) }
50
+ | identifier { result = FieldNode.new(val[0], nil, nil, nil) }
51
+ ;
52
+
53
+ alias_identifier
54
+ : AS identifier { result = val[1] }
55
+ ;
56
+
57
+ variables
58
+ : /* empty */ { result = [] }
59
+ | variable_list { result = val[0] }
60
+ ;
61
+
62
+ variable_list
63
+ : variable { result = val }
64
+ | variable_list variable { result.push val[1] }
65
+ ;
66
+
67
+ variable
68
+ : variable_identifier '=' variable_value { result = [val[0], val[2]] }
69
+ ;
70
+
71
+ variable_identifier
72
+ : '<' identifier '>' { result = val[1] }
73
+ ;
74
+
75
+ variable_value
76
+ : json_text
77
+ ;
78
+
79
+ json_text
80
+ : json_value { result = @json.result }
81
+
82
+ json_value
83
+ : object
84
+ | array
85
+ | scalar
86
+ ;
87
+
88
+ object
89
+ : start_object end_object
90
+ | start_object pairs end_object
91
+ ;
92
+
93
+ start_object : '{' { @json.start_object } ;
94
+ end_object : '}' { @json.end_object } ;
95
+
96
+ pairs
97
+ : pairs ',' pair
98
+ | pair
99
+ ;
100
+
101
+ pair
102
+ : string ':' json_value
103
+ ;
104
+
105
+ array
106
+ : start_array end_array
107
+ | start_array values end_array
108
+ ;
109
+
110
+ start_array : '[' { @json.start_array } ;
111
+ end_array : ']' { @json.end_array } ;
112
+
113
+ values
114
+ : values ',' json_value
115
+ | json_value
116
+ ;
117
+
118
+ scalar
119
+ : string
120
+ | literal { @json.scalar val[0] }
121
+ ;
122
+
123
+ string
124
+ : STRING { @json.scalar unescape_string(val[0]) }
125
+ ;
126
+
127
+ literal
128
+ : NUMBER { result = convert_number(val[0]) }
129
+ | TRUE { result = true }
130
+ | FALSE { result = false }
131
+ | NULL { result = nil }
132
+ ;
133
+
134
+ identifier
135
+ : IDENT { result = val[0].to_sym }
136
+ ;
137
+ end
138
+
139
+ ---- header
140
+
141
+ require 'json'
142
+ require 'active_support/core_ext/object/blank'
143
+
144
+ ---- inner
145
+
146
+ class QueryNode < Struct.new(:call, :variables)
147
+ end
148
+
149
+ class FieldNode < Struct.new(:name, :alias_name, :call, :fields)
150
+ end
151
+
152
+ class CallNode < Struct.new(:name, :arguments, :call, :fields)
153
+ end
154
+
155
+ class JSONHandler
156
+ attr_reader :stack
157
+
158
+ def initialize
159
+ clear
160
+ end
161
+
162
+ def start_object
163
+ push [:hash]
164
+ end
165
+
166
+ def start_array
167
+ push [:array]
168
+ end
169
+
170
+ def end_array
171
+ @stack.pop
172
+ end
173
+
174
+ alias :end_object :end_array
175
+
176
+ def scalar(s)
177
+ @stack.last << [:scalar, s]
178
+ end
179
+
180
+ def push(o)
181
+ @stack.last << o
182
+ @stack << o
183
+ end
184
+
185
+ def result
186
+ root = @stack.first.last
187
+ value = process(root.first, root.drop(1))
188
+ clear
189
+ value
190
+ end
191
+
192
+ private
193
+ def clear
194
+ @stack = [[:json]]
195
+ end
196
+
197
+ def process(type, rest)
198
+ case type
199
+ when :array
200
+ rest.map { |x| process(x.first, x.drop(1)) }
201
+ when :hash
202
+ Hash[rest.map { |x|
203
+ process(x.first, x.drop(1))
204
+ }.each_slice(2).to_a]
205
+ when :scalar
206
+ rest.first
207
+ end
208
+ end
209
+ end
210
+
211
+ UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
212
+
213
+ UNESCAPE_MAP.update(
214
+ ?" => '"',
215
+ ?\\ => '\\',
216
+ ?/ => '/',
217
+ ?b => "\b",
218
+ ?f => "\f",
219
+ ?n => "\n",
220
+ ?r => "\r",
221
+ ?t => "\t",
222
+ ?u => nil,
223
+ )
224
+
225
+ EMPTY_8BIT_STRING = ''
226
+
227
+ if String.method_defined? :encode
228
+ EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
229
+ end
230
+
231
+ def initialize(tokenizer)
232
+ super()
233
+
234
+ @tokenizer = tokenizer
235
+ end
236
+
237
+ def next_token
238
+ @tokenizer.next_token
239
+ end
240
+
241
+ def parse
242
+ @json = JSONHandler.new
243
+ do_parse
244
+ end
245
+
246
+ def on_error(token, value, vstack)
247
+ raise Errors::ParseError.new(value, token_to_str(token))
248
+ end
249
+
250
+ private
251
+ def unescape_string(str)
252
+ string = str.gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
253
+ if u = UNESCAPE_MAP[$&[1]]
254
+ u
255
+ else # \uXXXX
256
+ bytes = EMPTY_8BIT_STRING.dup
257
+ i = 0
258
+
259
+ while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
260
+ bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
261
+ i += 1
262
+ end
263
+
264
+ JSON.iconv('utf-8', 'utf-16be', bytes)
265
+ end
266
+ end
267
+
268
+ if string.respond_to? :force_encoding
269
+ string.force_encoding ::Encoding::UTF_8
270
+ end
271
+
272
+ string
273
+ end
274
+
275
+ def convert_number(str)
276
+ str.count('.') > 0 ? str.to_f : str.to_i
277
+ end
278
+
279
+ def convert_variables(arr1, arr2)
280
+ Hash[*arr1.flatten(1)].merge Hash[*arr2.flatten(1)]
281
+ end
@@ -0,0 +1,21 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
4
+ module GQL
5
+ class Schema
6
+ include Singleton
7
+
8
+ class << self
9
+ extend Forwardable
10
+
11
+ delegate [:root, :root=, :fields] => :instance
12
+ end
13
+
14
+ attr_accessor :root
15
+ attr_reader :fields
16
+
17
+ def initialize
18
+ @fields = {}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,130 @@
1
+ #--
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by rex 1.0.5
4
+ # from lexical definition file "lib/gql/tokenizer.rex".
5
+ #++
6
+
7
+ require 'racc/parser'
8
+ class GQL::Tokenizer < Racc::Parser
9
+ require 'strscan'
10
+
11
+ class ScanError < StandardError ; end
12
+
13
+ attr_reader :lineno
14
+ attr_reader :filename
15
+ attr_accessor :state
16
+
17
+ def scan_setup(str)
18
+ @ss = StringScanner.new(str)
19
+ @lineno = 1
20
+ @state = nil
21
+ end
22
+
23
+ def action
24
+ yield
25
+ end
26
+
27
+ def scan_str(str)
28
+ scan_setup(str)
29
+ do_parse
30
+ end
31
+ alias :scan :scan_str
32
+
33
+ def load_file( filename )
34
+ @filename = filename
35
+ open(filename, "r") do |f|
36
+ scan_setup(f.read)
37
+ end
38
+ end
39
+
40
+ def scan_file( filename )
41
+ load_file(filename)
42
+ do_parse
43
+ end
44
+
45
+
46
+ def next_token
47
+ return if @ss.eos?
48
+
49
+ # skips empty actions
50
+ until token = _next_token or @ss.eos?; end
51
+ token
52
+ end
53
+
54
+ def _next_token
55
+ text = @ss.peek(1)
56
+ @lineno += 1 if text == "\n"
57
+ token = case @state
58
+ when nil
59
+ case
60
+ when (text = @ss.scan(/\/\*/))
61
+ action { @state = :REMS; nil }
62
+
63
+ when (text = @ss.scan(/\/\//))
64
+ action { @state = :REM; nil }
65
+
66
+ when (text = @ss.scan(/"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"/))
67
+ action { [:STRING, text.gsub(/^"|"$/, '')] }
68
+
69
+ when (text = @ss.scan(/-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/))
70
+ action { [:NUMBER, text] }
71
+
72
+ when (text = @ss.scan(/true/))
73
+ action { [:TRUE, text] }
74
+
75
+ when (text = @ss.scan(/false/))
76
+ action { [:FALSE, text] }
77
+
78
+ when (text = @ss.scan(/null/))
79
+ action { [:NULL, text] }
80
+
81
+ when (text = @ss.scan(/[aA][sS]/))
82
+ action { [:AS, text] }
83
+
84
+ when (text = @ss.scan(/[a-zA-Z_][a-zA-Z0-9_]*/))
85
+ action { [:IDENT, text] }
86
+
87
+ when (text = @ss.scan(/\s+/))
88
+ ;
89
+
90
+ when (text = @ss.scan(/./))
91
+ action { [text, text] }
92
+
93
+ else
94
+ text = @ss.string[@ss.pos .. -1]
95
+ raise ScanError, "can not match: '" + text + "'"
96
+ end # if
97
+
98
+ when :REMS
99
+ case
100
+ when (text = @ss.scan(/\*\//))
101
+ action { @state = nil; nil }
102
+
103
+ when (text = @ss.scan(/.*(?=\*\/)/))
104
+ ;
105
+
106
+ else
107
+ text = @ss.string[@ss.pos .. -1]
108
+ raise ScanError, "can not match: '" + text + "'"
109
+ end # if
110
+
111
+ when :REM
112
+ case
113
+ when (text = @ss.scan(/\n/))
114
+ action { @state = nil; nil }
115
+
116
+ when (text = @ss.scan(/.*(?=$)/))
117
+ ;
118
+
119
+ else
120
+ text = @ss.string[@ss.pos .. -1]
121
+ raise ScanError, "can not match: '" + text + "'"
122
+ end # if
123
+
124
+ else
125
+ raise ScanError, "undefined state: '" + state.to_s + "'"
126
+ end # case state
127
+ token
128
+ end # def _next_token
129
+
130
+ end # class
@@ -0,0 +1,46 @@
1
+ class GQL::Tokenizer
2
+ macro
3
+ BLANK \s+
4
+ REM_IN \/\*
5
+ REM_OUT \*\/
6
+ REM \/\/
7
+ STRING "(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"
8
+ NUMBER -?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?
9
+ TRUE true
10
+ FALSE false
11
+ NULL null
12
+ IDENT [a-zA-Z_][a-zA-Z0-9_]*
13
+ AS [aA][sS]
14
+
15
+ rule
16
+
17
+ # [:state] pattern [action]
18
+
19
+ # remarks
20
+ {REM_IN} { @state = :REMS; nil }
21
+ :REMS {REM_OUT} { @state = nil; nil }
22
+ :REMS .*(?={REM_OUT}) # ignore
23
+ {REM} { @state = :REM; nil }
24
+ :REM \n { @state = nil; nil }
25
+ :REM .*(?=$) # ignore
26
+
27
+ # scalars
28
+ {STRING} { [:STRING, text.gsub(/^"|"$/, '')] }
29
+ {NUMBER} { [:NUMBER, text] }
30
+ {TRUE} { [:TRUE, text] }
31
+ {FALSE} { [:FALSE, text] }
32
+ {NULL} { [:NULL, text] }
33
+
34
+ # keywords
35
+ {AS} { [:AS, text] }
36
+
37
+ # identifier
38
+ {IDENT} { [:IDENT, text] }
39
+
40
+ # whitespace
41
+ {BLANK} # ignore
42
+
43
+ # rest
44
+ . { [text, text] }
45
+
46
+ end