tap 0.12.4 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +34 -0
- data/README +62 -41
- data/bin/tap +36 -40
- data/cmd/console.rb +14 -6
- data/cmd/manifest.rb +62 -58
- data/cmd/run.rb +49 -31
- data/doc/API +84 -0
- data/doc/Class Reference +83 -115
- data/doc/Examples/Command Line +36 -0
- data/doc/Examples/Workflow +40 -0
- data/lib/tap/app.rb +293 -214
- data/lib/tap/app/node.rb +43 -0
- data/lib/tap/app/queue.rb +77 -0
- data/lib/tap/app/stack.rb +16 -0
- data/lib/tap/app/state.rb +22 -0
- data/lib/tap/constants.rb +2 -2
- data/lib/tap/env.rb +400 -314
- data/lib/tap/env/constant.rb +227 -0
- data/lib/tap/env/gems.rb +63 -0
- data/lib/tap/env/manifest.rb +89 -0
- data/lib/tap/env/minimap.rb +292 -0
- data/lib/tap/{support → env}/string_ext.rb +2 -2
- data/lib/tap/exe.rb +113 -125
- data/lib/tap/join.rb +175 -0
- data/lib/tap/joins.rb +9 -0
- data/lib/tap/joins/switch.rb +44 -0
- data/lib/tap/joins/sync.rb +99 -0
- data/lib/tap/root.rb +100 -491
- data/lib/tap/root/utils.rb +220 -0
- data/lib/tap/{support → root}/versions.rb +31 -29
- data/lib/tap/schema.rb +248 -0
- data/lib/tap/schema/parser.rb +413 -0
- data/lib/tap/schema/utils.rb +82 -0
- data/lib/tap/support/intern.rb +19 -6
- data/lib/tap/support/templater.rb +8 -3
- data/lib/tap/task.rb +175 -171
- data/lib/tap/tasks/dump.rb +58 -0
- data/lib/tap/tasks/load.rb +62 -0
- metadata +30 -73
- data/cmd/destroy.rb +0 -27
- data/cmd/generate.rb +0 -27
- data/doc/Command Reference +0 -105
- data/doc/Syntax Reference +0 -234
- data/doc/Tutorial +0 -348
- data/lib/tap/dump.rb +0 -142
- data/lib/tap/file_task.rb +0 -384
- data/lib/tap/generator/arguments.rb +0 -13
- data/lib/tap/generator/base.rb +0 -176
- data/lib/tap/generator/destroy.rb +0 -60
- data/lib/tap/generator/generate.rb +0 -93
- data/lib/tap/generator/generators/command/command_generator.rb +0 -21
- data/lib/tap/generator/generators/command/templates/command.erb +0 -32
- data/lib/tap/generator/generators/config/config_generator.rb +0 -98
- data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
- data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
- data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
- data/lib/tap/generator/generators/root/root_generator.rb +0 -84
- data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
- data/lib/tap/generator/generators/root/templates/README +0 -14
- data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
- data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
- data/lib/tap/generator/generators/root/templates/gemspec +0 -27
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
- data/lib/tap/generator/generators/task/task_generator.rb +0 -25
- data/lib/tap/generator/generators/task/templates/task.erb +0 -14
- data/lib/tap/generator/generators/task/templates/test.erb +0 -19
- data/lib/tap/generator/manifest.rb +0 -20
- data/lib/tap/generator/preview.rb +0 -69
- data/lib/tap/load.rb +0 -64
- data/lib/tap/spec.rb +0 -41
- data/lib/tap/support/aggregator.rb +0 -65
- data/lib/tap/support/audit.rb +0 -333
- data/lib/tap/support/constant.rb +0 -143
- data/lib/tap/support/constant_manifest.rb +0 -126
- data/lib/tap/support/dependencies.rb +0 -54
- data/lib/tap/support/dependency.rb +0 -44
- data/lib/tap/support/executable.rb +0 -198
- data/lib/tap/support/executable_queue.rb +0 -125
- data/lib/tap/support/gems.rb +0 -43
- data/lib/tap/support/join.rb +0 -144
- data/lib/tap/support/joins.rb +0 -12
- data/lib/tap/support/joins/switch.rb +0 -27
- data/lib/tap/support/joins/sync_merge.rb +0 -38
- data/lib/tap/support/manifest.rb +0 -171
- data/lib/tap/support/minimap.rb +0 -90
- data/lib/tap/support/node.rb +0 -176
- data/lib/tap/support/parser.rb +0 -450
- data/lib/tap/support/schema.rb +0 -385
- data/lib/tap/support/shell_utils.rb +0 -67
- data/lib/tap/test.rb +0 -77
- data/lib/tap/test/assertions.rb +0 -38
- data/lib/tap/test/env_vars.rb +0 -29
- data/lib/tap/test/extensions.rb +0 -73
- data/lib/tap/test/file_test.rb +0 -362
- data/lib/tap/test/file_test_class.rb +0 -15
- data/lib/tap/test/regexp_escape.rb +0 -87
- data/lib/tap/test/script_test.rb +0 -46
- data/lib/tap/test/script_tester.rb +0 -115
- data/lib/tap/test/subset_test.rb +0 -260
- data/lib/tap/test/subset_test_class.rb +0 -99
- data/lib/tap/test/tap_test.rb +0 -109
- data/lib/tap/test/utils.rb +0 -231
data/lib/tap/support/node.rb
DELETED
@@ -1,176 +0,0 @@
|
|
1
|
-
module Tap
|
2
|
-
module Support
|
3
|
-
|
4
|
-
# Represents a task node in a Schema.
|
5
|
-
class Node
|
6
|
-
class << self
|
7
|
-
|
8
|
-
# Returns the natural round of a set of nodes. The natural round is
|
9
|
-
# the lowest round of any of the nodes, or the node ancestors.
|
10
|
-
#
|
11
|
-
# # (3)-o-[A]-o-[C]-o-[D]
|
12
|
-
# # |
|
13
|
-
# # (2)-o-[B]-o
|
14
|
-
#
|
15
|
-
# join1, join2 = Array.new(2) { [:join, [], []] }
|
16
|
-
# a = Node.new [], 3, join1
|
17
|
-
# b = Node.new [], 2, join1
|
18
|
-
# c = Node.new [], join1, join2
|
19
|
-
# d = Node.new [], join2
|
20
|
-
#
|
21
|
-
# Node.natural_round([d]) # => 2
|
22
|
-
#
|
23
|
-
# Tracking back, the natural round of D is 2. Node order does not
|
24
|
-
# matter and globals are ignored.
|
25
|
-
#
|
26
|
-
# # ( )-o-[E]-o
|
27
|
-
# # |
|
28
|
-
# # (1)-o-[F]-o
|
29
|
-
# # |
|
30
|
-
# # (2)-o-[G]-o-[H]
|
31
|
-
#
|
32
|
-
# join = [:join, [], []]
|
33
|
-
# e = Node.new [], nil, join
|
34
|
-
# f = Node.new [], 1, join
|
35
|
-
# g = Node.new [], 2, join
|
36
|
-
# h = Node.new [], join
|
37
|
-
#
|
38
|
-
# Node.natural_round([d, h]) # => 1
|
39
|
-
#
|
40
|
-
def natural_round(nodes, visited=[])
|
41
|
-
round = nil
|
42
|
-
nodes.each do |node|
|
43
|
-
next if visited.include?(node)
|
44
|
-
visited << node
|
45
|
-
|
46
|
-
case input = node.input
|
47
|
-
when Integer
|
48
|
-
|
49
|
-
# reassign current round if necesssary
|
50
|
-
unless round && round < input
|
51
|
-
round = input
|
52
|
-
end
|
53
|
-
|
54
|
-
when Array
|
55
|
-
round = natural_round(node.parents, visited)
|
56
|
-
end
|
57
|
-
|
58
|
-
# optimization; no round is less than 0
|
59
|
-
return 0 if round == 0
|
60
|
-
end
|
61
|
-
|
62
|
-
round || 0
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# An array of arguments used to instantiate the node
|
67
|
-
attr_accessor :argv
|
68
|
-
|
69
|
-
# The input for the node. Input may be:
|
70
|
-
#
|
71
|
-
# - a join array: [join_instance, input_nodes, output_nodes]
|
72
|
-
# - an Integer indicating the round for self
|
73
|
-
# - nil signifiying 'global'
|
74
|
-
#
|
75
|
-
attr_reader :input
|
76
|
-
|
77
|
-
# The output for the node. Output may be:
|
78
|
-
#
|
79
|
-
# - a join array: [join_instance, input_nodes, output_nodes]
|
80
|
-
# - nil signifying nothing
|
81
|
-
#
|
82
|
-
attr_reader :output
|
83
|
-
|
84
|
-
def initialize(argv=[], input=0, output=nil)
|
85
|
-
@argv = argv
|
86
|
-
@input = @output = nil
|
87
|
-
|
88
|
-
self.input = input
|
89
|
-
self.output = output
|
90
|
-
end
|
91
|
-
|
92
|
-
def input_join
|
93
|
-
input.kind_of?(Array) ? input : nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def output_join
|
97
|
-
output.kind_of?(Array) ? output : nil
|
98
|
-
end
|
99
|
-
|
100
|
-
# Returns an array of nodes that pass inputs to self via an input join.
|
101
|
-
# If input is not a join, parents is an empty array.
|
102
|
-
def parents
|
103
|
-
input.kind_of?(Array) ? input[1] : []
|
104
|
-
end
|
105
|
-
|
106
|
-
# Returns an array of nodes that receive the outputs self via an output
|
107
|
-
# join. If output is not a join, children is an empty array.
|
108
|
-
def children
|
109
|
-
output.kind_of?(Array) ? output[2] : []
|
110
|
-
end
|
111
|
-
|
112
|
-
# Sets the input for self.
|
113
|
-
def input=(value)
|
114
|
-
if input.kind_of?(Array)
|
115
|
-
input[2].delete(self)
|
116
|
-
end
|
117
|
-
|
118
|
-
if value.kind_of?(Array)
|
119
|
-
value[2] << self
|
120
|
-
end
|
121
|
-
|
122
|
-
@input = value
|
123
|
-
end
|
124
|
-
|
125
|
-
# Sets the output for self.
|
126
|
-
def output=(value)
|
127
|
-
if output.kind_of?(Array)
|
128
|
-
output[1].delete(self)
|
129
|
-
|
130
|
-
# cleanup orphan joins
|
131
|
-
if output[1].empty?
|
132
|
-
orphan_round = natural_round
|
133
|
-
output[2].dup.each {|node| node.input = orphan_round }
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
if value.kind_of?(Array)
|
138
|
-
value[1] << self
|
139
|
-
end
|
140
|
-
|
141
|
-
@output = value
|
142
|
-
end
|
143
|
-
|
144
|
-
# Sets the input to nil.
|
145
|
-
def globalize
|
146
|
-
self.input = nil
|
147
|
-
end
|
148
|
-
|
149
|
-
# True if the input is nil.
|
150
|
-
def global?
|
151
|
-
input == nil
|
152
|
-
end
|
153
|
-
|
154
|
-
# Returns the round for self; a round is indicated by an integer input.
|
155
|
-
# If input is anything but an integer, round returns nil.
|
156
|
-
def round
|
157
|
-
input.kind_of?(Integer) ? input : nil
|
158
|
-
end
|
159
|
-
|
160
|
-
# Alias for input=
|
161
|
-
def round=(input)
|
162
|
-
self.input = input
|
163
|
-
end
|
164
|
-
|
165
|
-
# Returns the natural round for self.
|
166
|
-
def natural_round
|
167
|
-
Node.natural_round([self])
|
168
|
-
end
|
169
|
-
|
170
|
-
def inspect
|
171
|
-
"#<#{self.class}:#{object_id} argv=[#{argv.join(' ')}] input=#{input.inspect} output=#{output.inspect}>"
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
data/lib/tap/support/parser.rb
DELETED
@@ -1,450 +0,0 @@
|
|
1
|
-
module Tap
|
2
|
-
module Support
|
3
|
-
|
4
|
-
# == Syntax
|
5
|
-
#
|
6
|
-
# ==== Round Assignment
|
7
|
-
# Tasks can be defined and set to a round using the following:
|
8
|
-
#
|
9
|
-
# break assigns task(s) to round
|
10
|
-
# -- next 0
|
11
|
-
# --+ next 1
|
12
|
-
# --++ next 2
|
13
|
-
# --+2 next 2
|
14
|
-
# --+2[1,2,3] 1,2,3 2
|
15
|
-
#
|
16
|
-
# Here all task (except c) are parsed into round 0, then the
|
17
|
-
# final argument reassigns e to round 3.
|
18
|
-
#
|
19
|
-
# schema = Parser.new("a -- b --+ c -- d -- e --+3[4]").schema
|
20
|
-
# a, b, c, d, e = schema.nodes
|
21
|
-
# schema.rounds # => [[a,b,d],[c], nil, [e]]
|
22
|
-
#
|
23
|
-
# ==== Workflow Assignment
|
24
|
-
# All simple workflow patterns except switch can be specified within
|
25
|
-
# the parse syntax (switch is the exception because there is no good
|
26
|
-
# way to define the switch block).
|
27
|
-
#
|
28
|
-
# break pattern source(s) target(s)
|
29
|
-
# --: sequence last next
|
30
|
-
# --[] fork last next
|
31
|
-
# --{} merge next last
|
32
|
-
# --() sync_merge next last
|
33
|
-
#
|
34
|
-
# example meaning
|
35
|
-
# --1:2 1.sequence(2)
|
36
|
-
# --1:2:3 1.sequence(2,3)
|
37
|
-
# --:2: last.sequence(2,next)
|
38
|
-
# --[] last.fork(next)
|
39
|
-
# --1{2,3,4} 1.merge(2,3,4)
|
40
|
-
# --(2,3,4) last.sync_merge(2,3,4)
|
41
|
-
#
|
42
|
-
# Note how all of the bracketed styles behave similarly; they are
|
43
|
-
# parsed with essentially the same code, but reverse the source
|
44
|
-
# and target in the case of merges.
|
45
|
-
#
|
46
|
-
# Here a and b are sequenced inline. Task c is assigned to no
|
47
|
-
# workflow until the final argument which sequenced b and c.
|
48
|
-
#
|
49
|
-
# schema = Parser.new("a --: b -- c --1:2i").schema
|
50
|
-
# a, b, c = schema.nodes
|
51
|
-
# schema.joins.collect do |join, inputs, outputs|
|
52
|
-
# [join.options, inputs, outputs]
|
53
|
-
# end # => [[{},[a],[b]], [{:iterate => true},[b],[c]]]
|
54
|
-
#
|
55
|
-
# ==== Globals
|
56
|
-
# Global instances of task (used, for example, by dependencies) may
|
57
|
-
# be assigned in the parse syntax as well. The break for a global
|
58
|
-
# is '--*'.
|
59
|
-
#
|
60
|
-
# schema = Parser.new("a -- b --* c").schema
|
61
|
-
# a, b, c = schema.nodes
|
62
|
-
# schema.globals # => [c]
|
63
|
-
#
|
64
|
-
# ==== Escapes and End Flags
|
65
|
-
# Breaks can be escaped by enclosing them in '-.' and '.-' delimiters;
|
66
|
-
# any number of arguments may be enclosed within the escape. After the
|
67
|
-
# end delimiter, breaks are active once again.
|
68
|
-
#
|
69
|
-
# schema = Parser.new("a -- b -- c").schema
|
70
|
-
# schema.argvs # => [["a"], ["b"], ["c"]]
|
71
|
-
#
|
72
|
-
# schema = Parser.new("a -. -- b .- -- c").schema
|
73
|
-
# schema.argvs # => [["a", "--", "b"], ["c"]]
|
74
|
-
#
|
75
|
-
# Parsing continues until the end of argv, or a an end flag '---' is
|
76
|
-
# reached. The end flag may also be escaped.
|
77
|
-
#
|
78
|
-
# schema = Parser.new("a -- b --- c").schema
|
79
|
-
# schema.argvs # => [["a"], ["b"]]
|
80
|
-
#
|
81
|
-
class Parser
|
82
|
-
|
83
|
-
# A set of parsing routines used internally by Tap::Support::Parser,
|
84
|
-
# modularized for ease of testing, and potential re-use. These methods
|
85
|
-
# require that <tt>current_index</tt> and <tt>previous_index</tt> be
|
86
|
-
# implemented in the including class.
|
87
|
-
module Utils
|
88
|
-
module_function
|
89
|
-
|
90
|
-
# Defines a break regexp that matches a bracketed-pairs
|
91
|
-
# break. The left and right brackets are specified as
|
92
|
-
# inputs. After a match:
|
93
|
-
#
|
94
|
-
# $1:: The source string after the break.
|
95
|
-
# (ex: '[]' => '', '1[]' => '1')
|
96
|
-
# $2:: The target string.
|
97
|
-
# (ex: '[]' => '', '1[1,2,3]' => '1,2,3')
|
98
|
-
# $3:: The modifier string.
|
99
|
-
# (ex: '[]i' => 'i', '1[1,2,3]is' => 'is')
|
100
|
-
#
|
101
|
-
def bracket_regexp(l, r)
|
102
|
-
/\A(\d*)#{Regexp.escape(l)}([\d,]*)#{Regexp.escape(r)}([A-z]*)\z/
|
103
|
-
end
|
104
|
-
|
105
|
-
# The escape begin argument
|
106
|
-
ESCAPE_BEGIN = "-."
|
107
|
-
|
108
|
-
# The escape end argument
|
109
|
-
ESCAPE_END = ".-"
|
110
|
-
|
111
|
-
# The parser end flag
|
112
|
-
END_FLAG = "---"
|
113
|
-
|
114
|
-
# Matches any breaking arg (ex: '--', '--+', '--1:2')
|
115
|
-
# After the match:
|
116
|
-
#
|
117
|
-
# $1:: The string after the break
|
118
|
-
# (ex: '--' => '', '--++' => '++', '--1(2,3)' => '1(2,3)')
|
119
|
-
#
|
120
|
-
BREAK = /\A--(\z|[\+\d\:\*\[\{\(].*\z)/
|
121
|
-
|
122
|
-
# Matches an execution-round break. After the match:
|
123
|
-
#
|
124
|
-
# $2:: The round string, or nil.
|
125
|
-
# (ex: '' => nil, '++' => '++', '+1' => '+1')
|
126
|
-
# $5:: The target string, or nil.
|
127
|
-
# (ex: '+' => nil, '+[1,2,3]' => '1,2,3')
|
128
|
-
#
|
129
|
-
ROUND = /\A((\+(\d*|\+*))(\[([\d,]*)\])?)?\z/
|
130
|
-
|
131
|
-
# Matches a sequence break. After the match:
|
132
|
-
#
|
133
|
-
# $1:: The sequence string after the break.
|
134
|
-
# (ex: ':' => ':', '1:2' => '1:2', '1:' => '1:', ':2' => ':2')
|
135
|
-
# $3:: The modifier string.
|
136
|
-
# (ex: ':i' => 'i', '1:2is' => 'is')
|
137
|
-
#
|
138
|
-
SEQUENCE = /\A(\d*(:\d*)+)([A-z]*)\z/
|
139
|
-
|
140
|
-
# Matches an instance break. After the match:
|
141
|
-
#
|
142
|
-
# $1:: The index string after the break.
|
143
|
-
# (ex: '*' => '', '*1' => '1')
|
144
|
-
#
|
145
|
-
INSTANCE = /\A\*(\d*)\z/
|
146
|
-
|
147
|
-
# A break regexp using "[]"
|
148
|
-
FORK = bracket_regexp("[", "]")
|
149
|
-
|
150
|
-
# A break regexp using "{}"
|
151
|
-
MERGE = bracket_regexp("{", "}")
|
152
|
-
|
153
|
-
# A break regexp using "()"
|
154
|
-
SYNC_MERGE = bracket_regexp("(", ")")
|
155
|
-
|
156
|
-
# Parses an indicies str along commas, and collects the indicies
|
157
|
-
# as integers. Ex:
|
158
|
-
#
|
159
|
-
# parse_indicies('') # => []
|
160
|
-
# parse_indicies('1') # => [1]
|
161
|
-
# parse_indicies('1,2,3') # => [1,2,3]
|
162
|
-
#
|
163
|
-
def parse_indicies(str, regexp=/,+/)
|
164
|
-
indicies = []
|
165
|
-
str.split(regexp).each do |n|
|
166
|
-
indicies << n.to_i unless n.empty?
|
167
|
-
end
|
168
|
-
indicies
|
169
|
-
end
|
170
|
-
|
171
|
-
# Parses the match of a ROUND regexp into a round index and an array
|
172
|
-
# of task indicies that should be added to the round. The inputs
|
173
|
-
# correspond to $2 and $5 for the match.
|
174
|
-
#
|
175
|
-
# If $2 is nil, a round index of zero is assumed; if $5 is nil or
|
176
|
-
# empty, then indicies of [:current_index] are assumed.
|
177
|
-
#
|
178
|
-
# parse_round("+", "") # => [1, [:current_index]]
|
179
|
-
# parse_round("+2", "1,2,3") # => [2, [1,2,3]]
|
180
|
-
# parse_round(nil, nil) # => [0, [:current_index]]
|
181
|
-
#
|
182
|
-
def parse_round(two, five)
|
183
|
-
index = case two
|
184
|
-
when nil then 0
|
185
|
-
when /\d/ then two[1, two.length-1].to_i
|
186
|
-
else two.length
|
187
|
-
end
|
188
|
-
[index, five.to_s.empty? ? [current_index] : parse_indicies(five)]
|
189
|
-
end
|
190
|
-
|
191
|
-
# Parses the match of a SEQUENCE regexp into an [indicies, options]
|
192
|
-
# array. The inputs corresponds to $1 and $3 for the match. The
|
193
|
-
# previous and current index are assumed if $1 starts and/or ends
|
194
|
-
# with a semi-colon.
|
195
|
-
#
|
196
|
-
# parse_sequence("1:2:3", '') # => [[1,2,3], {}]
|
197
|
-
# parse_sequence(":1:2:", '') # => [[:previous_index,1,2,:current_index], {}]
|
198
|
-
#
|
199
|
-
def parse_sequence(one, three)
|
200
|
-
seq = parse_indicies(one, /:+/)
|
201
|
-
seq.unshift previous_index if one[0] == ?:
|
202
|
-
seq << current_index if one[-1] == ?:
|
203
|
-
[seq, parse_options(three)]
|
204
|
-
end
|
205
|
-
|
206
|
-
# Parses the match of an INSTANCE regexp into an index.
|
207
|
-
# The input corresponds to $1 for the match. The current
|
208
|
-
# index is assumed if $1 is empty.
|
209
|
-
#
|
210
|
-
# parse_instance("1") # => 1
|
211
|
-
# parse_instance("") # => :current_index
|
212
|
-
#
|
213
|
-
def parse_instance(one)
|
214
|
-
one.empty? ? current_index : one.to_i
|
215
|
-
end
|
216
|
-
|
217
|
-
# Parses the match of an bracket_regexp into a [input_indicies,
|
218
|
-
# output_indicies, options] array. The inputs corresponds to $1,
|
219
|
-
# $2, and $3 for a match to a bracket regexp. The previous and
|
220
|
-
# current index are assumed if $1 and/or $2 is empty.
|
221
|
-
#
|
222
|
-
# parse_bracket("1", "2,3", "") # => [[1], [2,3], {}]
|
223
|
-
# parse_bracket("", "", "") # => [[:previous_index], [:current_index], {}]
|
224
|
-
# parse_bracket("1", "", "") # => [[1], [:current_index], {}]
|
225
|
-
# parse_bracket("", "2,3", "") # => [[:previous_index], [2,3], {}]
|
226
|
-
#
|
227
|
-
def parse_bracket(one, two, three)
|
228
|
-
targets = parse_indicies(two)
|
229
|
-
targets << current_index if targets.empty?
|
230
|
-
[[one.empty? ? previous_index : one.to_i], targets, parse_options(three)]
|
231
|
-
end
|
232
|
-
|
233
|
-
# Same as parse_bracket but reverses the input and output indicies.
|
234
|
-
def parse_reverse_bracket(one, two, three)
|
235
|
-
inputs, outputs, options = parse_bracket(one, two, three)
|
236
|
-
[outputs, inputs, options]
|
237
|
-
end
|
238
|
-
|
239
|
-
# Parses an options string into a hash. The input corresponds
|
240
|
-
# to $3 in a SEQUENCE or bracket_regexp match. Raises an error
|
241
|
-
# if the options string contains unknown options.
|
242
|
-
#
|
243
|
-
# parse_options("") # => {}
|
244
|
-
# parse_options("ik") # => {:iterate => true, :stack => true}
|
245
|
-
#
|
246
|
-
def parse_options(three)
|
247
|
-
options = {}
|
248
|
-
0.upto(three.length - 1) do |char_index|
|
249
|
-
char = three[char_index, 1]
|
250
|
-
|
251
|
-
entry = Join.configurations.find do |key, config|
|
252
|
-
config.attributes[:short] == char
|
253
|
-
end
|
254
|
-
key, config = entry
|
255
|
-
|
256
|
-
raise "unknown option in: #{three} (#{char})" unless key
|
257
|
-
options[key] = true
|
258
|
-
end
|
259
|
-
options
|
260
|
-
end
|
261
|
-
|
262
|
-
# Parses an arg hash into a schema argv. An arg hash is a hash
|
263
|
-
# using numeric keys to specify the [row][col] in a two-dimensional
|
264
|
-
# array where a set of values should go. Breaks are added between
|
265
|
-
# rows (if necessary) and the array is collapsed to yield the
|
266
|
-
# argv:
|
267
|
-
#
|
268
|
-
# argh = {
|
269
|
-
# 0 => {
|
270
|
-
# 0 => 'a',
|
271
|
-
# 1 => ['b', 'c']},
|
272
|
-
# 1 => 'z'
|
273
|
-
# }
|
274
|
-
# parse_argh(argh) # => ['--', 'a', 'b', 'c', '--', 'z']
|
275
|
-
#
|
276
|
-
# Non-numeric keys are converted to integers using to_i and
|
277
|
-
# existing breaks (such as workflow breaks) occuring at the
|
278
|
-
# start of a row are preseved.
|
279
|
-
#
|
280
|
-
# argh = {
|
281
|
-
# '0' => {
|
282
|
-
# '0' => 'a',
|
283
|
-
# '1' => ['b', 'c']},
|
284
|
-
# '1' => ['--:', 'z']
|
285
|
-
# }
|
286
|
-
# parse_argh(argh) # => ['--', 'a', 'b', 'c', '--:', 'z']
|
287
|
-
#
|
288
|
-
def parse_argh(argh)
|
289
|
-
rows = []
|
290
|
-
argh.each_pair do |row, values|
|
291
|
-
if values.kind_of?(Hash)
|
292
|
-
arry = []
|
293
|
-
values.each_pair {|col, value| arry[col.to_i] = value }
|
294
|
-
values = arry
|
295
|
-
end
|
296
|
-
|
297
|
-
rows[row.to_i] = values
|
298
|
-
end
|
299
|
-
|
300
|
-
argv = []
|
301
|
-
rows.each do |row|
|
302
|
-
row = [row].flatten.compact
|
303
|
-
if row.empty? || row[0] !~ BREAK
|
304
|
-
argv << '--'
|
305
|
-
end
|
306
|
-
argv.concat row
|
307
|
-
end
|
308
|
-
argv
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
include Utils
|
313
|
-
|
314
|
-
# The schema into which nodes are parsed
|
315
|
-
attr_reader :schema
|
316
|
-
|
317
|
-
def initialize(argv=[])
|
318
|
-
@current_index = 0
|
319
|
-
@schema = Schema.new
|
320
|
-
parse(argv)
|
321
|
-
end
|
322
|
-
|
323
|
-
# Iterates through the argv splitting out task and workflow definitions.
|
324
|
-
# Task definitions are split out (with configurations) along round and/or
|
325
|
-
# workflow break lines. Rounds and workflows are dynamically parsed;
|
326
|
-
# tasks may be reassigned to different rounds or workflows by later
|
327
|
-
# arguments.
|
328
|
-
#
|
329
|
-
# Parse is non-destructive to argv. If a string argv is provided, parse
|
330
|
-
# splits it into an array using Shellwords; if a hash argv is provided,
|
331
|
-
# parse converts it to an array using Parser::Utils#parse_argh.
|
332
|
-
#
|
333
|
-
def parse(argv)
|
334
|
-
parse!(argv.kind_of?(String) ? argv : argv.dup)
|
335
|
-
end
|
336
|
-
|
337
|
-
# Same as parse, but removes parsed args from argv.
|
338
|
-
def parse!(argv)
|
339
|
-
# prevent the addition of an empty node to schema
|
340
|
-
return if argv.empty?
|
341
|
-
|
342
|
-
argv = case argv
|
343
|
-
when Array then argv
|
344
|
-
when String then Shellwords.shellwords(argv)
|
345
|
-
when Hash then parse_argh(argv)
|
346
|
-
else argv
|
347
|
-
end
|
348
|
-
argv.unshift('--')
|
349
|
-
|
350
|
-
escape = false
|
351
|
-
current_argv = nil
|
352
|
-
while !argv.empty?
|
353
|
-
arg = argv.shift
|
354
|
-
|
355
|
-
# if escaping, add escaped arguments
|
356
|
-
# until an escape-end argument
|
357
|
-
if escape
|
358
|
-
if arg == ESCAPE_END
|
359
|
-
escape = false
|
360
|
-
else
|
361
|
-
(current_argv ||= schema[current_index].argv) << arg
|
362
|
-
end
|
363
|
-
|
364
|
-
next
|
365
|
-
end
|
366
|
-
|
367
|
-
case arg
|
368
|
-
when ESCAPE_BEGIN
|
369
|
-
# begin escaping if indicated
|
370
|
-
escape = true
|
371
|
-
|
372
|
-
when END_FLAG
|
373
|
-
# break on an end-flag
|
374
|
-
break
|
375
|
-
|
376
|
-
when BREAK
|
377
|
-
# a breaking argument was reached:
|
378
|
-
# unless the current argv is empty,
|
379
|
-
# append and start a new definition
|
380
|
-
if current_argv && !current_argv.empty?
|
381
|
-
self.current_index += 1
|
382
|
-
current_argv = nil
|
383
|
-
end
|
384
|
-
|
385
|
-
# parse the break string for any
|
386
|
-
# schema modifications
|
387
|
-
parse_break($1)
|
388
|
-
|
389
|
-
else
|
390
|
-
# add all other non-breaking args to
|
391
|
-
# the current argv; this includes
|
392
|
-
# both inputs and configurations
|
393
|
-
(current_argv ||= schema[current_index].argv) << arg
|
394
|
-
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
schema
|
399
|
-
end
|
400
|
-
|
401
|
-
def load(argv)
|
402
|
-
argv.each do |args|
|
403
|
-
case args
|
404
|
-
when Array
|
405
|
-
schema.nodes << Node.new(args, 0)
|
406
|
-
self.current_index += 1
|
407
|
-
else
|
408
|
-
parse_break(args)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
# cleanup is currently required if terminal joins like
|
413
|
-
# --0[] are allowed (since it's interpreted as --0[next])
|
414
|
-
schema.cleanup
|
415
|
-
end
|
416
|
-
|
417
|
-
protected
|
418
|
-
|
419
|
-
# The index of the node currently being parsed.
|
420
|
-
attr_accessor :current_index # :nodoc:
|
421
|
-
|
422
|
-
# Returns current_index-1, or raises an error if current_index < 1.
|
423
|
-
def previous_index # :nodoc:
|
424
|
-
raise ArgumentError, 'there is no previous index' if current_index < 1
|
425
|
-
current_index - 1
|
426
|
-
end
|
427
|
-
|
428
|
-
# determines the type of break and modifies self appropriately
|
429
|
-
def parse_break(arg) # :nodoc:
|
430
|
-
case arg
|
431
|
-
when ROUND
|
432
|
-
round, indicies = parse_round($2, $5)
|
433
|
-
indicies.each {|index| schema[index].round = round }
|
434
|
-
|
435
|
-
when SEQUENCE
|
436
|
-
indicies, options = parse_sequence($1, $3)
|
437
|
-
while indicies.length > 1
|
438
|
-
schema.set(Join, [indicies.shift], [indicies[0]], options)
|
439
|
-
end
|
440
|
-
|
441
|
-
when INSTANCE then schema[parse_instance($1)].globalize
|
442
|
-
when FORK then schema.set(Join, *parse_bracket($1, $2, $3))
|
443
|
-
when MERGE then schema.set(Join, *parse_reverse_bracket($1, $2, $3))
|
444
|
-
when SYNC_MERGE then schema.set(Joins::SyncMerge, *parse_reverse_bracket($1, $2, $3))
|
445
|
-
else raise ArgumentError, "invalid break argument: #{arg}"
|
446
|
-
end
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
end
|