Module: Taipo::Parser

Defined in:
lib/taipo/parser.rb,
lib/taipo/parser/stack.rb,
lib/taipo/parser/validater.rb,
lib/taipo/parser/syntax_state.rb

Overview

A parser of Taipo type definitions

Since:

  • 1.0.0

Defined Under Namespace

Modules: Validater Classes: Stack, SyntaxState

Class Method Summary collapse

Class Method Details

.escape?(c, states) ⇒ Boolean, Hash<Symbol,Boolean>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check whether the character should be skipped

This method determines whether a particular character c should be skipped based on states. It also updates states.

Parameters:

  • c (String)

    the character to check

  • states (Hash<Symbol,Boolean>)

    a state machine

Returns:

  • (Boolean, Hash<Symbol,Boolean>)

    the result and the updated state machine

Since:

  • 1.4.0



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/taipo/parser.rb', line 50

def self.escape?(c, states)
  if states[:esc]
    states[:esc] = false
    return skip, states
  end

  skip = true

  case c
  when "'"
    states[:ss] = !states[:ss] unless states[:re] || states[:ds]
  when '"'
    states[:ds] = !states[:ds] unless states[:re] || states[:ss]
  when '/'
    states[:re] = !states[:re] unless states[:ss] || states[:ds]
  when '\\'
    states[:esc] = true
  else
    skip = false
  end

 return skip, states
end

.parse(str) ⇒ Taipo::TypeElements

Return a Taipo::TypeElements object based on str

This method acts as a wrapping method to parse_definition. It first checks if the type definition has already been parsed and is in Taipo’s cache.

Parameters:

  • str (String)

    a type definition

Returns:

Raises:

  • (::TypeError)

    if str is not a String

  • (Taipo::SyntaxError)

    if str is not a valid type definition

Since:

  • 1.0.0



29
30
31
32
33
34
35
# File 'lib/taipo/parser.rb', line 29

def self.parse(str)
  if hit = Taipo::Cache[str]
    hit
  else
    Taipo::Cache[str] = Taipo::Parser.parse_definition str
  end
end

.parse_constraint(str) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

