clasp-ruby 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,467 @@
1
+
2
+ # ######################################################################## #
3
+ # File: clasp/arguments.rb
4
+ #
5
+ # Purpose: Definition of the Arguments class, the main class in
6
+ # CLASP.Ruby
7
+ #
8
+ # Created: 14th February 2014
9
+ # Updated: 11th June 2016
10
+ #
11
+ # Home: http://github.com/synesissoftware/CLASP.Ruby
12
+ #
13
+ # Author: Matthew Wilson
14
+ #
15
+ # Copyright (c) 2014-2016, Matthew Wilson and Synesis Software
16
+ # All rights reserved.
17
+ #
18
+ # Redistribution and use in source and binary forms, with or without
19
+ # modification, are permitted provided that the following conditions are
20
+ # met:
21
+ #
22
+ # * Redistributions of source code must retain the above copyright
23
+ # notice, this list of conditions and the following disclaimer.
24
+ #
25
+ # * Redistributions in binary form must reproduce the above copyright
26
+ # notice, this list of conditions and the following disclaimer in the
27
+ # documentation and/or other materials provided with the distribution.
28
+ #
29
+ # * Neither the names of the copyright holder nor the names of its
30
+ # contributors may be used to endorse or promote products derived from
31
+ # this software without specific prior written permission.
32
+ #
33
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
+ #
45
+ # ######################################################################## #
46
+
47
+
48
+
49
+
50
+ =begin
51
+ =end
52
+
53
+ module CLASP
54
+
55
+ # ######################################################################## #
56
+ # classes
57
+
58
+ # The main class for processing command-line arguments
59
+ class Arguments
60
+
61
+ #:stopdoc:
62
+ private
63
+ class Flag #:nodoc: all
64
+
65
+ #:nodoc:
66
+ def initialize(arg, given_index, given_name, resolved_name, argument_alias, given_hyphens, given_label, extras)
67
+
68
+ @arg = arg
69
+ @given_index = given_index
70
+ @given_name = given_name
71
+ @argument_alias = argument_alias
72
+ @given_hyphens = given_hyphens
73
+ @given_label = given_label
74
+ @name = resolved_name || given_name
75
+ @extras = extras.nil? ? {} : extras
76
+ end
77
+
78
+ attr_reader :given_index
79
+ attr_reader :given_name
80
+ attr_reader :argument_alias
81
+ attr_reader :given_hyphens
82
+ attr_reader :given_label
83
+ attr_reader :name
84
+ attr_reader :extras
85
+
86
+ #:nodoc:
87
+ def to_s
88
+
89
+ @name
90
+ end
91
+
92
+ #:nodoc:
93
+ def eql?(rhs)
94
+
95
+ return false if rhs.nil?
96
+
97
+ # check name and aliases
98
+ return true if @name == rhs
99
+ return argument_alias.aliases.include? rhs if argument_alias
100
+ false
101
+ end
102
+
103
+ #:nodoc:
104
+ def ==(rhs)
105
+
106
+ return false if rhs.nil?
107
+ if not rhs.instance_of? String
108
+
109
+ rhs = rhs.name
110
+ end
111
+ eql? rhs
112
+ end
113
+
114
+ #:nodoc:
115
+ def hash
116
+
117
+ @arg.hash
118
+ end
119
+ end
120
+
121
+ class Option #:nodoc: all
122
+
123
+ #:nodoc:
124
+ def initialize(arg, given_index, given_name, resolved_name, argument_alias, given_hyphens, given_label, value, extras)
125
+
126
+ @arg = arg
127
+ @given_index = given_index
128
+ @given_name = given_name
129
+ @argument_alias = argument_alias
130
+ @given_hyphens = given_hyphens
131
+ @given_label = given_label
132
+ @value = value
133
+ @name = resolved_name || given_name
134
+ @extras = extras.nil? ? {} : extras
135
+ end
136
+
137
+ attr_reader :given_index
138
+ attr_reader :given_name
139
+ attr_reader :argument_alias
140
+ attr_reader :given_hyphens
141
+ attr_reader :given_label
142
+ attr_reader :name
143
+ attr_reader :value
144
+ attr_reader :extras
145
+
146
+ #:nodoc:
147
+ def eql?(rhs)
148
+
149
+ return false if rhs.nil?
150
+
151
+ # check name and aliases
152
+ return true if @name == rhs
153
+ return argument_alias.aliases.include? rhs if argument_alias
154
+ false
155
+ end
156
+
157
+ #:nodoc:
158
+ def ==(rhs)
159
+
160
+ return false if rhs.nil?
161
+ if not rhs.instance_of? String
162
+
163
+ rhs = rhs.name
164
+ end
165
+ eql? rhs
166
+ end
167
+
168
+ #:nodoc:
169
+ def hash
170
+
171
+ @arg.hash
172
+ end
173
+
174
+ #:nodoc:
175
+ def to_s
176
+
177
+ "#{name}=#{value}"
178
+ end
179
+ end
180
+
181
+ class ImmutableArray #:nodoc: all
182
+
183
+ include Enumerable
184
+
185
+ #:nodoc:
186
+ def initialize(a)
187
+
188
+ @a = a
189
+ end
190
+
191
+ #:nodoc:
192
+ def each
193
+
194
+ @a.each { |i| yield i }
195
+ end
196
+
197
+ #:nodoc:
198
+ def size
199
+
200
+ @a.size
201
+ end
202
+
203
+ #:nodoc:
204
+ def empty?
205
+
206
+ @a.empty?
207
+ end
208
+
209
+ #:nodoc:
210
+ def [](index)
211
+
212
+ @a[index]
213
+ end
214
+
215
+ #:nodoc:
216
+ def ==(rhs)
217
+
218
+ return rhs == @a if rhs.is_a? self.class
219
+ @a == rhs
220
+ end
221
+ end
222
+
223
+ #:startdoc:
224
+
225
+ # ######################
226
+ # Construction
227
+
228
+ public
229
+ # Constructs an instance of the class, according to the given parameters
230
+ #
231
+ # See the documentation for the ::CLASP module for examples
232
+ #
233
+ # === Signature
234
+ #
235
+ # * *Parameters*:
236
+ # - +argv+:: (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+.
237
+ # - +aliases+:: (+Array+) The aliases array. Defaults to +nil+. If none supplied, no aliasing will be performed.
238
+ # - +options+:: An options hash, containing any of the following options.
239
+ #
240
+ # * *Options*:
241
+ # - +mutate_arg:+:: (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>.
242
+ #
243
+ def initialize(argv = ARGV, aliases = nil, options = {})
244
+
245
+ # have to do this name-swap, as 'options' has CLASP-specific
246
+ # meaning
247
+ init_opts, options = options.dup, nil
248
+
249
+ init_opts[:mutate_argv] = true unless init_opts.has_key? :mutate_argv
250
+
251
+
252
+ @argv = argv
253
+ @argv_original_copy = ImmutableArray.new(argv.dup)
254
+
255
+ @aliases = aliases
256
+
257
+ aliases = nil if aliases and aliases.empty?
258
+
259
+ flags, options, values = Arguments.parse(argv, aliases)
260
+
261
+ @flags = ImmutableArray.new(flags)
262
+ @options = ImmutableArray.new(options)
263
+ @values = ImmutableArray.new(values)
264
+
265
+
266
+ # do argv-mutation, if required
267
+ if init_opts[:mutate_argv]
268
+
269
+ while not argv.empty?
270
+
271
+ argv.shift
272
+ end
273
+
274
+ @values.each do |v|
275
+
276
+ argv << v
277
+ end
278
+ end
279
+ end
280
+
281
+ private
282
+ def self.parse(argv, aliases)
283
+
284
+ flags = []
285
+ options = []
286
+ values = []
287
+
288
+ forced_value = false
289
+ want_option_value = false
290
+
291
+ argv.each_with_index do |arg, index|
292
+
293
+ if not forced_value
294
+
295
+ if '--' == arg
296
+
297
+ # all subsequent arguments are values
298
+ forced_value = true
299
+ next
300
+ end
301
+
302
+ # do regex test to see if option/flag/value
303
+ if arg =~ /^(-+)([^=]+)/
304
+
305
+ hyphens = $1
306
+ given_label = $2
307
+ given_name = "#$1#$2"
308
+ value = ($' and not $'.empty?) ? $'[1 ... $'.size] : nil
309
+ argument_alias = nil
310
+ resolved_name = nil
311
+
312
+ (aliases || []).each do |a|
313
+
314
+ if a.name == given_name or a.aliases.include? given_name
315
+
316
+ argument_alias = a
317
+ resolved_name = a.name
318
+
319
+ # need to check whether the alias is a default-option
320
+ # and, if so, expand out its name and value, and replace
321
+ # the name and (if none previously specified) the value
322
+ if resolved_name =~ /^(-+)([^=]+)=/
323
+
324
+ resolved_name = "#$1#$2"
325
+ value ||= $'
326
+ end
327
+ break
328
+ end
329
+ end
330
+
331
+ # Here we intercept and (potentially) cater to grouped flags
332
+ if not argument_alias and not value and aliases and 1 == hyphens.size
333
+
334
+ # Must match all
335
+ flag_aliases = []
336
+ given_label[0 ... given_label.size].each_char do |c|
337
+
338
+ new_flag = "-#{c.chr}"
339
+
340
+ flag_alias = nil
341
+
342
+ # special case where the flag's actual name is short form and found here
343
+ flag_alias ||= aliases.detect { |a| a.is_a?(CLASP::Flag) && a.name == new_flag }
344
+
345
+ # if not found as a flag, look in each aliases' aliases
346
+ flag_alias ||= aliases.detect { |a| a.aliases.include? new_flag }
347
+
348
+ if not flag_alias
349
+
350
+ flag_aliases = nil
351
+ break
352
+ else
353
+
354
+ flag_aliases << flag_alias
355
+ end
356
+ end
357
+
358
+ if flag_aliases
359
+
360
+ # got them all, so now have to process them all
361
+ # as normal. Note: is this susceptible to
362
+ # infinite recursion
363
+
364
+ # convert to argv and invoke
365
+ flags_argv = flag_aliases.map { |a| a.name }
366
+
367
+ grp_flags, grp_options, grp_value = Arguments.parse flags_argv, aliases
368
+
369
+ grp_flags.map! { |f| Flag.new(arg, index, given_name, f.name, f.argument_alias, hyphens.size, given_label, argument_alias ? argument_alias.extras : nil) }
370
+ grp_options.map! { |o| Option.new(arg, index, given_name, o.name, o.argument_alias, hyphens.size, given_label, o.value, argument_alias ? argument_alias.extras : nil) }
371
+
372
+ flags.push(*grp_flags)
373
+ options.push(*grp_options)
374
+ values.push(*grp_value)
375
+
376
+ next
377
+ end
378
+ end
379
+
380
+ if argument_alias and argument_alias.is_a? CLASP::Option and not value
381
+
382
+ want_option_value = true
383
+ options << Option.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, nil, argument_alias ? argument_alias.extras : nil)
384
+ elsif value
385
+
386
+ want_option_value = false
387
+ options << Option.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, value, argument_alias ? argument_alias.extras : nil)
388
+ else
389
+
390
+ want_option_value = false
391
+ flags << Flag.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, argument_alias ? argument_alias.extras : nil)
392
+ end
393
+
394
+ next
395
+ end
396
+ end
397
+
398
+ if want_option_value and not forced_value
399
+
400
+ option = options[-1]
401
+ option.instance_eval("@value='#{arg}'")
402
+ want_option_value = false
403
+ else
404
+
405
+ values << arg
406
+ end
407
+ end
408
+
409
+ return flags, options, values
410
+
411
+ end
412
+
413
+ # ######################
414
+ # Attributes
415
+
416
+ public
417
+ # an immutable array of aliases
418
+ attr_reader :aliases
419
+
420
+ # an immutable array of flags
421
+ attr_reader :flags
422
+
423
+ # an immutable array of options
424
+ attr_reader :options
425
+
426
+ # an immutable array of values
427
+ attr_reader :values
428
+
429
+ # the (possibly mutated) array of arguments instance passed to new
430
+ attr_reader :argv
431
+
432
+ # unchanged copy of the original array of arguments passed to new
433
+ attr_reader :argv_original_copy
434
+ end
435
+
436
+ # ######################################################################## #
437
+ # module
438
+
439
+ end # module CLASP
440
+
441
+ # ######################################################################## #
442
+ # extensions
443
+
444
+ #:nodoc:
445
+ class Array
446
+
447
+ # Monkey-patched Array#== in order to handle comparison with
448
+ # ImmutableArray
449
+ #
450
+ # NOTE: do not do so for +eql?+
451
+
452
+ #:nodoc:
453
+ alias_method :old_equal, :==
454
+
455
+ undef :==
456
+
457
+ #:nodoc:
458
+ def ==(rhs)
459
+
460
+ return rhs == self if rhs.is_a? CLASP::Arguments::ImmutableArray
461
+
462
+ old_equal rhs
463
+ end
464
+ end
465
+
466
+ # ############################## end of file ############################# #
467
+