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.
- 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
|