Class: Graphlyte::Parser

Inherits:
Graphlyte::Parsing::BacktrackingParser show all
Defined in:
lib/graphlyte/parser.rb

Overview

A parser of GraphQL documents from a stream of lexical tokens.

Instance Attribute Summary

Attributes inherited from Graphlyte::Parsing::BacktrackingParser

#max_depth, #tokens

Instance Method Summary collapse

Methods inherited from Graphlyte::Parsing::BacktrackingParser

#advance, #bracket, #current, #expect, #initialize, #inspect, #many, #name, #next_token, #one_of, #optional, #optional_list, #peek, #punctuator, #some, #subfeature, #to_s, #too_deep?, #try_parse

Constructor Details

This class inherits a constructor from Graphlyte::Parsing::BacktrackingParser

Instance Method Details

#argumentsObject



117
118
119
# File 'lib/graphlyte/parser.rb', line 117

def arguments
  bracket('(', ')') { some { parse_argument } }
end

#default_valueObject



211
212
213
214
215
# File 'lib/graphlyte/parser.rb', line 211

def default_value
  expect(:PUNCTUATOR, '=')

  parse_value
end

#definitionObject



30
31
32
# File 'lib/graphlyte/parser.rb', line 30

def definition
  one_of(:executable_definition, :type_definition)
end

#directivesObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/graphlyte/parser.rb', line 173

def directives
  ret = []
  while peek(offset: 1).punctuator?('@')
    d = Graphlyte::Syntax::Directive.new

    expect(:PUNCTUATOR, '@')
    d.name = name
    d.arguments = optional { arguments }

    ret << d
  end

  ret
end

#documentObject



11
12
13
14
15
16
17
18
# File 'lib/graphlyte/parser.rb', line 11

def document
  doc = Graphlyte::Document.new
  doc.definitions = some { definition }

  expect(:EOF)

  doc
end

#executable_definitionObject



34
35
36
# File 'lib/graphlyte/parser.rb', line 34

def executable_definition
  one_of(:fragment, :operation)
end

#field_selectionObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/graphlyte/parser.rb', line 99

def field_selection
  field = Graphlyte::Syntax::Field.new

  field.as = optional do
    n = name
    punctuator(':')

    n
  end

  field.name = name
  field.arguments = optional_list { arguments }
  field.directives = directives
  field.selection = optional_list { selection_set }

  field
end

#fragmentObject



250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/graphlyte/parser.rb', line 250

def fragment
  frag = Graphlyte::Syntax::Fragment.new

  expect(:NAME, 'fragment')
  frag.name = name

  expect(:NAME, 'on')

  frag.type_name = name
  frag.directives = directives
  frag.selection = selection_set

  frag
end

#fragment_spreadObject



76
77
78
79
80
81
82
83
84
# File 'lib/graphlyte/parser.rb', line 76

def fragment_spread
  frag = Graphlyte::Syntax::FragmentSpread.new

  punctuator('...')
  frag.name = name
  frag.directives = directives

  frag
end

#implicit_queryObject



52
53
54
# File 'lib/graphlyte/parser.rb', line 52

def implicit_query
  Graphlyte::Syntax::Operation.new(type: :query, selection: selection_set)
end

#inline_fragmentObject



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/graphlyte/parser.rb', line 86

def inline_fragment
  punctuator('...')
  name('on')

  frag = Graphlyte::Syntax::InlineFragment.new

  frag.type_name = name
  frag.directives = directives
  frag.selection = selection_set

  frag
end

#list_type_nameObject



241
242
243
244
245
246
247
248
# File 'lib/graphlyte/parser.rb', line 241

def list_type_name
  type = bracket('[', ']') { type_name(list: true) }
  t = peek(offset: 1)
  type.non_null_list = t.punctuator?('!')
  advance if type.non_null_list

  type
end

#operationObject



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/graphlyte/parser.rb', line 38

