Class: Contraction::TypeParser

Inherits:
Object
  • Object
show all
Defined in:
lib/parser/type_parser.rb

Class Method Summary collapse

Class Method Details

.class_nameObject

A class name is anything that has a capitol first-letter



166
167
168
169
170
171
172
173
174
175
# File 'lib/parser/type_parser.rb', line 166

def self.class_name
  thing = @stack.pop
  return nil if thing.nil?
  if thing[0] =~ /^[A-Z]/
    return Type.new(thing.constantize)
  else
    @stack.push thing
    return nil
  end
end

.duck_typeObject

A duck-type is a thing prefaced with ‘#’, indicating that it must have that method.



179
180
181
182
183
184
185
186
187
188
# File 'lib/parser/type_parser.rb', line 179

def self.duck_type
  thing = @stack.pop
  return nil if thing.nil?
  if thing != '#'
    @stack.push thing
    return nil
  end

  DuckType.new @stack.pop
end

.hashObject

A hash starts with an optional “Hash”, and this then followed by an opening followed by a type-list, followed by a fat arrow (“=>”), followed by another type-list, followed by a closing curly brace (“”)



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/parser/type_parser.rb', line 217

def self.hash
  things = []
  things << @stack.pop
  if things.first != 'Hash' && things.first != '{'
    things.size.times { @stack.push things.pop }
    return nil
  end

  if things.first == 'Hash'
    things << @stack.pop
  end

  if things.last != '{'
    things.size.times { @stack.push things.pop }
    return nil
  end

  # Get the first type
  key_type = type_list
  if !key_type
    things.size.times { @stack.push things.pop }
    return nil
  else
    things << key_type
  end

  # And the arrow
  things <<  @stack.pop
  if things.last != '=>'
    things.size.times { @stack.push things.pop }
    return nil
  end

  # And the value type
  value_type = type_list
  if !value_type
    things.size.times { @stack.push things.pop }
    return nil
  end

  # Finally, the colosing brace
  things << @stack.pop
  if things.last != '}'
    things.size.times { @stack.push things.pop }
    return nil
  end

  HashType.new(key_type, value_type)
end

.parse(string) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/parser/type_parser.rb', line 141

def self.parse(string)
  @stack = TypeLexer.lex(string)

  # We are going to walk though this one at a time, popping off the
  # end, and seeing if the list of thing we have so far matches any
  # known rules, being as greedy as possible.
  things = [:typed_container, :sized_container, :type_list, :reference, :hash, :duck_type]
  something_happened = false
  data = []
  begin
    something_happened = false
    things.each do |t|
      thing = send(t)
      if thing
        data << thing
        something_happened = true
      end
    end
  end while something_happened

  raise "Type parse error #{@stack.reverse.join ' '}" unless @stack.compact.empty?
  data.flatten
end

.referenceObject

A reference is a “followed by a type, followed by a “”



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/parser/type_parser.rb', line 307

def self.reference
  b = @stack.pop
  return nil if b.nil?
  if b != '{'
    @stack.push b
    return nil
  end

  t = class_name
  if !t
    @stack.push b
    return nil
  end

  b2 = @stack.pop
  if b2.nil? || b2 != '}'
    @stack.push b2 if b2
    @stack.push t.klass.to_s
    @stack.push b
    return nil
  end

  return ReferenceType.new t
end

.sized_containerObject

A sized container is like a typed container, except it has a type for every member of the set. So if there are 3 types in the type list, the final type must be a container with exactly three members, conforming to their respective types. An example would be a Vector3 class, with the initializer defined as either 3 floats, or a Array[Float, Float, Float]



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/parser/type_parser.rb', line 337

def self.sized_container
  class_type = class_name

  bracket = @stack.pop
  if bracket.nil?
    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end
  if bracket != '('
    @stack.push bracket

    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end

  types = type_list
  if !types
    @stack.push bracket

    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end

  bracket2 = @stack.pop
  if bracket2.nil? || bracket2 != ')'
    raise "Expected ']', got #{bracket2}: #{@stack.inspect}"
  end

  SizedContainer.new(class_type, types)
end

.typeObject

A type is either hash, or any class-name like thing



191
192
193
# File 'lib/parser/type_parser.rb', line 191

def self.type
  reference || hash || typed_container || sized_container || class_name || duck_type
end

.type_listObject

A type-list is a Type, optionally followed by a comma and another type-list



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/parser/type_parser.rb', line 197

def self.type_list
  things = []
  things << type
  return nil if things.first.nil?

  things << @stack.pop
  if things.last != ','
    @stack.push things.pop
    return TypeList.new things
  end
  things.pop # Remove the ',' from the list

  things << type_list.types
  TypeList.new(things.flatten)
end

.typed_containerObject

A typed container is an optional class type, followed by a ‘<’, followed by a type list, followed by a ‘>’



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/parser/type_parser.rb', line 269

def self.typed_container
  class_type = class_name

  bracket = @stack.pop
  if bracket.nil?
    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end
  if bracket != '<'
    @stack.push bracket

    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end

  types = type_list
  if !types
    @stack.push bracket

    if class_type
      @stack.push class_type.klass.to_s
    end
    return nil
  end

  bracket2 = @stack.pop
  if bracket2.nil? || bracket2 != '>'
    raise "Expected '>', got #{bracket2}: #{@stack.inspect}"
  end

  TypedContainer.new(class_type, types)
end