argument_parser.hpp 10.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2022 Advanced Micro Devices, Inc. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
Paul's avatar
Paul committed
24
25
26
#ifndef MIGRAPHX_GUARD_RTGLIB_ARGUMENT_PARSER_HPP
#define MIGRAPHX_GUARD_RTGLIB_ARGUMENT_PARSER_HPP

Paul's avatar
Paul committed
27
28
29
30
#include <algorithm>
#include <functional>
#include <iostream>
#include <set>
Paul's avatar
Paul committed
31
#include <string>
Paul's avatar
Paul committed
32
#include <sstream>
Paul's avatar
Paul committed
33
34
#include <type_traits>
#include <unordered_map>
Paul's avatar
Paul committed
35
#include <utility>
Paul's avatar
Paul committed
36
37
#include <vector>

Paul's avatar
Paul committed
38
#include <migraphx/config.hpp>
Paul's avatar
Paul committed
39
40
#include <migraphx/requires.hpp>
#include <migraphx/type_name.hpp>
Paul's avatar
Paul committed
41
#include <migraphx/functional.hpp>
Paul's avatar
Paul committed
42
#include <migraphx/stringutils.hpp>
kahmed10's avatar
kahmed10 committed
43
#include <migraphx/rank.hpp>
Paul's avatar
Paul committed
44
45
46
47

namespace migraphx {
namespace driver {
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
48

Paul's avatar
Paul committed
49
50
51
52
53
54
#ifdef MIGRAPHX_USE_CLANG_TIDY
#define MIGRAPHX_DRIVER_STATIC
#else
#define MIGRAPHX_DRIVER_STATIC static
#endif

Paul's avatar
Paul committed
55
template <class T>
Paul's avatar
Paul committed
56
57
58
59
60
using bare = std::remove_cv_t<std::remove_reference_t<T>>;

namespace detail {

template <class T>
Paul's avatar
Paul committed
61
auto is_container(int, T&& x) -> decltype(x.insert(x.end(), *x.begin()), std::true_type{});
Paul's avatar
Paul committed
62
63
64
65
66
67
68
69
70
71
72
73
74

template <class T>
std::false_type is_container(float, T&&);

} // namespace detail

template <class T>
struct is_container : decltype(detail::is_container(int(0), std::declval<T>()))
{
};

template <class T>
using is_multi_value =
Paul's avatar
Paul committed
75
    std::integral_constant<bool, (is_container<T>{} and not std::is_convertible<T, std::string>{})>;
Paul's avatar
Paul committed
76

Paul's avatar
Paul committed
77
78
79
template <class T>
struct value_parser
{
Paul's avatar
Paul committed
80
    template <MIGRAPHX_REQUIRES(not std::is_enum<T>{} and not is_multi_value<T>{})>
Paul's avatar
Paul committed
81
82
83
84
85
86
87
88
89
90
91
    static T apply(const std::string& x)
    {
        T result;
        std::stringstream ss;
        ss.str(x);
        ss >> result;
        if(ss.fail())
            throw std::runtime_error("Failed to parse: " + x);
        return result;
    }

Paul's avatar
Paul committed
92
    template <MIGRAPHX_REQUIRES(std::is_enum<T>{} and not is_multi_value<T>{})>
Paul's avatar
Paul committed
93
94
95
96
97
98
99
100
101
102
    static T apply(const std::string& x)
    {
        std::ptrdiff_t i;
        std::stringstream ss;
        ss.str(x);
        ss >> i;
        if(ss.fail())
            throw std::runtime_error("Failed to parse: " + x);
        return static_cast<T>(i);
    }
Paul's avatar
Paul committed
103
104
105
106
107
108
109
110
111