def operation
  t = next_token

  case t.type
  when :PUNCTUATOR
    @index -= 1
    implicit_query
  when :NAME
    operation_from_kind(t.value.to_sym)
  else
    raise Unexpected, t
  end
end

#operation_from_kind(kind) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/graphlyte/parser.rb', line 56

def operation_from_kind(kind)
  op = Graphlyte::Syntax::Operation.new

  try_parse do
    op.type = kind
    op.name = optional { name }
    op.variables = optional { variable_definitions }
    op.directives = directives
    op.selection = selection_set
  end

  op
end

#operation_typeObject

Raises:



188
189
190
191
192
# File 'lib/graphlyte/parser.rb', line 188

def operation_type
  raise Unexpected, current unless current.type == :NAME

  current.value.to_sym
end

#parse_argumentObject



121
122
123
124
125
126
127
128
129
# File 'lib/graphlyte/parser.rb', line 121

def parse_argument
  arg = Graphlyte::Syntax::Argument.new

  arg.name = name
  expect(:PUNCTUATOR, ':')
  arg.value = parse_value

  arg
end

#parse_array_valueObject



157
158
159
# File 'lib/graphlyte/parser.rb', line 157

def parse_array_value
  bracket('[', ']') { many { parse_value } }
end

#parse_object_valueObject



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/graphlyte/parser.rb', line 161

def parse_object_value
  bracket('{', '}') do
    many do
      n = name
      expect(:PUNCTUATOR, ':')
      value = parse_value

      [n, value]
    end.to_h
  end
end

#parse_valueObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/graphlyte/parser.rb', line 131

def parse_value
  t = next_token

  case t.type
  when :STRING, :NUMBER
    Graphlyte::Syntax::Value.new(t.value, t.type)
  when :NAME
    Graphlyte::Syntax::Value.from_name(t.value)
  when :PUNCTUATOR
    case t.value
    when '$'
      Graphlyte::Syntax::VariableReference.new(name)
    when '{'
      @index -= 1
      parse_object_value
    when '['
      @index -= 1
      parse_array_value
    else
      raise Unexpected, t
    end
  else
    raise Unexpected, t
  end
end

#queryObject

Restricted parser: only parses executable definitions



21
22
23
24
25
26
27
28
# File 'lib/graphlyte/parser.rb', line 21

def query
  doc = Graphlyte::Document.new
  doc.definitions = some { executable_definition }

  expect(:EOF)

  doc
end

#selection_setObject



70
71
72
73
74
# File 'lib/graphlyte/parser.rb', line 70

def selection_set
  bracket('{', '}') do
    some { one_of(:inline_fragment, :fragment_spread, :field_selection) }
  end
end

#type_definitionObject

Raises:



265
266
267
# File 'lib/graphlyte/parser.rb', line 265

def type_definition
  raise ParseError, "TODO: #{current.location}"
end

#type_name(list: false) ⇒ Object



223
224
225
226
227
228
229
230
231
232
# File 'lib/graphlyte/parser.rb', line 223

def type_name(list: false)
  ty = Graphlyte::Syntax::Type.new(name)

  t = peek(offset: 1)
  ty.non_null = t.punctuator?('!')
  ty.is_list = list
  advance if ty.non_null

  ty
end

#type_name!Object



234
235
236
237
238
239
# File 'lib/graphlyte/parser.rb', line 234

def type_name!
  ty = type_name
  expect(:EOF)

  ty
end

#variable_definitionsObject



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/graphlyte/parser.rb', line 194

def variable_definitions
  bracket('(', ')') do
    some do
      var = Graphlyte::Syntax::VariableDefinition.new

      var.variable = variable_name
      expect(:PUNCTUATOR, ':')
      var.type = one_of(:list_type_name, :type_name)

      var.default_value = optional { default_value }
      var.directives = directives

      var
    end
  end
end

#variable_nameObject



217
218
219
220
221
# File 'lib/graphlyte/parser.rb', line 217

def variable_name
  expect(:PUNCTUATOR, '$')

  name
end