shellopts 2.0.0.pre.14 → 2.0.0
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/.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
|