    template <MIGRAPHX_REQUIRES(is_multi_value<T>{} and not std::is_enum<T>{})>
    static T apply(const std::string& x)
    {
        T result;
        using value_type = typename T::value_type;
        result.insert(result.end(), value_parser<value_type>::apply(x));
        return result;
    }
Paul's avatar
Paul committed
112
113
114
115
116
117
118
};

struct argument_parser
{
    struct argument
    {
        std::vector<std::string> flags;
Paul's avatar
Paul committed
119
        std::function<bool(argument_parser&, const std::vector<std::string>&)> action{};
Paul's avatar
Paul committed
120
121
122
        std::string type          = "";
        std::string help          = "";
        std::string metavar       = "";
Paul's avatar
Paul committed
123
        std::string default_value = "";
Paul's avatar
Paul committed
124
        unsigned nargs            = 1;
Paul's avatar
Paul committed
125
126
    };

Paul's avatar
Paul committed
127
    template <class T, MIGRAPHX_REQUIRES(is_multi_value<T>{})>
Paul's avatar
Paul committed
128
129
130
131
132
    std::string as_string_value(const T& x)
    {
        return to_string_range(x);
    }

kahmed10's avatar
kahmed10 committed
133
134
135
136
137
138
139
140
141
142
143
144
    template <class T>
    auto as_string_value(rank<1>, const T& x) -> decltype(to_string(x))
    {
        return to_string(x);
    }