If the constraint is in the form of an instance method (eg. #foo) this method uses TypeElement::Constraint::METHOD as the name returned.

Parse the constraint expressed as a string

Parameters:

  • str (String)

    the constraint expressed as a string

Returns:

  • (String, String)

    the name and the value

Since:

  • 1.4.0



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/taipo/parser.rb', line 86

def self.parse_constraint(str)
  str.strip!
  in_name = nil
  name = ''
  content = ''
  str.each_char do |c|
    if c == '#' && in_name.nil?
      name = '#'
      in_name = false
    elsif c == ':' && in_name.nil?
      name = 'val'
      content = content + c
      in_name = false
    elsif c == ':' && in_name
      name = content
      content = ''
      in_name = false
    else
      content = content + c
      in_name = true if in_name.nil?
    end
  end
  value = content.strip
  return name, value
end

.parse_definition(str) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a Taipo::TypeElements object based on str

Since:

  • 1.5.0



122
123
124
125
126
127
128
129
130
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
156
157
158
159
160
161
162
163
164
# File 'lib/taipo/parser.rb', line 122

def self.parse_definition(str)
  Taipo::Parser::Validater.validate str

  stack = Taipo::Parser::Stack.new
  i = 0
  subject = :implied
  chars = str.chars
  content = ''

  while (i < chars.size)
    reset = true

    case chars[i]
    when ' '
      i += 1
      next
    when '|'
      stack = process_sum stack, name: content
      subject = :implied
    when '<'
      stack = process_collection :open, stack, name: content
      subject = :implied
    when '>'
      stack = process_collection :close, stack, name: content
      subject = :made
    when ','
      stack = process_component stack, name: content
      subject = :implied
    when '('
      stack = process_subject stack, name: content, subject: subject
      stack, i = process_constraints stack, chars: chars, index: i+1
    else
      reset = false
      subject = :unmade
    end

    content = (reset) ? '' : content + chars[i]
    i += 1
  end

  stack = process_end stack, name: content
  stack.result
end

.process_collection(direction, stack, name:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process a collection

This method either adds or updates a collection on stack depending on direction. If direction is :open, this adds a TypeElement to stack representing the class of the collection. If direction is :close, this adds a TypeElement to stack representing the class of the final component of the collection.

Parameters:

  • direction (Symbol)

    Either :open or :close depending on whether this has been called because the parser reached a < character or a > character

  • stack (Taipo::Parser::Stack)

    the stack

  • name (String)

    the name of the class of the TypeElement to add to stack

Returns:

Since:

  • 1.4.0



185
186
187
188
189
190
191
192
193
194
195
# File 'lib/taipo/parser.rb', line 185

def self.process_collection(direction, stack, name:)
  case direction
  when :open
    stack = process_name stack, name: name
    stack.add_children
  when :close
    stack = process_name stack, name: name unless name.empty?
    children = stack.remove_children
    stack.update_element :children=, children
  end
end

.process_component(stack, name:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process a component

This method adds a TypeElement to stack representing a component of a collection.

Parameters:

Returns:

Since:

  • 1.4.0



210
211
212
213
# File 'lib/taipo/parser.rb', line 210

def self.process_component(stack, name:)
  stack = process_name stack, name: name
  stack.add_child
end

.process_constraint(stack, raw:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process a constraint

This method adds a TypeElement::Constraint to the last element in stack.

Parameters:

Returns:

Since:

  • 1.4.0



227
228
229
230
# File 'lib/taipo/parser.rb', line 227

def self.process_constraint(stack, raw:)
  n, v = parse_constraint raw
  stack.add_constraint name: n, value: v
end

.process_constraints(stack, chars:, index:) ⇒ Taipo::Parser::Stack, Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process a series of constraints

This method adds a TypeElement::Constraints to the last element in stack. Because it parses chars, it also returns an updated index.

Parameters:

  • stack (Taipo::Parser::Stack)

    the stack

  • chars (Array<String>)

    a character array

  • index (Integer)

    the index of chars at which to begin parsing

Returns:

Since:

  • 1.4.0



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/taipo/parser.rb', line 246

def self.process_constraints(stack, chars:, index:)
  stack.add_constraints

  inside = { ss: false, ds: false, re: false, esc: false }
  content = ''

  while (index < chars.size)
    skip, inside = escape?(chars[index], inside)
    if skip
      content = content + chars[index]
      index += 1
      next
    end

    case chars[index]
    when ')'
      stack = process_constraint stack, raw: content
      break
    when ','
      stack = process_constraint stack, raw: content
      content = ''
    else
      content = content + chars[index]
    end

    index += 1
  end

  constraints = stack.remove_constraints
  stack.update_element :constraints=, constraints

  return stack, index
end

.process_end(stack, name:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process the end of the type definition

The design of parse means that at the end of the loop, an element may remain to be added. This method add any remaining element to stack.

Parameters:

Returns:

Since:

  • 1.4.0



294
295
296
297
298
# File 'lib/taipo/parser.rb', line 294

def self.process_end(stack, name:)
  return stack if name.empty?

  process_name stack, name: name
end

.process_name(stack, name:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Taipo allows certain bare constraints to be written in type definitions. If name is a bare constraint (either an instance method or a symbol), this method adds a TypeElement representing the Object class with the relevant constraint.

Process the name of a TypeElement

This method adds a TypeElement to stack with the name name.

Parameters:

Returns:

Since:

  • 1.4.0



317
318
319
320
321
322
323
324
325
326
# File 'lib/taipo/parser.rb', line 317

def self.process_name(stack, name:)
  if name.bare_constraint?
    chars = "(#{name})".chars
    stack = process_subject stack, name: '', subject: :implied
    stack, i = process_constraints stack, chars: chars, index: 1
    stack
  else
    stack.add_element name: name
  end
end

.process_subject(stack, name:, subject:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process the subject of a series of constraints

Taipo allows for a type definition to specify a series of constraints that constrain the particular type (the subject). This method adds a TypeElement to stack depending on the value of subject.

Parameters:

  • stack (Taipo::Parser::Stack)

    the stack

  • name (String)

    the name of the class of the TypeElement to add to stack

  • subject (Symbol)

    whether the subject is :made, :unmade or :implied

Returns:

Since:

  • 1.4.0



343
344
345
346
347
348
349
350
351
352
# File 'lib/taipo/parser.rb', line 343

def self.process_subject(stack, name:, subject:)
  case subject
  when :made
    stack
  when :unmade
    process_name stack, name: name
  when :implied
    process_name stack, name: 'Object'
  end
end

.process_sum(stack, name:) ⇒ Taipo::Parser::Stack

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process a sum of types

This method adds a TypeElement to stack representing the former of the types in the sum.

Parameters:

Returns:

Since:

  • 1.4.0



367
368
369
370
371
# File 'lib/taipo/parser.rb', line 367

def self.process_sum(stack, name:)
  return stack if name.empty?

  process_name stack, name: name
end