Module: Tap::Support::Parser::Utils

Included in:
Tap::Support::Parser
Defined in:
lib/tap/support/parser.rb

Overview

A set of parsing routines used internally by Tap::Support::Parser, modularized for ease of testing, and potential re-use. These methods require that current_index and previous_index be implemented in the including class.

Constant Summary collapse

ESCAPE_BEGIN =

The escape begin argument

"-."
ESCAPE_END =

The escape end argument

".-"
END_FLAG =

The parser end flag

"---"
BREAK =

Matches any breaking arg (ex: ‘–’, ‘–+’, ‘–1:2’) After the match:

$1:: The string after the break
     (ex: '--' => '', '--++' => '++', '--1(2,3)' => '1(2,3)')
/\A--(\z|[\+\d\:\*\[\{\(].*\z)/
ROUND =

Matches an execution-round break. After the match:

$2:: The round string, or nil.
     (ex: '' => nil, '++' => '++', '+1' => '+1')
$5:: The target string, or nil. 
     (ex: '+' => nil, '+[1,2,3]' => '1,2,3')
/\A((\+(\d*|\+*))(\[([\d,]*)\])?)?\z/
SEQUENCE =

Matches a sequence break. After the match:

$1:: The sequence string after the break. 
     (ex: ':' => ':', '1:2' => '1:2', '1:' => '1:', ':2' => ':2')
$3:: The modifier string.
     (ex: ':i' => 'i', '1:2is' => 'is')
/\A(\d*(:\d*)+)([A-z]*)\z/
INSTANCE =

Matches an instance break. After the match:

$1:: The index string after the break.
     (ex: '*' => '', '*1' => '1')
/\A\*(\d*)\z/
FORK =

A break regexp using “[]”

bracket_regexp("[", "]")
MERGE =

A break regexp using “{}”

bracket_regexp("{", "}")
SYNC_MERGE =

A break regexp using “()”

bracket_regexp("(", ")")

Class Method Summary collapse

Class Method Details

.bracket_regexp(l, r) ⇒ Object

Defines a break regexp that matches a bracketed-pairs break. The left and right brackets are specified as inputs. After a match:

$1:: The source string after the break. 
     (ex: '[]' => '', '1[]' => '1')
$2:: The target string. 
     (ex: '[]' => '', '1[1,2,3]' => '1,2,3')
$3:: The modifier string.
     (ex: '[]i' => 'i', '1[1,2,3]is' => 'is')


100
101
102
# File 'lib/tap/support/parser.rb', line 100

def bracket_regexp(l, r)
  /\A(\d*)#{Regexp.escape(l)}([\d,]*)#{Regexp.escape(r)}([A-z]*)\z/
end

.parse_argh(argh) ⇒ Object

Parses an arg hash into a schema argv. An arg hash is a hash using numeric keys to specify the [row] in a two-dimensional array where a set of values should go. Breaks are added between rows (if necessary) and the array is collapsed to yield the argv:

argh = {
  0 => {
    0 => 'a',
    1 => ['b', 'c']},
  1 => 'z'
}
parse_argh(argh)    # => ['--', 'a', 'b', 'c', '--', 'z']

Non-numeric keys are converted to integers using to_i and existing breaks (such as workflow breaks) occuring at the start of a row are preseved.

argh = {
  '0' => {
    '0' => 'a',
    '1' => ['b', 'c']},
  '1' => ['--:', 'z']
}
parse_argh(argh)    # => ['--', 'a', 'b', 'c', '--:', 'z']


278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/tap/support/parser.rb', line 278

def parse_argh(argh)
  rows = []
  argh.each_pair do |row, values|
    if values.kind_of?(Hash)
      arry =  []
      values.each_pair {|col, value| arry[col.to_i] = value }
      values = arry
    end

    rows[row.to_i] = values
  end
  
  argv = []
  rows.each do |row|
    row = [row].flatten.compact
    if row.empty? || row[0] !~ BREAK
      argv << '--'
    end
    argv.concat row
  end
  argv
end

.parse_bracket(one, two, three) ⇒ Object

Parses the match of an bracket_regexp into a [source_index, target_indicies, options] array. The inputs corresponds to $1, $2, and $3 for the match. The previous and current index are assumed if $1 and/or $2 is empty.

parse_bracket("1", "2,3", "")       # => [1, [2,3], {}]
parse_bracket("", "", "")           # => [:previous_index, [:current_index], {}]
parse_bracket("1", "", "")          # => [1, [:current_index], {}]
parse_bracket("", "2,3", "")        # => [:previous_index, [2,3], {}]


226
227
228
229
230
# File 'lib/tap/support/parser.rb', line 226

def parse_bracket(one, two, three)
  targets = parse_indicies(two)
  targets << current_index if targets.empty?
  [one.empty? ? previous_index : one.to_i, targets, parse_options(three)]
end

.parse_indicies(str, regexp = /,+/) ⇒ Object

Parses an indicies str along commas, and collects the indicies as integers. Ex:

parse_indicies('')                  # => []
parse_indicies('1')                 # => [1]
parse_indicies('1,2,3')             # => [1,2,3]


162
163
164
165
166
167
168
# File 'lib/tap/support/parser.rb', line 162

def parse_indicies(str, regexp=/,+/)
  indicies = []
  str.split(regexp).each do |n|
    indicies << n.to_i unless n.empty?
  end
  indicies
end

.parse_instance(one) ⇒ Object

Parses the match of an INSTANCE regexp into an index. The input corresponds to $1 for the match. The current index is assumed if $1 is empty.

parse_instance("1")                 # => 1
parse_instance("")                  # => :current_index


212
213
214
# File 'lib/tap/support/parser.rb', line 212

def parse_instance(one)
  one.empty? ? current_index : one.to_i
end

.parse_options(three) ⇒ Object

Parses an options string into a hash. The input corresponds to $3 in a SEQUENCE or bracket_regexp match. Raises an error if the options string contains unknown options.

parse_options("")                   # => {}
parse_options("is")                 # => {:iterate => true, :stack => true}


239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/tap/support/parser.rb', line 239

def parse_options(three)
  options = {}
  0.upto(three.length - 1) do |char_index|
    char = three[char_index, 1]
    unless index = Join::SHORT_FLAGS.index(char)
      raise "unknown option in: #{three} (#{char})"
    end
    
    options[Join::FLAGS[index]] = true
  end
  options
end

.parse_round(two, five) ⇒ Object

Parses the match of a ROUND regexp into a round index and an array of task indicies that should be added to the round. The inputs correspond to $2 and $5 for the match.

If $2 is nil, a round index of zero is assumed; if $5 is nil or empty, then indicies of [:current_index] are assumed.

parse_round("+", "")                # => [1, [:current_index]]
parse_round("+2", "1,2,3")          # => [2, [1,2,3]]
parse_round(nil, nil)               # => [0, [:current_index]]


181
182
183
184
185
186
187
188
# File 'lib/tap/support/parser.rb', line 181

def parse_round(two, five)
  index = case two
  when nil then 0
  when /\d/ then two[1, two.length-1].to_i
  else two.length
  end
  [index, five.to_s.empty? ? [current_index] : parse_indicies(five)]
end

.parse_sequence(one, three) ⇒ Object

Parses the match of a SEQUENCE regexp into an [indicies, options] array. The inputs corresponds to $1 and $3 for the match. The previous and current index are assumed if $1 starts and/or ends with a semi-colon.

parse_sequence("1:2:3", '')         # => [[1,2,3], {}]
parse_sequence(":1:2:", '')         # => [[:previous_index,1,2,:current_index], {}]


198
199
200
201
202
203
# File 'lib/tap/support/parser.rb', line 198

def parse_sequence(one, three)
  seq = parse_indicies(one, /:+/)
  seq.unshift previous_index if one[0] == ?:
  seq << current_index if one[-1] == ?:
  [seq, parse_options(three)]
end