    template <class T>
    std::string as_string_value(rank<0>, const T&)
    {
        throw std::runtime_error("Can't convert to string");
    }

Paul's avatar
Paul committed
145
    template <class T, MIGRAPHX_REQUIRES(not is_multi_value<T>{})>
Paul's avatar
Paul committed
146
147
    std::string as_string_value(const T& x)
    {
kahmed10's avatar
kahmed10 committed
148
        return as_string_value(rank<1>{}, x);
Paul's avatar
Paul committed
149
150
    }

Paul's avatar
Paul committed
151
    template <class T, class... Fs>
Paul's avatar
Paul committed
152
    void operator()(T& x, const std::vector<std::string>& flags, Fs... fs)
Paul's avatar
Paul committed
153
    {
Paul's avatar
Paul committed
154
        arguments.push_back({flags, [&](auto&&, const std::vector<std::string>& params) {
Paul's avatar
Paul committed
155
156
157
158
159
                                 if(params.empty())
                                     throw std::runtime_error("Flag with no value.");
                                 x = value_parser<T>::apply(params.back());
                                 return false;
                             }});
Paul's avatar
Paul committed
160

kahmed10's avatar
kahmed10 committed
161
162
        argument& arg = arguments.back();
        arg.type      = migraphx::get_type_name<T>();
Paul's avatar
Paul committed
163
        migraphx::each_args([&](auto f) { f(x, arg); }, fs...);
kahmed10's avatar
kahmed10 committed
164
165
        if(not arg.default_value.empty() and arg.nargs > 0)
            arg.default_value = as_string_value(x);
Paul's avatar
Paul committed
166
167
    }

Paul's avatar
Paul committed
168
    template <class... Fs>
Paul's avatar
Paul committed
169
    void operator()(std::nullptr_t x, std::vector<std::string> flags, Fs... fs)
Paul's avatar
Paul committed
170
    {
Paul's avatar
Paul committed
171
        arguments.push_back({std::move(flags)});
Paul's avatar
Paul committed
172
173
174

        argument& arg = arguments.back();
        arg.type      = "";
Paul's avatar
Paul committed
175
        arg.nargs     = 0;
Paul's avatar
Paul committed
176
177
178
        migraphx::each_args([&](auto f) { f(x, arg); }, fs...);
    }

Paul's avatar
Paul committed
179
    MIGRAPHX_DRIVER_STATIC auto nargs(unsigned n = 1)
Paul's avatar
Paul committed
180
    {
Paul's avatar
Paul committed
181
        return [=](auto&&, auto& arg) { arg.nargs = n; };
Paul's avatar
Paul committed
182
183
    }

Paul's avatar
Paul committed
184
    template <class F>
Paul's avatar
Paul committed
185
    MIGRAPHX_DRIVER_STATIC auto write_action(F f)
Paul's avatar
Paul committed
186
187
    {
        return [=](auto& x, auto& arg) {
Paul's avatar
Paul committed
188
            arg.action = [&, f](auto& self, const std::vector<std::string>& params) {
Paul's avatar
Paul committed
189
190
191
192
193
194
                f(self, x, params);
                return false;
            };
        };
    }

Paul's avatar
Paul committed
195
    template <class F>
Paul's avatar
Paul committed
196
    MIGRAPHX_DRIVER_STATIC auto do_action(F f)
Paul's avatar
Paul committed
197
198
    {
        return [=](auto&, auto& arg) {
Paul's avatar
Paul committed
199
            arg.nargs  = 0;
Paul's avatar
Paul committed
200
            arg.action = [&, f](auto& self, const std::vector<std::string>&) {
Paul's avatar
Paul committed
201
202
203
204
205
206
                f(self);
                return true;
            };
        };
    }

Paul's avatar
Paul committed
207
    MIGRAPHX_DRIVER_STATIC auto append()
Paul's avatar
Paul committed
208
    {
Paul's avatar
Paul committed
209
        return write_action([](auto&, auto& x, auto& params) {
Paul's avatar
Paul committed
210
            using type = typename bare<decltype(params)>::value_type;
Paul's avatar
Paul committed
211
            std::transform(params.begin(),
Paul's avatar
Paul committed
212
213
                           params.end(),
                           std::inserter(x, x.end()),
Paul's avatar
Paul committed
214
215
                           [](std::string y) { return value_parser<type>::apply(y); });
        });
Paul's avatar
Paul committed
216
217
    }

Paul's avatar
Paul committed
218
    MIGRAPHX_DRIVER_STATIC auto show_help(const std::string& msg = "")
Paul's avatar
Paul committed
219
    {
Paul's avatar
Paul committed
220
        return do_action([=](auto& self) {
Paul's avatar
Paul committed
221
            for(auto&& arg : self.arguments)
Paul's avatar
Paul committed
222
223
224
            {
                std::cout << std::endl;
                std::string prefix = "    ";
Paul's avatar
Paul committed
225
                if(arg.flags.empty())
Paul's avatar
Paul committed
226
227
228
229
                {
                    std::cout << prefix;
                    std::cout << arg.metavar;
                }
Paul's avatar
Paul committed
230
231
232
233
234
235
                for(const std::string& a : arg.flags)
                {
                    std::cout << prefix;
                    std::cout << a;
                    prefix = ", ";
                }
Paul's avatar
Paul committed
236
                if(not arg.type.empty())
Paul's avatar
Paul committed
237
                {
Paul's avatar
Paul committed
238
                    std::cout << " [" << arg.type << "]";
Paul's avatar
Paul committed
239
                    if(not arg.default_value.empty())
Paul's avatar
Paul committed
240
241
                        std::cout << " (Default: " << arg.default_value << ")";
                }
Paul's avatar
Paul committed
242
243
244
245
                std::cout << std::endl;
                std::cout << "        " << arg.help << std::endl;
            }
            std::cout << std::endl;
Paul's avatar
Paul committed
246
            if(not msg.empty())
Paul's avatar
Paul committed
247
                std::cout << msg << std::endl;
Paul's avatar
Paul committed
248
249
250
        });
    }

Paul's avatar
Paul committed
251
    MIGRAPHX_DRIVER_STATIC auto help(const std::string& help)
Paul's avatar
Paul committed
252
    {
Paul's avatar
Paul committed
253
        return [=](auto&, auto& arg) { arg.help = help; };
Paul's avatar
Paul committed
254
255
    }

Paul's avatar
Paul committed
256
    MIGRAPHX_DRIVER_STATIC auto metavar(const std::string& metavar)
Paul's avatar
Paul committed
257
258
259
260
    {
        return [=](auto&, auto& arg) { arg.metavar = metavar; };
    }

261
262
263
264
265
    MIGRAPHX_DRIVER_STATIC auto type(const std::string& type)
    {
        return [=](auto&, auto& arg) { arg.type = type; };
    }

Paul's avatar
Paul committed
266
    template <class T>
Paul's avatar
Paul committed
267
    MIGRAPHX_DRIVER_STATIC auto set_value(T value)
Paul's avatar
Paul committed
268
269
    {
        return [=](auto& x, auto& arg) {
Paul's avatar
Paul committed
270
            arg.nargs  = 0;
Paul's avatar
Paul committed
271
            arg.type   = "";
Paul's avatar
Paul committed
272
            arg.action = [&, value](auto&, const std::vector<std::string>&) {
Paul's avatar
Paul committed
273
274
275
276
277
278
                x = value;
                return false;
            };
        };
    }

Paul's avatar
Paul committed
279
    bool parse(std::vector<std::string> args)
Paul's avatar
Paul committed
280
    {
Paul's avatar
Paul committed
281
        std::unordered_map<std::string, unsigned> keywords;
Paul's avatar
Paul committed
282
        for(auto&& arg : arguments)
Paul's avatar
Paul committed
283
        {
Paul's avatar
Paul committed
284
            for(auto&& flag : arg.flags)
Paul's avatar
Paul committed
285
                keywords[flag] = arg.nargs + 1;
Paul's avatar
Paul committed
286
        }
Paul's avatar
Paul committed
287
288
        auto arg_map =
            generic_parse(std::move(args), [&](const std::string& x) { return keywords[x]; });
Paul's avatar
Paul committed
289
        for(auto&& arg : arguments)
Paul's avatar
Paul committed
290
        {
Paul's avatar
Paul committed
291
            auto flags = arg.flags;
Paul's avatar
Paul committed
292
            if(flags.empty())
Paul's avatar
Paul committed
293
                flags = {""};
Paul's avatar
Paul committed
294
            for(auto&& flag : flags)
Paul's avatar
Paul committed
295
            {
Paul's avatar
Paul committed
296
                if(arg_map.count(flag) > 0)
Paul's avatar
Paul committed
297
                {
Paul's avatar
Paul committed
298
                    if(arg.action(*this, arg_map[flag]))
Paul's avatar
Paul committed
299
                        return true;
Paul's avatar
Paul committed
300
301
302
                }
            }
        }
Paul's avatar
Paul committed
303
        return false;
Paul's avatar
Paul committed
304
305
306
307
308
309
310
311
312
    }

