help_parser 6.4.1 → 8.0.210917
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/README.md +122 -70
- data/lib/help_parser/completion.rb +31 -23
- data/lib/help_parser/constants.rb +22 -13
- data/lib/help_parser/exceptions.rb +4 -4
- data/lib/help_parser/k2t2r.rb +8 -4
- data/lib/help_parser/macros.rb +8 -6
- data/lib/help_parser/options.rb +27 -13
- data/lib/help_parser/parseh.rb +17 -14
- data/lib/help_parser/{validations.rb → validate.rb} +25 -17
- data/lib/help_parser.rb +12 -22
- metadata +12 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89d031ef15e33460fc4f23ccec23cc98f01cb9808fd77bb478c63be6a638f034
|
4
|
+
data.tar.gz: 9521262caa0b0a1617fc52bdeec42c4e305a4fc4ce34c5871128c64eecf5aae7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e6c0faa99fd04e8b8e2f289b0f1894538747defaeb557400b36904f7c330961b9cc068aa8bdbe06493f8339af19cd4397cfdca9e1f82bc20ddbf84b91553955
|
7
|
+
data.tar.gz: 135b86c92d72faa5b49cdfedd7b6c66a9e568e592ae5b8afc86c612efa23020c828acb45693435d5f5349679f61a963bac81041da6d6bdae9549c1ab2de3214b
|
data/README.md
CHANGED
@@ -1,78 +1,130 @@
|
|
1
|
-
# Help Parser
|
1
|
+
# Help Parser VIII: Helpland
|
2
2
|
|
3
|
-
* [
|
3
|
+
* [VERSION 8.0.210917](https://github.com/carlosjhr64/help_parser/releases)
|
4
|
+
* [github](https://www.github.com/carlosjhr64/help_parser)
|
4
5
|
* [rubygems](https://rubygems.org/gems/help_parser)
|
5
6
|
|
6
7
|
## DESCRIPTION:
|
7
|
-
Welcome to the best Help Parser of all!
|
8
|
-
Tweeker!
|
9
|
-
Which do you find most helpful?
|
10
|
-
Hard?
|
11
|
-
I prefer easy.
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
require "help_parser"
|
17
|
-
|
18
|
-
HELP = <<-HELP
|
19
|
-
# <= Hash here, parser skips
|
20
|
-
# The Awesome Command.
|
21
|
-
Usage:
|
22
|
-
awesome [:options+] <args>+
|
23
|
-
awesome :alternate <arg=NAME>
|
24
|
-
Options:
|
25
|
-
-v --version \t Give version and quit
|
26
|
-
-h --help \t Give help and quit
|
27
|
-
-s --long \t Short long synonyms
|
28
|
-
--name=NAME \t Typed
|
29
|
-
--number 5 \t Defaulted
|
30
|
-
--value=FLOAT 1.23 \t Typed and Defaulted
|
31
|
-
-a --all=YN y \t Short, long, typed, and defaulted
|
32
|
-
Alternate:
|
33
|
-
-V \t Just short
|
34
|
-
Types:
|
35
|
-
NAME /^[A-Z][a-z]+$/
|
36
|
-
FLOAT /^\\d+\\.\\d+$/
|
37
|
-
YN /^[YNyn]$/
|
38
|
-
Exclusive:
|
39
|
-
version help \t Tells parser these are exclusive keys
|
40
|
-
# <= Hash here, parser breaks out
|
41
|
-
# Notes #
|
42
|
-
Blah blah blah
|
43
|
-
HELP
|
44
|
-
|
45
|
-
VERSION = "6.4.1"
|
46
|
-
|
47
|
-
# Macros:
|
48
|
-
HelpParser.string(name) # for options.name : String
|
49
|
-
HelpParser.strings(args) # for options.args : Array(String)
|
50
|
-
HelpParser.float(value) # for options.value : Float
|
51
|
-
HelpParser.int?(number) # for options.number? : Int32 | Nil
|
52
|
-
|
53
|
-
HelpParser.run(VERSION, HELP) do |options|
|
54
|
-
hash = options._hash
|
55
|
-
pp hash # to inspect the hash
|
56
|
-
|
57
|
-
pp options.name if hash["name"]?
|
58
|
-
pp options.args if hash["args"]?
|
59
|
-
pp options.value if hash["value"]?
|
60
|
-
pp options.number?
|
61
|
-
end
|
62
|
-
|
63
|
-
Well, what do you think?
|
64
|
-
PERFECT!
|
65
|
-
|
66
|
-
## New for 6.4.0:
|
67
|
-
|
68
|
-
* Automates $VERBOSE=true on --verbose
|
69
|
-
* Reports typos you may have on options usage
|
70
|
-
|
71
|
-
## New for 6.1.0:
|
72
|
-
|
73
|
-
Running your `awesome` command with the `--help` flag will also check your help text for errors,
|
74
|
-
on top of giving the help text. Otherwise, the parser no longer checks for help text errors.
|
9
|
+
Welcome to Help Parser!
|
10
|
+
Do you have your help text?
|
11
|
+
Let's parse!
|
75
12
|
|
76
13
|
## INSTALL:
|
77
14
|
|
78
|
-
|
15
|
+
```console
|
16
|
+
$ gem install help_parser
|
17
|
+
```
|
18
|
+
|
19
|
+
## SYNOPSIS:
|
20
|
+
<!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
|
21
|
+
For the README validator that checks against me lying....
|
22
|
+
```ruby
|
23
|
+
unless File.basename($PROGRAM_NAME) == 'party'
|
24
|
+
# For example's sake say
|
25
|
+
$PROGRAM_NAME = 'party'
|
26
|
+
# and ARGV is
|
27
|
+
ARGV.concat ["-\-age", "-\-date=2020-09-07", 'touch', 'that']
|
28
|
+
# and proceed as if run as:
|
29
|
+
# awesome -\-name=Doe -\-value a b c
|
30
|
+
end
|
31
|
+
```
|
32
|
+
The following gem has been rated
|
33
|
+
| M | Mature |
|
34
|
+
-->
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require "help_parser"
|
38
|
+
|
39
|
+
HELP = <<-HELP
|
40
|
+
# <= Hash here, parser skips
|
41
|
+
# HelpParser: Party command example #
|
42
|
+
Usage:
|
43
|
+
party :options+ [<args>+]
|
44
|
+
party [:alternate] <arg=FLOAT>
|
45
|
+
party literal <arg1=WORD> <arg2=WORD>
|
46
|
+
Options:
|
47
|
+
-v --version \t Give version and quit
|
48
|
+
-h --help \t Give help and quit
|
49
|
+
-s --long \t Short long synonyms
|
50
|
+
--touch that \t Defaulted
|
51
|
+
--date=DATE \t Typed
|
52
|
+
--age=INTEGER 80 \t Typed and Defaulted
|
53
|
+
-a --all=YN y \t Short, long, typed, and defaulted
|
54
|
+
--to_be
|
55
|
+
--not_to_be
|
56
|
+
--rain
|
57
|
+
--water
|
58
|
+
--wet
|
59
|
+
Exclusive:
|
60
|
+
to_be not_to_be \t Tells parser these are mutually exclusive keys
|
61
|
+
Inclusive:
|
62
|
+
date age \t Tells parser any of these must include all of these
|
63
|
+
Conditional:
|
64
|
+
rain water wet \t Tells parser if first then all
|
65
|
+
Alternate:
|
66
|
+
--invoke
|
67
|
+
--wut
|
68
|
+
Types:
|
69
|
+
WORD /^[A-Za-z]+$/
|
70
|
+
DATE /^\\d\\d\\d\\d-\\d\\d-\\d\\d$/
|
71
|
+
INTEGER /^\\d+$/
|
72
|
+
FLOAT /^\\d+\\.\\d+$/
|
73
|
+
YN /^[YNyn]$/
|
74
|
+
# <= Hash here, parser breaks out
|
75
|
+
# Notes #
|
76
|
+
I wouldn't touch that!
|
77
|
+
HELP
|
78
|
+
|
79
|
+
VERSION = "1.2.3"
|
80
|
+
|
81
|
+
OPTIONS = HelpParser[VERSION, HELP] #~> HelpParser
|
82
|
+
|
83
|
+
# Macros:
|
84
|
+
HelpParser.strings?(:args) # for OPTIONS.args : Array(String) | Nil
|
85
|
+
HelpParser.int?(:age) # for OPTIONS.age? : Integer | Nil
|
86
|
+
HelpParser.float(:arg) # for options.arg : Float
|
87
|
+
HelpParser.string(:arg1, :arg2, :arg3) # for OPTIONS.arg1, etc : String
|
88
|
+
#=> [:arg1, :arg2, :arg3]
|
89
|
+
|
90
|
+
## If run as:
|
91
|
+
## party --age --date=2020-09-07 touch that
|
92
|
+
OPTIONS.age? #=> 80
|
93
|
+
OPTIONS.age?.class #=> Integer
|
94
|
+
OPTIONS.args? #=> ["touch", "that"]
|
95
|
+
OPTIONS.args?.class #=> Array
|
96
|
+
OPTIONS.arg? and OPTIONS.arg #=> false
|
97
|
+
OPTIONS.arg?.class #=> FalseClass
|
98
|
+
```
|
99
|
+
|
100
|
+
## Features
|
101
|
+
|
102
|
+
* `$DEBUG=true` on --debug
|
103
|
+
* `$VERBOSE=true` on --verbose
|
104
|
+
* -h and --help simultaneously will check help string for errors
|
105
|
+
* `HelpParser::REDTTY[msg]` will red color output `msg` to `STDERR`.
|
106
|
+
|
107
|
+
## LICENSE:
|
108
|
+
|
109
|
+
(The MIT License)
|
110
|
+
|
111
|
+
Copyright (c) 2021 CarlosJHR64
|
112
|
+
|
113
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
114
|
+
a copy of this software and associated documentation files (the
|
115
|
+
'Software'), to deal in the Software without restriction, including
|
116
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
117
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
118
|
+
permit persons to whom the Software is furnished to do so, subject to
|
119
|
+
the following conditions:
|
120
|
+
|
121
|
+
The above copyright notice and this permission notice shall be
|
122
|
+
included in all copies or substantial portions of the Software.
|
123
|
+
|
124
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
125
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
126
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
127
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
128
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
129
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
130
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -3,29 +3,34 @@ module HelpParser
|
|
3
3
|
def initialize(hash, specs)
|
4
4
|
@hash,@specs = hash,specs
|
5
5
|
@cache = NoDupHash.new
|
6
|
-
usage if @specs.has_key?(USAGE)
|
6
|
+
usage or diagnose if @specs.has_key?(USAGE)
|
7
7
|
pad
|
8
|
-
types
|
8
|
+
types if @specs.has_key?(TYPES)
|
9
9
|
end
|
10
10
|
|
11
|
+
# Which usage does the user's command options match?
|
11
12
|
def usage
|
12
13
|
@specs[USAGE].each do |cmd|
|
13
14
|
begin
|
14
15
|
i = matches(cmd)
|
15
16
|
raise NoMatch unless @hash.size==i
|
16
17
|
@cache.each{|k,v|@hash[k]=v} # Variables
|
17
|
-
return
|
18
|
+
return true # Good! Found matching usage.
|
18
19
|
rescue NoMatch
|
19
20
|
next
|
20
21
|
ensure
|
21
22
|
@cache.clear
|
22
23
|
end
|
23
24
|
end
|
25
|
+
return false # Bad! Did not match any of the expected usage.
|
26
|
+
end
|
24
27
|
|
28
|
+
# Diagnose user's usage.
|
29
|
+
def diagnose
|
25
30
|
dict = {}
|
26
31
|
@specs.each do |k,v|
|
27
|
-
next if
|
28
|
-
v.flatten.map{
|
32
|
+
next if RESERVED.include? k
|
33
|
+
v.flatten.map{_1.scan(/\w+/).first}.each{dict[_1]=true}
|
29
34
|
end
|
30
35
|
typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
|
31
36
|
raise UsageError, MSG[UNRECOGNIZED, typos] unless typos.empty?
|
@@ -34,22 +39,25 @@ module HelpParser
|
|
34
39
|
end
|
35
40
|
|
36
41
|
def types
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
|
43
|
+
@hash.each do |key,value|
|
44
|
+
next unless key.is_a?(String)
|
45
|
+
if type = k2t[key]
|
46
|
+
regex = t2r[type]
|
47
|
+
case value
|
48
|
+
when String
|
49
|
+
unless value=~regex
|
50
|
+
raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
|
51
|
+
end
|
52
|
+
when Array
|
53
|
+
value.each do |string|
|
54
|
+
unless string=~regex
|
55
|
+
raise UsageError,
|
56
|
+
"--#{key}=#{string} !~ #{type}=#{regex.inspect}"
|
49
57
|
end
|
50
|
-
else
|
51
|
-
raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
|
52
58
|
end
|
59
|
+
else
|
60
|
+
raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -74,7 +82,7 @@ module HelpParser
|
|
74
82
|
elsif value = @hash[long]
|
75
83
|
@hash[short] = true
|
76
84
|
if value==true && !default.nil?
|
77
|
-
@hash.delete(long)
|
85
|
+
@hash.delete(long) # ArgvHash reset
|
78
86
|
@hash[long]=default
|
79
87
|
end
|
80
88
|
end
|
@@ -83,7 +91,7 @@ module HelpParser
|
|
83
91
|
long,default = first[2..(i-1)],second
|
84
92
|
value = @hash[long]
|
85
93
|
if value==true
|
86
|
-
@hash.delete(long)
|
94
|
+
@hash.delete(long) # ArgvHash reset
|
87
95
|
@hash[long] = default
|
88
96
|
end
|
89
97
|
end
|
@@ -103,7 +111,7 @@ module HelpParser
|
|
103
111
|
end
|
104
112
|
next
|
105
113
|
elsif m=FLAG_GROUP.match(token)
|
106
|
-
group,plus = m[
|
114
|
+
group,plus = m[:k],m[:p]
|
107
115
|
key = keys[i]
|
108
116
|
raise NoMatch if key.nil? || key.is_a?(Integer)
|
109
117
|
list = @specs[group].flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
|
@@ -118,7 +126,7 @@ module HelpParser
|
|
118
126
|
elsif m=VARIABLE.match(token)
|
119
127
|
key = keys[i]
|
120
128
|
raise NoMatch unless key.is_a?(Integer)
|
121
|
-
variable,plus = m[
|
129
|
+
variable,plus = m[:k],m[:p]
|
122
130
|
if plus.nil?
|
123
131
|
@cache[variable] = @hash[key]
|
124
132
|
else
|
@@ -1,15 +1,19 @@
|
|
1
1
|
module HelpParser
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
end
|
6
|
-
def self.validate?
|
7
|
-
@@validate
|
8
|
-
end
|
2
|
+
V,VSN = 'v','version'
|
3
|
+
H,HLP = 'h','help'
|
4
|
+
VRBS,DBG = 'verbose','debug'
|
9
5
|
|
6
|
+
# reserved name
|
10
7
|
USAGE = 'usage'
|
11
8
|
TYPES = 'types'
|
12
9
|
EXCLUSIVE = 'exclusive'
|
10
|
+
INCLUSIVE = 'inclusive'
|
11
|
+
CONDITIONAL = 'conditional'
|
12
|
+
FLAG_CLUMPS = [EXCLUSIVE,INCLUSIVE,CONDITIONAL]
|
13
|
+
RESERVED = [USAGE,TYPES,EXCLUSIVE,INCLUSIVE,CONDITIONAL]
|
14
|
+
|
15
|
+
# sections
|
16
|
+
SECTION_NAME = /^[A-Z]\w+:$/
|
13
17
|
|
14
18
|
# usage
|
15
19
|
FLAG = /^[-][-]?(?<k>\w+)$/
|
@@ -23,13 +27,14 @@ module HelpParser
|
|
23
27
|
|
24
28
|
# spec -w,? --w+
|
25
29
|
SHORT_LONG = /^[-](?<s>\w),?\s+[-][-](?<k>\w+)$/
|
26
|
-
SHORT_LONG_DEFAULT =
|
30
|
+
SHORT_LONG_DEFAULT =
|
31
|
+
/^[-](?<s>\w),?\s+[-][-](?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>[^-\s]\S*)$/
|
27
32
|
|
28
33
|
# spec W+ /~/
|
29
34
|
TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
|
30
35
|
|
31
36
|
# spec w+( w+)+
|
32
|
-
X_DEF = /^\w+( +\w+)+$/
|
37
|
+
X_DEF = /^\w+( +\w+)+$/ # for both exclusive and inclusive specs
|
33
38
|
|
34
39
|
CSV = /,?\s+/
|
35
40
|
|
@@ -42,17 +47,19 @@ module HelpParser
|
|
42
47
|
DUP_KEY = 'Duplicate key'
|
43
48
|
DUP_WORD = 'Duplicate word'
|
44
49
|
DUP_FLAG = 'Duplicate flag'
|
45
|
-
DUP_X = 'Duplicate exclusive spec'
|
50
|
+
DUP_X = 'Duplicate exclusive/inclusive spec'
|
46
51
|
UNSEEN_FLAG = 'Undefined flag'
|
47
52
|
INCONSISTENT = 'Inconsistent use of variable'
|
48
53
|
UNEXPECTED = 'Unexpected string in help text'
|
49
54
|
BAD_REGEX = 'Bad regex'
|
50
55
|
REDUNDANT = 'Redundant'
|
51
56
|
EXCLUSIVE_KEYS = 'Exclusive keys'
|
57
|
+
INCLUSIVE_KEYS = 'Inclusive keys'
|
58
|
+
CONDITIONAL_KEYS = 'Conditional keys'
|
52
59
|
UNBALANCED = 'Unbalanced brackets'
|
53
60
|
UNRECOGNIZED_TOKEN = 'Unrecognized usage token'
|
54
61
|
UNRECOGNIZED_TYPE = 'Unrecognized type spec'
|
55
|
-
UNRECOGNIZED_X = 'Unrecognized exclusive spec'
|
62
|
+
UNRECOGNIZED_X = 'Unrecognized exclusive/inclusive spec'
|
56
63
|
UNRECOGNIZED_OPTION = 'Unrecognized option spec'
|
57
64
|
UNRECOGNIZED = 'Unrecognized'
|
58
65
|
UNDEFINED_SECTION = 'Section not defined'
|
@@ -72,7 +79,9 @@ module HelpParser
|
|
72
79
|
EXTRANEOUS_SPACES = 'Extraneous spaces in help.'
|
73
80
|
|
74
81
|
# lambda utilities
|
75
|
-
MSG = lambda{|msg,*keys| "
|
82
|
+
MSG = lambda{|msg,*keys| "#{msg}: #{keys.join(' ')}"}
|
76
83
|
F2K = lambda{|f| f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
|
77
|
-
|
84
|
+
REDTTY = lambda{|msg,out=$stderr|
|
85
|
+
out.tty? ? out.puts("\033[0;31m#{msg}\033[0m"): out.puts(msg)
|
86
|
+
}
|
78
87
|
end
|
@@ -10,9 +10,9 @@ module HelpParser
|
|
10
10
|
|
11
11
|
def exit
|
12
12
|
if @code > 0
|
13
|
-
|
13
|
+
REDTTY[self.message]
|
14
14
|
else
|
15
|
-
puts self.message
|
15
|
+
$stdout.puts self.message
|
16
16
|
end
|
17
17
|
Kernel.exit @code
|
18
18
|
end
|
@@ -36,10 +36,10 @@ module HelpParser
|
|
36
36
|
end
|
37
37
|
|
38
38
|
class NoMatch < HelpParserException
|
39
|
-
# used to
|
39
|
+
# used to short-circuit out
|
40
40
|
def _init; @code = EX_SOFTWARE; end
|
41
41
|
|
42
|
-
# Forces it's
|
42
|
+
# Forces it's own message
|
43
43
|
def initialize
|
44
44
|
super(NO_MATCH)
|
45
45
|
end
|
data/lib/help_parser/k2t2r.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
module HelpParser
|
2
2
|
def self.k2t(specs)
|
3
3
|
k2t = NoDupHash.new
|
4
|
-
tokens = specs.select{|k,v| !(k==TYPES)}.values.flatten
|
4
|
+
tokens = specs.select{|k,v| !(k==TYPES)}.values.flatten
|
5
|
+
.select{|v|v.include?('=')}
|
5
6
|
tokens.each do |token|
|
6
7
|
if match = VARIABLE.match(token) || LONG.match(token)
|
7
|
-
name, type = match[
|
8
|
-
k2t[name]
|
9
|
-
|
8
|
+
name, type = match[:k], match[:t]
|
9
|
+
if _=k2t[name]
|
10
|
+
raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
|
11
|
+
else
|
12
|
+
k2t[name] = type
|
13
|
+
end
|
10
14
|
else
|
11
15
|
# Expected these to be caught earlier...
|
12
16
|
raise SoftwareError, MSG[UNEXPECTED,token]
|
data/lib/help_parser/macros.rb
CHANGED
@@ -20,7 +20,8 @@ module HelpParser
|
|
20
20
|
class Options
|
21
21
|
def #{name}?
|
22
22
|
s = @hash['#{name}']
|
23
|
-
raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.nil? ||
|
23
|
+
raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.nil? ||
|
24
|
+
s.is_a?(String)
|
24
25
|
return s
|
25
26
|
end
|
26
27
|
end
|
@@ -50,7 +51,8 @@ module HelpParser
|
|
50
51
|
class Options
|
51
52
|
def #{name}?
|
52
53
|
a = @hash['#{name}']
|
53
|
-
raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.nil? ||
|
54
|
+
raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.nil? ||
|
55
|
+
a.is_a?(Array)
|
54
56
|
return a
|
55
57
|
end
|
56
58
|
end
|
@@ -100,7 +102,7 @@ module HelpParser
|
|
100
102
|
def #{name}
|
101
103
|
f = @hash['#{name}']
|
102
104
|
raise unless f.is_a?(Array)
|
103
|
-
f.map{
|
105
|
+
f.map{_1.to_f}
|
104
106
|
rescue
|
105
107
|
raise UsageError, MSG[#{NOT_FLOATS},'#{name}']
|
106
108
|
end
|
@@ -118,7 +120,7 @@ module HelpParser
|
|
118
120
|
f = @hash['#{name}']
|
119
121
|
return nil unless f
|
120
122
|
raise unless f.is_a?(Array)
|
121
|
-
f.map{
|
123
|
+
f.map{_1.to_f}
|
122
124
|
rescue
|
123
125
|
raise UsageError, MSG[NOT_FLOATS,'#{name}']
|
124
126
|
end
|
@@ -169,7 +171,7 @@ module HelpParser
|
|
169
171
|
def #{name}
|
170
172
|
f = @hash['#{name}']
|
171
173
|
raise unless f.is_a?(Array)
|
172
|
-
f.map{
|
174
|
+
f.map{_1.to_i}
|
173
175
|
rescue
|
174
176
|
raise UsageError, MSG[NOT_INTEGERS,'#{name}']
|
175
177
|
end
|
@@ -187,7 +189,7 @@ module HelpParser
|
|
187
189
|
f = @hash['#{name}']
|
188
190
|
return nil unless f
|
189
191
|
raise unless f.is_a?(Array)
|
190
|
-
f.map{
|
192
|
+
f.map{_1.to_i}
|
191
193
|
rescue
|
192
194
|
raise UsageError, MSG[NOT_INTEGERS,'#{name}']
|
193
195
|
end
|
data/lib/help_parser/options.rb
CHANGED
@@ -2,31 +2,45 @@ module HelpParser
|
|
2
2
|
class Options
|
3
3
|
def initialize(version, help, argv)
|
4
4
|
@hash = HelpParser.parsea(argv)
|
5
|
-
if version && (@hash.has_key?(
|
5
|
+
if version && (@hash.has_key?(V) || @hash.has_key?(VSN))
|
6
6
|
# -v or --version
|
7
7
|
raise VersionException, version
|
8
8
|
end
|
9
9
|
if help
|
10
|
-
|
11
|
-
if @hash.
|
12
|
-
|
13
|
-
# validates help
|
14
|
-
HelpParser.parseh(help, true)
|
15
|
-
rescue HelpError
|
16
|
-
$stderr.puts $!
|
17
|
-
end if _
|
10
|
+
h = [H, HLP]
|
11
|
+
if h.any?{@hash.key? _1}
|
12
|
+
HelpParser.parseh(help, validate: true) if h.all?{@hash.key? _1}
|
18
13
|
raise HelpException, help
|
19
14
|
end
|
20
|
-
specs = HelpParser.parseh(help
|
15
|
+
specs = HelpParser.parseh(help)
|
21
16
|
Completion.new(@hash, specs)
|
22
17
|
if exclusive=specs[EXCLUSIVE]
|
23
|
-
exclusive.each
|
18
|
+
exclusive.each do |x|
|
19
|
+
count = x.count{@hash.key? _1}
|
20
|
+
raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
if inclusive=specs[INCLUSIVE]
|
24
|
+
inclusive.each do |i|
|
25
|
+
count = i.count{@hash.key? _1}
|
26
|
+
unless count==0 or count==i.length
|
27
|
+
raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
if conditional=specs[CONDITIONAL]
|
32
|
+
conditional.each do |c|
|
33
|
+
if @hash.key? c[0] and not c.all?{@hash.key? _1}
|
34
|
+
raise HelpParser::UsageError, MSG[CONDITIONAL_KEYS,*c]
|
35
|
+
end
|
36
|
+
end
|
24
37
|
end
|
25
38
|
end
|
26
|
-
$VERBOSE = true if @hash[
|
39
|
+
$VERBOSE = true if @hash[VRBS]==true
|
40
|
+
$DEBUG = true if @hash[DBG]==true
|
27
41
|
end
|
28
42
|
|
29
|
-
def
|
43
|
+
def to_h
|
30
44
|
@hash
|
31
45
|
end
|
32
46
|
|
data/lib/help_parser/parseh.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module HelpParser
|
2
|
-
def self.parseh(help, validate
|
2
|
+
def self.parseh(help, validate: false)
|
3
3
|
specs,name = NoDupHash.new,''
|
4
4
|
help.each_line do |line|
|
5
5
|
line.chomp!
|
6
6
|
next if line==''
|
7
|
-
if line
|
7
|
+
if line=~SECTION_NAME
|
8
8
|
name = line[0..-2].downcase
|
9
9
|
specs[name] = []
|
10
10
|
else
|
@@ -15,29 +15,32 @@ module HelpParser
|
|
15
15
|
raise HelpError, EXTRANEOUS_SPACES if validate and spec==''
|
16
16
|
case name
|
17
17
|
when USAGE
|
18
|
-
|
18
|
+
Validate.line_chars(spec.chars) if validate
|
19
19
|
tokens = HelpParser.parseu(spec.chars)
|
20
|
-
|
21
|
-
specs[
|
20
|
+
Validate.usage_tokens(tokens) if validate
|
21
|
+
specs[USAGE].push tokens
|
22
22
|
when TYPES
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
if validate and not spec=~TYPE_DEF
|
24
|
+
raise HelpError, MSG[UNRECOGNIZED_TYPE,spec]
|
25
|
+
end
|
26
|
+
specs[TYPES].push spec.split(CSV)
|
27
|
+
when *FLAG_CLUMPS # EXCLUSIVE,INCLUSIVE,CONDITIONAL,...
|
28
|
+
if validate and not spec=~X_DEF
|
29
|
+
raise HelpError, MSG[UNRECOGNIZED_X,spec]
|
30
|
+
end
|
27
31
|
specs[name].push spec.split(CSV)
|
28
32
|
else
|
29
|
-
|
30
|
-
|
31
|
-
end
|
33
|
+
raise HelpError, MSG[UNRECOGNIZED_OPTION,spec] if validate and
|
34
|
+
not [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].any?{_1=~spec}
|
32
35
|
specs[name].push spec.split(CSV)
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
if validate
|
37
|
-
|
40
|
+
Validate.usage_specs(specs)
|
38
41
|
if t2r = HelpParser.t2r(specs)
|
39
42
|
k2t = HelpParser.k2t(specs)
|
40
|
-
|
43
|
+
Validate.k2t2r(specs, k2t, t2r)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
return specs
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module HelpParser
|
2
|
-
|
2
|
+
module Validate
|
3
|
+
def self.line_chars(chars)
|
3
4
|
count = 0
|
4
5
|
chars.each do |c|
|
5
6
|
if c=='['
|
@@ -12,7 +13,7 @@ module HelpParser
|
|
12
13
|
raise HelpError, MSG[UNBALANCED,chars.join] unless count==0
|
13
14
|
end
|
14
15
|
|
15
|
-
def self.
|
16
|
+
def self.usage_tokens(tokens)
|
16
17
|
words = []
|
17
18
|
tokens.flatten.each do |token|
|
18
19
|
match = token.match(FLAG) ||
|
@@ -20,24 +21,28 @@ module HelpParser
|
|
20
21
|
token.match(VARIABLE) ||
|
21
22
|
token.match(FLAG_GROUP)
|
22
23
|
raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless match
|
23
|
-
words.push match[
|
24
|
+
words.push match[:k] # key
|
24
25
|
end
|
25
26
|
words.each_with_index do |word,i|
|
26
27
|
raise HelpError, MSG[DUP_WORD,word] unless i==words.rindex(word)
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
def self.
|
31
|
-
option_specs = specs.select{|a,b|
|
31
|
+
def self.usage_specs(specs)
|
32
|
+
option_specs = specs.select{|a,b| not RESERVED.include? a}
|
32
33
|
flags = option_specs.values.flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
FLAG_CLUMPS.each do |k|
|
35
|
+
if a=specs[k]
|
36
|
+
seen = {}
|
37
|
+
a.each do |xs|
|
38
|
+
k = xs.sort.join(' ').to_sym
|
39
|
+
if seen[k] or not xs.length==xs.uniq.length
|
40
|
+
raise HelpError, MSG[DUP_X,k]
|
41
|
+
end
|
42
|
+
seen[k] = true
|
43
|
+
xs.each do |x|
|
44
|
+
raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
end
|
@@ -49,7 +54,7 @@ module HelpParser
|
|
49
54
|
unless specs_usage.nil?
|
50
55
|
specs_usage.flatten.each do |token|
|
51
56
|
if match = token.match(FLAG_GROUP)
|
52
|
-
key = match[
|
57
|
+
key = match[:k]
|
53
58
|
raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
|
54
59
|
group.push(key)
|
55
60
|
end
|
@@ -57,12 +62,12 @@ module HelpParser
|
|
57
62
|
end
|
58
63
|
specs.each do |key,tokens|
|
59
64
|
raise HelpError, MSG[MISSING_CASES,key] unless tokens.size>0
|
60
|
-
next if specs_usage.nil? or RESERVED
|
65
|
+
next if specs_usage.nil? or RESERVED.include? key
|
61
66
|
raise HelpError, MSG[MISSING_USAGE,key] unless group.include?(key)
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
65
|
-
def self.
|
70
|
+
def self.k2t2r(specs, k2t, t2r)
|
66
71
|
a,b = k2t.values.uniq.sort,t2r.keys.sort
|
67
72
|
unless a==b
|
68
73
|
c = (a+b).uniq.select{|x|!(a.include?(x) && b.include?(x))}
|
@@ -80,8 +85,11 @@ module HelpParser
|
|
80
85
|
long = long_type[2..(i-1)]
|
81
86
|
type = long_type[(i+1)..-1]
|
82
87
|
regex = t2r[type]
|
83
|
-
|
88
|
+
unless regex=~default
|
89
|
+
raise HelpError, MSG[BAD_DEFAULT,long,default,type,regex.inspect]
|
90
|
+
end
|
84
91
|
end
|
85
92
|
end
|
86
93
|
end
|
87
94
|
end
|
95
|
+
end
|
data/lib/help_parser.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
11
|
-
require_relative './help_parser/macros'
|
1
|
+
require_relative 'help_parser/constants'
|
2
|
+
require_relative 'help_parser/exceptions'
|
3
|
+
require_relative 'help_parser/aliases'
|
4
|
+
require_relative 'help_parser/parsea'
|
5
|
+
require_relative 'help_parser/parseu'
|
6
|
+
require_relative 'help_parser/parseh'
|
7
|
+
require_relative 'help_parser/k2t2r'
|
8
|
+
require_relative 'help_parser/completion'
|
9
|
+
require_relative 'help_parser/options'
|
10
|
+
require_relative 'help_parser/macros'
|
12
11
|
|
13
12
|
module HelpParser
|
14
|
-
VERSION = '
|
13
|
+
VERSION = '8.0.210917'
|
14
|
+
autoload :Validate, 'help_parser/validate'
|
15
15
|
|
16
16
|
def self.[](
|
17
17
|
version = nil,
|
@@ -21,16 +21,6 @@ module HelpParser
|
|
21
21
|
rescue HelpParserException => exception
|
22
22
|
exception.exit
|
23
23
|
end
|
24
|
-
|
25
|
-
def self.run(
|
26
|
-
version = nil,
|
27
|
-
help = nil,
|
28
|
-
argv = [File.basename($0)]+ARGV)
|
29
|
-
options = Options.new(version, help, argv)
|
30
|
-
yield options
|
31
|
-
rescue HelpParserException => exception
|
32
|
-
exception.exit
|
33
|
-
end
|
34
24
|
end
|
35
25
|
|
36
26
|
# Requires:
|
metadata
CHANGED
@@ -1,21 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: help_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 8.0.210917
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- CarlosJHR64
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
14
|
-
Welcome to the best Help Parser of all!
|
15
|
-
Tweeker!
|
16
|
-
Which do you find most helpful?
|
17
|
-
Hard?
|
18
|
-
I prefer easy.
|
13
|
+
description: "Welcome to Help Parser! \nDo you have your help text? \nLet's parse!\n"
|
19
14
|
email: carlosjhr64@gmail.com
|
20
15
|
executables: []
|
21
16
|
extensions: []
|
@@ -34,12 +29,12 @@ files:
|
|
34
29
|
- lib/help_parser/parsea.rb
|
35
30
|
- lib/help_parser/parseh.rb
|
36
31
|
- lib/help_parser/parseu.rb
|
37
|
-
- lib/help_parser/
|
38
|
-
homepage: https://github.com/carlosjhr64/
|
32
|
+
- lib/help_parser/validate.rb
|
33
|
+
homepage: https://github.com/carlosjhr64/help_parser
|
39
34
|
licenses:
|
40
35
|
- MIT
|
41
36
|
metadata: {}
|
42
|
-
post_install_message:
|
37
|
+
post_install_message:
|
43
38
|
rdoc_options: []
|
44
39
|
require_paths:
|
45
40
|
- lib
|
@@ -54,11 +49,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
49
|
- !ruby/object:Gem::Version
|
55
50
|
version: '0'
|
56
51
|
requirements:
|
57
|
-
- 'ruby: ruby
|
58
|
-
|
59
|
-
|
60
|
-
signing_key:
|
52
|
+
- 'ruby: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux]'
|
53
|
+
rubygems_version: 3.2.22
|
54
|
+
signing_key:
|
61
55
|
specification_version: 4
|
62
|
-
summary: Welcome to
|
63
|
-
Hard? I prefer easy.
|
56
|
+
summary: Welcome to Help Parser! Do you have your help text? Let's parse!
|
64
57
|
test_files: []
|