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
@@ -0,0 +1,127 @@
|
|
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
|
+
$typejust = 30
|
18
|
+
$switchesjust = 40
|
19
|
+
|
20
|
+
$type_to_C_type = {
|
21
|
+
:uint32 => "uint32_t",
|
22
|
+
:uint64 => "uint64_t",
|
23
|
+
:int32 => "int32_t",
|
24
|
+
:int64 => "int64_t",
|
25
|
+
:int => "int",
|
26
|
+
:long => "long",
|
27
|
+
:double => "double",
|
28
|
+
:string => "string",
|
29
|
+
:c_string => "const char *",
|
30
|
+
:enum => "int",
|
31
|
+
}
|
32
|
+
$type_default = {
|
33
|
+
:uint32 => "0",
|
34
|
+
:uint64 => "0",
|
35
|
+
:int32 => "0",
|
36
|
+
:int64 => "0",
|
37
|
+
:int => "0",
|
38
|
+
:long => "0",
|
39
|
+
:double => "0.0",
|
40
|
+
:string => "",
|
41
|
+
:c_string => "",
|
42
|
+
:enum => "0",
|
43
|
+
}
|
44
|
+
|
45
|
+
def dflt_typestr(type, *argv)
|
46
|
+
case type
|
47
|
+
when :c_string
|
48
|
+
"string"
|
49
|
+
when :enum
|
50
|
+
argv[0].join("|")
|
51
|
+
else
|
52
|
+
type.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def suffix_arg(suffix)
|
57
|
+
case suffix
|
58
|
+
when true
|
59
|
+
"true"
|
60
|
+
when false
|
61
|
+
"false"
|
62
|
+
when String
|
63
|
+
suffix
|
64
|
+
else
|
65
|
+
raise "Invalid suffix specifier"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def str_conv(arg, type, *argv)
|
70
|
+
case type
|
71
|
+
when :string
|
72
|
+
"string(#{arg})"
|
73
|
+
when :c_string
|
74
|
+
arg
|
75
|
+
when :uint32, :uint64
|
76
|
+
"conv_uint<#{$type_to_C_type[type]}>((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
|
77
|
+
when :int32, :int64, :long, :int
|
78
|
+
"conv_int<#{$type_to_C_type[type]}>((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
|
79
|
+
when :double
|
80
|
+
"conv_double((const char*)#{arg}, err, #{suffix_arg(argv[0])})"
|
81
|
+
when :enum
|
82
|
+
# Convert a string to its equivalent enum value
|
83
|
+
"conv_enum((const char*)#{arg}, err, #{argv[0]})"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_error_header bt
|
88
|
+
bt.each { |l| l =~ /^\(eval\):\d+:/ and return $& }
|
89
|
+
return ""
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_block(name, b)
|
93
|
+
eval("#{$option_variables.join(" = ")} = nil", $main_binding)
|
94
|
+
b.call
|
95
|
+
$option_variables.each { |n| eval("#{n} #{n} unless #{n}.nil?", $main_binding) }
|
96
|
+
rescue NoMethodError => e
|
97
|
+
header = find_error_header(e.backtrace)
|
98
|
+
raise "#{header} In #{name}: invalid keyword '#{e.name}' in statement '#{e.name} #{e.args.map { |s| "\"#{s}\"" }.join(" ")}'"
|
99
|
+
rescue NameError => e
|
100
|
+
header = find_error_header(e.backtrace)
|
101
|
+
raise "#{header} In #{name}: invalid keyword '#{e.name}'"
|
102
|
+
rescue RuntimeError, ArgumentError => e
|
103
|
+
header = find_error_header(e.backtrace)
|
104
|
+
raise "#{header} In #{name}: #{e.message}"
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def check_conflict_exclude
|
109
|
+
$options.each { |o|
|
110
|
+
$opt_hash[o.long] = o unless o.long.nil?
|
111
|
+
$opt_hash[o.short] = o unless o.short.nil?
|
112
|
+
}
|
113
|
+
$options.each { |o|
|
114
|
+
o.conflict.each { |co|
|
115
|
+
$opt_hash[co] or
|
116
|
+
raise "Unknown conflict option '#{co}' for switch #{o.long}|#{o.short}"
|
117
|
+
}
|
118
|
+
}
|
119
|
+
$options.each { |o|
|
120
|
+
o.imply.each { |ios|
|
121
|
+
io = $opt_hash[ios] or
|
122
|
+
raise "Unknown implied option '#{io}' for switch #{o.long}|#{o.short}"
|
123
|
+
io.type == :flag or
|
124
|
+
raise "Implied option '#{io}' for switch #{o.long}|#{o.short} is not a flag"
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
@@ -0,0 +1,209 @@
|
|
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
|
+
def output_conversion_code file
|
18
|
+
file.puts(<<EOS)
|
19
|
+
static bool adjust_double_si_suffix(double &res, const char *suffix) {
|
20
|
+
if(*suffix == '\\0')
|
21
|
+
return true;
|
22
|
+
if(*(suffix + 1) != '\\0')
|
23
|
+
return false;
|
24
|
+
|
25
|
+
switch(*suffix) {
|
26
|
+
case 'a': res *= 1e-18; break;
|
27
|
+
case 'f': res *= 1e-15; break;
|
28
|
+
case 'p': res *= 1e-12; break;
|
29
|
+
case 'n': res *= 1e-9; break;
|
30
|
+
case 'u': res *= 1e-6; break;
|
31
|
+
case 'm': res *= 1e-3; break;
|
32
|
+
case 'k': res *= 1e3; break;
|
33
|
+
case 'M': res *= 1e6; break;
|
34
|
+
case 'G': res *= 1e9; break;
|
35
|
+
case 'T': res *= 1e12; break;
|
36
|
+
case 'P': res *= 1e15; break;
|
37
|
+
case 'E': res *= 1e18; break;
|
38
|
+
default: return false;
|
39
|
+
}
|
40
|
+
return true;
|
41
|
+
}
|
42
|
+
|
43
|
+
static double conv_double(const char *str, ::std::string &err, bool si_suffix) {
|
44
|
+
char *endptr = 0;
|
45
|
+
errno = 0;
|
46
|
+
double res = strtod(str, &endptr);
|
47
|
+
if(endptr == str) {
|
48
|
+
err.assign("Invalid floating point string");
|
49
|
+
return (double)0.0;
|
50
|
+
}
|
51
|
+
if(errno) {
|
52
|
+
err.assign(strerror(errno));
|
53
|
+
return (double)0.0;
|
54
|
+
}
|
55
|
+
bool invalid =
|
56
|
+
si_suffix ? !adjust_double_si_suffix(res, endptr) : *endptr != '\\0';
|
57
|
+
if(invalid) {
|
58
|
+
err.assign("Invalid character");
|
59
|
+
return (double)0.0;
|
60
|
+
}
|
61
|
+
return res;
|
62
|
+
}
|
63
|
+
|
64
|
+
static int conv_enum(const char* str, ::std::string& err, const char* const strs[]) {
|
65
|
+
int res = 0;
|
66
|
+
for(const char* const* cstr = strs; *cstr; ++cstr, ++res)
|
67
|
+
if(!strcmp(*cstr, str))
|
68
|
+
return res;
|
69
|
+
err += "Invalid constant '";
|
70
|
+
err += str;
|
71
|
+
err += "'. Expected one of { ";
|
72
|
+
for(const char* const* cstr = strs; *cstr; ++cstr) {
|
73
|
+
if(cstr != strs)
|
74
|
+
err += ", ";
|
75
|
+
err += *cstr;
|
76
|
+
}
|
77
|
+
err += " }";
|
78
|
+
return -1;
|
79
|
+
}
|
80
|
+
|
81
|
+
template<typename T>
|
82
|
+
static bool adjust_int_si_suffix(T &res, const char *suffix) {
|
83
|
+
if(*suffix == '\\0')
|
84
|
+
return true;
|
85
|
+
if(*(suffix + 1) != '\\0')
|
86
|
+
return false;
|
87
|
+
|
88
|
+
switch(*suffix) {
|
89
|
+
case 'k': res *= (T)1000; break;
|
90
|
+
case 'M': res *= (T)1000000; break;
|
91
|
+
case 'G': res *= (T)1000000000; break;
|
92
|
+
case 'T': res *= (T)1000000000000; break;
|
93
|
+
case 'P': res *= (T)1000000000000000; break;
|
94
|
+
case 'E': res *= (T)1000000000000000000; break;
|
95
|
+
default: return false;
|
96
|
+
}
|
97
|
+
return true;
|
98
|
+
}
|
99
|
+
|
100
|
+
template<typename T>
|
101
|
+
static T conv_int(const char *str, ::std::string &err, bool si_suffix) {
|
102
|
+
char *endptr = 0;
|
103
|
+
errno = 0;
|
104
|
+
long long int res = strtoll(str, &endptr, 0);
|
105
|
+
if(endptr == str) {
|
106
|
+
err.assign("Invalid signed int string");
|
107
|
+
return (T)0;
|
108
|
+
}
|
109
|
+
if(errno) {
|
110
|
+
err.assign(strerror(errno));
|
111
|
+
return (T)0;
|
112
|
+
}
|
113
|
+
bool invalid =
|
114
|
+
si_suffix ? !adjust_int_si_suffix(res, endptr) : *endptr != '\\0';
|
115
|
+
if(invalid) {
|
116
|
+
err.assign("Invalid character");
|
117
|
+
return (T)0;
|
118
|
+
}
|
119
|
+
if(res > ::std::numeric_limits<T>::max() ||
|
120
|
+
res < ::std::numeric_limits<T>::min()) {
|
121
|
+
err.assign("Value out of range");
|
122
|
+
return (T)0;
|
123
|
+
}
|
124
|
+
return (T)res;
|
125
|
+
}
|
126
|
+
|
127
|
+
template<typename T>
|
128
|
+
static T conv_uint(const char *str, ::std::string &err, bool si_suffix) {
|
129
|
+
char *endptr = 0;
|
130
|
+
errno = 0;
|
131
|
+
while(isspace(*str)) { ++str; }
|
132
|
+
if(*str == '-') {
|
133
|
+
err.assign("Negative value");
|
134
|
+
return (T)0;
|
135
|
+
}
|
136
|
+
unsigned long long int res = strtoull(str, &endptr, 0);
|
137
|
+
if(endptr == str) {
|
138
|
+
err.assign("Invalid unsigned int string");
|
139
|
+
return (T)0;
|
140
|
+
}
|
141
|
+
if(errno) {
|
142
|
+
err.assign(strerror(errno));
|
143
|
+
return (T)0;
|
144
|
+
}
|
145
|
+
bool invalid =
|
146
|
+
si_suffix ? !adjust_int_si_suffix(res, endptr) : *endptr != '\\0';
|
147
|
+
if(invalid) {
|
148
|
+
err.assign("Invalid character");
|
149
|
+
return (T)0;
|
150
|
+
}
|
151
|
+
if(res > ::std::numeric_limits<T>::max()) {
|
152
|
+
err.assign("Value out of range");
|
153
|
+
return (T)0;
|
154
|
+
}
|
155
|
+
return (T)res;
|
156
|
+
}
|
157
|
+
|
158
|
+
template<typename T>
|
159
|
+
static ::std::string vec_str(const std::vector<T> &vec) {
|
160
|
+
::std::ostringstream os;
|
161
|
+
for(typename ::std::vector<T>::const_iterator it = vec.begin();
|
162
|
+
it != vec.end(); ++it) {
|
163
|
+
if(it != vec.begin())
|
164
|
+
os << ",";
|
165
|
+
os << *it;
|
166
|
+
}
|
167
|
+
return os.str();
|
168
|
+
}
|
169
|
+
|
170
|
+
class string : public ::std::string {
|
171
|
+
public:
|
172
|
+
string() : ::std::string() {}
|
173
|
+
explicit string(const ::std::string &s) : std::string(s) {}
|
174
|
+
explicit string(const char *s) : ::std::string(s) {}
|
175
|
+
int as_enum(const char* const strs[]) {
|
176
|
+
::std::string err;
|
177
|
+
int res = #{str_conv("this->c_str()", :enum, "strs")};
|
178
|
+
if(!err.empty())
|
179
|
+
throw ::std::runtime_error(err);
|
180
|
+
return res;
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
EOS
|
185
|
+
[:uint32, :uint64, :int32, :int64, :int, :long, :double].each do |type|
|
186
|
+
file.puts(<<EOS)
|
187
|
+
#{$type_to_C_type[type]} as_#{type}_suffix() const { return as_#{type}(true); }
|
188
|
+
#{$type_to_C_type[type]} as_#{type}(bool si_suffix = false) const {
|
189
|
+
::std::string err;
|
190
|
+
#{$type_to_C_type[type]} res = #{str_conv("this->c_str()", type, "si_suffix")};
|
191
|
+
if(!err.empty()) {
|
192
|
+
::std::string msg("Invalid conversion of '");
|
193
|
+
msg += *this;
|
194
|
+
msg += "' to #{type}_t: ";
|
195
|
+
msg += err;
|
196
|
+
throw ::std::runtime_error(msg);
|
197
|
+
}
|
198
|
+
return res;
|
199
|
+
}
|
200
|
+
EOS
|
201
|
+
end
|
202
|
+
|
203
|
+
file.puts(<<EOS)
|
204
|
+
};
|
205
|
+
|
206
|
+
EOS
|
207
|
+
# }
|
208
|
+
end
|
209
|
+
|
data/lib/yaggo/main.rb
ADDED
@@ -0,0 +1,152 @@
|
|
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
|
+
require 'optparse'
|
17
|
+
|
18
|
+
require 'yaggo/version'
|
19
|
+
require 'yaggo/man_page'
|
20
|
+
require 'yaggo/stub'
|
21
|
+
require 'yaggo/general'
|
22
|
+
require 'yaggo/library'
|
23
|
+
require 'yaggo/dsl'
|
24
|
+
require 'yaggo/parser'
|
25
|
+
require 'yaggo/zsh_completion'
|
26
|
+
|
27
|
+
def main
|
28
|
+
$yaggo_options = {
|
29
|
+
:output => nil,
|
30
|
+
:license => nil,
|
31
|
+
:stub => false,
|
32
|
+
:zc => nil,
|
33
|
+
:extended => false,
|
34
|
+
:debug => false,
|
35
|
+
}
|
36
|
+
|
37
|
+
parser = OptionParser.new do |o|
|
38
|
+
o.version = $yaggo_version
|
39
|
+
o.banner = "Usage: #{$0} [options] [file.yaggo]"
|
40
|
+
o.separator ""
|
41
|
+
o.separator "Specific options:"
|
42
|
+
|
43
|
+
o.on("-o", "--output FILE", "Output file") { |v|
|
44
|
+
$yaggo_options[:output] = v
|
45
|
+
}
|
46
|
+
o.on("-l", "--license PATH", "License file to copy in header") { |v|
|
47
|
+
$yaggo_options[:license] = v
|
48
|
+
}
|
49
|
+
o.on("-m", "--man [FILE]", "Display or write manpage") { |v|
|
50
|
+
display_man_page v
|
51
|
+
exit 0;
|
52
|
+
}
|
53
|
+
o.on("-s", "--stub", "Output a stub yaggo file") {
|
54
|
+
$yaggo_options[:stub] = true
|
55
|
+
}
|
56
|
+
o.on("--zc PATH", "Write zsh completion file") { |v|
|
57
|
+
$yaggo_options[:zc] = v
|
58
|
+
}
|
59
|
+
o.on("-e", "--extended-syntax", "Use extended syntax") {
|
60
|
+
$yaggo_options[:extended] = true
|
61
|
+
}
|
62
|
+
o.on("--debug", "Debug yaggo") {
|
63
|
+
$yaggo_options[:debug] = true
|
64
|
+
}
|
65
|
+
|
66
|
+
o.on_tail("-h", "--help", "Show this message") {
|
67
|
+
puts o
|
68
|
+
exit 0
|
69
|
+
}
|
70
|
+
end
|
71
|
+
parser.parse! ARGV
|
72
|
+
|
73
|
+
if $yaggo_options[:stub]
|
74
|
+
begin
|
75
|
+
display_stub_yaggo_file $yaggo_options[:output]
|
76
|
+
rescue => e
|
77
|
+
STDERR.puts("Failed to write stub: #{e.message}")
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
|
84
|
+
if !$yaggo_options[:stub] && !$yaggo_options[:manual] && ARGV.empty?
|
85
|
+
STDERR.puts "Error: some yaggo files and/or --lib switch is required", parser
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
if !$yaggo_options[:output].nil?
|
89
|
+
if $yaggo_options[:stub]
|
90
|
+
if ARGV.size > 0
|
91
|
+
STDERR.puts "Error: no input file needed with the --stub switch", parser
|
92
|
+
exit 1
|
93
|
+
end
|
94
|
+
elsif ARGV.size != 1
|
95
|
+
STDERR.puts "Error: output switch meaningfull only with 1 input file", parser
|
96
|
+
exit 1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
ARGV.each do |input_file|
|
101
|
+
pid = fork do
|
102
|
+
begin
|
103
|
+
yaggo_script = File.read(input_file)
|
104
|
+
if $yaggo_options[:extended]
|
105
|
+
yaggo_script.gsub!(/\)\s*\n\s*\{/, ") {")
|
106
|
+
end
|
107
|
+
eval(File.read(input_file))
|
108
|
+
parsed = true
|
109
|
+
check_conflict_exclude
|
110
|
+
rescue RuntimeError, SyntaxError, Errno::ENOENT, Errno::EACCES => e
|
111
|
+
raise e if $yaggo_options[:debug]
|
112
|
+
STDERR.puts(e.message.gsub(/^\(eval\)/, input_file))
|
113
|
+
exit 1
|
114
|
+
rescue NoMethodError => e
|
115
|
+
raise e if $yaggo_options[:debug]
|
116
|
+
STDERR.puts("Invalid keyword '#{e.name}'")
|
117
|
+
exit 1
|
118
|
+
end
|
119
|
+
|
120
|
+
fsplit = File.basename(input_file).split(/\./)
|
121
|
+
$klass ||= fsplit.size > 1 ? fsplit[0..-2].join(".") : fsplit[0]
|
122
|
+
$output = $yaggo_options[:output] if $yaggo_options[:output]
|
123
|
+
$output ||= input_file.gsub(/\.yaggo$/, "") + ".hpp"
|
124
|
+
|
125
|
+
begin
|
126
|
+
out_fd = open($output, "w")
|
127
|
+
output_cpp_parser(out_fd, $klass)
|
128
|
+
rescue RuntimeError => e
|
129
|
+
raise e if $yaggo_options[:debug]
|
130
|
+
STDERR.puts("#{input_file}: #{e.message}")
|
131
|
+
exit 1
|
132
|
+
ensure
|
133
|
+
out_fd.close if out_fd
|
134
|
+
end
|
135
|
+
|
136
|
+
if $yaggo_options[:zc]
|
137
|
+
begin
|
138
|
+
out_fd = open($yaggo_options[:zc], "w")
|
139
|
+
output_zsh_completion(out_fd, $yaggo_options[:zc])
|
140
|
+
rescue RuntimeError => e
|
141
|
+
raise e if $yaggo_options[:debug]
|
142
|
+
STDERR.puts("#{input_file}: #{e.message}")
|
143
|
+
exit 1
|
144
|
+
ensure
|
145
|
+
out_fd.close if out_fd
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
Process.waitpid pid
|
150
|
+
exit 1 if !$?.exited? || ($?.exited? && $?.exitstatus != 0)
|
151
|
+
end
|
152
|
+
end
|