yaggo 1.5.10

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2729fc31583ff7592035aba2520244e2f75aad7b
4
+ data.tar.gz: 9b94b60df60cd15b396811b062884230657af54b
5
+ SHA512:
6
+ metadata.gz: 4f74b6efeaca8b74f1e4dc5149d3d01d0df3daca7f40fa5635aa815f72b35298bfbee07a6b2b990ca45a31cc3a4f018437cfc47339ee7c71190e1008ed8a1913
7
+ data.tar.gz: aee821d6b32ebec7af2e94091341f4ef7dccfdccad5d9c59445285f116075cf15d0da67ee3f1e63e4a5b68881d9db7850878f99fcff675018e2aa9e909443b38
@@ -0,0 +1,107 @@
1
+ # What is yaggo?
2
+
3
+ Yaggo is a tool to generate command line parsers for C++. Yaggo stands
4
+ for "Yet Another GenGetOpt" and is inspired by [GNU Gengetopt](https://www.gnu.org/software/gengetopt/gengetopt.html).
5
+
6
+ It reads a configuration file describing the switches and argument for
7
+ a C++ program and it generates one header file that parses the command
8
+ line using getopt_long(3). See the Example section below for more details.
9
+
10
+ # Installation
11
+
12
+ ## Quick and easy
13
+
14
+ Download the standalone script from the [release](https://github.com/gmarcais/yaggo/releases)
15
+ and copy it into a directory in your PATH (e.g. `~/bin`)
16
+
17
+ From the source tree, the same is achieved with:
18
+
19
+ ```Shell
20
+ make DEST=$HOME/bin
21
+ ```
22
+
23
+ ## As a gem
24
+
25
+ Download the gem from the [release](https://github.com/gmarcais/yaggo/releases) and install it
26
+ with `sudo gem install ./yaggo-1.5.8.gem` (adjust the version!).
27
+
28
+ Similarly, from the source tree, first generate the gem
29
+ and then install it. For example here with version 1.5.3:
30
+
31
+ ```Shell
32
+ rake gem
33
+ sudo gem install ./pkg/yaggo-1.5.3.gem
34
+ ```
35
+
36
+ # Documentation
37
+
38
+ After installation, documentation is available with `yaggo --man`.
39
+
40
+ # Simple example
41
+
42
+ Given the following configuration file 'parser.yaggo':
43
+
44
+ ```Ruby
45
+ purpose "Demonstrate yaggo capabilities"
46
+ description "This simple configuration file shows some of the capabilities of yaggo.
47
+ This is supposed to be a longer description of the program.
48
+ "
49
+
50
+ option("f", "flag") {
51
+ description "This is a flag"
52
+ off
53
+ }
54
+ option("i", "int") {
55
+ description "This take an integer"
56
+ int
57
+ default 20
58
+ }
59
+ arg("path") {
60
+ description "Path to file"
61
+ c_string
62
+ }
63
+ ```
64
+
65
+ The following C++ program ('parser.cc') does switch parsing, generate
66
+ appropriate errors and has an automatically generated help (accessible
67
+ with '-h' or '--help').
68
+
69
+ ```C
70
+ #include <iostream>
71
+ #include "parser.hpp"
72
+
73
+ int main(int argc, char* argv[]) {
74
+ parser args(argc, argv); // Does all the parsing
75
+
76
+ std::cout << "--flag " << (args.flag_flag ? "not passed" : "passed") << "\n"
77
+ << "--int: " << args.int_arg << "\n"
78
+ << "path: " << args.path_arg << "\n";
79
+
80
+ return 0;
81
+ }
82
+ ```
83
+
84
+ All of this is compiled with:
85
+
86
+ ```Shell
87
+ yaggo parser.yaggo
88
+ g++ -o parser parser.cc
89
+ ```
90
+
91
+ Then, './parser --help' returns:
92
+
93
+ ```
94
+ Usage: parser [options] path:string
95
+
96
+ Demonstrate yaggo capabilities
97
+
98
+ This simple configuration file shows some of the capabilities of yaggo.
99
+ This is supposed to be a longer description of the program.
100
+
101
+ Options (default value in (), *required):
102
+ -f, --flag This is a flag (false)
103
+ -i, --int=int This take an integer (20)
104
+ -U, --usage Usage
105
+ -h, --help This message
106
+ -V, --version Version
107
+ ```
@@ -0,0 +1,45 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ def create_binary src, dest
4
+ to_load = []
5
+ loaded = {}
6
+ open(dest, "w", 0755) do |wfd|
7
+ wfd.puts(<<'EOS')
8
+ #! /usr/bin/env ruby
9
+
10
+ if !$load_self
11
+ $load_self = true
12
+ load(__FILE__)
13
+ main
14
+ exit(0)
15
+ end
16
+
17
+ EOS
18
+
19
+ open(src, "r") do |rfd|
20
+ rfd.each_line { |l|
21
+ if l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
22
+ to_load << $1
23
+ else
24
+ wfd.print(l)
25
+ end
26
+ }
27
+ end
28
+
29
+ to_load.each { |f|
30
+ next if loaded[f]
31
+ wfd.puts("", "# Loading yaggo/#{f}", "")
32
+ open(File.join("lib", "yaggo", f + ".rb"), "r") { |nfd|
33
+ nfd.each_line { |l|
34
+ wfd.print(l) unless l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
35
+ }
36
+ }
37
+ loaded[f] = true
38
+ }
39
+ end
40
+ end
41
+
42
+ if __FILE__ == $0
43
+ dest = ARGV.shift || "yaggo"
44
+ create_binary("lib/yaggo/main.rb", dest)
45
+ end
@@ -0,0 +1,22 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ # Yaggo. Yet Another GenGetOpt. Generate command line switch parsers
4
+ # using getopt_long.
5
+ # Copyright (C) 2011 Guillaume Marcais.
6
+ #
7
+ # This program is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ # General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see
19
+ # <http://www.gnu.org/licenses/>.
20
+
21
+ require 'yaggo/main.rb'
22
+ main
@@ -0,0 +1,573 @@
1
+ # This file is part of Yaggo.
2
+
3
+ # Yaggo is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+
8
+ # Yaggo is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Yaggo. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ ##############################
18
+ # Process an input files. Define the Domain Specific Language.
19
+ ##############################
20
+ $options = []
21
+ $opt_hash = {}
22
+ $args = []
23
+
24
+ class NoTarget
25
+ def description str; $description = str; end
26
+ def description= str; $description = str; end
27
+ def method_missing(m, *args)
28
+ raise "'#{m}' used outside of option or arg description"
29
+ end
30
+ end
31
+ $target = NoTarget.new
32
+
33
+ def output str; $output = str; end
34
+ def name str; $klass = str; end
35
+ def purpose str; $purpose = str; end
36
+ def package str; $package = str; end
37
+ def usage str; $usage = str; end
38
+ def text str; $after_text = str; end
39
+ def description str; $target.description = str; end
40
+ def version str; $version = str; end
41
+ def posix *args; $posix = true; end
42
+ def license str; $license = str; end
43
+ $global_variables = [:output, :name, :purpose, :package,
44
+ :description, :version, :license, :posix]
45
+ output = name = purpose = package = description = version = license = nil
46
+
47
+ # def set_type t
48
+ # raise "More than 1 type specified: '#{$target.type}' and '#{t}'" unless $target.type.nil?
49
+ # $target.type = t
50
+ # end
51
+
52
+ def int32; $target.type = :int32; end
53
+ def int64; $target.type = :int64; end
54
+ def uint32; $target.type = :uint32; end
55
+ def uint64; $target.type = :uint64; end
56
+ def int; $target.type = :int; end
57
+ def long; $target.type = :long; end
58
+ def double; $target.type = :double; end
59
+ def string; $target.type = :string; end
60
+ def c_string; $target.type = :c_string; end
61
+ def flag; $target.type = :flag; end
62
+ def enum(*argv); $target.type = :enum; $target.enum = argv; end
63
+
64
+ def suffix; $target.suffix = true; end
65
+ def required; $target.required = true; end
66
+ def hidden; $target.hidden = true; end
67
+ def secret; $target.secret = true; end
68
+ def on; $target.on; end
69
+ def off; $target.off; end
70
+ def no; $target.no; end
71
+ def default str; $target.default = str; end
72
+ def typestr str; $target.typestr = str; end
73
+ def multiple; $target.multiple = true; end
74
+ def at_least n; $target.at_least = n; end
75
+ def conflict *a; $target.conflict= a; end
76
+ def imply *a; $target.imply= a; end
77
+ def access *types; $target.access= types; end
78
+ # Define the following local variables and check their value after
79
+ # yielding the block to catch syntax such as default="value".
80
+ $option_variables = [:default, :typestr, :at_least]
81
+ default = typestr = at_least = nil
82
+ $main_binding = binding
83
+
84
+ def default_val(val, type, *argv)
85
+ case type
86
+ when :string, :c_string
87
+ "\"#{val || $type_default[type]}\""
88
+ when :uint32, :uint64, :int32, :int64, :int, :long, :double
89
+ val ? "(#{$type_to_C_type[type]})#{val}" : $type_default[type]
90
+ else
91
+ val.to_s || $type_default[type]
92
+ end
93
+ end
94
+
95
+ class BaseOptArg
96
+ def at_least=(n)
97
+ multiple = true
98
+ nb = case n
99
+ when Integer
100
+ n
101
+ when String
102
+ n =~ /^\d+$/ ? n.to_i : nil
103
+ else
104
+ nil
105
+ end
106
+ raise "Invalid minimum number for at_least (#{n})" if nb.nil?
107
+ self.multiple = true
108
+ @at_least = nb
109
+ end
110
+
111
+ def type=(t)
112
+ raise "More than 1 type specified: '#{type}' and '#{t}'" unless @type.nil? || @type == t
113
+ @type = t
114
+ end
115
+
116
+ def suffix=(t)
117
+ case type
118
+ when nil
119
+ raise "A numerical type must be specify before suffix"
120
+ when :flag, :string, :c_string
121
+ raise "Suffix is meaningless with the type #{type}"
122
+ end
123
+ @suffix = t
124
+ end
125
+
126
+ def access=(types)
127
+ types.all? { |t| ["read", "write", "exec"].include?(t) } or
128
+ raise "Invalid access type(s): #{types.join(", ")}"
129
+ @access_types = types
130
+ end
131
+
132
+ def check
133
+ if !@access_types.empty? && @type != :c_string
134
+ raise "Access checking is valid only with a path (a c_string)"
135
+ end
136
+ end
137
+ end
138
+
139
+ class Option < BaseOptArg
140
+ attr_accessor :description, :required, :typestr
141
+ attr_accessor :hidden, :secret, :conflict, :multiple, :access_types, :noflag
142
+ attr_reader :long, :short, :var, :type, :at_least, :default, :suffix, :enum
143
+ attr_reader :imply
144
+
145
+ def initialize(long, short)
146
+ @long, @short = long, short
147
+ @var = (@long || @short).gsub(/[^a-zA-Z0-9_]/, "_")
148
+ @type = nil
149
+ @no = false # Also generate the --noswitch for a flag
150
+ @default = nil
151
+ @suffix = false
152
+ @at_least = nil
153
+ @conflict = []
154
+ @enum = []
155
+ @imply = []
156
+ @access_types = []
157
+ end
158
+
159
+ def on
160
+ self.type = :flag
161
+ self.default = "true"
162
+ end
163
+
164
+ def off
165
+ self.type = :flag
166
+ self.default = "false"
167
+ end
168
+
169
+ def no
170
+ self.type = :flag
171
+ self.noflag = true
172
+ end
173
+
174
+ def tf_to_on_off v
175
+ case v
176
+ when "true"
177
+ "on"
178
+ when "false"
179
+ "off"
180
+ else
181
+ v
182
+ end
183
+ end
184
+
185
+ def convert_int(x, signed = true)
186
+ x =~ /^([+-]?\d+)([kMGTPE]?)$/ or return nil
187
+ v = $1.to_i
188
+ return nil if v < 0 && !signed
189
+ case $2
190
+ when "k"
191
+ v *= 1000
192
+ when "M"
193
+ v *= 1000_000
194
+ when "G"
195
+ v *= 1000_000_000
196
+ when "T"
197
+ v *= 1000_000_000_000
198
+ when "P"
199
+ v *= 1000_000_000_000_000
200
+ when "E"
201
+ v *= 1000_000_000_000_000_000
202
+ end
203
+ return v
204
+ end
205
+
206
+ def convert_double(x)
207
+ x =~ /^([+-]?[\d]+(?:\.\d*))?(?:([afpnumkMGTPE])|([eE][+-]?\d+))?$/ or return nil
208
+ v = "#{$1}#{$3}".to_f
209
+ case $2
210
+ when "a"
211
+ v *= 1e-18
212
+ when "f"
213
+ v *= 1e-15
214
+ when "p"
215
+ v *= 1e-12
216
+ when "n"
217
+ v *= 1e-9
218
+ when "u"
219
+ v *= 1e-6
220
+ when "m"
221
+ v *= 1e-3
222
+ when "k"
223
+ v *= 1e3
224
+ when "M"
225
+ v *= 1e6
226
+ when "G"
227
+ v *= 1e9
228
+ when "T"
229
+ v *= 1e12
230
+ when "P"
231
+ v *= 1e15
232
+ when "E"
233
+ v *= 1e18
234
+ end
235
+ return v
236
+ end
237
+
238
+ def default=(v)
239
+ type.nil? and raise "A type must be specified before defining a default value"
240
+ unless default.nil?
241
+ if type == :flag
242
+ v1, v2 = tf_to_on_off(default), tf_to_on_off(v)
243
+ else
244
+ v1, v2 = default, v
245
+ end
246
+ raise "More than 1 default value specified: '#{v1}' and '#{v2}'"
247
+ end
248
+ pref = "Option #{long || ""}|#{short || ""}:"
249
+ bv = v # Backup v for display
250
+ case @type
251
+ when nil
252
+ raise "#{pref} No type specified"
253
+ when :uint32, :uint64
254
+ (Integer === v && v >= 0) || (String === v && v = convert_int(v, false)) or
255
+ raise "#{pref} Invalid unsigned integer '#{bv}'"
256
+ when :int32, :int64, :int, :long
257
+ (Integer === v) || (String === v && v = convert_int(v, true)) or
258
+ raise "#{pref} Invalid integer #{bv}"
259
+ when :double
260
+ (Float === v) || (String === v && v = convert_double(v)) or
261
+ raise "#{pref} Invalid double #{bv}"
262
+ when :enum
263
+ v = v.to_i if v =~ /^\d+$/
264
+ case v
265
+ when Integer
266
+ (v >= 0 && v < @enum.size) or
267
+ raise "Default is out of range [0, #{@enum.size-1}]"
268
+ when String
269
+ nv = @enum.index(v) or
270
+ raise "Unknown constant '#{v}'. Should be one of { #{@enum.join(", ")} }"
271
+ v = nv
272
+ else
273
+ raise "Expected an Integer or a String"
274
+ end
275
+ end
276
+ @default = v
277
+ end
278
+
279
+ def enum=(*argv)
280
+ @type == :enum or raise "#{pref} Enum valid only for enum types."
281
+ @enum = argv.flatten
282
+ end
283
+
284
+ def conflict= a; @conflict += a.map { |x| x.gsub(/^-+/, "") }; end
285
+ def imply= a; @imply += a.map { |x| x.gsub(/^-+/, "") }; end
286
+
287
+ def check
288
+ pref = "Option #{long || ""}|#{short || ""}:"
289
+ raise "#{pref} No type specified" if type.nil?
290
+
291
+ if multiple
292
+ raise "#{pref} Multiple is meaningless with a flag" if type == :flag
293
+ raise "#{pref} An option marked multiple cannot have a default value" unless default.nil?
294
+ raise "#{pref} Multiple is incompatible with enum type" if type == :enum
295
+ end
296
+
297
+ if @type == :flag && noflag && !short.nil?
298
+ raise "#{pref} flag with 'no' option cannot have a short switch"
299
+ end
300
+
301
+ super
302
+
303
+ # case @type
304
+ # when nil
305
+ # raise "#{pref} No type specified"
306
+ # when :uint32, :uint64
307
+ # @default.nil? || @default =~ /^\d+$/ or
308
+ # raise "#{pref} Invalid unsigned integer #{@default}"
309
+ # when :int32, :int64, :int, :long
310
+ # @default.nil? || @default =~ /^[+-]?\d+$/ or
311
+ # raise "#{pref} Invalid integer #{@default}"
312
+ # when :double
313
+ # @default.nil? || @default =~ /^[+-]?[\d.]+([eE][+-]?\d+)?$/ or
314
+ # raise "#{pref} Invalid double #{@default}"
315
+ # when :flag
316
+ # raise "#{pref} A flag cannot be declared multiple" if @multiple
317
+ # raise "#{pref} Suffix is meaningless for a flag" if @suffix
318
+ # end
319
+ end
320
+
321
+ def static_decl
322
+ a = []
323
+ if @type == :enum
324
+ a << "struct #{@var} {"
325
+ a << " enum { #{@enum.map { |x| x.gsub(/[^a-zA-Z0-9_]/, "_") }.join(", ")} };"
326
+ a << " static const char* const strs[#{@enum.size + 1}];"
327
+ a << "};"
328
+ end
329
+ a
330
+ end
331
+
332
+ def var_decl
333
+ if @type == :flag
334
+ ["#{"bool".ljust($typejust)} #{@var}_flag;"]
335
+ else
336
+ a = []
337
+ if @multiple
338
+ c_type = "::std::vector<#{$type_to_C_type[@type]}>"
339
+ a << (c_type.ljust($typejust) + " #{@var}_arg;")
340
+ a << ("typedef #{c_type}::iterator #{@var}_arg_it;")
341
+ a << ("typedef #{c_type}::const_iterator #{@var}_arg_const_it;")
342
+ else
343
+ a << "#{$type_to_C_type[@type].ljust($typejust)} #{@var}_arg;"
344
+ end
345
+ a << "#{"bool".ljust($typejust)} #{@var}_given;"
346
+ end
347
+ end
348
+
349
+ def init
350
+ s = "#{@var}_#{@type == :flag ? "flag" : "arg"}("
351
+ s += default_val(@default, @type, @enum) unless @multiple
352
+ s += ")"
353
+ unless @type == :flag
354
+ s += ", #{@var}_given(false)"
355
+ end
356
+ s
357
+ end
358
+
359
+ def long_enum
360
+ return nil if !@short.nil?
361
+ res = [@var.upcase + "_OPT"]
362
+ if @type == :flag && noflag
363
+ res << "NO#{@var.upcase}_OPT"
364
+ end
365
+ res
366
+ end
367
+
368
+ def struct
369
+ res = ["{\"#{long}\", #{@type == :flag ? 0 : 1}, 0, #{@short ? "'" + @short + "'" : long_enum[0]}}"]
370
+ if @type == :flag && noflag
371
+ res << "{\"no#{long}\", 0, 0, #{long_enum()[1]}}"
372
+ end
373
+ res
374
+ end
375
+ def short_str
376
+ return nil if @short.nil?
377
+ @short + (@type == :flag ? "" : ":")
378
+ end
379
+ def switches
380
+ s = @short.nil? ? " " : "-#{@short}"
381
+ s += ", " unless @short.nil? || @long.nil?
382
+ unless @long.nil?
383
+ if @type == :flag && @noflag
384
+ s += "--[no]#{@long}"
385
+ else
386
+ s += "--#{@long}"
387
+ end
388
+ s += "=#{@typestr || dflt_typestr(@type, @enum)}" unless @type == :flag
389
+ end
390
+ s
391
+ end
392
+
393
+ def default_str
394
+ return @default unless @type == :enum
395
+ @enum[@default || 0]
396
+ end
397
+
398
+ def help
399
+ s = @required ? "*" : " "
400
+ @description ||= "Switch #{switches}"
401
+ s += @description.gsub(/"/, '\"') || ""
402
+ default = default_str
403
+ s += " (#{default})" unless default.nil?
404
+ s
405
+ end
406
+
407
+ def dump
408
+ case @type
409
+ when :flag
410
+ ["\"#{@var}_flag:\"", "#{@var}_flag"]
411
+ when :enum
412
+ ["\"#{@var}_given:\"", "#{@var}_given",
413
+ "\" #{@var}_arg:\"", "#{@var}_arg", '"|"', "#{@var}::strs[#{@var}_arg]"]
414
+ else
415
+ ["\"#{@var}_given:\"", "#{@var}_given",
416
+ "\" #{@var}_arg:\"", @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
417
+ end
418
+ end
419
+
420
+ def parse_arg(no = false)
421
+ a = @imply.map { |ios| "#{$opt_hash[ios].var}_flag = true;" }
422
+ a << "#{@var}_given = true;" unless @type == :flag
423
+ case @type
424
+ when :flag
425
+ if @noflag
426
+ a << ["#{@var}_flag = #{no ? "false" : "true"};"]
427
+ else
428
+ a << ["#{@var}_flag = #{@default == "true" ? "false" : "true"};"]
429
+ end
430
+ when :string
431
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg.assign(optarg);")
432
+ when :c_string
433
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg = optarg;")
434
+ when :uint32, :uint64, :int32, :int64, :int, :long, :double
435
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, @suffix)});" : "#{@var}_arg = #{str_conv("optarg", @type, @suffix)};")
436
+ a << "CHECK_ERR(#{@type}_t, optarg, \"#{switches}\")"
437
+ when :enum
438
+ a << "#{@var}_arg = #{str_conv("optarg", @type, "#{@var}::strs")};"
439
+ a << "CHECK_ERR(#{@type}, optarg, \"#{switches}\")"
440
+ end
441
+ a
442
+ end
443
+ end
444
+
445
+ class Arg < BaseOptArg
446
+ attr_accessor :description, :type, :typestr, :multiple, :access_types
447
+ attr_reader :name, :at_least, :suffix, :var
448
+ def initialize(str)
449
+ @name = str
450
+ @var = @name.gsub(/[^a-zA-Z0-9_]/, "_")
451
+ @type = nil
452
+ @at_least = 0
453
+ @suffix = false
454
+ @access_types = []
455
+ end
456
+
457
+ def type=(t)
458
+ super
459
+ raise "An arg cannot be of type '#{t}'" if t == :flag
460
+ end
461
+
462
+ def on; raise "An arg cannot be a flag with default value on"; end
463
+ def off; raise "An arg cannot be a flag with default value off"; end
464
+
465
+ def default=(*args)
466
+ raise "An arg cannot have a default value (#{args[0]})"
467
+ end
468
+
469
+ def hidden=(*args)
470
+ raise "An arg cannot be marked hidden"
471
+ end
472
+
473
+ def secret=(*args)
474
+ raise "An arg cannot be marked secret"
475
+ end
476
+
477
+ def required=(*args)
478
+ raise "An arg cannot be marked required"
479
+ end
480
+
481
+ def check
482
+ super
483
+
484
+ pref = "Arg #{name}:"
485
+ raise "#{pref} No type specified" if type.nil?
486
+ end
487
+
488
+ def var_decl
489
+ if @multiple
490
+ c_type = "::std::vector<#{$type_to_C_type[@type]}>"
491
+ [c_type.ljust($typejust) + " #{@var}_arg;",
492
+ "typedef #{c_type}::iterator #{@var}_arg_it;",
493
+ "typedef #{c_type}::const_iterator #{@var}_arg_const_it;"]
494
+ else
495
+ ["#{$type_to_C_type[@type]}".ljust($typejust) + " #{@var}_arg;"]
496
+ end
497
+ end
498
+
499
+ def init
500
+ s = "#{@var}_arg("
501
+ s += default_val(@default, @type) unless @multiple
502
+ s += ")"
503
+ s
504
+ end
505
+
506
+ def dump
507
+ ["\"#{@var}_arg:\"",
508
+ @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
509
+ end
510
+
511
+ def parse_arg
512
+ a = []
513
+ off = ""
514
+ if @multiple
515
+ a << "for( ; optind < argc; ++optind) {"
516
+ a << " #{@var}_arg.push_back(#{str_conv("argv[optind]", @type, @suffix)});"
517
+ off = " "
518
+ else
519
+ a << "#{@var}_arg = #{str_conv("argv[optind]", @type, @suffix)};"
520
+ end
521
+ unless @type == :string || @type == :c_string
522
+ a << (off + "CHECK_ERR(#{@type}_t, argv[optind], \"#{@var}\")")
523
+ end
524
+ a << (@multiple ? "}" : "++optind;")
525
+ a
526
+ end
527
+ end
528
+
529
+ def option(name1, name2 = nil, &b)
530
+ long = short = nil
531
+ if name1 =~ /^--/ || name1.length >= 2
532
+ long, short = name1, name2
533
+ elsif !name2.nil? && (name2 =~ /^--/ || name2.length >= 2)
534
+ long, short = name2, name1
535
+ else
536
+ long, short = nil, name1
537
+ end
538
+
539
+ long.gsub!(/^--/, "") unless long.nil?
540
+ short.gsub!(/^-/, "") unless short.nil?
541
+ o = Option.new(long, short)
542
+ $options.each { |lo|
543
+ if (!long.nil? && lo.long == long) || (!short.nil? && lo.short == short)
544
+ raise "#{b.source_location.join(":")}: Option #{long}|#{short} conflicts with existing option #{lo.long}|#{lo.short}"
545
+ end
546
+ }
547
+ $options << o
548
+ $target = o
549
+ name = "Option #{long || ""}|#{short || ""}"
550
+ run_block(name, b)
551
+ $target = NoTarget.new
552
+ begin
553
+ o.check
554
+ rescue => e
555
+ raise "#{b.source_location.join(":")}: #{e.message}"
556
+ end
557
+ end
558
+
559
+ def arg(name, &b)
560
+ a = Arg.new(name)
561
+ $args.any? { |la| la.name == name } and
562
+ raise "#{b.source_location.join(":")}: Arg '#{name}' already exists"
563
+ $args << a
564
+ $target = a
565
+ name = "Arg #{name}"
566
+ run_block(name, b)
567
+ $target = NoTarget.new
568
+ begin
569
+ a.check
570
+ rescue => e
571
+ raise "#{b.source_location.join(":")}: #{e.message}"
572
+ end
573
+ end