wooga_docopt 0.6.0.1

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.
@@ -0,0 +1,24 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Example of program which uses [options] shortcut in pattern.
5
+
6
+ Usage:
7
+ #{__FILE__} [options] <port>
8
+
9
+ Options:
10
+ -h --help show this help message and exit
11
+ --version show version and exit
12
+ -n, --number N use N as a number
13
+ -t, --timeout TIMEOUT set timeout TIMEOUT seconds
14
+ --apply apply changes to database
15
+ -q operate in quiet mode
16
+
17
+ DOCOPT
18
+
19
+
20
+ begin
21
+ puts Docopt::docopt(doc, version: '1.0.0rc2').to_s
22
+ rescue Docopt::Exit => e
23
+ puts e.message
24
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Usage:
5
+ #{__FILE__} tcp <host> <port> [--timeout=<seconds>]
6
+ #{__FILE__} serial <port> [--baud=9600] [--timeout=<seconds>]
7
+ #{__FILE__} -h | --help | --version
8
+
9
+ DOCOPT
10
+
11
+ begin
12
+ require "pp"
13
+ pp Docopt::docopt(doc)
14
+ rescue Docopt::Exit => e
15
+ puts e.message
16
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Usage: #{__FILE__} --help
5
+ #{__FILE__} -v...
6
+ #{__FILE__} go [go]
7
+ #{__FILE__} (--path=<path>)...
8
+ #{__FILE__} <file> <file>
9
+
10
+ Try: #{__FILE__} -vvvvvvvvvv
11
+ #{__FILE__} go go
12
+ #{__FILE__} --path ./here --path ./there
13
+ #{__FILE__} this.txt that.txt
14
+
15
+ DOCOPT
16
+
17
+ begin
18
+ require "pp"
19
+ pp Docopt::docopt(doc)
20
+ rescue Docopt::Exit => e
21
+ puts e.message
22
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Example of program with many options using docopt.
5
+
6
+ Usage:
7
+ #{__FILE__} [-hvqrf NAME] [--exclude=PATTERNS]
8
+ [--select=ERRORS | --ignore=ERRORS] [--show-source]
9
+ [--statistics] [--count] [--benchmark] PATH...
10
+ #{__FILE__} (--doctest | --testsuite=DIR)
11
+ #{__FILE__} --version
12
+
13
+ Arguments:
14
+ PATH destination path
15
+
16
+ Options:
17
+ -h --help show this help message and exit
18
+ --version show version and exit
19
+ -v --verbose print status messages
20
+ -q --quiet report only file names
21
+ -r --repeat show all occurrences of the same error
22
+ --exclude=PATTERNS exclude files or directories which match these comma
23
+ separated patterns [default: .svn,CVS,.bzr,.hg,.git]
24
+ -f NAME --file=NAME when parsing directories, only check filenames matching
25
+ these comma separated patterns [default: *#{__FILE__}]
26
+ --select=ERRORS select errors and warnings (e.g. E,W6)
27
+ --ignore=ERRORS skip errors and warnings (e.g. E4,W)
28
+ --show-source show source code for each error
29
+ --statistics count errors and warnings
30
+ --count print total number of errors and warnings to standard
31
+ error and set exit code to 1 if total is not null
32
+ --benchmark measure processing speed
33
+ --testsuite=DIR run regression tests from dir
34
+ --doctest run doctest on myself
35
+
36
+
37
+ DOCOPT
38
+
39
+ begin
40
+ require "pp"
41
+ pp Docopt::docopt(doc)
42
+ rescue Docopt::Exit => e
43
+ puts e.message
44
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Usage:
5
+ #{__FILE__} remote [-v | --verbose]
6
+ #{__FILE__} remote add [-t <branch>] [-m <master>] [-f]
7
+ [--tags|--no-tags] [--mirror] <name> <url>
8
+ #{__FILE__} remote rename <old> <new>
9
+ #{__FILE__} remote rm <name>
10
+ #{__FILE__} remote set-head <name> (-a | -d | <branch>)
11
+ #{__FILE__} remote set-branches <name> [--add] <branch>...
12
+ #{__FILE__} remote set-url [--push] <name> <newurl> [<oldurl>]
13
+ #{__FILE__} remote set-url --add [--push] <name> <newurl>
14
+ #{__FILE__} remote set-url --delete [--push] <name> <url>
15
+ #{__FILE__} remote [-v | --verbose] show [-n] <name>
16
+ #{__FILE__} remote prune [-n | --dry-run] <name>
17
+ #{__FILE__} remote [-v | --verbose] update [-p | --prune]
18
+ [(<group> | <remote>)...]
19
+
20
+ Options:
21
+ -v, --verbose
22
+ -t <branch>
23
+ -m <master>
24
+ -f
25
+ --tags
26
+ --no-tags
27
+ --mittor
28
+ -a
29
+ -d
30
+ -n, --dry-run
31
+ -p, --prune
32
+ --add
33
+ --delete
34
+ --push
35
+ --mirror
36
+
37
+ DOCOPT
38
+
39
+ begin
40
+ require "pp"
41
+ pp Docopt::docopt(doc)
42
+ rescue Docopt::Exit => e
43
+ puts e.message
44
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ #The *popular* naval fate example
4
+
5
+ doc = <<DOCOPT
6
+ Naval Fate.
7
+
8
+ Usage:
9
+ #{__FILE__} ship new <name>...
10
+ #{__FILE__} ship <name> move <x> <y> [--speed=<kn>]
11
+ #{__FILE__} ship shoot <x> <y>
12
+ #{__FILE__} mine (set|remove) <x> <y> [--moored|--drifting]
13
+ #{__FILE__} -h | --help
14
+ #{__FILE__} --version
15
+
16
+ Options:
17
+ -h --help Show this screen.
18
+ --version Show version.
19
+ --speed=<kn> Speed in knots [default: 10].
20
+ --moored Moored (anchored) mine.
21
+ --drifting Drifting mine.
22
+
23
+ DOCOPT
24
+
25
+ begin
26
+ require "pp"
27
+ pp Docopt::docopt(doc)
28
+ rescue Docopt::Exit => e
29
+ puts e.message
30
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Usage: #{__FILE__} [-h | --help] (ODD EVEN)...
5
+
6
+ Example, try:
7
+ #{__FILE__} 1 2 3 4
8
+
9
+ Options:
10
+ -h, --help
11
+
12
+ DOCOPT
13
+
14
+ begin
15
+ require "pp"
16
+ pp Docopt::docopt(doc)
17
+ rescue Docopt::Exit => e
18
+ puts e.message
19
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path("../../lib/docopt.rb", __FILE__)
2
+
3
+ doc = <<DOCOPT
4
+ Usage:
5
+ #{__FILE__} tcp <host> <port> [--timeout=<seconds>]
6
+ #{__FILE__} serial <port> [--baud=9600] [--timeout=<seconds>]
7
+ #{__FILE__} -h | --help | --version
8
+
9
+ DOCOPT
10
+
11
+ begin
12
+ require "pp"
13
+ pp Docopt::docopt(doc)
14
+ rescue Docopt::Exit => e
15
+ puts e.message
16
+ end
data/lib/docopt.rb ADDED
@@ -0,0 +1,670 @@
1
+ module Docopt
2
+ VERSION = '0.6.0'
3
+ end
4
+ module Docopt
5
+ class DocoptLanguageError < SyntaxError
6
+ end
7
+
8
+ class Exit < RuntimeError
9
+ def self.usage
10
+ @@usage
11
+ end
12
+
13
+ def self.set_usage(usage)
14
+ @@usage = usage ? usage : ''
15
+ end
16
+
17
+ def message
18
+ @@message
19
+ end
20
+
21
+ def initialize(message='')
22
+ @@message = (message + "\n" + @@usage).strip
23
+ end
24
+ end
25
+
26
+ class Pattern
27
+ attr_accessor :children
28
+
29
+ def ==(other)
30
+ return self.inspect == other.inspect
31
+ end
32
+
33
+ def to_str
34
+ return self.inspect
35
+ end
36
+
37
+ def dump
38
+ puts ::Docopt::dump_patterns(self)
39
+ end
40
+
41
+ def fix
42
+ fix_identities
43
+ fix_repeating_arguments
44
+ return self
45
+ end
46
+
47
+ def fix_identities(uniq=nil)
48
+ if not instance_variable_defined?(:@children)
49
+ return self
50
+ end
51
+ uniq ||= flat.uniq
52
+
53
+ @children.each_with_index do |c, i|
54
+ if not c.instance_variable_defined?(:@children)
55
+ if !uniq.include?(c)
56
+ raise RuntimeError
57
+ end
58
+ @children[i] = uniq[uniq.index(c)]
59
+ else
60
+ c.fix_identities(uniq)
61
+ end
62
+ end
63
+ end
64
+
65
+ def fix_repeating_arguments
66
+ either.children.map { |c| c.children }.each do |case_|
67
+ case_.select { |c| case_.count(c) > 1 }.each do |e|
68
+ if e.class == Argument or (e.class == Option and e.argcount > 0)
69
+ if e.value == nil
70
+ e.value = []
71
+ elsif e.value.class != Array
72
+ e.value = e.value.split
73
+ end
74
+ end
75
+ if e.class == Command or (e.class == Option and e.argcount == 0)
76
+ e.value = 0
77
+ end
78
+ end
79
+ end
80
+
81
+ return self
82
+ end
83
+
84
+ def either
85
+ ret = []
86
+ groups = [[self]]
87
+ while groups.count > 0
88
+ children = groups.shift
89
+ types = children.map { |c| c.class }
90
+
91
+ if types.include?(Either)
92
+ either = children.select { |c| c.class == Either }[0]
93
+ children.slice!(children.index(either))
94
+ for c in either.children
95
+ groups << [c] + children
96
+ end
97
+ elsif types.include?(Required)
98
+ required = children.select { |c| c.class == Required }[0]
99
+ children.slice!(children.index(required))
100
+ groups << required.children + children
101
+
102
+ elsif types.include?(Optional)
103
+ optional = children.select { |c| c.class == Optional }[0]
104
+ children.slice!(children.index(optional))
105
+ groups << optional.children + children
106
+
107
+ elsif types.include?(AnyOptions)
108
+ anyoptions = children.select { |c| c.class == AnyOptions }[0]
109
+ children.slice!(children.index(anyoptions))
110
+ groups << anyoptions.children + children
111
+
112
+ elsif types.include?(OneOrMore)
113
+ oneormore = children.select { |c| c.class == OneOrMore }[0]
114
+ children.slice!(children.index(oneormore))
115
+ groups << (oneormore.children * 2) + children
116
+
117
+ else
118
+ ret << children
119
+ end
120
+ end
121
+
122
+ args = ret.map { |e| Required.new(*e) }
123
+ return Either.new(*args)
124
+ end
125
+ end
126
+
127
+
128
+ class ChildPattern < Pattern
129
+ attr_accessor :name, :value
130
+
131
+ def initialize(name, value=nil)
132
+ @name = name
133
+ @value = value
134
+ end
135
+
136
+ def inspect()
137
+ "#{self.class.name}(#{self.name}, #{self.value})"
138
+ end
139
+
140
+ def flat(*types)
141
+ if types.empty? or types.include?(self.class)
142
+ [self]
143
+ else
144
+ []
145
+ end
146
+ end
147
+
148
+
149
+ def match(left, collected=nil)
150
+ collected ||= []
151
+ pos, match = self.single_match(left)
152
+ if match == nil
153
+ return [false, left, collected]
154
+ end
155
+
156
+ left_ = left.dup
157
+ left_.slice!(pos)
158
+
159
+ same_name = collected.select { |a| a.name == self.name }
160
+ if @value.is_a? Array or @value.is_a? Integer
161
+ if @value.is_a? Integer
162
+ increment = 1
163
+ else
164
+ increment = match.value.is_a?(String) ? [match.value] : match.value
165
+ end
166
+ if same_name.count == 0
167
+ match.value = increment
168
+ return [true, left_, collected + [match]]
169
+ end
170
+ same_name[0].value += increment
171
+ return [true, left_, collected]
172
+ end
173
+ return [true, left_, collected + [match]]
174
+ end
175
+ end
176
+
177
+ class ParentPattern < Pattern
178
+ attr_accessor :children
179
+
180
+ def initialize(*children)
181
+ @children = children
182
+ end
183
+
184
+ def inspect
185
+ childstr = self.children.map { |a| a.inspect }
186
+ return "#{self.class.name}(#{childstr.join(", ")})"
187
+ end
188
+
189
+ def flat(*types)
190
+ if types.include?(self.class)
191
+ [self]
192
+ else
193
+ self.children.map { |c| c.flat(*types) }.flatten
194
+ end
195
+ end
196
+ end
197
+
198
+ class Argument < ChildPattern
199
+
200
+ def single_match(left)
201
+ left.each_with_index do |p, n|
202
+ if p.class == Argument
203
+ return [n, Argument.new(self.name, p.value)]
204
+ end
205
+ end
206
+ return [nil, nil]
207
+ end
208
+
209
+ def self.parse(class_, source)
210
+ name = /(<\S*?>)/.match(source)[0]
211
+ value = /\[default: (.*)\]/i.match(source)
212
+ class_.new(name, (value ? value[0] : nil))
213
+ end
214
+ end
215
+
216
+
217
+ class Command < Argument
218
+ def initialize(name, value=false)
219
+ @name = name
220
+ @value = value
221
+ end
222
+
223
+ def single_match(left)
224
+ left.each_with_index do |p, n|
225
+ if p.class == Argument
226
+ if p.value == self.name
227
+ return n, Command.new(self.name, true)
228
+ else
229
+ break
230
+ end
231
+ end
232
+ end
233
+ return [nil, nil]
234
+ end
235
+ end
236
+
237
+
238
+ class Option < ChildPattern
239
+ attr_reader :short, :long
240
+ attr_accessor :argcount
241
+
242
+ def initialize(short=nil, long=nil, argcount=0, value=false)
243
+ unless [0, 1].include? argcount
244
+ raise RuntimeError
245
+ end
246
+
247
+ @short, @long = short, long
248
+ @argcount, @value = argcount, value
249
+
250
+ if value == false and argcount > 0
251
+ @value = nil
252
+ else
253
+ @value = value
254
+ end
255
+ end
256
+
257
+ def self.parse(option_description)
258
+ short, long, argcount, value = nil, nil, 0, false
259
+ options, _, description = option_description.strip.partition(' ')
260
+
261
+ options = options.gsub(',', ' ').gsub('=', ' ')
262
+
263
+ for s in options.split
264
+ if s.start_with?('--')
265
+ long = s
266
+ elsif s.start_with?('-')
267
+ short = s
268
+ else
269
+ argcount = 1
270
+ end
271
+ end
272
+ if argcount > 0
273
+ matched = description.scan(/\[default: (.*)\]/i)
274
+ value = matched[0][0] if matched.count > 0
275
+ end
276
+ new(short, long, argcount, value)
277
+ end
278
+
279
+ def single_match(left)
280
+ left.each_with_index do |p, n|
281
+ if self.name == p.name
282
+ return [n, p]
283
+ end
284
+ end
285
+ return [nil, nil]
286
+ end
287
+
288
+ def name
289
+ return self.long ? self.long : self.short
290
+ end
291
+
292
+ def inspect
293
+ return "Option(#{self.short}, #{self.long}, #{self.argcount}, #{self.value})"
294
+ end
295
+ end
296
+
297
+ class Required < ParentPattern
298
+ def match(left, collected=nil)
299
+ collected ||= []
300
+ l = left
301
+ c = collected
302
+
303
+ for p in self.children
304
+ matched, l, c = p.match(l, c)
305
+ if not matched
306
+ return [false, left, collected]
307
+ end
308
+ end
309
+ return [true, l, c]
310
+ end
311
+ end
312
+
313
+ class Optional < ParentPattern
314
+ def match(left, collected=nil)
315
+ collected ||= []
316
+ for p in self.children
317
+ m, left, collected = p.match(left, collected)
318
+ end
319
+ return [true, left, collected]
320
+ end
321
+ end
322
+
323
+ class AnyOptions < Optional
324
+ end
325
+
326
+ class OneOrMore < ParentPattern
327
+ def match(left, collected=nil)
328
+ if self.children.count != 1
329
+ raise RuntimeError
330
+ end
331
+
332
+ collected ||= []
333
+ l = left
334
+ c = collected
335
+ l_ = nil
336
+ matched = true
337
+ times = 0
338
+ while matched
339
+ # could it be that something didn't match but changed l or c?
340
+ matched, l, c = self.children[0].match(l, c)
341
+ times += (matched ? 1 : 0)
342
+ if l_ == l
343
+ break
344
+ end
345
+ l_ = l
346
+ end
347
+ if times >= 1
348
+ return [true, l, c]
349
+ end
350
+ return [false, left, collected]
351
+ end
352
+ end
353
+
354
+ class Either < ParentPattern
355
+ def match(left, collected=nil)
356
+ collected ||= []
357
+ outcomes = []
358
+ for p in self.children
359
+ matched, _, _ = outcome = p.match(left, collected)
360
+ if matched
361
+ outcomes << outcome
362
+ end
363
+ end
364
+
365
+ if outcomes.count > 0
366
+ return outcomes.min_by do |outcome|
367
+ outcome[1] == nil ? 0 : outcome[1].count
368
+ end
369
+ end
370
+ return [false, left, collected]
371
+ end
372
+ end
373
+
374
+ class TokenStream < Array
375
+ attr_reader :error
376
+
377
+ def initialize(source, error)
378
+ if !source
379
+ source = []
380
+ elsif source.class != ::Array
381
+ source = source.split
382
+ end
383
+ super(source)
384
+ @error = error
385
+ end
386
+
387
+ def move
388
+ return self.shift
389
+ end
390
+
391
+ def current
392
+ return self[0]
393
+ end
394
+ end
395
+
396
+ class << self
397
+ def parse_long(tokens, options)
398
+ long, eq, value = tokens.move().partition('=')
399
+ unless long.start_with?('--')
400
+ raise RuntimeError
401
+ end
402
+ value = (eq == value and eq == '') ? nil : value
403
+
404
+ similar = options.select { |o| o.long and o.long == long }
405
+
406
+ if tokens.error == Exit and similar == []
407
+ similar = options.select { |o| o.long and o.long.start_with?(long) }
408
+ end
409
+
410
+ if similar.count > 1
411
+ ostr = similar.map { |o| o.long }.join(', ')
412
+ raise tokens.error, "#{long} is not a unique prefix: #{ostr}?"
413
+ elsif similar.count < 1
414
+ argcount = (eq == '=' ? 1 : 0)
415
+ o = Option.new(nil, long, argcount)
416
+ options << o
417
+ if tokens.error == Exit
418
+ o = Option.new(nil, long, argcount, (argcount == 1 ? value : true))
419
+ end
420
+ else
421
+ s0 = similar[0]
422
+ o = Option.new(s0.short, s0.long, s0.argcount, s0.value)
423
+ if o.argcount == 0
424
+ if !value.nil?
425
+ raise tokens.error, "#{o.long} must not have an argument"
426
+ end
427
+ else
428
+ if value.nil?
429
+ if tokens.current().nil?
430
+ raise tokens.error, "#{o.long} requires argument"
431
+ end
432
+ value = tokens.move()
433
+ end
434
+ end
435
+ if tokens.error == Exit
436
+ o.value = (!value.nil? ? value : true)
437
+ end
438
+ end
439
+ return [o]
440
+ end
441
+
442
+ def parse_shorts(tokens, options)
443
+ token = tokens.move()
444
+ unless token.start_with?('-') && !token.start_with?('--')
445
+ raise RuntimeError
446
+ end
447
+ left = token[1..-1]
448
+ parsed = []
449
+ while left != ''
450
+ short, left = '-' + left[0], left[1..-1]
451
+ similar = options.select { |o| o.short == short }
452
+ if similar.count > 1
453
+ raise tokens.error, "#{short} is specified ambiguously #{similar.count} times"
454
+ elsif similar.count < 1
455
+ o = Option.new(short, nil, 0)
456
+ options << o
457
+ if tokens.error == Exit
458
+ o = Option.new(short, nil, 0, true)
459
+ end
460
+ else
461
+ s0 = similar[0]
462
+ o = Option.new(short, s0.long, s0.argcount, s0.value)
463
+ value = nil
464
+ if o.argcount != 0
465
+ if left == ''
466
+ if tokens.current().nil?
467
+ raise tokens.error, "#{short} requires argument"
468
+ end
469
+ value = tokens.move()
470
+ else
471
+ value = left
472
+ left = ''
473
+ end
474
+ end
475
+ if tokens.error == Exit
476
+ o.value = (!value.nil? ? value : true)
477
+ end
478
+ end
479
+ parsed << o
480
+ end
481
+ return parsed
482
+ end
483
+
484
+
485
+ def parse_pattern(source, options)
486
+ tokens = TokenStream.new(source.gsub(/([\[\]\(\)\|]|\.\.\.)/, ' \1 '), DocoptLanguageError)
487
+
488
+ result = parse_expr(tokens, options)
489
+ if tokens.current() != nil
490
+ raise tokens.error, "unexpected ending: #{tokens.join(" ")}"
491
+ end
492
+ return Required.new(*result)
493
+ end
494
+
495
+
496
+ def parse_expr(tokens, options)
497
+ seq = parse_seq(tokens, options)
498
+ if tokens.current() != '|'
499
+ return seq
500
+ end
501
+ result = seq.count > 1 ? [Required.new(*seq)] : seq
502
+
503
+ while tokens.current() == '|'
504
+ tokens.move()
505
+ seq = parse_seq(tokens, options)
506
+ result += seq.count > 1 ? [Required.new(*seq)] : seq
507
+ end
508
+ return result.count > 1 ? [Either.new(*result)] : result
509
+ end
510
+
511
+ def parse_seq(tokens, options)
512
+ result = []
513
+ stop = [nil, ']', ')', '|']
514
+ while !stop.include?(tokens.current)
515
+ atom = parse_atom(tokens, options)
516
+ if tokens.current() == '...'
517
+ atom = [OneOrMore.new(*atom)]
518
+ tokens.move()
519
+ end
520
+ result += atom
521
+ end
522
+ return result
523
+ end
524
+
525
+ def parse_atom(tokens, options)
526
+ token = tokens.current()
527
+ result = []
528
+
529
+ if ['(' , '['].include? token
530
+ tokens.move()
531
+ if token == '('
532
+ matching = ')'
533
+ pattern = Required
534
+ else
535
+ matching = ']'
536
+ pattern = Optional
537
+ end
538
+ result = pattern.new(*parse_expr(tokens, options))
539
+ if tokens.move() != matching
540
+ raise tokens.error, "unmatched '#{token}'"
541
+ end
542
+ return [result]
543
+ elsif token == 'options'
544
+ tokens.move()
545
+ return [AnyOptions.new]
546
+ elsif token.start_with?('--') and token != '--'
547
+ return parse_long(tokens, options)
548
+ elsif token.start_with?('-') and not ['-', '--'].include? token
549
+ return parse_shorts(tokens, options)
550
+ elsif token.start_with?('<') and token.end_with?('>') or (token.upcase == token && token.match(/[A-Z]/))
551
+ return [Argument.new(tokens.move())]
552
+ else
553
+ return [Command.new(tokens.move())]
554
+ end
555
+ end
556
+
557
+ def parse_argv(tokens, options, options_first=false)
558
+ parsed = []
559
+ while tokens.current() != nil
560
+ if tokens.current() == '--'
561
+ return parsed + tokens.map { |v| Argument.new(nil, v) }
562
+ elsif tokens.current().start_with?('--')
563
+ parsed += parse_long(tokens, options)
564
+ elsif tokens.current().start_with?('-') and tokens.current() != '-'
565
+ parsed += parse_shorts(tokens, options)
566
+ elsif options_first
567
+ return parsed + tokens.map { |v| Argument.new(nil, v) }
568
+ else
569
+ parsed << Argument.new(nil, tokens.move())
570
+ end
571
+ end
572
+ return parsed
573
+ end
574
+
575
+ def parse_defaults(doc)
576
+ split = doc.split(/^ *(<\S+?>|-\S+?)/).drop(1)
577
+ split = split.each_slice(2).reject { |pair| pair.count != 2 }.map { |s1, s2| s1 + s2 }
578
+ split.select { |s| s.start_with?('-') }.map { |s| Option.parse(s) }
579
+ end
580
+
581
+ def printable_usage(doc)
582
+ usage_split = doc.split(/([Uu][Ss][Aa][Gg][Ee]:)/)
583
+ if usage_split.count < 3
584
+ raise DocoptLanguageError, '"usage:" (case-insensitive) not found.'
585
+ end
586
+ if usage_split.count > 3
587
+ raise DocoptLanguageError, 'More than one "usage:" (case-insensitive).'
588
+ end
589
+ return usage_split.drop(1).join().split(/\n\s*\n/)[0].strip
590
+ end
591
+
592
+ def formal_usage(printable_usage)
593
+ pu = printable_usage.split().drop(1) # split and drop "usage:"
594
+
595
+ ret = []
596
+ for s in pu.drop(1)
597
+ if s == pu[0]
598
+ ret << ') | ('
599
+ else
600
+ ret << s
601
+ end
602
+ end
603
+
604
+ return '( ' + ret.join(' ') + ' )'
605
+ end
606
+
607
+ def dump_patterns(pattern, indent=0)
608
+ ws = " " * 4 * indent
609
+ out = ""
610
+ if pattern.class == Array
611
+ if pattern.count > 0
612
+ out << ws << "[\n"
613
+ for p in pattern
614
+ out << dump_patterns(p, indent+1).rstrip << "\n"
615
+ end
616
+ out << ws << "]\n"
617
+ else
618
+ out << ws << "[]\n"
619
+ end
620
+
621
+ elsif pattern.class.ancestors.include?(ParentPattern)
622
+ out << ws << pattern.class.name << "(\n"
623
+ for p in pattern.children
624
+ out << dump_patterns(p, indent+1).rstrip << "\n"
625
+ end
626
+ out << ws << ")\n"
627
+
628
+ else
629
+ out << ws << pattern.inspect
630
+ end
631
+ return out
632
+ end
633
+
634
+ def extras(help, version, options, doc)
635
+ if help and options.any? { |o| ['-h', '--help'].include?(o.name) && o.value }
636
+ Exit.set_usage(nil)
637
+ raise Exit, doc.strip
638
+ end
639
+ if version and options.any? { |o| o.name == '--version' && o.value }
640
+ Exit.set_usage(nil)
641
+ raise Exit, version
642
+ end
643
+ end
644
+
645
+ def docopt(doc, params={})
646
+ default = {:version => nil, :argv => nil, :help => true, :options_first => false}
647
+ params = default.merge(params)
648
+ params[:argv] = ARGV if !params[:argv]
649
+
650
+ Exit.set_usage(printable_usage(doc))
651
+ options = parse_defaults(doc)
652
+ pattern = parse_pattern(formal_usage(Exit.usage), options)
653
+ argv = parse_argv(TokenStream.new(params[:argv], Exit), options, params[:options_first])
654
+ pattern_options = pattern.flat(Option).uniq
655
+ pattern.flat(AnyOptions).each do |ao|
656
+ doc_options = parse_defaults(doc)
657
+ ao.children = doc_options.reject { |o| pattern_options.include?(o) }.uniq
658
+ end
659
+ extras(params[:help], params[:version], argv, doc)
660
+
661
+ matched, left, collected = pattern.fix().match(argv)
662
+ collected ||= []
663
+
664
+ if matched and (left.count == 0)
665
+ return Hash[(pattern.flat + collected).map { |a| [a.name, a.value] }]
666
+ end
667
+ raise Exit
668
+ end
669
+ end
670
+ end