    using string_map = std::unordered_map<std::string, std::vector<std::string>>;
    template <class IsKeyword>
    static string_map generic_parse(std::vector<std::string> as, IsKeyword is_keyword)
    {
        string_map result;

        std::string flag;
Paul's avatar
Paul committed
313
        bool clear = false;
Paul's avatar
Paul committed
314
315
        for(auto&& x : as)
        {
Paul's avatar
Paul committed
316
317
            auto k = is_keyword(x);
            if(k > 0)
Paul's avatar
Paul committed
318
319
320
            {
                flag = x;
                result[flag]; // Ensure the flag exists
Paul's avatar
Paul committed
321
                if(k == 1)
Paul's avatar
Paul committed
322
                    flag = "";
Paul's avatar
Paul committed
323
                else if(k == 2)
Paul's avatar
Paul committed
324
325
326
                    clear = true;
                else
                    clear = false;
Paul's avatar
Paul committed
327
328
329
330
            }
            else
            {
                result[flag].push_back(x);
Paul's avatar
Paul committed
331
                if(clear)
Paul's avatar
Paul committed
332
333
                    flag = "";
                clear = false;
Paul's avatar
Paul committed
334
335
336
337
            }
        }
        return result;
    }
Paul's avatar
Paul committed
338

Paul's avatar
Paul committed
339
    private:
Paul's avatar
Paul committed
340
    std::vector<argument> arguments;
Paul's avatar
Paul committed
341
342
};

Paul's avatar
Paul committed
343
344
345
346
} // namespace MIGRAPHX_INLINE_NS
} // namespace driver
} // namespace migraphx

Paul's avatar
Paul committed
347
#endif