help_parser 8.0.210917 → 8.2.230210

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89d031ef15e33460fc4f23ccec23cc98f01cb9808fd77bb478c63be6a638f034
4
- data.tar.gz: 9521262caa0b0a1617fc52bdeec42c4e305a4fc4ce34c5871128c64eecf5aae7
3
+ metadata.gz: f19d24ec56736d0d1bbcce1e0a847ac45e9d82defc89c7799c7b9c8c7c4ae078
4
+ data.tar.gz: fe92cf2d740771329bee184dd205d59bc72c1265d79ac50af3a6c4d0fa735297
5
5
  SHA512:
6
- metadata.gz: 4e6c0faa99fd04e8b8e2f289b0f1894538747defaeb557400b36904f7c330961b9cc068aa8bdbe06493f8339af19cd4397cfdca9e1f82bc20ddbf84b91553955
7
- data.tar.gz: 135b86c92d72faa5b49cdfedd7b6c66a9e568e592ae5b8afc86c612efa23020c828acb45693435d5f5349679f61a963bac81041da6d6bdae9549c1ab2de3214b
6
+ metadata.gz: f20dad3a1453e625236af9cb176328652352e9f2f35f00da02cc6ab1853671c82ae13d5ec8226247be116cbd82bdbb4aee24490200f04b2ddba79e380822f36b
7
+ data.tar.gz: 0aebde782784afb644dd1047f32af8de07d07af4c53435ec47ed8c8f7e54a3997a6e6a08c372f23e1a38f37c10f6e0604c26afcb5f16b8d7677120891f10ed71
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Help Parser VIII: Helpland
2
2
 
