1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
/*
** Copyright (C) 2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
** Free Software Foundation; either version 3, or (at your option) any
** later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/
#ifndef MU_COMMAND_PARSER_HH__
#define MU_COMMAND_PARSER_HH__
#include <vector>
#include <string>
#include <ostream>
#include <stdexcept>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include "utils/mu-error.hh"
#include "utils/mu-sexp.hh"
#include "utils/mu-option.hh"
namespace Mu {
namespace Command {
///
/// Commands are s-expressions with the follow properties:
/// 1) a command is a list with a command-name as its first argument
/// 2) the rest of the parameters are pairs of colon-prefixed symbol and a value of some
/// type (ie. 'keyword arguments')
/// 3) each command is described by its CommandInfo structure, which defines the type
/// 4) calls to the command must include all required parameters
/// 5) all parameters must be of the specified type; however the symbol 'nil' is allowed
/// for specify a non-required parameter to be absent; this is for convenience on the
/// call side.
/// Information about a function argument
struct ArgInfo {
ArgInfo(Sexp::Type typearg, bool requiredarg, std::string&& docarg)
: type{typearg}, required{requiredarg}, docstring{std::move(docarg)}
{
}
const Sexp::Type type; /**< Sexp::Type of the argument */
const bool required; /**< Is this argument required? */
const std::string docstring; /**< Documentation */
};
/// The arguments for a function, which maps their names to the information.
using ArgMap = std::unordered_map<std::string, ArgInfo>;
// The parameters to a Handler.
using Parameters = Sexp::Seq;
Option<int> get_int(const Parameters& parms, const std::string& argname);
Option<unsigned> get_unsigned(const Parameters& parms, const std::string& argname);
Option<bool> get_bool(const Parameters& parms, const std::string& argname);
Option<std::string> get_string(const Parameters& parms, const std::string& argname);
Option<std::string> get_symbol(const Parameters& parms, const std::string& argname);
std::vector<std::string> get_string_vec(const Parameters& params, const std::string& argname);
/*
* backward compat
*/
static inline int
get_int_or(const Parameters& parms, const std::string& arg, int alt = 0) {
return get_int(parms, arg).value_or(alt);
}
static inline bool
get_bool_or(const Parameters& parms, const std::string& arg, bool alt = false) {
return get_bool(parms, arg).value_or(alt);
}
static inline std::string
get_string_or(const Parameters& parms, const std::string& arg, const std::string& alt = ""){
return get_string(parms, arg).value_or(alt);
}
static inline std::string
get_symbol_or(const Parameters& parms, const std::string& arg, const std::string& alt = "nil") {
return get_symbol(parms, arg).value_or(alt);
}
// A handler function
using Handler = std::function<void(const Parameters&)>;
/// Information about some command
struct CommandInfo {
CommandInfo(ArgMap&& argmaparg, std::string&& docarg, Handler&& handlerarg)
: args{std::move(argmaparg)}, docstring{std::move(docarg)}, handler{
std::move(handlerarg)}
{
}
const ArgMap args;
const std::string docstring;
const Handler handler;
/**
* Get a sorted list of argument names, for display. Required args come
* first, then alphabetical.
*
* @return vec with the sorted names.
*/
std::vector<std::string> sorted_argnames() const
{ // sort args -- by required, then alphabetical.
std::vector<std::string> names;
for (auto&& arg : args)
names.emplace_back(arg.first);
std::sort(names.begin(), names.end(), [&](const auto& name1, const auto& name2) {
const auto& arg1{args.find(name1)->second};
const auto& arg2{args.find(name2)->second};
if (arg1.required != arg2.required)
return arg1.required;
else
return name1 < name2;
});
return names;
}
};
/// All commands, mapping their name to information about them.
using CommandMap = std::unordered_map<std::string, CommandInfo>;
/**
* Validate that the call (a Sexp) specifies a valid call, then invoke it.
*
* A call uses keyword arguments, e.g. something like:
* (foo :bar 1 :cuux "fnorb")
*
* On error, throw Error.
*
* @param cmap map of commands
* @param call node describing a call.
*/
void invoke(const Command::CommandMap& cmap, const Sexp& call);
static inline std::ostream&
operator<<(std::ostream& os, const Command::ArgInfo& info)
{
os << info.type << " (" << (info.required ? "required" : "optional") << ")";
return os;
}
static inline std::ostream&
operator<<(std::ostream& os, const Command::CommandInfo& info)
{
for (auto&& arg : info.args)
os << " " << arg.first << " " << arg.second << '\n'
<< " " << arg.second.docstring << "\n";
return os;
}
static inline std::ostream&
operator<<(std::ostream& os, const Command::CommandMap& map)
{
for (auto&& c : map)
os << c.first << '\n' << c.second;
return os;
}
} // namespace Command
} // namespace Mu
#endif /* MU_COMMAND_PARSER_HH__ */
|