Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
chromebrew
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
chromebrew
Commits
78510bff
Commit
78510bff
authored
Aug 18, 2017
by
Kazushi (Jam) Marukawa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add docopt.rb
parent
513709d6
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
692 additions
and
0 deletions
+692
-0
lib/docopt.LICENSE
lib/docopt.LICENSE
+22
-0
lib/docopt.rb
lib/docopt.rb
+670
-0
No files found.
lib/docopt.LICENSE
0 → 100644
View file @
78510bff
Copyright (c) 2012 Vladimir Keleshev <vladimir@keleshev.com>
Blake Williams <code@shabbyrobe.org>
Alex Speller <alex@alexspeller.com>
Nima Johari
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
lib/docopt.rb
0 → 100644
View file @
78510bff
module
Docopt
VERSION
=
'0.6.0'
end
module
Docopt
class
DocoptLanguageError
<
SyntaxError
end
class
Exit
<
RuntimeError
def
self
.
usage
@@usage
end
def
self
.
set_usage
(
usage
)
@@usage
=
usage
?
usage
:
''
end
def
message
@@message
end
def
initialize
(
message
=
''
)
@@message
=
(
message
+
"
\n
"
+
@@usage
).
strip
end
end
class
Pattern
attr_accessor
:children
def
==
(
other
)
return
self
.
inspect
==
other
.
inspect
end
def
to_str
return
self
.
inspect
end
def
dump
puts
::
Docopt
::
dump_patterns
(
self
)
end
def
fix
fix_identities
fix_repeating_arguments
return
self
end
def
fix_identities
(
uniq
=
nil
)
if
not
instance_variable_defined?
(
:@children
)
return
self
end
uniq
||=
flat
.
uniq
@children
.
each_with_index
do
|
c
,
i
|
if
not
c
.
instance_variable_defined?
(
:@children
)
if
!
uniq
.
include?
(
c
)
raise
RuntimeError
end
@children
[
i
]
=
uniq
[
uniq
.
index
(
c
)]
else
c
.
fix_identities
(
uniq
)
end
end
end
def
fix_repeating_arguments
either
.
children
.
map
{
|
c
|
c
.
children
}.
each
do
|
case_
|
case_
.
select
{
|
c
|
case_
.
count
(
c
)
>
1
}.
each
do
|
e
|
if
e
.
class
==
Argument
or
(
e
.
class
==
Option
and
e
.
argcount
>
0
)
if
e
.
value
==
nil
e
.
value
=
[]
elsif
e
.
value
.
class
!=
Array
e
.
value
=
e
.
value
.
split
end
end
if
e
.
class
==
Command
or
(
e
.
class
==
Option
and
e
.
argcount
==
0
)
e
.
value
=
0
end
end
end
return
self
end
def
either
ret
=
[]
groups
=
[[
self
]]
while
groups
.
count
>
0
children
=
groups
.
shift
types
=
children
.
map
{
|
c
|
c
.
class
}
if
types
.
include?
(
Either
)
either
=
children
.
select
{
|
c
|
c
.
class
==
Either
}[
0
]
children
.
slice!
(
children
.
index
(
either
))
for
c
in
either
.
children
groups
<<
[
c
]
+
children
end
elsif
types
.
include?
(
Required
)
required
=
children
.
select
{
|
c
|
c
.
class
==
Required
}[
0
]
children
.
slice!
(
children
.
index
(
required
))
groups
<<
required
.
children
+
children
elsif
types
.
include?
(
Optional
)
optional
=
children
.
select
{
|
c
|
c
.
class
==
Optional
}[
0
]
children
.
slice!
(
children
.
index
(
optional
))
groups
<<
optional
.
children
+
children
elsif
types
.
include?
(
AnyOptions
)
anyoptions
=
children
.
select
{
|
c
|
c
.
class
==
AnyOptions
}[
0
]
children
.
slice!
(
children
.
index
(
anyoptions
))
groups
<<
anyoptions
.
children
+
children
elsif
types
.
include?
(
OneOrMore
)
oneormore
=
children
.
select
{
|
c
|
c
.
class
==
OneOrMore
}[
0
]
children
.
slice!
(
children
.
index
(
oneormore
))
groups
<<
(
oneormore
.
children
*
2
)
+
children
else
ret
<<
children
end
end
args
=
ret
.
map
{
|
e
|
Required
.
new
(
*
e
)
}
return
Either
.
new
(
*
args
)
end
end
class
ChildPattern
<
Pattern
attr_accessor
:name
,
:value
def
initialize
(
name
,
value
=
nil
)
@name
=
name
@value
=
value
end
def
inspect
()
"
#{
self
.
class
.
name
}
(
#{
self
.
name
}
,
#{
self
.
value
}
)"
end
def
flat
(
*
types
)
if
types
.
empty?
or
types
.
include?
(
self
.
class
)
[
self
]
else
[]
end
end
def
match
(
left
,
collected
=
nil
)
collected
||=
[]
pos
,
match
=
self
.
single_match
(
left
)
if
match
==
nil
return
[
false
,
left
,
collected
]
end
left_
=
left
.
dup
left_
.
slice!
(
pos
)
same_name
=
collected
.
select
{
|
a
|
a
.
name
==
self
.
name
}
if
@value
.
is_a?
Array
or
@value
.
is_a?
Integer
if
@value
.
is_a?
Integer
increment
=
1
else
increment
=
match
.
value
.
is_a?
(
String
)
?
[
match
.
value
]
:
match
.
value
end
if
same_name
.
count
==
0
match
.
value
=
increment
return
[
true
,
left_
,
collected
+
[
match
]]
end
same_name
[
0
].
value
+=
increment
return
[
true
,
left_
,
collected
]
end
return
[
true
,
left_
,
collected
+
[
match
]]
end
end
class
ParentPattern
<
Pattern
attr_accessor
:children
def
initialize
(
*
children
)
@children
=
children
end
def
inspect
childstr
=
self
.
children
.
map
{
|
a
|
a
.
inspect
}
return
"
#{
self
.
class
.
name
}
(
#{
childstr
.
join
(
", "
)
}
)"
end
def
flat
(
*
types
)
if
types
.
include?
(
self
.
class
)
[
self
]
else
self
.
children
.
map
{
|
c
|
c
.
flat
(
*
types
)
}.
flatten
end
end
end
class
Argument
<
ChildPattern
def
single_match
(
left
)
left
.
each_with_index
do
|
p
,
n
|
if
p
.
class
==
Argument
return
[
n
,
Argument
.
new
(
self
.
name
,
p
.
value
)]
end
end
return
[
nil
,
nil
]
end
def
self
.
parse
(
class_
,
source
)
name
=
/(<\S*?>)/
.
match
(
source
)[
0
]
value
=
/\[default: (.*)\]/i
.
match
(
source
)
class_
.
new
(
name
,
(
value
?
value
[
0
]
:
nil
))
end
end
class
Command
<
Argument
def
initialize
(
name
,
value
=
false
)
@name
=
name
@value
=
value
end
def
single_match
(
left
)
left
.
each_with_index
do
|
p
,
n
|
if
p
.
class
==
Argument
if
p
.
value
==
self
.
name
return
n
,
Command
.
new
(
self
.
name
,
true
)
else
break
end
end
end
return
[
nil
,
nil
]
end
end
class
Option
<
ChildPattern
attr_reader
:short
,
:long
attr_accessor
:argcount
def
initialize
(
short
=
nil
,
long
=
nil
,
argcount
=
0
,
value
=
false
)
unless
[
0
,
1
].
include?
argcount
raise
RuntimeError
end
@short
,
@long
=
short
,
long
@argcount
,
@value
=
argcount
,
value
if
value
==
false
and
argcount
>
0
@value
=
nil
else
@value
=
value
end
end
def
self
.
parse
(
option_description
)
short
,
long
,
argcount
,
value
=
nil
,
nil
,
0
,
false
options
,
_
,
description
=
option_description
.
strip
.
partition
(
' '
)
options
=
options
.
gsub
(
','
,
' '
).
gsub
(
'='
,
' '
)
for
s
in
options
.
split
if
s
.
start_with?
(
'--'
)
long
=
s
elsif
s
.
start_with?
(
'-'
)
short
=
s
else
argcount
=
1
end
end
if
argcount
>
0
matched
=
description
.
scan
(
/\[default: (.*)\]/i
)
value
=
matched
[
0
][
0
]
if
matched
.
count
>
0
end
new
(
short
,
long
,
argcount
,
value
)
end
def
single_match
(
left
)
left
.
each_with_index
do
|
p
,
n
|
if
self
.
name
==
p
.
name
return
[
n
,
p
]
end
end
return
[
nil
,
nil
]
end
def
name
return
self
.
long
?
self
.
long
:
self
.
short
end
def
inspect
return
"Option(
#{
self
.
short
}
,
#{
self
.
long
}
,
#{
self
.
argcount
}
,
#{
self
.
value
}
)"
end
end
class
Required
<
ParentPattern
def
match
(
left
,
collected
=
nil
)
collected
||=
[]
l
=
left
c
=
collected
for
p
in
self
.
children
matched
,
l
,
c
=
p
.
match
(
l
,
c
)
if
not
matched
return
[
false
,
left
,
collected
]
end
end
return
[
true
,
l
,
c
]
end
end
class
Optional
<
ParentPattern
def
match
(
left
,
collected
=
nil
)
collected
||=
[]
for
p
in
self
.
children
m
,
left
,
collected
=
p
.
match
(
left
,
collected
)
end
return
[
true
,
left
,
collected
]
end
end
class
AnyOptions
<
Optional
end
class
OneOrMore
<
ParentPattern
def
match
(
left
,
collected
=
nil
)
if
self
.
children
.
count
!=
1
raise
RuntimeError
end
collected
||=
[]
l
=
left
c
=
collected
l_
=
nil
matched
=
true
times
=
0
while
matched
# could it be that something didn't match but changed l or c?
matched
,
l
,
c
=
self
.
children
[
0
].
match
(
l
,
c
)
times
+=
(
matched
?
1
:
0
)
if
l_
==
l
break
end
l_
=
l
end
if
times
>=
1
return
[
true
,
l
,
c
]
end
return
[
false
,
left
,
collected
]
end
end
class
Either
<
ParentPattern
def
match
(
left
,
collected
=
nil
)
collected
||=
[]
outcomes
=
[]
for
p
in
self
.
children
matched
,
_
,
_
=
outcome
=
p
.
match
(
left
,
collected
)
if
matched
outcomes
<<
outcome
end
end
if
outcomes
.
count
>
0
return
outcomes
.
min_by
do
|
outcome
|
outcome
[
1
]
==
nil
?
0
:
outcome
[
1
].
count
end
end
return
[
false
,
left
,
collected
]
end
end
class
TokenStream
<
Array
attr_reader
:error
def
initialize
(
source
,
error
)
if
!
source
source
=
[]
elsif
source
.
class
!=
::
Array
source
=
source
.
split
end
super
(
source
)
@error
=
error
end
def
move
return
self
.
shift
end
def
current
return
self
[
0
]
end
end
class
<<
self
def
parse_long
(
tokens
,
options
)
long
,
eq
,
value
=
tokens
.
move
().
partition
(
'='
)
unless
long
.
start_with?
(
'--'
)
raise
RuntimeError
end
value
=
(
eq
==
value
and
eq
==
''
)
?
nil
:
value
similar
=
options
.
select
{
|
o
|
o
.
long
and
o
.
long
==
long
}
if
tokens
.
error
==
Exit
and
similar
==
[]
similar
=
options
.
select
{
|
o
|
o
.
long
and
o
.
long
.
start_with?
(
long
)
}
end
if
similar
.
count
>
1
ostr
=
similar
.
map
{
|
o
|
o
.
long
}.
join
(
', '
)
raise
tokens
.
error
,
"
#{
long
}
is not a unique prefix:
#{
ostr
}
?"
elsif
similar
.
count
<
1
argcount
=
(
eq
==
'='
?
1
:
0
)
o
=
Option
.
new
(
nil
,
long
,
argcount
)
options
<<
o
if
tokens
.
error
==
Exit
o
=
Option
.
new
(
nil
,
long
,
argcount
,
(
argcount
==
1
?
value
:
true
))
end
else
s0
=
similar
[
0
]
o
=
Option
.
new
(
s0
.
short
,
s0
.
long
,
s0
.
argcount
,
s0
.
value
)
if
o
.
argcount
==
0
if
!
value
.
nil?
raise
tokens
.
error
,
"
#{
o
.
long
}
must not have an argument"
end
else
if
value
.
nil?
if
tokens
.
current
().
nil?
raise
tokens
.
error
,
"
#{
o
.
long
}
requires argument"
end
value
=
tokens
.
move
()
end
end
if
tokens
.
error
==
Exit
o
.
value
=
(
!
value
.
nil?
?
value
:
true
)
end
end
return
[
o
]
end
def
parse_shorts
(
tokens
,
options
)
token
=
tokens
.
move
()
unless
token
.
start_with?
(
'-'
)
&&
!
token
.
start_with?
(
'--'
)
raise
RuntimeError
end
left
=
token
[
1
..-
1
]
parsed
=
[]
while
left
!=
''
short
,
left
=
'-'
+
left
[
0
],
left
[
1
..-
1
]
similar
=
options
.
select
{
|
o
|
o
.
short
==
short
}
if
similar
.
count
>
1
raise
tokens
.
error
,
"
#{
short
}
is specified ambiguously
#{
similar
.
count
}
times"
elsif
similar
.
count
<
1
o
=
Option
.
new
(
short
,
nil
,
0
)
options
<<
o
if
tokens
.
error
==
Exit
o
=
Option
.
new
(
short
,
nil
,
0
,
true
)
end
else
s0
=
similar
[
0
]
o
=
Option
.
new
(
short
,
s0
.
long
,
s0
.
argcount
,
s0
.
value
)
value
=
nil
if
o
.
argcount
!=
0
if
left
==
''
if
tokens
.
current
().
nil?
raise
tokens
.
error
,
"
#{
short
}
requires argument"
end
value
=
tokens
.
move
()
else
value
=
left
left
=
''
end
end
if
tokens
.
error
==
Exit
o
.
value
=
(
!
value
.
nil?
?
value
:
true
)
end
end
parsed
<<
o
end
return
parsed
end
def
parse_pattern
(
source
,
options
)
tokens
=
TokenStream
.
new
(
source
.
gsub
(
/([\[\]\(\)\|]|\.\.\.)/
,
' \1 '
),
DocoptLanguageError
)
result
=
parse_expr
(
tokens
,
options
)
if
tokens
.
current
()
!=
nil
raise
tokens
.
error
,
"unexpected ending:
#{
tokens
.
join
(
" "
)
}
"
end
return
Required
.
new
(
*
result
)
end
def
parse_expr
(
tokens
,
options
)
seq
=
parse_seq
(
tokens
,
options
)
if
tokens
.
current
()
!=
'|'
return
seq
end
result
=
seq
.
count
>
1
?
[
Required
.
new
(
*
seq
)]
:
seq
while
tokens
.
current
()
==
'|'
tokens
.
move
()
seq
=
parse_seq
(
tokens
,
options
)
result
+=
seq
.
count
>
1
?
[
Required
.
new
(
*
seq
)]
:
seq
end
return
result
.
count
>
1
?
[
Either
.
new
(
*
result
)]
:
result
end
def
parse_seq
(
tokens
,
options
)
result
=
[]
stop
=
[
nil
,
']'
,
')'
,
'|'
]
while
!
stop
.
include?
(
tokens
.
current
)
atom
=
parse_atom
(
tokens
,
options
)
if
tokens
.
current
()
==
'...'
atom
=
[
OneOrMore
.
new
(
*
atom
)]
tokens
.
move
()
end
result
+=
atom
end
return
result
end
def
parse_atom
(
tokens
,
options
)
token
=
tokens
.
current
()
result
=
[]
if
[
'('
,
'['
].
include?
token
tokens
.
move
()
if
token
==
'('
matching
=
')'
pattern
=
Required
else
matching
=
']'
pattern
=
Optional
end
result
=
pattern
.
new
(
*
parse_expr
(
tokens
,
options
))
if
tokens
.
move
()
!=
matching
raise
tokens
.
error
,
"unmatched '
#{
token
}
'"
end
return
[
result
]
elsif
token
==
'options'
tokens
.
move
()
return
[
AnyOptions
.
new
]
elsif
token
.
start_with?
(
'--'
)
and
token
!=
'--'
return
parse_long
(
tokens
,
options
)
elsif
token
.
start_with?
(
'-'
)
and
not
[
'-'
,
'--'
].
include?
token
return
parse_shorts
(
tokens
,
options
)
elsif
token
.
start_with?
(
'<'
)
and
token
.
end_with?
(
'>'
)
or
(
token
.
upcase
==
token
&&
token
.
match
(
/[A-Z]/
))
return
[
Argument
.
new
(
tokens
.
move
())]
else
return
[
Command
.
new
(
tokens
.
move
())]
end
end
def
parse_argv
(
tokens
,
options
,
options_first
=
false
)
parsed
=
[]
while
tokens
.
current
()
!=
nil
if
tokens
.
current
()
==
'--'
return
parsed
+
tokens
.
map
{
|
v
|
Argument
.
new
(
nil
,
v
)
}
elsif
tokens
.
current
().
start_with?
(
'--'
)
parsed
+=
parse_long
(
tokens
,
options
)
elsif
tokens
.
current
().
start_with?
(
'-'
)
and
tokens
.
current
()
!=
'-'
parsed
+=
parse_shorts
(
tokens
,
options
)
elsif
options_first
return
parsed
+
tokens
.
map
{
|
v
|
Argument
.
new
(
nil
,
v
)
}
else
parsed
<<
Argument
.
new
(
nil
,
tokens
.
move
())
end
end
return
parsed
end
def
parse_defaults
(
doc
)
split
=
doc
.
split
(
/^ *(<\S+?>|-\S+?)/
).
drop
(
1
)
split
=
split
.
each_slice
(
2
).
reject
{
|
pair
|
pair
.
count
!=
2
}.
map
{
|
s1
,
s2
|
s1
+
s2
}
split
.
select
{
|
s
|
s
.
start_with?
(
'-'
)
}.
map
{
|
s
|
Option
.
parse
(
s
)
}
end
def
printable_usage
(
doc
)
usage_split
=
doc
.
split
(
/([Uu][Ss][Aa][Gg][Ee]:)/
)
if
usage_split
.
count
<
3
raise
DocoptLanguageError
,
'"usage:" (case-insensitive) not found.'
end
if
usage_split
.
count
>
3
raise
DocoptLanguageError
,
'More than one "usage:" (case-insensitive).'
end
return
usage_split
.
drop
(
1
).
join
().
split
(
/\n\s*\n/
)[
0
].
strip
end
def
formal_usage
(
printable_usage
)
pu
=
printable_usage
.
split
().
drop
(
1
)
# split and drop "usage:"
ret
=
[]
for
s
in
pu
.
drop
(
1
)
if
s
==
pu
[
0
]
ret
<<
') | ('
else
ret
<<
s
end
end
return
'( '
+
ret
.
join
(
' '
)
+
' )'
end
def
dump_patterns
(
pattern
,
indent
=
0
)
ws
=
" "
*
4
*
indent
out
=
""
if
pattern
.
class
==
Array
if
pattern
.
count
>
0
out
<<
ws
<<
"[
\n
"
for
p
in
pattern
out
<<
dump_patterns
(
p
,
indent
+
1
).
rstrip
<<
"
\n
"
end
out
<<
ws
<<
"]
\n
"
else
out
<<
ws
<<
"[]
\n
"
end
elsif
pattern
.
class
.
ancestors
.
include?
(
ParentPattern
)
out
<<
ws
<<
pattern
.
class
.
name
<<
"(
\n
"
for
p
in
pattern
.
children
out
<<
dump_patterns
(
p
,
indent
+
1
).
rstrip
<<
"
\n
"
end
out
<<
ws
<<
")
\n
"
else
out
<<
ws
<<
pattern
.
inspect
end
return
out
end
def
extras
(
help
,
version
,
options
,
doc
)
if
help
and
options
.
any?
{
|
o
|
[
'-h'
,
'--help'
].
include?
(
o
.
name
)
&&
o
.
value
}
Exit
.
set_usage
(
nil
)
raise
Exit
,
doc
.
strip
end
if
version
and
options
.
any?
{
|
o
|
o
.
name
==
'--version'
&&
o
.
value
}
Exit
.
set_usage
(
nil
)
raise
Exit
,
version
end
end
def
docopt
(
doc
,
params
=
{})
default
=
{
:version
=>
nil
,
:argv
=>
nil
,
:help
=>
true
,
:options_first
=>
false
}
params
=
default
.
merge
(
params
)
params
[
:argv
]
=
ARGV
if
!
params
[
:argv
]
Exit
.
set_usage
(
printable_usage
(
doc
))
options
=
parse_defaults
(
doc
)
pattern
=
parse_pattern
(
formal_usage
(
Exit
.
usage
),
options
)
argv
=
parse_argv
(
TokenStream
.
new
(
params
[
:argv
],
Exit
),
options
,
params
[
:options_first
])
pattern_options
=
pattern
.
flat
(
Option
).
uniq
pattern
.
flat
(
AnyOptions
).
each
do
|
ao
|
doc_options
=
parse_defaults
(
doc
)
ao
.
children
=
doc_options
.
reject
{
|
o
|
pattern_options
.
include?
(
o
)
}.
uniq
end
extras
(
params
[
:help
],
params
[
:version
],
argv
,
doc
)
matched
,
left
,
collected
=
pattern
.
fix
().
match
(
argv
)
collected
||=
[]
if
matched
and
(
left
.
count
==
0
)
return
Hash
[(
pattern
.
flat
+
collected
).
map
{
|
a
|
[
a
.
name
,
a
.
value
]
}]
end
raise
Exit
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment