shellopts 2.0.0.pre.3 → 2.0.0.pre.11
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/TODO +4 -1
- data/lib/main.rb +1 -0
- data/lib/shellopts.rb +138 -44
- data/lib/shellopts/args.rb +34 -29
- data/lib/shellopts/ast/command.rb +6 -6
- data/lib/shellopts/compiler.rb +19 -21
- data/lib/shellopts/generator.rb +3 -3
- data/lib/shellopts/grammar/command.rb +12 -13
- data/lib/shellopts/grammar/node.rb +11 -3
- data/lib/shellopts/grammar/option.rb +2 -1
- data/lib/shellopts/grammar/program.rb +4 -4
- data/lib/shellopts/idr.rb +71 -44
- data/lib/shellopts/main.rb +10 -0
- data/lib/shellopts/option_struct.rb +62 -159
- data/lib/shellopts/parser.rb +11 -11
- data/lib/shellopts/shellopts.rb +58 -35
- data/lib/shellopts/version.rb +1 -1
- metadata +4 -5
- data/lib/shellopts/messenger.rb +0 -71
- data/lib/shellopts/utils.rb +0 -16
- data/rs +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf9123d8aaeb0abd253cbbb879763200762cbed326eff55afa1bddc5bc6825d8
|
4
|
+
data.tar.gz: 78de60189d46a9d9c36d36ac79f754b9cedc820495b69e77171e8839033fda1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6012864adf0539064c11b700263692cec91a39d8519ec0d11030d586b7ab039f393c5595f07930f0a3e2b27a4418d18e6ee01c01deae0b7f056c9f7162106ae8
|
7
|
+
data.tar.gz: a740ce82c8134cfba058df3c0d8abed3cada1abdef0f3b640f75a8af73efee800eb650d70ca41416af9730f55bf317cf3977683d80c69b28aea2578132def4be
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.6.6
|
data/TODO
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
TODO
|
3
|
-
o
|
3
|
+
o Rethink #error and #fail <- The use-case is one-file ruby scripts. Idea: Only use in main exe file?
|
4
|
+
o Create exceptions: UserError SystemFail and allow them to be used instead of #error and #fail
|
5
|
+
? Remove ! from OptionStruct#subcommand return value. We know we're
|
4
6
|
processing commands so there is no need to have a distinct name and it
|
5
7
|
feels a lot more intuitive without it
|
6
8
|
o Add validation block to ShellOpts class methods
|
@@ -43,6 +45,7 @@ TODO
|
|
43
45
|
o Long version usage strings (major release)
|
44
46
|
o Doc: Example of processing of sub-commands and sub-sub-commands
|
45
47
|
|
48
|
+
+ Add a 'mandatory' argument to #subcommand
|
46
49
|
+ More tests
|
47
50
|
+ More doc
|
48
51
|
+ Implement value-name-before-flags rule
|
data/lib/main.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
hej
|
data/lib/shellopts.rb
CHANGED
@@ -4,11 +4,10 @@ require 'shellopts/compiler.rb'
|
|
4
4
|
require 'shellopts/parser.rb'
|
5
5
|
require 'shellopts/generator.rb'
|
6
6
|
require 'shellopts/option_struct.rb'
|
7
|
-
require 'shellopts/
|
8
|
-
require 'shellopts/utils.rb'
|
7
|
+
require 'shellopts/main.rb'
|
9
8
|
|
10
9
|
# Name of program. Defined as the basename of the program file
|
11
|
-
PROGRAM = File.basename($PROGRAM_NAME)
|
10
|
+
#PROGRAM = File.basename($PROGRAM_NAME)
|
12
11
|
|
13
12
|
# ShellOpts main Module
|
14
13
|
#
|
@@ -21,7 +20,7 @@ PROGRAM = File.basename($PROGRAM_NAME)
|
|
21
20
|
#
|
22
21
|
# For example; the following process and convert a command line into a struct
|
23
22
|
# representation and also sets ShellOpts.shellopts object so that the #error
|
24
|
-
# method can print a relevant
|
23
|
+
# method can print a relevant spec string:
|
25
24
|
#
|
26
25
|
# USAGE = "a,all f,file=FILE -- ARG1 ARG2"
|
27
26
|
# opts, args = ShellOpts.as_struct(USAGE, ARGV)
|
@@ -54,18 +53,64 @@ PROGRAM = File.basename($PROGRAM_NAME)
|
|
54
53
|
# ShellOpts injects the constant PROGRAM into the global scope. It contains the
|
55
54
|
# name of the program
|
56
55
|
#
|
56
|
+
# INCLUDING SHELLOPTS
|
57
|
+
#
|
58
|
+
# ShellOpts can optionally be included in your shell application main file but
|
59
|
+
# it is not supposed to be included anywhere else
|
60
|
+
#
|
61
|
+
# Some behind the scenes magic happen if you include the ShellOpts module in your
|
62
|
+
# main exe file
|
63
|
+
#
|
57
64
|
module ShellOpts
|
65
|
+
def self.default_name()
|
66
|
+
@default_name || defined?(PROGRAM) ? PROGRAM : File.basename($0)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.default_name=(name)
|
70
|
+
@default_name = name
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.default_usage()
|
74
|
+
@default_usage || defined?(USAGE) ? USAGE : nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.default_usage=(usage)
|
78
|
+
@default_usage = usage
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.default_key_type()
|
82
|
+
@default_key_type || ::ShellOpts::DEFAULT_KEY_TYPE
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.default_key_type=(type)
|
86
|
+
@default_key_type = type
|
87
|
+
end
|
88
|
+
|
58
89
|
# Base class for ShellOpts exceptions
|
59
90
|
class Error < RuntimeError; end
|
60
91
|
|
61
|
-
# Raised when a syntax error is detected in the
|
92
|
+
# Raised when a syntax error is detected in the spec string
|
62
93
|
class CompilerError < Error
|
63
|
-
def initialize(start,
|
64
|
-
super(
|
94
|
+
def initialize(start, usage)
|
95
|
+
super(usage)
|
65
96
|
set_backtrace(caller(start))
|
66
97
|
end
|
67
98
|
end
|
68
99
|
|
100
|
+
# Raised when an error is detected in the command line
|
101
|
+
class ParserError < Error; end
|
102
|
+
|
103
|
+
# Raised when the command line error is caused by the user. It is raised by
|
104
|
+
# the parser but can also be used by the application if the command line
|
105
|
+
# fails a semantic check
|
106
|
+
class UserError < ParserError; end
|
107
|
+
|
108
|
+
# Raised when the error is caused by a failed assumption about the system. It
|
109
|
+
# is not raised by the ShellOpts library as it only concerns itself with
|
110
|
+
# command line syntax but can be used by the application to report a failure
|
111
|
+
# through ShellOpts#fail method when the ShellOpts module is included
|
112
|
+
class SystemFail < Error; end
|
113
|
+
|
69
114
|
# Raised when an error is detected during conversion from the Idr to array,
|
70
115
|
# hash, or struct
|
71
116
|
class ConversionError < Error; end
|
@@ -76,74 +121,123 @@ module ShellOpts
|
|
76
121
|
# The current compilation object. It is set by #process
|
77
122
|
def self.shellopts() @shellopts end
|
78
123
|
|
79
|
-
#
|
80
|
-
def
|
124
|
+
# Name of program
|
125
|
+
def program_name() shellopts!.name end
|
126
|
+
def program_name=(name) shellopts!.name = name end
|
127
|
+
|
128
|
+
# Usage string
|
129
|
+
def usage() shellopts!.spec end
|
130
|
+
def usage=(spec) shellopts!.spec = spec end
|
131
|
+
|
132
|
+
# Process command line, set current shellopts object, and return it.
|
133
|
+
# Remaining arguments from the command line can be accessed through
|
134
|
+
# +shellopts.args+
|
135
|
+
def self.process(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
81
136
|
@shellopts.nil? or reset
|
82
|
-
|
83
|
-
@shellopts
|
137
|
+
@shellopts = ShellOpts.new(spec, argv, name: name, usage: usage)
|
138
|
+
@shellopts.process
|
84
139
|
end
|
85
140
|
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# the
|
89
|
-
def self.as_program(
|
90
|
-
|
141
|
+
# Process command line, set current shellopts object, and return a
|
142
|
+
# [Idr::Program, argv] tuple. Automatically includes the ShellOpts module
|
143
|
+
# if called from the main Ruby object (ie. your executable)
|
144
|
+
def self.as_program(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
145
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
146
|
+
process(spec, argv, name: name, usage: usage)
|
91
147
|
[shellopts.idr, shellopts.args]
|
92
148
|
end
|
93
149
|
|
94
|
-
# Process command line, set current shellopts object, and return a [array,
|
95
|
-
# tuple.
|
96
|
-
#
|
97
|
-
def self.as_array(
|
98
|
-
|
150
|
+
# Process command line, set current shellopts object, and return a [array,
|
151
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
152
|
+
# main Ruby object (ie. your executable)
|
153
|
+
def self.as_array(spec, argv, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage)
|
154
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
155
|
+
process(spec, argv, name: name, usage: usage)
|
99
156
|
[shellopts.to_a, shellopts.args]
|
100
157
|
end
|
101
158
|
|
102
|
-
# Process command line, set current shellopts object, and return a [hash,
|
103
|
-
# tuple.
|
104
|
-
#
|
105
|
-
def self.as_hash(
|
106
|
-
|
107
|
-
|
159
|
+
# Process command line, set current shellopts object, and return a [hash,
|
160
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
161
|
+
# main Ruby object (ie. your executable)
|
162
|
+
def self.as_hash(
|
163
|
+
spec, argv,
|
164
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
165
|
+
key_type: ::ShellOpts.default_key_type,
|
166
|
+
aliases: {})
|
167
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
168
|
+
process(spec, argv, name: name, usage: usage)
|
169
|
+
[shellopts.to_h(key_type: key_type, aliases: aliases), shellopts.args]
|
108
170
|
end
|
109
171
|
|
110
|
-
# Process command line, set current shellopts object, and return a [struct,
|
111
|
-
# tuple.
|
112
|
-
#
|
113
|
-
def self.as_struct(
|
114
|
-
|
115
|
-
|
172
|
+
# Process command line, set current shellopts object, and return a [struct,
|
173
|
+
# argv] tuple. Automatically includes the ShellOpts module if called from the
|
174
|
+
# main Ruby object (ie. your executable)
|
175
|
+
def self.as_struct(
|
176
|
+
spec, argv,
|
177
|
+
name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage,
|
178
|
+
aliases: {})
|
179
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
180
|
+
process(spec, argv, name: name, usage: usage)
|
181
|
+
[shellopts.to_struct(aliases: aliases), shellopts.args]
|
116
182
|
end
|
117
183
|
|
118
184
|
# Process command line, set current shellopts object, and then iterate
|
119
185
|
# options and commands as an array. Returns an enumerator to the array
|
120
186
|
# representation of the current shellopts object if not given a block
|
121
|
-
# argument
|
122
|
-
|
123
|
-
|
187
|
+
# argument. Automatically includes the ShellOpts module if called from the
|
188
|
+
# main Ruby object (ie. your executable)
|
189
|
+
def self.each(spec = nil, argv = nil, name: ::ShellOpts.default_name, usage: ::ShellOpts.default_usage, &block)
|
190
|
+
Main.main.send(:include, ::ShellOpts) if caller.last =~ Main::CALLER_RE
|
191
|
+
process(spec, argv, name: name, usage: usage)
|
124
192
|
shellopts.each(&block)
|
125
193
|
end
|
126
194
|
|
127
|
-
# Print error
|
195
|
+
# Print error usage and spec string and exit with status 1. This method
|
128
196
|
# should be called in response to user-errors (eg. specifying an illegal
|
129
197
|
# option)
|
130
|
-
def self.error(*msgs)
|
131
|
-
|
132
|
-
shellopts.error(*msgs)
|
198
|
+
def self.error(*msgs, exit: true)
|
199
|
+
shellopts!.error(msgs, exit: exit)
|
133
200
|
end
|
134
201
|
|
135
|
-
# Print error
|
202
|
+
# Print error usage and exit with status 1. This method should not be
|
136
203
|
# called in response to system errors (eg. disk full)
|
137
|
-
def self.fail(*msgs)
|
138
|
-
|
139
|
-
|
204
|
+
def self.fail(*msgs, exit: true)
|
205
|
+
shellopts!.fail(*msgs, exit: exit)
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.included(base)
|
209
|
+
# base.equal?(Object) is only true when included in main (we hope)
|
210
|
+
if !@is_included_in_main && base.equal?(Object)
|
211
|
+
@is_included_in_main = true
|
212
|
+
at_exit do
|
213
|
+
case $!
|
214
|
+
when ShellOpts::UserError
|
215
|
+
::ShellOpts.error($!.message, exit: false)
|
216
|
+
exit!(1)
|
217
|
+
when ShellOpts::SystemFail
|
218
|
+
::ShellOpts.fail($!.message)
|
219
|
+
exit!(1)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
super
|
140
224
|
end
|
141
225
|
|
142
226
|
private
|
227
|
+
# Default default key type
|
228
|
+
DEFAULT_KEY_TYPE = :name
|
229
|
+
|
143
230
|
# Reset state variables
|
144
231
|
def self.reset()
|
145
232
|
@shellopts = nil
|
146
233
|
end
|
147
234
|
|
235
|
+
# (shorthand) Raise an InternalError if shellopts is nil. Return shellopts
|
236
|
+
def self.shellopts!
|
237
|
+
::ShellOpts.shellopts or raise UserError, "No ShellOpts.shellopts object"
|
238
|
+
end
|
239
|
+
|
148
240
|
@shellopts = nil
|
241
|
+
@is_included_in_main = false
|
149
242
|
end
|
243
|
+
|
data/lib/shellopts/args.rb
CHANGED
@@ -2,48 +2,53 @@
|
|
2
2
|
module ShellOpts
|
3
3
|
# Specialization of Array for arguments lists. Args extends Array with a
|
4
4
|
# #extract and an #expect method to extract elements from the array. The
|
5
|
-
# methods
|
5
|
+
# methods raise a ShellOpts::UserError exception in case of errors
|
6
6
|
class Args < Array
|
7
7
|
def initialize(shellopts, *args)
|
8
8
|
@shellopts = shellopts
|
9
9
|
super(*args)
|
10
10
|
end
|
11
11
|
|
12
|
-
# Remove and return elements from beginning of the array
|
13
|
-
#
|
14
|
-
# If
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
r.size == 0 ? nil : (r.size == 1 ? r.first : r)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Remove and returns elements from the array. If +count_or_range+ is a
|
27
|
-
# number, that number of elements will be returned. If the count is
|
28
|
-
# negative, the elements will be removed from the end of the array. If
|
29
|
-
# +count_or_range+ is a range, the number of elements returned will be in
|
30
|
-
# that range. The range can't contain negative numbers. #expect calls
|
31
|
-
# #error() if the array has remaning elemens after removal satisfy the
|
32
|
-
# request
|
33
|
-
def expect(count_or_range, message = nil)
|
12
|
+
# Remove and return elements from beginning of the array
|
13
|
+
#
|
14
|
+
# If +count_or_range+ is a number, that number of elements will be
|
15
|
+
# returned. If the count is one, a simple value is returned instead of an
|
16
|
+
# array. If the count is negative, the elements will be removed from the
|
17
|
+
# end of the array. If +count_or_range+ is a range, the number of elements
|
18
|
+
# returned will be in that range. The range can't contain negative numbers
|
19
|
+
#
|
20
|
+
# #extract raise a ShellOpts::UserError exception if there's is not enough
|
21
|
+
# elements in the array to satisfy the request
|
22
|
+
def extract(count_or_range, message = nil)
|
34
23
|
if count_or_range.is_a?(Range)
|
35
|
-
|
36
|
-
|
24
|
+
range = count_or_range
|
25
|
+
range.min <= self.size or inoa(message)
|
26
|
+
n_extract = [self.size, range.max].min
|
27
|
+
n_extend = range.max > self.size ? range.max - self.size : 0
|
28
|
+
r = self.shift(n_extract) + Array.new(n_extend)
|
29
|
+
range.max <= 1 ? r.first : r
|
37
30
|
else
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
count = count_or_range
|
32
|
+
self.size >= count.abs or inoa(message)
|
33
|
+
start = count >= 0 ? 0 : size + count
|
34
|
+
r = slice!(start, count.abs)
|
35
|
+
r.size <= 0 ? nil : (r.size == 1 ? r.first : r)
|
41
36
|
end
|
42
37
|
end
|
43
38
|
|
39
|
+
# As #extract except it doesn't allow negative counts and that the array is
|
40
|
+
# expect to be emptied by the operation
|
41
|
+
#
|
42
|
+
# #expect raise a ShellOpts::UserError exception if the array is not emptied
|
43
|
+
# by the operation
|
44
|
+
def expect(count_or_range, message = nil)
|
45
|
+
count_or_range === self.size or inoa(message)
|
46
|
+
extract(count_or_range) # Can't fail
|
47
|
+
end
|
48
|
+
|
44
49
|
private
|
45
50
|
def inoa(message = nil)
|
46
|
-
|
51
|
+
raise ShellOpts::UserError, message || "Illegal number of arguments"
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -7,17 +7,17 @@ module ShellOpts
|
|
7
7
|
|
8
8
|
# Optional sub-command (Ast::Command). Initially nil but assigned by the
|
9
9
|
# parser
|
10
|
-
attr_accessor :
|
10
|
+
attr_accessor :subcommand
|
11
11
|
|
12
12
|
def initialize(grammar, name)
|
13
13
|
super(grammar, name)
|
14
14
|
@options = []
|
15
|
-
@
|
15
|
+
@subcommand = nil
|
16
16
|
end
|
17
17
|
|
18
18
|
# Array of option or command tuples
|
19
19
|
def values
|
20
|
-
(options + (Array(
|
20
|
+
(options + (Array(subcommand || []))).map { |node| node.to_tuple }
|
21
21
|
end
|
22
22
|
|
23
23
|
# :nocov:
|
@@ -26,10 +26,10 @@ module ShellOpts
|
|
26
26
|
yield if block_given?
|
27
27
|
puts "options:"
|
28
28
|
indent { options.each { |opt| opt.dump } }
|
29
|
-
print "
|
30
|
-
if
|
29
|
+
print "subcommand:"
|
30
|
+
if subcommand
|
31
31
|
puts
|
32
|
-
indent {
|
32
|
+
indent { subcommand.dump }
|
33
33
|
else
|
34
34
|
puts "nil"
|
35
35
|
end
|
data/lib/shellopts/compiler.rb
CHANGED
@@ -29,11 +29,11 @@ module ShellOpts
|
|
29
29
|
def initialize(name, source)
|
30
30
|
@name, @tokens = name, source.split(/\s+/).reject(&:empty?)
|
31
31
|
|
32
|
-
# @
|
32
|
+
# @subcommands_by_path is an hash from subcommand-path to Command or Program
|
33
33
|
# object. The top level Program object has nil as its path.
|
34
|
-
# @
|
35
|
-
# link sub-
|
36
|
-
@
|
34
|
+
# @subcommands_by_path is used to check for uniqueness of subcommands and to
|
35
|
+
# link sub-subcommands to their parents
|
36
|
+
@subcommands_by_path = {}
|
37
37
|
end
|
38
38
|
|
39
39
|
def call
|
@@ -49,30 +49,26 @@ module ShellOpts
|
|
49
49
|
# Returns the current token and advance to the next token
|
50
50
|
def next_token() @tokens.shift end
|
51
51
|
|
52
|
-
def error(msg) # Just a shorthand. Unrelated to ShellOpts.error
|
53
|
-
raise Compiler::Error.new(msg)
|
54
|
-
end
|
55
|
-
|
56
52
|
def compile_program
|
57
|
-
program = @
|
53
|
+
program = @subcommands_by_path[nil] = Grammar::Program.new(@name, compile_options)
|
58
54
|
while curr_token && curr_token != "--"
|
59
|
-
|
55
|
+
compile_subcommand
|
60
56
|
end
|
61
57
|
program.args.concat(@tokens[1..-1]) if curr_token
|
62
58
|
program
|
63
59
|
end
|
64
60
|
|
65
|
-
def
|
61
|
+
def compile_subcommand
|
66
62
|
path = curr_token[0..-2]
|
67
63
|
ident_list = compile_ident_list(path, ".")
|
68
64
|
parent_path = ident_list.size > 1 ? ident_list[0..-2].join(".") : nil
|
69
65
|
name = ident_list[-1]
|
70
66
|
|
71
|
-
parent = @
|
72
|
-
|
73
|
-
!@
|
67
|
+
parent = @subcommands_by_path[parent_path] or
|
68
|
+
raise Compiler::Error, "No such subcommand: #{parent_path.inspect}"
|
69
|
+
!@subcommands_by_path.key?(path) or raise Compiler::Error, "Duplicate subcommand: #{path.inspect}"
|
74
70
|
next_token
|
75
|
-
@
|
71
|
+
@subcommands_by_path[path] = Grammar::Command.new(parent, name, compile_options)
|
76
72
|
end
|
77
73
|
|
78
74
|
def compile_options
|
@@ -81,7 +77,7 @@ module ShellOpts
|
|
81
77
|
option_list << compile_option
|
82
78
|
end
|
83
79
|
dup = option_list.map(&:names).flatten.find_dup and
|
84
|
-
|
80
|
+
raise Compiler::Error, "Duplicate option name: #{dup.inspect}"
|
85
81
|
option_list
|
86
82
|
end
|
87
83
|
|
@@ -102,7 +98,7 @@ module ShellOpts
|
|
102
98
|
long_names = []
|
103
99
|
ident_list = compile_ident_list(names, ",")
|
104
100
|
(dup = ident_list.find_dup).nil? or
|
105
|
-
|
101
|
+
raise Compiler::Error, "Duplicate identifier #{dup.inspect} in #{curr_token.inspect}"
|
106
102
|
ident_list.each { |ident|
|
107
103
|
if ident.size == 1
|
108
104
|
short_names << "-#{ident}"
|
@@ -115,13 +111,15 @@ module ShellOpts
|
|
115
111
|
Grammar::Option.new(short_names, long_names, flags, label)
|
116
112
|
end
|
117
113
|
|
118
|
-
# Compile list of option names or a
|
114
|
+
# Compile list of option names or a subcommand path
|
119
115
|
def compile_ident_list(ident_list_str, sep)
|
120
116
|
ident_list_str.split(sep, -1).map { |str|
|
121
|
-
!str.empty? or
|
122
|
-
|
117
|
+
!str.empty? or
|
118
|
+
raise Compiler::Error, "Empty identifier in #{curr_token.inspect}"
|
119
|
+
!str.start_with?("-") or
|
120
|
+
raise Compiler::Error, "Identifier can't start with '-' in #{curr_token.inspect}"
|
123
121
|
str !~ /([^\w\d#{sep}-])/ or
|
124
|
-
|
122
|
+
raise Compiler::Error, "Illegal character #{$1.inspect} in #{curr_token.inspect}"
|
125
123
|
str
|
126
124
|
}
|
127
125
|
end
|