tap 0.12.4 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|