shellopts 2.0.0.pre.14 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.ruby-version +1 -1
- data/README.md +201 -267
- data/TODO +37 -5
- data/doc/format.rb +95 -0
- data/doc/grammar.txt +27 -0
- data/doc/syntax.rb +110 -0
- data/doc/syntax.txt +10 -0
- data/lib/ext/array.rb +62 -0
- data/lib/ext/forward_to.rb +15 -0
- data/lib/ext/lcs.rb +34 -0
- data/lib/shellopts/analyzer.rb +130 -0
- data/lib/shellopts/ansi.rb +8 -0
- data/lib/shellopts/args.rb +25 -15
- data/lib/shellopts/argument_type.rb +139 -0
- data/lib/shellopts/dump.rb +158 -0
- data/lib/shellopts/formatter.rb +292 -92
- data/lib/shellopts/grammar.rb +375 -0
- data/lib/shellopts/interpreter.rb +103 -0
- data/lib/shellopts/lexer.rb +175 -0
- data/lib/shellopts/parser.rb +293 -0
- data/lib/shellopts/program.rb +279 -0
- data/lib/shellopts/renderer.rb +227 -0
- data/lib/shellopts/stack.rb +7 -0
- data/lib/shellopts/token.rb +44 -0
- data/lib/shellopts/version.rb +1 -1
- data/lib/shellopts.rb +359 -3
- data/main +1180 -0
- data/shellopts.gemspec +8 -14
- metadata +86 -41
- data/lib/ext/algorithm.rb +0 -14
- data/lib/ext/ruby_env.rb +0 -8
- data/lib/shellopts/ast/command.rb +0 -112
- data/lib/shellopts/ast/dump.rb +0 -28
- data/lib/shellopts/ast/option.rb +0 -15
- data/lib/shellopts/ast/parser.rb +0 -106
- data/lib/shellopts/constants.rb +0 -88
- data/lib/shellopts/exceptions.rb +0 -21
- data/lib/shellopts/grammar/analyzer.rb +0 -76
- data/lib/shellopts/grammar/command.rb +0 -87
- data/lib/shellopts/grammar/dump.rb +0 -56
- data/lib/shellopts/grammar/lexer.rb +0 -56
- data/lib/shellopts/grammar/option.rb +0 -55
- data/lib/shellopts/grammar/parser.rb +0 -78
data/shellopts.gemspec
CHANGED
@@ -11,20 +11,10 @@ Gem::Specification.new do |spec|
|
|
11
11
|
|
12
12
|
spec.summary = %q{Parse command line options and arguments}
|
13
13
|
spec.description = %q{ShellOpts is a simple command line parsing libray
|
14
|
-
that
|
15
|
-
|
16
|
-
getopt(1)-like string that is interpreted by the
|
17
|
-
library to process the command line}
|
14
|
+
that supports short and long options and subcommands,
|
15
|
+
and has built-in help and error messages}
|
18
16
|
spec.homepage = "http://github.com/clrgit/shellopts"
|
19
17
|
|
20
|
-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
21
|
-
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
22
|
-
if spec.respond_to?(:metadata)
|
23
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
24
|
-
else
|
25
|
-
raise "RubyGems 2.0+ is required to protect against public gem pushes"
|
26
|
-
end
|
27
|
-
|
28
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
29
19
|
f.match(%r{^(test|spec|features)/})
|
30
20
|
end
|
@@ -32,9 +22,13 @@ Gem::Specification.new do |spec|
|
|
32
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
23
|
spec.require_paths = ["lib"]
|
34
24
|
|
35
|
-
spec.
|
25
|
+
spec.add_dependency "forward_to"
|
26
|
+
spec.add_dependency "constrain"
|
27
|
+
spec.add_dependency "ruby-terminfo"
|
28
|
+
spec.add_dependency "indented_io"
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 2.2.10"
|
36
31
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
37
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
38
|
-
spec.add_development_dependency "indented_io"
|
39
33
|
spec.add_development_dependency "simplecov"
|
40
34
|
end
|
metadata
CHANGED
@@ -1,71 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shellopts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: forward_to
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: constrain
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: ruby-terminfo
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: indented_io
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
73
|
- - "~>"
|
46
74
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
75
|
+
version: 2.2.10
|
48
76
|
type: :development
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
82
|
+
version: 2.2.10
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
84
|
+
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
58
86
|
requirements:
|
59
87
|
- - ">="
|
60
88
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
89
|
+
version: 12.3.3
|
62
90
|
type: :development
|
63
91
|
prerelease: false
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
65
93
|
requirements:
|
66
94
|
- - ">="
|
67
95
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
96
|
+
version: 12.3.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: simplecov
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,10 +124,8 @@ dependencies:
|
|
82
124
|
version: '0'
|
83
125
|
description: |-
|
84
126
|
ShellOpts is a simple command line parsing libray
|
85
|
-
that
|
86
|
-
|
87
|
-
getopt(1)-like string that is interpreted by the
|
88
|
-
library to process the command line
|
127
|
+
that supports short and long options and subcommands,
|
128
|
+
and has built-in help and error messages
|
89
129
|
email:
|
90
130
|
- claus.l.rasmussen@gmail.com
|
91
131
|
executables: []
|
@@ -103,30 +143,35 @@ files:
|
|
103
143
|
- bin/console
|
104
144
|
- bin/mkdoc
|
105
145
|
- bin/setup
|
146
|
+
- doc/format.rb
|
147
|
+
- doc/grammar.txt
|
106
148
|
- doc/stylesheet.css
|
107
|
-
-
|
108
|
-
-
|
149
|
+
- doc/syntax.rb
|
150
|
+
- doc/syntax.txt
|
151
|
+
- lib/ext/array.rb
|
152
|
+
- lib/ext/forward_to.rb
|
153
|
+
- lib/ext/lcs.rb
|
109
154
|
- lib/shellopts.rb
|
155
|
+
- lib/shellopts/analyzer.rb
|
156
|
+
- lib/shellopts/ansi.rb
|
110
157
|
- lib/shellopts/args.rb
|
111
|
-
- lib/shellopts/
|
112
|
-
- lib/shellopts/
|
113
|
-
- lib/shellopts/ast/option.rb
|
114
|
-
- lib/shellopts/ast/parser.rb
|
115
|
-
- lib/shellopts/constants.rb
|
116
|
-
- lib/shellopts/exceptions.rb
|
158
|
+
- lib/shellopts/argument_type.rb
|
159
|
+
- lib/shellopts/dump.rb
|
117
160
|
- lib/shellopts/formatter.rb
|
118
|
-
- lib/shellopts/grammar
|
119
|
-
- lib/shellopts/
|
120
|
-
- lib/shellopts/
|
121
|
-
- lib/shellopts/
|
122
|
-
- lib/shellopts/
|
123
|
-
- lib/shellopts/
|
161
|
+
- lib/shellopts/grammar.rb
|
162
|
+
- lib/shellopts/interpreter.rb
|
163
|
+
- lib/shellopts/lexer.rb
|
164
|
+
- lib/shellopts/parser.rb
|
165
|
+
- lib/shellopts/program.rb
|
166
|
+
- lib/shellopts/renderer.rb
|
167
|
+
- lib/shellopts/stack.rb
|
168
|
+
- lib/shellopts/token.rb
|
124
169
|
- lib/shellopts/version.rb
|
170
|
+
- main
|
125
171
|
- shellopts.gemspec
|
126
172
|
homepage: http://github.com/clrgit/shellopts
|
127
173
|
licenses: []
|
128
|
-
metadata:
|
129
|
-
allowed_push_host: https://rubygems.org
|
174
|
+
metadata: {}
|
130
175
|
post_install_message:
|
131
176
|
rdoc_options: []
|
132
177
|
require_paths:
|
@@ -138,11 +183,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
183
|
version: '0'
|
139
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
185
|
requirements:
|
141
|
-
- - "
|
186
|
+
- - ">="
|
142
187
|
- !ruby/object:Gem::Version
|
143
|
-
version:
|
188
|
+
version: '0'
|
144
189
|
requirements: []
|
145
|
-
rubygems_version: 3.
|
190
|
+
rubygems_version: 3.2.26
|
146
191
|
signing_key:
|
147
192
|
specification_version: 4
|
148
193
|
summary: Parse command line options and arguments
|
data/lib/ext/algorithm.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
|
2
|
-
module Algorithm
|
3
|
-
def follow(object, sym = nil, &block)
|
4
|
-
sym.nil? == block_given? or raise "Can't use both symbol and block"
|
5
|
-
a = []
|
6
|
-
while object
|
7
|
-
a << object
|
8
|
-
object = block_given? ? yield(object) : object.send(sym)
|
9
|
-
end
|
10
|
-
a
|
11
|
-
end
|
12
|
-
|
13
|
-
module_function :follow
|
14
|
-
end
|
data/lib/ext/ruby_env.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
module ShellOpts
|
2
|
-
module Ast
|
3
|
-
# Note that Command is derived from BasicObject to minimize the number of
|
4
|
-
# reserved names
|
5
|
-
class Command < BasicObject
|
6
|
-
def initialize(grammar)
|
7
|
-
@grammar = grammar
|
8
|
-
@options_list = []
|
9
|
-
@options_hash = {}
|
10
|
-
@subcommand = nil
|
11
|
-
@subcommands_hash = {} # have at most one element
|
12
|
-
|
13
|
-
@grammar.opts.each { |opt|
|
14
|
-
if opt.argument?
|
15
|
-
self.instance_eval("def #{opt.ident}() @options_hash[:#{opt.ident}] end")
|
16
|
-
end
|
17
|
-
self.instance_eval("def #{opt.ident}?() @options_hash.key?(:#{opt.ident}) end")
|
18
|
-
}
|
19
|
-
|
20
|
-
@grammar.cmds.each { |cmd|
|
21
|
-
self.instance_eval("def #{cmd.ident}!() @subcommands_hash[:#{cmd.ident}] end")
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
# Return true if the option was used. Defined in #initialize for each option
|
26
|
-
# def <option>?() end
|
27
|
-
|
28
|
-
# Return the value of the option. Note that repeated options have their
|
29
|
-
# values aggregated into an array. Defined in #initialize for each option
|
30
|
-
# def <option>() end
|
31
|
-
|
32
|
-
# List of Ast::Option objects in the same order as on the command line
|
33
|
-
def options() @options_list end
|
34
|
-
|
35
|
-
# Hash from option identifier to option value. Note that repeated options
|
36
|
-
# have their values aggregated into an array
|
37
|
-
def [](ident) @options_hash[ident].argument end
|
38
|
-
|
39
|
-
# Return the sub-command Command object or nil if not present. Defined in
|
40
|
-
# #initialize for each sub-command
|
41
|
-
# def <command>!() end
|
42
|
-
|
43
|
-
# The sub-command identifier or nil if not present
|
44
|
-
def subcommand() @subcommand && Command.grammar(@subcommand).ident end
|
45
|
-
|
46
|
-
# The sub-command Command object or nil if not present
|
47
|
-
def subcommand!() @subcommand end
|
48
|
-
|
49
|
-
# Class-level accessor methods
|
50
|
-
def self.program?(command) command.__send__(:__is_program__) end
|
51
|
-
def self.grammar(command) command.__send__(:__get_grammar__) end
|
52
|
-
|
53
|
-
# Class-level mutating methods
|
54
|
-
def self.add_option(command, option) command.__send__(:__add_option__, option) end
|
55
|
-
def self.add_command(command, subcommand) command.__send__(:__add_command__, subcommand) end
|
56
|
-
|
57
|
-
private
|
58
|
-
# True if this is a Program object
|
59
|
-
def __is_program__() false end
|
60
|
-
|
61
|
-
# Get grammar
|
62
|
-
def __get_grammar__()
|
63
|
-
@grammar
|
64
|
-
end
|
65
|
-
|
66
|
-
# Add an option. Only used from the parser
|
67
|
-
def __add_option__(option)
|
68
|
-
@options_list << option
|
69
|
-
if option.grammar.repeatable?
|
70
|
-
(@options_hash[option.grammar.ident] ||= []) << option.argument
|
71
|
-
else
|
72
|
-
@options_hash[option.grammar.ident] = option.argument
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Set sub-command. Only used from the parser
|
77
|
-
def __add_command__(command)
|
78
|
-
ident = Command.grammar(command).ident
|
79
|
-
@subcommand = command
|
80
|
-
@subcommands_hash[ident] = command
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class Program < Command
|
85
|
-
def __is_program__() true end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# # TODO: Create class-level methods for access
|
91
|
-
# private
|
92
|
-
# # Return class of object. #class is not defined for BasicObjects so this
|
93
|
-
# # method provides an alternative way of getting the class
|
94
|
-
# def self.class_of(object)
|
95
|
-
# # https://stackoverflow.com/a/18621313/2130986
|
96
|
-
# ::Kernel.instance_method(:class).bind(object).call
|
97
|
-
# end
|
98
|
-
#
|
99
|
-
# # Class method implementation of ObjectStruct#instance_variable_set that is
|
100
|
-
# # not defined in a BasicObject
|
101
|
-
# def self.set_variable(this, var, value)
|
102
|
-
# # https://stackoverflow.com/a/18621313/2130986
|
103
|
-
# ::Kernel.instance_method(:instance_variable_set).bind(this).call(var, value)
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# # Class method implementation of ObjectStruct#instance_variable_get that is
|
107
|
-
# # not defined in a BasicObject
|
108
|
-
# def self.get_variable(this, var)
|
109
|
-
# # https://stackoverflow.com/a/18621313/2130986
|
110
|
-
# ::Kernel.instance_method(:instance_variable_get).bind(this).call(var)
|
111
|
-
# end
|
112
|
-
|
data/lib/shellopts/ast/dump.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module ShellOpts
|
2
|
-
module Ast
|
3
|
-
class Command < BasicObject
|
4
|
-
def dump
|
5
|
-
klass = __is_program__ ? "Program" : "Command"
|
6
|
-
::Kernel.puts "#{@grammar.ident.inspect} (#{klass})"
|
7
|
-
::Kernel.indent {
|
8
|
-
if !options.empty?
|
9
|
-
options.map(&:dump)
|
10
|
-
end
|
11
|
-
if subcommand
|
12
|
-
subcommand!.dump
|
13
|
-
end
|
14
|
-
}
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class Option
|
19
|
-
def dump
|
20
|
-
puts "#{grammar.ident.inspect} (Option)"
|
21
|
-
indent {
|
22
|
-
puts "name: #{name.inspect}"
|
23
|
-
puts "argument: #{argument.inspect}"
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/shellopts/ast/option.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
module ShellOpts
|
2
|
-
module Ast
|
3
|
-
class Option
|
4
|
-
attr_reader :grammar
|
5
|
-
attr_reader :name # The actual name used on the command line
|
6
|
-
attr_reader :argument
|
7
|
-
|
8
|
-
def initialize(grammar, name, argument)
|
9
|
-
@grammar = grammar
|
10
|
-
@name = name
|
11
|
-
@argument = argument
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
data/lib/shellopts/ast/parser.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
module ShellOpts
|
2
|
-
module Ast
|
3
|
-
# Parse a subcommand
|
4
|
-
class Parser
|
5
|
-
def initialize(grammar, argv)
|
6
|
-
@grammar, @argv = grammar, argv.dup
|
7
|
-
@seen_options = {} # Used to keep track of repeated options
|
8
|
-
@current = nil # Current command
|
9
|
-
end
|
10
|
-
|
11
|
-
def call
|
12
|
-
@current = program = Program.new(@grammar)
|
13
|
-
parse_command(program)
|
14
|
-
cmd = Command.grammar(@current)
|
15
|
-
!cmd.virtual? or error("'%s' command requires a sub-command")
|
16
|
-
[program, Args.new(cmd, @argv)]
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.parse(grammar, argv)
|
20
|
-
self.new(grammar, argv).call
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
def error(message)
|
25
|
-
grammar = Command.grammar(@current)
|
26
|
-
raise Error.new(grammar), message % grammar.name
|
27
|
-
end
|
28
|
-
|
29
|
-
def parse_command(command)
|
30
|
-
@seen_options = {} # Every new command resets the seen options
|
31
|
-
while arg = @argv.first
|
32
|
-
if arg == "--"
|
33
|
-
@argv.shift
|
34
|
-
break
|
35
|
-
elsif arg.start_with?("-")
|
36
|
-
parse_option(command)
|
37
|
-
elsif cmd = Command.grammar(command).commands[arg]
|
38
|
-
@argv.shift
|
39
|
-
@current = subcommand = Ast::Command.new(cmd)
|
40
|
-
Command.add_command(command, subcommand)
|
41
|
-
parse_command(subcommand)
|
42
|
-
break
|
43
|
-
else
|
44
|
-
break
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse_option(command)
|
50
|
-
# Split into name and argument
|
51
|
-
case @argv.first
|
52
|
-
when /^--(.+?)(?:=(.*))?$/
|
53
|
-
name, arg, short = $1, $2, false
|
54
|
-
opt_name = "--#{name}"
|
55
|
-
when /^-(.)(.+)?$/
|
56
|
-
name, arg, short = $1, $2, true
|
57
|
-
opt_name = "-#{name}"
|
58
|
-
end
|
59
|
-
@argv.shift
|
60
|
-
|
61
|
-
option = Command.grammar(command).options[name] or error "Unknown option '#{opt_name}'"
|
62
|
-
!@seen_options.key?(option.ident) || option.repeatable? or error "Duplicate option '#{opt_name}'"
|
63
|
-
@seen_options[option.ident] = true
|
64
|
-
|
65
|
-
# Parse (optional) argument
|
66
|
-
if option.argument?
|
67
|
-
if arg.nil? && !option.optional?
|
68
|
-
if !@argv.empty?
|
69
|
-
arg = @argv.shift
|
70
|
-
else
|
71
|
-
error "Missing argument for option '#{opt_name}'"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
arg &&= parse_option_arg(option, name, arg)
|
75
|
-
elsif arg && short
|
76
|
-
@argv.unshift("-#{arg}")
|
77
|
-
arg = nil
|
78
|
-
elsif !arg.nil?
|
79
|
-
error "No argument allowed for option '#{opt_name}'"
|
80
|
-
end
|
81
|
-
|
82
|
-
Command.add_option(command, Option.new(option, name, arg))
|
83
|
-
end
|
84
|
-
|
85
|
-
def parse_option_arg(option, name, arg)
|
86
|
-
if option.string?
|
87
|
-
arg
|
88
|
-
elsif arg == ""
|
89
|
-
nil
|
90
|
-
elsif option.integer?
|
91
|
-
arg =~ /^-?\d+$/ or error "Illegal integer in '#{name}' argument: '#{arg}'"
|
92
|
-
arg.to_i
|
93
|
-
else # option.float?
|
94
|
-
# https://stackoverflow.com/a/21891705/2130986
|
95
|
-
arg =~ /^[+-]?(?:0|[1-9]\d*)(?:\.(?:\d*[1-9]|0))?$/ or
|
96
|
-
error "Illegal float in '#{name}' argument: '#{arg}'"
|
97
|
-
arg.to_f
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
data/lib/shellopts/constants.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
|
2
|
-
module ShellOpts
|
3
|
-
# FIXME: An option group is -abcd, an option list is a,b,c,d
|
4
|
-
module Constants
|
5
|
-
# Short and long option names
|
6
|
-
SHORT_OPTION_NAME_SUB_RE = /[a-zA-Z0-9]/
|
7
|
-
LONG_OPTION_NAME_SUB_RE = /[a-z](?:[\w-]*\w)/
|
8
|
-
OPTION_NAME_SUB_RE = /#{SHORT_OPTION_NAME_SUB_RE}|#{LONG_OPTION_NAME_SUB_RE}/
|
9
|
-
|
10
|
-
# Initial option in a group
|
11
|
-
INITIAL_SHORT_OPTION_SUB_RE = /[-+]#{SHORT_OPTION_NAME_SUB_RE}/
|
12
|
-
INITIAL_LONG_OPTION_SUB_RE = /(?:--|\+\+)#{LONG_OPTION_NAME_SUB_RE}/
|
13
|
-
INITIAL_OPTION_SUB_RE = /#{INITIAL_SHORT_OPTION_SUB_RE}|#{INITIAL_LONG_OPTION_SUB_RE}/
|
14
|
-
|
15
|
-
# A list of short and long options
|
16
|
-
OPTION_GROUP_SUB_RE = /#{INITIAL_OPTION_SUB_RE}(?:,#{OPTION_NAME_SUB_RE})*/
|
17
|
-
|
18
|
-
# Option argument
|
19
|
-
OPTION_ARG_SUB_RE = /[A-Z](?:[A-Z0-9_-]*[A-Z0-9])?/
|
20
|
-
|
21
|
-
# Matches option flags and argument. It defines the following captures
|
22
|
-
#
|
23
|
-
# $1 - Argument flag ('=')
|
24
|
-
# $2 - Type flag ('#' or '$')
|
25
|
-
# $3 - Argument name
|
26
|
-
# $4 - Optional flag ('?')
|
27
|
-
#
|
28
|
-
OPTION_FLAGS_SUB_RE = /(=)(#|\$)?(#{OPTION_ARG_SUB_RE})?(\?)?/
|
29
|
-
|
30
|
-
# Matches a declaration of an option. The RE defines the following captures:
|
31
|
-
#
|
32
|
-
# $1 - Option group
|
33
|
-
# $2 - Argument flag ('=')
|
34
|
-
# $3 - Type flag ('#' or '$')
|
35
|
-
# $4 - Argument name
|
36
|
-
# $5 - Optional flag ('?')
|
37
|
-
#
|
38
|
-
OPTION_SUB_RE = /(#{OPTION_GROUP_SUB_RE})#{OPTION_FLAGS_SUB_RE}?/
|
39
|
-
|
40
|
-
# Command and command paths
|
41
|
-
COMMAND_IDENT_SUB_RE = /[a-z](?:[a-z0-9_-]*[a-z0-9])?/
|
42
|
-
COMMAND_SUB_RE = /#{COMMAND_IDENT_SUB_RE}!/
|
43
|
-
COMMAND_PATH_SUB_RE = /#{COMMAND_IDENT_SUB_RE}(?:\.#{COMMAND_IDENT_SUB_RE})*!/
|
44
|
-
|
45
|
-
# Command argument
|
46
|
-
ARGUMENT_SUB_RE = /[A-Z][A-Z0-9_-]*[A-Z0-9](?:\.\.\.)?/
|
47
|
-
ARGUMENT_EXPR_SUB_RE = /\[?#{ARGUMENT_SUB_RE}(?:#{ARGUMENT_SUB_RE}|[\[\]\|\s])*/
|
48
|
-
|
49
|
-
# Matches a line starting with a command or an option
|
50
|
-
SCAN_RE = /^(?:#{COMMAND_PATH_SUB_RE}|#{OPTION_SUB_RE})(?:\s+.*)?$/
|
51
|
-
|
52
|
-
|
53
|
-
# Create anchored REs for all SUB_REs
|
54
|
-
self.constants.each { |c|
|
55
|
-
next if c.to_s !~ /_SUB_RE$/
|
56
|
-
sub_re = self.const_get(c)
|
57
|
-
next if !sub_re.is_a?(Regexp)
|
58
|
-
re = /^#{sub_re}$/
|
59
|
-
name = c.to_s.sub(/_SUB_RE$/, "_RE")
|
60
|
-
self.const_set(name, re)
|
61
|
-
}
|
62
|
-
|
63
|
-
# Method names reserved by the BasicObject class
|
64
|
-
BASIC_OBJECT_RESERVED_WORDS = %w(
|
65
|
-
! != == __id__ __send__ equal? instance_eval instance_exec method_missing
|
66
|
-
singleton_method_added singleton_method_removed singleton_method_undefined)
|
67
|
-
|
68
|
-
# Method names reserved by the Ast::Command class
|
69
|
-
AST_COMMAND_RESERVED_WORDS = %w(
|
70
|
-
initialize options subcommand __is_program__ __get_grammar__
|
71
|
-
__add_option__ __add_command__)
|
72
|
-
|
73
|
-
# Reserved option names
|
74
|
-
OPTION_RESERVED_WORDS =
|
75
|
-
(BASIC_OBJECT_RESERVED_WORDS + AST_COMMAND_RESERVED_WORDS).grep(OPTION_NAME_RE)
|
76
|
-
|
77
|
-
# Reserved command names
|
78
|
-
COMMAND_RESERVED_WORDS = %w(subcommand)
|
79
|
-
end
|
80
|
-
|
81
|
-
include Constants
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
data/lib/shellopts/exceptions.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
|
2
|
-
module ShellOpts
|
3
|
-
class CompileError < StandardError; end
|
4
|
-
|
5
|
-
class ShellOptsError < RuntimeError; end
|
6
|
-
|
7
|
-
class Error < ShellOptsError
|
8
|
-
attr_reader :subject
|
9
|
-
|
10
|
-
def initialize(subject = nil)
|
11
|
-
super()
|
12
|
-
@subject = subject
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class Fail < ShellOptsError; end
|
17
|
-
end
|
18
|
-
|
19
|
-
class NotYet < NotImplementedError; end
|
20
|
-
class NotThis < ScriptError; end
|
21
|
-
class NotHere < ScriptError; end
|