3
- * [VERSION 8.0.210917](https://github.com/carlosjhr64/help_parser/releases)
3
+ * [VERSION 8.2.230210](https://github.com/carlosjhr64/help_parser/releases)
4
4
  * [github](https://www.github.com/carlosjhr64/help_parser)
5
5
  * [rubygems](https://rubygems.org/gems/help_parser)
6
6
 
@@ -11,11 +11,9 @@ Do you have your help text?
11
11
  Let's parse!
12
12
 
13
13
  ## INSTALL:
14
-
15
14
  ```console
16
15
  $ gem install help_parser
17
16
  ```
18
-
19
17
  ## SYNOPSIS:
20
18
  <!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
21
19
  For the README validator that checks against me lying....
@@ -32,7 +30,6 @@ end
32
30
  The following gem has been rated
33
31
  | M | Mature |
34
32
  -->
35
-
36
33
  ```ruby
37
34
  require "help_parser"
38
35
 
@@ -96,19 +93,19 @@ OPTIONS.args?.class #=> Array
96
93
  OPTIONS.arg? and OPTIONS.arg #=> false
97
94
  OPTIONS.arg?.class #=> FalseClass
98
95
  ```
99
-
100
96
  ## Features
101
97
 
98
+ * `ARGV` setup for `ARGF` when one of the "Types:" given is "ARGF"
102
99
  * `$DEBUG=true` on --debug
103
100
  * `$VERBOSE=true` on --verbose
104
101
  * -h and --help simultaneously will check help string for errors
105
- * `HelpParser::REDTTY[msg]` will red color output `msg` to `STDERR`.
102
+ * `HelpParser::REDTTY[msg]` will red color output `msg` to `$stderr`.
106
103
 
107
104
  ## LICENSE:
108
105
 
109
106
  (The MIT License)
110
107
 
111
- Copyright (c) 2021 CarlosJHR64
108
+ Copyright (c) 2023 CarlosJHR64
112
109
 
113
110
  Permission is hereby granted, free of charge, to any person obtaining
114
111
  a copy of this software and associated documentation files (the
@@ -1,14 +1,14 @@
1
1
  module HelpParser
2
2
  class NoDupHash < Hash
3
3
  def []=(k,v)
4
- raise HelpError, MSG[DUP_KEY,k] if self.has_key?(k)
4
+ raise HelpError, MSG[DUP_KEY,k] if key?(k)
5
5
  super
6
6
  end
7
7
  end
8
8
 
9
9
  class ArgvHash < Hash
10
10
  def []=(k,v)
11
- raise UsageError, MSG[DUP_KEY,k] if self.has_key?(k)
11
+ raise UsageError, MSG[DUP_KEY,k] if key?(k)
12
12
  super
13
13
  end
14
14
  end
@@ -3,36 +3,50 @@ module HelpParser
3
3
  def initialize(hash, specs)
4
4
  @hash,@specs = hash,specs
5
5
  @cache = NoDupHash.new
6
- usage or diagnose if @specs.has_key?(USAGE)
6
+ usage or diagnose if @specs.key?(USAGE)
7
7
  pad
8
- types if @specs.has_key?(TYPES)
8
+ if @specs.key?(TYPES)
9
+ k2t = types
10
+ handle_argf(k2t) if k2t.detect{|_,v|v=='ARGF'}
11
+ end
12
+ end
13
+
14
+ # Prepare ARGV for ARGF.
15
+ def handle_argf(k2t)
16
+ files = @hash.select{|k,_|k2t[k]=='ARGF'}.map{|_,v|v}.flatten
17
+ e = files.reject{File.exist?_1}.join(', ')
18
+ raise UsageError, MSG[NOT_EXIST, e] unless e.empty?
19
+ ARGV.replace files
9
20
  end
10
21
 
11
22
  # Which usage does the user's command options match?
12
23
  def usage
13
24
  @specs[USAGE].each do |cmd|
14
- begin
15
- i = matches(cmd)
16
- raise NoMatch unless @hash.size==i
17
- @cache.each{|k,v|@hash[k]=v} # Variables
18
- return true # Good! Found matching usage.
19
- rescue NoMatch
20
- next
21
- ensure
22
- @cache.clear
23
- end
25
+ i = matches(cmd)
26
+ raise NoMatch unless @hash.size==i
27
+ @cache.each{|k,v|@hash[k]=v} # Variables
28
+ return true # Good! Found matching usage.
29
+ rescue NoMatch
30
+ next
31
+ ensure
32
+ @cache.clear
24
33
  end
25
- return false # Bad! Did not match any of the expected usage.
34
+ false # Bad! Did not match any of the expected usage.
26
35
  end
27
36
 
28
37
  # Diagnose user's usage.
29
38
  def diagnose
30
39
  dict = {}
31
- @specs.each do |k,v|
32
- next if RESERVED.include? k
33
- v.flatten.map{_1.scan(/\w+/).first}.each{dict[_1]=true}
40
+ @specs.each do |section,list|
41
+ next if RESERVED.include? section
42
+ list.flatten.select{_1[0]=='-'}.each do |key_type|
43
+ key_type.scan(/\w+/) do |key|
44
+ dict[key]=true
45
+ break
46
+ end
47
+ end
34
48
  end
35
- typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
49
+ typos = @hash.keys.select{_1.is_a?(String) && !dict[_1]}
36
50
  raise UsageError, MSG[UNRECOGNIZED, typos] unless typos.empty?
37
51
 
38
52
  raise UsageError, MATCH_USAGE
@@ -41,32 +55,31 @@ module HelpParser
41
55
  def types
42
56
  t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
43
57
  @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}"
57
- end
58
+ next unless key.is_a?(String) && (type=k2t[key])
59
+ regex = t2r[type]
60
+ case value
61
+ when String
62
+ unless value=~regex
63
+ raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}"
64
+ end
65
+ when Array
66
+ value.each do |string|
67
+ unless string=~regex
68
+ raise UsageError,
69
+ "--#{key}=#{string} !~ #{type}=#{regex.inspect}"
58
70
  end
59
- else
60
- raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
61
71
  end
72
+ else
73
+ raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
62
74
  end
63
75
  end
76
+ k2t
64
77
  end
65
78
 
66
79
  def pad
67
80
  # Synonyms and defaults:
68
81
  @specs.each do |section,options|
69
- next if section==USAGE || section==TYPES
82
+ next if RESERVED.any?{section==_1}
70
83
  options.each do |words|
71
84
  next unless words.size>1
72
85
  first,second,default = words[0],words[1],words[2]
@@ -74,12 +87,12 @@ module HelpParser
74
87
  if second[0]=='-'
75
88
  i = second.index('=') || 0
76
89
  short,long = first[1],second[2..(i-1)]
77
- if @hash.has_key?(short)
78
- if @hash.has_key?(long)
90
+ if @hash.key?(short)
91
+ if @hash.key?(long)
79
92
  raise UsageError, MSG[REDUNDANT, short, long]
80
93
  end
81
- @hash[long] = (default.nil?) ? true : default
82
- elsif value = @hash[long]
94
+ @hash[long] = default.nil? ? true : default
95
+ elsif (value=@hash[long])
83
96
  @hash[short] = true
84
97
  if value==true && !default.nil?
85
98
  @hash.delete(long) # ArgvHash reset
@@ -95,6 +108,8 @@ module HelpParser
95
108
  @hash[long] = default
96
109
  end
97
110
  end
111
+ else
112
+ raise SoftwareError, MSG[UNEXPECTED, words]
98
113
  end
99
114
  end
100
115
  end
@@ -110,20 +125,20 @@ module HelpParser
110
125
  # OK, NEVERMIND!
111
126
  end
112
127
  next
113
- elsif m=FLAG_GROUP.match(token)
128
+ elsif (m=FLAG_GROUP.match token)
114
129
  group,plus = m[:k],m[:p]
115
130
  key = keys[i]
116
- raise NoMatch if key.nil? || key.is_a?(Integer)
131
+ raise NoMatch unless key.is_a? String
117
132
  list = @specs[group].flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
118
133
  raise NoMatch unless list.include?(key)
119
134
  unless plus.nil?
120
135
  loop do
121
136
  key = keys[i+1]
122
- break if key.nil? || key.is_a?(Integer) || !list.include?(key)
137
+ break unless key.is_a?(String) && list.include?(key)
123
138
  i+=1
124
139
  end
125
140
  end
126
- elsif m=VARIABLE.match(token)
141
+ elsif (m=VARIABLE.match(token))
127
142
  key = keys[i]
128
143
  raise NoMatch unless key.is_a?(Integer)
129
144
  variable,plus = m[:k],m[:p]
@@ -146,7 +161,7 @@ module HelpParser
146
161
  end
147
162
  i += 1
148
163
  end
149
- return i
164
+ i
150
165
  end
151
166
  end
152
167
  end
@@ -1,6 +1,6 @@
1
1
  module HelpParser
2
- V,VSN = 'v','version'
3
- H,HLP = 'h','help'
2
+ VSN = %w[v version]
3
+ HLP = %w[h help]
4
4
  VRBS,DBG = 'verbose','debug'
5
5
 
6
6
  # reserved name
@@ -13,22 +13,22 @@ module HelpParser
13
13
  RESERVED = [USAGE,TYPES,EXCLUSIVE,INCLUSIVE,CONDITIONAL]
14
14
 
15
15
  # sections
16
- SECTION_NAME = /^[A-Z]\w+:$/
16
+ SECTION_NAME = /^(?<name>[A-Z]\w+):$/
17
17
 
18
18
  # usage
19
- FLAG = /^[-][-]?(?<k>\w+)$/
19
+ FLAG = /^--?(?<k>\w+)$/
20
20
  LITERAL = /^(?<k>\w[\w.-]*:?)$/
21
21
  VARIABLE = /^<(?<k>\w+)(=(?<t>[A-Z]+))?>(?<p>[+])?$/
22
22
  FLAG_GROUP = /^:(?<k>\w+)(?<p>[+])?$/
23
23
 
24
24
  # spec --?w+
25
- SHORT = /^[-](?<s>\w)$/
26
- LONG = /^[-][-](?<k>\w+)(=(?<t>[A-Z]+))?(,?\s+(?<d>[^-\s]\S*))?$/
25
+ SHORT = /^-(?<s>\w)$/
26
+ LONG = /^--(?<k>\w+)(=(?<t>[A-Z]+))?(,?\s+(?<d>[^-\s]\S*))?$/
27
27
 
28
28
  # spec -w,? --w+
29
- SHORT_LONG = /^[-](?<s>\w),?\s+[-][-](?<k>\w+)$/
29
+ SHORT_LONG = /^-(?<s>\w),?\s+--(?<k>\w+)$/
30
30
  SHORT_LONG_DEFAULT =
31
- /^[-](?<s>\w),?\s+[-][-](?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>[^-\s]\S*)$/
31
+ /^-(?<s>\w),?\s+--(?<k>\w+)(=(?<t>[A-Z]+))?,?\s+(?<d>\S*)$/
32
32
 
33
33
  # spec W+ /~/
34
34
  TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
@@ -73,15 +73,16 @@ module HelpParser
73
73
  NOT_FLOATS = 'Not all Floats'
74
74
  NOT_INTEGER = 'Not an Integer'
75
75
  NOT_INTEGERS = 'Not all Integers'
76
+ NOT_EXIST = 'Does not exist'
76
77
  # error messages, full:
77
78
  NO_MATCH = 'Software Error: NoMatch was not caught by HelpParser.'
78
79
  MATCH_USAGE = 'Please match usage.'
79
80
  EXTRANEOUS_SPACES = 'Extraneous spaces in help.'
80
81
 
81
82
  # lambda utilities
82
- MSG = lambda{|msg,*keys| "#{msg}: #{keys.join(' ')}"}
83
- F2K = lambda{|f| f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
84
- REDTTY = lambda{|msg,out=$stderr|
83
+ MSG = ->(msg,*keys){"#{msg}: #{keys.join(' ')}"}
84
+ F2K = ->(f){f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
85
+ REDTTY = lambda do |msg,out=$stderr|
85
86
  out.tty? ? out.puts("\033[0;31m#{msg}\033[0m"): out.puts(msg)
86
- }
87
+ end
87
88
  end
@@ -1,6 +1,8 @@
1
1
  module HelpParser
2
2
  class HelpParserException < Exception
3
- def _init; @code = 1; end
3
+ def _init
4
+ @code = 1
5
+ end
4
6
 
5
7
  # Must give message
6
8
  def initialize(message)
@@ -9,35 +11,45 @@ module HelpParser
9
11
  end
10
12
 
11
13
  def exit
12
- if @code > 0
13
- REDTTY[self.message]
14
+ if @code.positive?
15
+ REDTTY[message]
14
16
  else
15
- $stdout.puts self.message
17
+ $stdout.puts message
16
18
  end
17
19
  Kernel.exit @code
18
20
  end
19
21
  end
20
22
 
21
23
  class VersionException < HelpParserException
22
- def _init; @code = 0; end
24
+ def _init
25
+ @code = 0
26
+ end
23
27
  end
24
28
 
25
29
  class HelpException < HelpParserException
26
- def _init; @code = 0; end
30
+ def _init
31
+ @code = 0
32
+ end
27
33
  end
28
34
 
29
35
  class UsageError < HelpParserException
30
- def _init; @code = EX_USAGE; end
36
+ def _init
37
+ @code = EX_USAGE
38
+ end
31
39
  end
32
40
 
33
41
  class SoftwareError < HelpParserException
34
42
  # Stuff that should not happen
35
- def _init; @code = EX_SOFTWARE; end
43
+ def _init
44
+ @code = EX_SOFTWARE
45
+ end
36
46
  end
37
47
 
38
48
  class NoMatch < HelpParserException
39
49
  # used to short-circuit out
40
- def _init; @code = EX_SOFTWARE; end
50
+ def _init
51
+ @code = EX_SOFTWARE
52
+ end
41
53
 
42
54
  # Forces it's own message
43
55
  def initialize
@@ -46,6 +58,8 @@ module HelpParser
46
58
  end
47
59
 
48
60
  class HelpError < HelpParserException
49
- def _init; @code = EX_CONFIG; end
61
+ def _init
62
+ @code = EX_CONFIG
63
+ end
50
64
  end
51
65
  end
@@ -1,13 +1,16 @@
1
1
  module HelpParser
2
+ # k2t is an acronym for the "key to type" mapping
2
3
  def self.k2t(specs)
3
4
  k2t = NoDupHash.new
4
- tokens = specs.select{|k,v| !(k==TYPES)}.values.flatten
5
- .select{|v|v.include?('=')}
5
+ # If specs section is not a RESERVED section, it's an options list.
6
+ tokens = specs.select{|k,_| k==USAGE or !RESERVED.include?(k)}
7
+ # Tokens associating a key to a type.
8
+ .values.flatten.select{|v|v.include?('=')}
6
9
  tokens.each do |token|
7
- if match = VARIABLE.match(token) || LONG.match(token)
10
+ if (match = VARIABLE.match(token) || LONG.match(token))
8
11
  name, type = match[:k], match[:t]
9
- if _=k2t[name]
10
- raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
12
+ if (_=k2t[name])
13
+ raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
11
14
  else
12
15
  k2t[name] = type
13
16
  end
@@ -16,11 +19,12 @@ module HelpParser
16
19
  raise SoftwareError, MSG[UNEXPECTED,token]
17
20
  end
18
21
  end
19
- return k2t
22
+ k2t
20
23
  end
21
24
 
25
+ # t2r is an acronym for "type to regexp"
22
26
  def self.t2r(specs)
23
- if types = specs[TYPES]
27
+ if (types=specs[TYPES])
24
28
  t2r = NoDupHash.new
25
29
  types.each do |pair|
26
30
  type, pattern = *pair
@@ -32,6 +36,6 @@ module HelpParser
32
36
  end
33
37
  return t2r
34
38
  end
35
- return nil
39
+ nil
36
40
  end
37
41
  end
@@ -1,201 +1,145 @@
1
1
  module HelpParser
2
2
  def self.string(*names)
3
3
  names.each do |name|
4
- code = <<-CODE
5
- class Options
6
- def #{name}
7
- s = @hash['#{name}']
8
- raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.is_a?(String)
9
- return s
10
- end
11
- end
12
- CODE
13
- eval code
4
+ Options.instance_eval do
5
+ define_method(name) do
6
+ s = @hash[name.to_s]
7
+ raise UsageError, MSG[NOT_STRING,name] unless s.is_a?(String)
8
+ s
9
+ end
10
+ end
14
11
  end
15
12
  end
16
13
 
17
14
  def self.string?(*names)
18
15
  names.each do |name|
19
- code = <<-CODE
20
- class Options
21
- def #{name}?
22
- s = @hash['#{name}']
23
- raise UsageError, MSG[NOT_STRING,'#{name}'] unless s.nil? ||
24
- s.is_a?(String)
25
- return s
26
- end
27
- end
28
- CODE
29
- eval code
16
+ Options.instance_eval do
17
+ define_method("#{name}?") do
18
+ s = @hash[name.to_s]
19
+ raise UsageError,MSG[NOT_STRING,name] unless s.nil? || s.is_a?(String)
20
+ s
21
+ end
22
+ end
30
23
  end
31
24
  end
32
25
 
33
26
  def self.strings(*names)
34
27
  names.each do |name|
35
- code = <<-CODE
36
- class Options
37
- def #{name}
38
- a = @hash['#{name}']
39
- raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.is_a?(Array)
40
- return a
41
- end
42
- end
43
- CODE
44
- eval code
28
+ Options.instance_eval do
29
+ define_method(name) do
30
+ a = @hash[name.to_s]
31
+ raise UsageError, MSG[NOT_STRINGS,name] unless a.is_a?(Array)
32
+ a
33
+ end
34
+ end
45
35
  end
46
36
  end
47
37
 
48
38
  def self.strings?(*names)
49
39
  names.each do |name|
50
- code = <<-CODE
51
- class Options
52
- def #{name}?
53
- a = @hash['#{name}']
54
- raise UsageError, MSG[NOT_STRINGS,'#{name}'] unless a.nil? ||
55
- a.is_a?(Array)
56
- return a
57
- end
58
- end
59
- CODE
60
- eval code
40
+ Options.instance_eval do
41
+ define_method("#{name}?") do
42
+ a = @hash[name.to_s]
43
+ raise UsageError,MSG[NOT_STRINGS,name] unless a.nil? || a.is_a?(Array)
44
+ a
45
+ end
46
+ end
61
47
  end
62
48
  end
63
49
 
64
50
  def self.float(*names)
65
51
  names.each do |name|
66
- code = <<-CODE
67
- class Options
68
- def #{name}
69
- f = @hash['#{name}']
70
- raise if f.nil?
71
- f.to_f
72
- rescue
73
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
74
- end
75
- end
76
- CODE
77
- eval code
52
+ Options.instance_eval do
53
+ define_method(name) do
54
+ @hash[name.to_s]&.to_f or raise
55
+ rescue
56
+ raise UsageError, MSG[NOT_FLOAT,name]
57
+ end
58
+ end
78
59
  end
79
60
  end
80
61
 
81
62
  def self.float?(*names)
82
63
  names.each do |name|
83
- code = <<-CODE
84
- class Options
85
- def #{name}?
86
- f = @hash['#{name}']
87
- f = f.to_f if f
88
- return f
89
- rescue
90
- raise UsageError, MSG[NOT_FLOAT,'#{name}']
91
- end
92
- end
93
- CODE
94
- eval code
64
+ Options.instance_eval do
65
+ define_method("#{name}?") do
66
+ @hash[name.to_s]&.to_f
67
+ rescue
68
+ raise UsageError, MSG[NOT_FLOAT,name]
69
+ end
70
+ end
95
71
  end
96
72
  end
97
73
 
98
74
  def self.floats(*names)
99
75
  names.each do |name|
100
- code = <<-CODE
101
- class Options
102
- def #{name}
103
- f = @hash['#{name}']
104
- raise unless f.is_a?(Array)
105
- f.map{_1.to_f}
106
- rescue
107
- raise UsageError, MSG[#{NOT_FLOATS},'#{name}']
108
- end
109
- end
110
- CODE
111
- eval code
76
+ Options.instance_eval do
77
+ define_method(name) do
78
+ @hash[name.to_s].map(&:to_f)
79
+ rescue
80
+ raise UsageError, MSG[NOT_FLOATS,name]
81
+ end
82
+ end
112
83
  end
113
84
  end
114
85
 
115
86
  def self.floats?(*names)
116
87
  names.each do |name|
117
- code = <<-CODE
118
- class Options
119
- def #{name}?
120
- f = @hash['#{name}']
121
- return nil unless f
122
- raise unless f.is_a?(Array)
123
- f.map{_1.to_f}
124
- rescue
125
- raise UsageError, MSG[NOT_FLOATS,'#{name}']
126
- end
127
- end
128
- CODE
129
- eval code
88
+ Options.instance_eval do
89
+ define_method("#{name}?") do
90
+ @hash[name.to_s]&.map(&:to_f)
91
+ rescue
92
+ raise UsageError, MSG[NOT_FLOATS,name]
93
+ end
94
+ end
130
95
  end
131
96
  end
132
97
 
133
98
  def self.int(*names)
134
99
  names.each do |name|
135
- code = <<-CODE
136
- class Options
137
- def #{name}
138
- f = @hash['#{name}']
139
- raise if f.nil?
140
- f.to_i
141
- rescue
142
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
143
- end
144
- end
145
- CODE
146
- eval code
100
+ Options.instance_eval do
101
+ define_method(name) do
102
+ @hash[name.to_s]&.to_i or raise
103
+ rescue
104
+ raise UsageError, MSG[NOT_INTEGER,name]
105
+ end
106
+ end
147
107
  end
148
108
  end
149
109
 
150
110
  def self.int?(*names)
151
111
  names.each do |name|
152
- code = <<-CODE
153
- class Options
154
- def #{name}?
155
- f = @hash['#{name}']
156
- f = f.to_i if f
157
- return f
158
- rescue
159
- raise UsageError, MSG[NOT_INTEGER,'#{name}']
160
- end
161
- end
162
- CODE
163
- eval code
112
+ Options.instance_eval do
113
+ define_method("#{name}?") do
114
+ @hash[name.to_s]&.to_i
115
+ rescue
116
+ raise UsageError, MSG[NOT_INTEGER,name]
117
+ end
118
+ end
164
119
  end
165
120
  end
166
121
 
167
122
  def self.ints(*names)
168
123
  names.each do |name|
169
- code = <<-CODE
170
- class Options
171
- def #{name}
172
- f = @hash['#{name}']
173
- raise unless f.is_a?(Array)
174
- f.map{_1.to_i}
175
- rescue
176
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
177
- end
178
- end
179
- CODE
180
- eval code
124
+ Options.instance_eval do
125
+ define_method(name) do
126
+ @hash[name.to_s].map(&:to_i)
127
+ rescue
128
+ raise UsageError, MSG[NOT_INTEGERS,name]
129
+ end
130
+ end
181
131
  end
182
132
  end
183
133
 
184
134
  def self.ints?(*names)
185
135
  names.each do |name|
186
- code = <<-CODE
187
- class Options
188
- def #{name}?
189
- f = @hash['#{name}']
190
- return nil unless f
191
- raise unless f.is_a?(Array)
192
- f.map{_1.to_i}
193
- rescue
194
- raise UsageError, MSG[NOT_INTEGERS,'#{name}']
195
- end
196
- end
197
- CODE
198
- eval code
136
+ Options.instance_eval do
137
+ define_method("#{name}?") do
138
+ @hash[name.to_s]&.map(&:to_i)
139
+ rescue
140
+ raise UsageError, MSG[NOT_INTEGERS,name]
141
+ end
142
+ end
199
143
  end
200
144
  end
201
145
  end
@@ -2,35 +2,35 @@ 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?(V) || @hash.has_key?(VSN))
5
+ if version && VSN.any?{@hash.key? _1}
6
6
  # -v or --version
7
- raise VersionException, version
7
+ raise VersionException, String(version)
8
8
  end
9
9
  if help
10
- h = [H, HLP]
11
- if h.any?{@hash.key? _1}
12
- HelpParser.parseh(help, validate: true) if h.all?{@hash.key? _1}
10
+ help = String(help)
11
+ if HLP.any?{@hash.key? _1}
12
+ HelpParser.parseh(help, validate: true) if HLP.all?{@hash.key? _1}
13
13
  raise HelpException, help
14
14
  end
15
15
  specs = HelpParser.parseh(help)
16
16
  Completion.new(@hash, specs)
17
- if exclusive=specs[EXCLUSIVE]
17
+ if (exclusive=specs[EXCLUSIVE])
18
18
  exclusive.each do |x|
19
19
  count = x.count{@hash.key? _1}
20
20
  raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
21
21
  end
22
22
  end
23
- if inclusive=specs[INCLUSIVE]
23
+ if (inclusive=specs[INCLUSIVE])
24
24
  inclusive.each do |i|
25
25
  count = i.count{@hash.key? _1}
26
- unless count==0 or count==i.length
26
+ unless count.zero? || count==i.length
27
27
  raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i]
28
28
  end
29
29
  end
30
30
  end
31
- if conditional=specs[CONDITIONAL]
31
+ if (conditional=specs[CONDITIONAL])
32
32
  conditional.each do |c|
33
- if @hash.key? c[0] and not c.all?{@hash.key? _1}
33
+ if @hash.key?(c[0]) && !c.all?{@hash.key? _1}
34
34
  raise HelpParser::UsageError, MSG[CONDITIONAL_KEYS,*c]
35
35
  end
36
36
  end
@@ -48,17 +48,16 @@ module HelpParser
48
48
  @hash[k]
49
49
  end
50
50
 
51
- def method_missing(mthd, *args, &block)
52
- super if block or args.length > 0
53
- m = mthd.to_s
54
- case m[-1]
55
- when '?'
56
- @hash.key? m[0..-2]
57
- when '!'
58
- super
59
- else
60
- @hash[m]
51
+ def respond_to_missing?(m, include_all=false)
52
+ m[-1]=='!' ? super : true
53
+ end
54
+
55
+ def method_missing(m, *args, &block)
56
+ super unless respond_to_missing?(m)
57
+ unless args.empty? && block.nil?
58
+ raise ArgumentError, 'expected neither args nor block'
61
59
  end
60
+ m[-1]=='?' ? @hash.key?(m[0..-2].to_s) : @hash[m.to_s]
62
61
  end
63
62
  end
64
63
  end
@@ -7,7 +7,7 @@ module HelpParser
7
7
  break if a.size==1 # '-' quits argv processing
8
8
  if a[1]=='-'
9
9
  break if a.size==2 # '--' also quits argv processing
10
- s = a[2..-1]
10
+ s = a[2..]
11
11
  if s.include?('=')
12
12
  k,v = s.split('=',2)
13
13
  hsh[k] = v
@@ -15,7 +15,7 @@ module HelpParser
15
15
  hsh[s] = true
16
16
  end
17
17
  else
18
- a.chars[1..-1].each do |c|
18
+ a.chars[1..].each do |c|
19
19
  hsh[c] = true
20
20
  end
21
21
  end
@@ -24,6 +24,6 @@ module HelpParser
24
24
  n += 1
25
25
  end
26
26
  end
27
- return hsh
27
+ hsh
28
28
  end
29
29
  end
@@ -4,45 +4,47 @@ module HelpParser
4
4
  help.each_line do |line|
5
5
  line.chomp!
6
6
  next if line==''
7
- if line=~SECTION_NAME
8
- name = line[0..-2].downcase
7
+ if (md=SECTION_NAME.match(line))
8
+ name = md[:name].downcase
9
9
  specs[name] = []
10
10
  else
11
11
  next if name==''
12
12
  break if line[0]=='#'
13
- next if !(line[0]==' ')
13
+ next unless line[0]==' '
14
14
  spec = (index=line.rindex("\t"))? line[0,index].strip : line.strip
15
- raise HelpError, EXTRANEOUS_SPACES if validate and spec==''
15
+ raise HelpError, EXTRANEOUS_SPACES if validate && spec.empty?
16
16
  case name
17
17
  when USAGE
18
- Validate.line_chars(spec.chars) if validate
18
+ Validate.balanced_brackets(spec.chars) if validate
19
19
  tokens = HelpParser.parseu(spec.chars)
20
20
  Validate.usage_tokens(tokens) if validate
21
21
  specs[USAGE].push tokens
22
22
  when TYPES
23
- if validate and not spec=~TYPE_DEF
23
+ if validate && !TYPE_DEF.match?(spec)
24
24
  raise HelpError, MSG[UNRECOGNIZED_TYPE,spec]
25
25
  end
26
26
  specs[TYPES].push spec.split(CSV)
27
27
  when *FLAG_CLUMPS # EXCLUSIVE,INCLUSIVE,CONDITIONAL,...
28
- if validate and not spec=~X_DEF
28
+ if validate && !X_DEF.match?(spec)
29
29
  raise HelpError, MSG[UNRECOGNIZED_X,spec]
30
30
  end
31
31
  specs[name].push spec.split(CSV)
32
32
  else
33
- raise HelpError, MSG[UNRECOGNIZED_OPTION,spec] if validate and
34
- not [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].any?{_1=~spec}
33
+ if validate &&
34
+ [SHORT, LONG, SHORT_LONG, SHORT_LONG_DEFAULT].none?{_1=~spec}
35
+ raise HelpError, MSG[UNRECOGNIZED_OPTION,spec]
36
+ end
35
37
  specs[name].push spec.split(CSV)
36
38
  end
37
39
  end
38
40
  end
39
41
  if validate
40
42
  Validate.usage_specs(specs)
41
- if t2r = HelpParser.t2r(specs)
43
+ if (t2r=HelpParser.t2r(specs))
42
44
  k2t = HelpParser.k2t(specs)
43
45
  Validate.k2t2r(specs, k2t, t2r)
44
46
  end
45
47
  end
46
- return specs
48
+ specs
47
49
  end
48
50
  end
@@ -1,7 +1,11 @@
1
1
  module HelpParser
2
+ # Chars := String.split(/\t/,2).first.strip.chars
3
+ # Token := String=~/^[^ \[\]]$/
4
+ # Note that emergent Token is String=~/^[^\s\[\]]$/
5
+ # Tokens := Array(Token|Tokens)
2
6
  def self.parseu(chars)
3
7
  tokens,token = [],''
4
- while c = chars.shift
8
+ while (c=chars.shift)
5
9
  case c
6
10
  when ' ','[',']'
7
11
  unless token==''
@@ -15,6 +19,6 @@ module HelpParser
15
19
  end
16
20
  end
17
21
  tokens.push(token) unless token==''
18
- return tokens
22
+ tokens
19
23
  end
20
24
  end
@@ -1,27 +1,20 @@
1
1
  module HelpParser
2
2
  module Validate
3
- def self.line_chars(chars)
3
+ def self.balanced_brackets(chars)
4
4
  count = 0
5
5
  chars.each do |c|
6
- if c=='['
7
- count += 1
8
- elsif c==']'
9
- count -= 1
10
- end
11
- break if count<0
6
+ c=='[' && (count+=1) or c==']' && (count-=1)
7
+ break if count.negative?
12
8
  end
13
- raise HelpError, MSG[UNBALANCED,chars.join] unless count==0
9
+ raise HelpError, MSG[UNBALANCED,chars.join] unless count.zero?
14
10
  end
15
11
 
16
12
  def self.usage_tokens(tokens)
17
13
  words = []
18
14
  tokens.flatten.each do |token|
19
- match = token.match(FLAG) ||
20
- token.match(LITERAL) ||
21
- token.match(VARIABLE) ||
22
- token.match(FLAG_GROUP)
23
- raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless match
24
- words.push match[:k] # key
15
+ raise HelpError, MSG[UNRECOGNIZED_TOKEN,token] unless
16
+ [FLAG,LITERAL,VARIABLE,FLAG_GROUP]
17
+ .detect{(_=token.match _1) && words.push(_[:k])}
25
18
  end
26
19
  words.each_with_index do |word,i|
27
20
  raise HelpError, MSG[DUP_WORD,word] unless i==words.rindex(word)
@@ -29,40 +22,37 @@ module Validate
29
22
  end
30
23
 
31
24
  def self.usage_specs(specs)
32
- option_specs = specs.select{|a,b| not RESERVED.include? a}
33
- flags = option_specs.values.flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
25
+ flags = specs.except(*RESERVED).values.flatten
26
+ .select{_1[0]=='-'}.map{F2K[_1]}
34
27
  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
28
+ next unless (a=specs[k])
29
+ seen = {}
30
+ a.each do |xs|
31
+ k = xs.sort.join(' ').to_sym
32
+ if seen[k] || xs.length!=xs.uniq.length
33
+ raise HelpError, MSG[DUP_X,k]
34
+ end
35
+ seen[k] = true
36
+ xs.each do |x|
37
+ raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
46
38
  end
47
39
  end
48
40
  end
49
41
  flags.each_with_index do |flag,i|
50
42
  raise HelpError, MSG[DUP_FLAG,flag] unless i==flags.rindex(flag)
51
- end
43
+ end
52
44
  group = []
53
45
  specs_usage = specs[USAGE]
54
- unless specs_usage.nil?
55
- specs_usage.flatten.each do |token|
56
- if match = token.match(FLAG_GROUP)
57
- key = match[:k]
58
- raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
59
- group.push(key)
60
- end
46
+ specs_usage&.flatten&.each do |token|
47
+ if (match=token.match FLAG_GROUP)
48
+ key = match[:k]
49
+ raise HelpError, MSG[UNDEFINED_SECTION,key] unless specs[key]
50
+ group.push(key)
61
51
  end
62
52
  end
63
53
  specs.each do |key,tokens|
64
- raise HelpError, MSG[MISSING_CASES,key] unless tokens.size>0
65
- next if specs_usage.nil? or RESERVED.include? key
54
+ raise HelpError, MSG[MISSING_CASES,key] if tokens.empty?
55
+ next if specs_usage.nil? || RESERVED.include?(key)
66
56
  raise HelpError, MSG[MISSING_USAGE,key] unless group.include?(key)
67
57
  end
68
58
  end
@@ -70,11 +60,11 @@ module Validate
70
60
  def self.k2t2r(specs, k2t, t2r)
71
61
  a,b = k2t.values.uniq.sort,t2r.keys.sort
72
62
  unless a==b
73
- c = (a+b).uniq.select{|x|!(a.include?(x) && b.include?(x))}
63
+ c = (a+b).uniq.reject{|x|a.include?(x) && b.include?(x)}
74
64
  raise HelpError, MSG[UNCOMPLETED_TYPES,c.join(',')]
75
65
  end
76
66
  specs.each do |section,tokens|
77
- next if section==USAGE || section==TYPES
67
+ next if RESERVED.include? section
78
68
  tokens.each do |words|
79
69
  next if words.size<2
80
70
  default = words[-1]
@@ -83,7 +73,7 @@ module Validate
83
73
  i = long_type.index('=')
84
74
  next if i.nil?
85
75
  long = long_type[2..(i-1)]
86
- type = long_type[(i+1)..-1]
76
+ type = long_type[(i+1)..]
87
77
  regex = t2r[type]
88
78
  unless regex=~default
89
79
  raise HelpError, MSG[BAD_DEFAULT,long,default,type,regex.inspect]
data/lib/help_parser.rb CHANGED
@@ -10,7 +10,7 @@ require_relative 'help_parser/options'
10
10
  require_relative 'help_parser/macros'
11
11
 
12
12
  module HelpParser
13
- VERSION = '8.0.210917'
13
+ VERSION = '8.2.230210'
14
14
  autoload :Validate, 'help_parser/validate'
15
15
 
16
16
  def self.[](
@@ -18,10 +18,10 @@ module HelpParser
18
18
  help = nil,
19
19
  argv = [File.basename($0)]+ARGV)
20
20
  Options.new(version, help, argv)
21
- rescue HelpParserException => exception
22
- exception.exit
21
+ rescue HelpParserException => e
22
+ e.exit
23
23
  end
24
24
  end
25
25
 
26
26
  # Requires:
27
- #`ruby`
27
+ # `ruby`
metadata CHANGED
@@ -1,22 +1,81 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: help_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.210917
4
+ version: 8.2.230210
5
5
  platform: ruby
6
6
  authors:
7
7
  - CarlosJHR64
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-02-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.8.1
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: parser
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.2.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: test-unit
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.5'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.5.7
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.5'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.5.7
13
73
  description: "Welcome to Help Parser! \nDo you have your help text? \nLet's parse!\n"
14
74
  email: carlosjhr64@gmail.com
15
75
  executables: []
16
76
  extensions: []
17
77
  extra_rdoc_files: []
18
78
  files:
19
- - LICENSE
20
79
  - README.md
21
80
  - lib/help_parser.rb
22
81
  - lib/help_parser/aliases.rb
@@ -34,7 +93,7 @@ homepage: https://github.com/carlosjhr64/help_parser
34
93
  licenses:
35
94
  - MIT
36
95
  metadata: {}
37
- post_install_message:
96
+ post_install_message:
38
97
  rdoc_options: []
39
98
  require_paths:
40
99
  - lib
@@ -49,9 +108,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
108
  - !ruby/object:Gem::Version
50
109
  version: '0'
51
110
  requirements:
52
- - 'ruby: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux]'
53
- rubygems_version: 3.2.22
54
- signing_key:
111
+ - 'git: 2.30'
112
+ rubygems_version: 3.4.6
113
+ signing_key:
55
114
  specification_version: 4
56
115
  summary: Welcome to Help Parser! Do you have your help text? Let's parse!
57
116
  test_files: []
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2017 carlosjhr64
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.