blob: 2220b760396b2957a88e90602a203e26e94ca1a9 [file] [log] [blame]
Václav Kubernát0a2a2e82018-05-11 13:59:12 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
9#pragma once
10
Václav Kubernát509ce652019-05-29 19:46:44 +020011#include <boost/spirit/home/x3.hpp>
Václav Kubernát24df80e2018-06-06 15:18:03 +020012#include "ast_commands.hpp"
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020013#include "ast_handlers.hpp"
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010014#include "common_parsers.hpp"
15#include "leaf_data.hpp"
Václav Kubernátd0ea9b22020-04-24 00:44:15 +020016#include "path_parser.hpp"
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020017
Václav Kubernát7ff22072022-04-05 10:22:40 +020018/**
19 * Returns a parser that generates suggestions based on a Completion set.
20 * Usage:
21 * const auto suggestSomeStuff = staticSuggestions({"some", "stuff", "yay"});
22 *
23 * You can use this as a standard parser in a Spirit grammar.
24 */
25auto staticSuggestions(const std::initializer_list<std::string>& strings)
26{
27 std::set<Completion> completions;
28 std::transform(begin(strings), end(strings), std::inserter(completions, completions.end()),
29 [](const auto s) { return Completion{s, " "}; });
30 return as<x3::unused_type>[x3::eps[([completions](auto& ctx) {
31 auto& parserContext = x3::get<parser_context_tag>(ctx);
32 parserContext.m_suggestions = completions;
33 parserContext.m_completionIterator = _where(ctx).begin();
34 })]];
35}
36
Václav Kubernát68c89422021-10-20 15:32:20 +020037#if BOOST_VERSION <= 107700
38namespace boost::spirit::x3::traits
39{
40 // Backport https://github.com/boostorg/spirit/pull/702
41 // with instructions from https://github.com/boostorg/spirit/issues/701#issuecomment-946743099
42 template <typename... Types, typename T>
43 struct variant_find_substitute<boost::variant<Types...>, T>
44 {
45 using variant_type = boost::variant<Types...>;
46
47 typedef typename variant_type::types types;
48 typedef typename mpl::end<types>::type end;
49
50 typedef typename mpl::find<types, T>::type iter_1;
51
52 typedef typename
53 mpl::eval_if<
54 is_same<iter_1, end>,
55 mpl::find_if<types, traits::is_substitute<T, mpl::_1> >,
56 mpl::identity<iter_1>
57 >::type
58 iter;
59
60 typedef typename
61 mpl::eval_if<
62 is_same<iter, end>,
63 mpl::identity<T>,
64 mpl::deref<iter>
65 >::type
66 type;
67 };
68}
69#endif
Václav Kubernát60d6f292018-05-25 09:45:32 +020070
Václav Kubernát6d791432018-10-25 16:00:35 +020071x3::rule<discard_class, discard_> const discard = "discard";
Václav Kubernát11afac72018-07-18 14:59:53 +020072x3::rule<ls_class, ls_> const ls = "ls";
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020073x3::rule<cd_class, cd_> const cd = "cd";
Václav Kubernát07204242018-06-04 18:12:09 +020074x3::rule<set_class, set_> const set = "set";
Václav Kubernátb6ff0b62018-08-30 16:14:53 +020075x3::rule<get_class, get_> const get = "get";
Václav Kubernátb61336d2018-05-28 17:35:03 +020076x3::rule<create_class, create_> const create = "create";
77x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
Václav Kubernát812ee282018-08-30 17:10:03 +020078x3::rule<commit_class, commit_> const commit = "commit";
Václav Kubernát9cfcd872020-02-18 12:34:02 +010079x3::rule<describe_class, describe_> const describe = "describe";
Václav Kubernát054cc992019-02-21 14:23:52 +010080x3::rule<help_class, help_> const help = "help";
Václav Kubernát7160a132020-04-03 02:11:01 +020081x3::rule<copy_class, copy_> const copy = "copy";
Václav Kubernátbf65dd72020-05-28 02:32:31 +020082x3::rule<move_class, move_> const move = "move";
Václav Kubernát70d7f7a2020-06-23 14:40:40 +020083x3::rule<dump_class, dump_> const dump = "dump";
Václav Kubernátaa4250a2020-07-22 00:02:23 +020084x3::rule<prepare_class, prepare_> const prepare = "prepare";
Václav Kubernáte7248b22020-06-26 15:38:59 +020085x3::rule<exec_class, exec_> const exec = "exec";
Václav Kubernát1d593902021-03-08 08:55:16 +010086x3::rule<switch_class, switch_> const switch_rule = "switch";
Václav Kubernáte7248b22020-06-26 15:38:59 +020087x3::rule<cancel_class, cancel_> const cancel = "cancel";
Václav Kubernátb61336d2018-05-28 17:35:03 +020088x3::rule<command_class, command_> const command = "command";
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020089
Václav Kubernát57272422019-02-08 12:48:24 +010090x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
Václav Kubernát5c75b252018-10-10 18:33:47 +020091
Václav Kubernát41378452018-06-06 16:29:40 +020092#if __clang__
93#pragma GCC diagnostic push
94#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
95#endif
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020096
Václav Kubernát509ce652019-05-29 19:46:44 +020097namespace ascii = boost::spirit::x3::ascii;
98
Václav Kubernáte7d4aea2018-09-11 18:15:48 +020099struct ls_options_table : x3::symbols<LsOption> {
100 ls_options_table()
101 {
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100102 add
103 ("--recursive", LsOption::Recursive);
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200104 }
105} const ls_options;
106
Václav Kubernát11afac72018-07-18 14:59:53 +0200107auto const ls_def =
Václav Kubernát4a58ce62020-05-14 17:58:10 +0200108 ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (anyPath | (module >> "*")));
Václav Kubernát11afac72018-07-18 14:59:53 +0200109
Václav Kubernát0a2a2e82018-05-11 13:59:12 +0200110auto const cd_def =
Václav Kubernáte7248b22020-06-26 15:38:59 +0200111 cd_::name >> space_separator > cdPath;
Václav Kubernát0a2a2e82018-05-11 13:59:12 +0200112
Václav Kubernát38175022021-11-04 14:58:57 +0100113#if BOOST_VERSION <= 107700
114auto const create_def =
115 create_::name >> space_separator >
116 (x3::eps >> presenceContainerPath |
117 x3::eps >> listInstancePath |
118 x3::eps >> leafListElementPath);
119
120auto const delete_rule_def =
121 delete_::name >> space_separator >
122 (x3::eps >> presenceContainerPath |
123 x3::eps >> listInstancePath |
124 x3::eps >> leafListElementPath |
125 x3::eps >> writableLeafPath);
126#else
Václav Kubernátb61336d2018-05-28 17:35:03 +0200127auto const create_def =
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200128 create_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
Václav Kubernátb61336d2018-05-28 17:35:03 +0200129
130auto const delete_rule_def =
Jan Kundrátb7206ad2020-06-18 21:08:14 +0200131 delete_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath | writableLeafPath);
Václav Kubernát38175022021-11-04 14:58:57 +0100132#endif
Václav Kubernát07204242018-06-04 18:12:09 +0200133
Václav Kubernáte066fe22022-04-06 00:32:26 +0200134const auto dsTargetSuggestions = staticSuggestions({"running", "startup", "operational"});
135
136struct ds_target_table : x3::symbols<DatastoreTarget> {
137 ds_target_table()
138 {
139 add
140 ("operational", DatastoreTarget::Operational)
141 ("startup", DatastoreTarget::Startup)
142 ("running", DatastoreTarget::Running);
143 }
144} const ds_target_table;
145
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200146auto const get_def =
Václav Kubernáte066fe22022-04-06 00:32:26 +0200147 get_::name
148 >> -(space_separator >> "-" > staticSuggestions({"-datastore"}) > "-datastore" > space_separator > dsTargetSuggestions > ds_target_table)
149 >> -(space_separator >> getPath);
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200150
Václav Kubernát07204242018-06-04 18:12:09 +0200151auto const set_def =
Václav Kubernátabf52802020-05-19 01:31:17 +0200152 set_::name >> space_separator > writableLeafPath > space_separator > leaf_data;
Václav Kubernátb61336d2018-05-28 17:35:03 +0200153
Václav Kubernát812ee282018-08-30 17:10:03 +0200154auto const commit_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100155 commit_::name >> x3::attr(commit_());
Václav Kubernát812ee282018-08-30 17:10:03 +0200156
Václav Kubernát6d791432018-10-25 16:00:35 +0200157auto const discard_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100158 discard_::name >> x3::attr(discard_());
Václav Kubernát6d791432018-10-25 16:00:35 +0200159
Václav Kubernát054cc992019-02-21 14:23:52 +0100160struct command_names_table : x3::symbols<decltype(help_::m_cmd)> {
161 command_names_table()
162 {
163 boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
Václav Kubernát672889c2020-05-27 04:11:36 +0200164 add(commandNamesVisitor(cmd), decltype(help_::m_cmd)(cmd));
Václav Kubernát054cc992019-02-21 14:23:52 +0100165 });
166 }
167} const command_names;
168
169auto const help_def =
170 help_::name > createCommandSuggestions >> -command_names;
171
Václav Kubernát7160a132020-04-03 02:11:01 +0200172struct datastore_symbol_table : x3::symbols<Datastore> {
173 datastore_symbol_table()
174 {
175 add
176 ("running", Datastore::Running)
177 ("startup", Datastore::Startup);
178 }
179} const datastore;
180
Václav Kubernát21fc5e22020-11-26 04:28:27 +0100181const auto copy_source = x3::rule<class source, Datastore>{"source datastore"} = datastore;
182const auto copy_destination = x3::rule<class source, Datastore>{"destination datastore"} = datastore;
Václav Kubernát7160a132020-04-03 02:11:01 +0200183
Václav Kubernát7ff22072022-04-05 10:22:40 +0200184const auto datastoreSuggestions = staticSuggestions({"running", "startup"});
Václav Kubernát7160a132020-04-03 02:11:01 +0200185
186struct copy_args : x3::parser<copy_args> {
187 using attribute_type = copy_;
188 template <typename It, typename Ctx, typename RCtx>
189 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, copy_& attr) const
190 {
191 auto& parserContext = x3::get<parser_context_tag>(ctx);
192 auto iterBeforeDestination = begin;
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100193 auto save_iter = x3::no_skip[x3::eps[([&iterBeforeDestination](auto& ctx) { iterBeforeDestination = _where(ctx).begin(); })]];
Václav Kubernát7160a132020-04-03 02:11:01 +0200194 auto grammar = datastoreSuggestions > copy_source > space_separator > datastoreSuggestions > save_iter > copy_destination;
195
196 try {
197 grammar.parse(begin, end, ctx, rctx, attr);
198 } catch (x3::expectation_failure<It>& ex) {
199 using namespace std::string_literals;
200 parserContext.m_errorMsg = "Expected "s + ex.which() + " here:";
201 throw;
202 }
203
204 if (attr.m_source == attr.m_destination) {
205 begin = iterBeforeDestination; // Restoring the iterator here makes the error caret point to the second datastore
206 parserContext.m_errorMsg = "Source datastore and destination datastore can't be the same.";
207 return false;
208 }
209
210 return true;
211 }
Václav Kubernát21fc5e22020-11-26 04:28:27 +0100212} const copy_args;
Václav Kubernát7160a132020-04-03 02:11:01 +0200213
214auto const copy_def =
215 copy_::name > space_separator > copy_args;
216
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100217auto const describe_def =
Václav Kubernát3200b862020-12-03 02:34:55 +0100218 describe_::name >> space_separator > anyPath;
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100219
Václav Kubernát162165e2021-02-22 09:46:53 +0100220struct move_mode_table : x3::symbols<MoveMode> {
221 move_mode_table()
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200222 {
223 add
224 ("after", MoveMode::After)
225 ("before", MoveMode::Before)
226 ("begin", MoveMode::Begin)
227 ("end", MoveMode::End);
228 }
Václav Kubernát162165e2021-02-22 09:46:53 +0100229} const move_mode_table;
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200230
231struct move_absolute_table : x3::symbols<yang::move::Absolute> {
232 move_absolute_table()
233 {
234 add
235 ("begin", yang::move::Absolute::Begin)
236 ("end", yang::move::Absolute::End);
237 }
238} const move_absolute_table;
239
240struct move_relative_table : x3::symbols<yang::move::Relative::Position> {
241 move_relative_table()
242 {
243 add
244 ("before", yang::move::Relative::Position::Before)
245 ("after", yang::move::Relative::Position::After);
246 }
247} const move_relative_table;
248
249struct move_args : x3::parser<move_args> {
250 using attribute_type = move_;
251 template <typename It, typename Ctx, typename RCtx>
252 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, move_& attr) const
253 {
254 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
255 dataPath_ movePath;
Václav Kubernát38175022021-11-04 14:58:57 +0100256#if BOOST_VERSION <= 107700
257 auto movePathGrammar = x3::eps >> listInstancePath | x3::eps >> leafListElementPath;
258#else
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200259 auto movePathGrammar = listInstancePath | leafListElementPath;
Václav Kubernát38175022021-11-04 14:58:57 +0100260#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200261 auto res = movePathGrammar.parse(begin, end, ctx, rctx, attr.m_source);
262 if (!res) {
263 parserContext.m_errorMsg = "Expected source path here:";
264 return false;
265 }
266
267 // Try absolute move first.
268 res = (space_separator >> move_absolute_table).parse(begin, end, ctx, rctx, attr.m_destination);
269 if (res) {
270 // Absolute move parsing succeeded, we don't need to parse anything else.
271 return true;
272 }
273
274 // If absolute move didn't succeed, try relative.
275 attr.m_destination = yang::move::Relative{};
276 res = (space_separator >> move_relative_table).parse(begin, end, ctx, rctx, std::get<yang::move::Relative>(attr.m_destination).m_position);
277
278 if (!res) {
279 parserContext.m_errorMsg = "Expected a move position (begin, end, before, after) here:";
280 return false;
281 }
282
283 if (std::holds_alternative<leafListElement_>(attr.m_source.m_nodes.back().m_suffix)) {
284 leaf_data_ value;
285 res = (space_separator >> leaf_data).parse(begin, end, ctx, rctx, value);
286 if (res) {
287 std::get<yang::move::Relative>(attr.m_destination).m_path = {{".", value}};
288 }
289 } else {
290 ListInstance listInstance;
291 // The source list instance will be stored inside the parser context path.
292 // The source list instance will be full data path (with keys included).
293 // However, m_tmpListPath is supposed to store a path with a list without the keys.
294 // So, I pop the last listElement_ (which has the keys) and put in a list_ (which doesn't have the keys).
295 // Example: /mod:cont/protocols[name='ftp'] gets turned into /mod:cont/protocols
296 parserContext.m_tmpListPath = parserContext.currentDataPath();
297 parserContext.m_tmpListPath.m_nodes.pop_back();
298 auto list = list_{std::get<listElement_>(attr.m_source.m_nodes.back().m_suffix).m_name};
Václav Kubernátfaacd022020-07-08 16:44:38 +0200299 parserContext.m_tmpListPath.m_nodes.emplace_back(attr.m_source.m_nodes.back().m_prefix, list);
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200300
301 res = (space_separator >> listSuffix).parse(begin, end, ctx, rctx, listInstance);
302 if (res) {
303 std::get<yang::move::Relative>(attr.m_destination).m_path = listInstance;
304 }
305 }
306
307 if (!res) {
308 parserContext.m_errorMsg = "Expected a destination here:";
309 }
310
311 return res;
312 }
313} const move_args;
314
315auto const move_def =
316 move_::name >> space_separator >> move_args;
317
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200318struct format_table : x3::symbols<DataFormat> {
319 format_table()
320 {
321 add
322 ("xml", DataFormat::Xml)
323 ("json", DataFormat::Json);
324 }
325} const format_table;
326
Václav Kubernát3b5e6522020-06-26 18:36:09 +0200327struct dump_args : x3::parser<dump_args> {
328 using attribute_type = dump_;
329 template <typename It, typename Ctx, typename RCtx>
330 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, dump_& attr) const
331 {
332 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
333 parserContext.m_suggestions = {{"xml"}, {"json"}};
334 parserContext.m_completionIterator = begin;
335 auto res = format_table.parse(begin, end, ctx, rctx, attr);
336 if (!res) {
337 parserContext.m_errorMsg = "Expected a data format (xml, json) here:";
338 }
339 return res;
340 }
341} const dump_args;
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200342
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200343auto const prepare_def =
Václav Kubernátd8408e02020-12-02 05:13:27 +0100344 prepare_::name > space_separator > as<dataPath_>[RpcActionPath<AllowInput::Yes>{}];
Václav Kubernáte7248b22020-06-26 15:38:59 +0200345
346auto const exec_def =
Václav Kubernátaff1f492021-02-17 10:56:28 +0100347 exec_::name > -(space_separator > -as<dataPath_>[RpcActionPath<AllowInput::No>{}]);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200348
Václav Kubernát162165e2021-02-22 09:46:53 +0100349auto const switch_rule_def =
Václav Kubernát7ff22072022-04-05 10:22:40 +0200350 switch_::name > space_separator > dsTargetSuggestions > ds_target_table;
Václav Kubernát162165e2021-02-22 09:46:53 +0100351
Václav Kubernáte7248b22020-06-26 15:38:59 +0200352auto const cancel_def =
353 cancel_::name >> x3::attr(cancel_{});
354
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200355auto const dump_def =
Václav Kubernát3b5e6522020-06-26 18:36:09 +0200356 dump_::name > space_separator >> dump_args;
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200357
Václav Kubernát57272422019-02-08 12:48:24 +0100358auto const createCommandSuggestions_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100359 x3::eps;
Václav Kubernát57272422019-02-08 12:48:24 +0100360
Václav Kubernátb61336d2018-05-28 17:35:03 +0200361auto const command_def =
Václav Kubernát5b8e2822022-01-14 03:19:54 +0100362#if BOOST_VERSION <= 107800
Václav Kubernátf9533c22021-11-04 15:13:30 +0100363 x3::eps >>
364#endif
Václav Kubernát162165e2021-02-22 09:46:53 +0100365 createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel | switch_rule];
Václav Kubernát41378452018-06-06 16:29:40 +0200366
367#if __clang__
368#pragma GCC diagnostic pop
369#endif
Václav Kubernátb61336d2018-05-28 17:35:03 +0200370
Václav Kubernát07204242018-06-04 18:12:09 +0200371BOOST_SPIRIT_DEFINE(set)
Václav Kubernát812ee282018-08-30 17:10:03 +0200372BOOST_SPIRIT_DEFINE(commit)
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200373BOOST_SPIRIT_DEFINE(get)
Václav Kubernát11afac72018-07-18 14:59:53 +0200374BOOST_SPIRIT_DEFINE(ls)
Václav Kubernát6d791432018-10-25 16:00:35 +0200375BOOST_SPIRIT_DEFINE(discard)
Václav Kubernát0a2a2e82018-05-11 13:59:12 +0200376BOOST_SPIRIT_DEFINE(cd)
Václav Kubernátb61336d2018-05-28 17:35:03 +0200377BOOST_SPIRIT_DEFINE(create)
378BOOST_SPIRIT_DEFINE(delete_rule)
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100379BOOST_SPIRIT_DEFINE(describe)
Václav Kubernát054cc992019-02-21 14:23:52 +0100380BOOST_SPIRIT_DEFINE(help)
Václav Kubernát7160a132020-04-03 02:11:01 +0200381BOOST_SPIRIT_DEFINE(copy)
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200382BOOST_SPIRIT_DEFINE(move)
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200383BOOST_SPIRIT_DEFINE(dump)
Václav Kubernátaa4250a2020-07-22 00:02:23 +0200384BOOST_SPIRIT_DEFINE(prepare)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200385BOOST_SPIRIT_DEFINE(exec)
Václav Kubernát162165e2021-02-22 09:46:53 +0100386BOOST_SPIRIT_DEFINE(switch_rule)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200387BOOST_SPIRIT_DEFINE(cancel)
Václav Kubernátb61336d2018-05-28 17:35:03 +0200388BOOST_SPIRIT_DEFINE(command)
Václav Kubernát57272422019-02-08 12:48:24 +0100389BOOST_SPIRIT_DEFINE(createCommandSuggestions)