yaggo 1.5.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +107 -0
- data/bin/create_yaggo_one_file +45 -0
- data/bin/yaggo +22 -0
- data/lib/yaggo/dsl.rb +573 -0
- data/lib/yaggo/general.rb +127 -0
- data/lib/yaggo/library.rb +209 -0
- data/lib/yaggo/main.rb +152 -0
- data/lib/yaggo/man_page.rb +449 -0
- data/lib/yaggo/parser.rb +404 -0
- data/lib/yaggo/stub.rb +42 -0
- data/lib/yaggo/version.rb +1 -0
- data/lib/yaggo/zsh_completion.rb +94 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/bin/yaggo
ADDED
@@ -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
|
data/lib/yaggo/dsl.rb
ADDED
@@ -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
|