blob: a70cce01a9810daac1f15b29274176e247c64f7b [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át68c89422021-10-20 15:32:20 +020018#if BOOST_VERSION <= 107700
Václav Kubernátf5f6d242022-04-07 01:06:19 +020019namespace boost::spirit::x3::traits {
Václav Kubernát68c89422021-10-20 15:32:20 +020020 // Backport https://github.com/boostorg/spirit/pull/702
21 // with instructions from https://github.com/boostorg/spirit/issues/701#issuecomment-946743099
22 template <typename... Types, typename T>
23 struct variant_find_substitute<boost::variant<Types...>, T>
24 {
25 using variant_type = boost::variant<Types...>;
26
27 typedef typename variant_type::types types;
28 typedef typename mpl::end<types>::type end;
29
30 typedef typename mpl::find<types, T>::type iter_1;
31
32 typedef typename
33 mpl::eval_if<
34 is_same<iter_1, end>,
35 mpl::find_if<types, traits::is_substitute<T, mpl::_1> >,
36 mpl::identity<iter_1>
37 >::type
38 iter;
39
40 typedef typename
41 mpl::eval_if<
42 is_same<iter, end>,
43 mpl::identity<T>,
44 mpl::deref<iter>
45 >::type
46 type;
47 };
48}
49#endif
Václav Kubernát60d6f292018-05-25 09:45:32 +020050
Václav Kubernát5c75b252018-10-10 18:33:47 +020051
Václav Kubernát41378452018-06-06 16:29:40 +020052#if __clang__
53#pragma GCC diagnostic push
54#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
55#endif
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020056
Václav Kubernát509ce652019-05-29 19:46:44 +020057namespace ascii = boost::spirit::x3::ascii;
58
Václav Kubernáte7d4aea2018-09-11 18:15:48 +020059struct ls_options_table : x3::symbols<LsOption> {
60 ls_options_table()
61 {
Václav Kubernátbf083ec2019-02-19 13:58:09 +010062 add
63 ("--recursive", LsOption::Recursive);
Václav Kubernáte7d4aea2018-09-11 18:15:48 +020064 }
65} const ls_options;
66
Václav Kubernátf20f6f92022-04-27 12:13:13 +020067auto const ls = x3::rule<struct ls_class, ls_>{"ls"} =
Václav Kubernát76bb8c22022-01-19 01:32:31 +010068 ls_::name >> *(space_separator >> ls_options) >> -(space_separator > (anyPath | (module >> "*")));
Václav Kubernát11afac72018-07-18 14:59:53 +020069
Václav Kubernát52b90222022-04-27 11:29:54 +020070auto const cd = x3::rule<cd_class, cd_>{"cd"} =
Václav Kubernáte7248b22020-06-26 15:38:59 +020071 cd_::name >> space_separator > cdPath;
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020072
Václav Kubernát38175022021-11-04 14:58:57 +010073#if BOOST_VERSION <= 107700
Václav Kubernát52b90222022-04-27 11:29:54 +020074auto const create = x3::rule<create_class, create_>{"create"} =
Václav Kubernát38175022021-11-04 14:58:57 +010075 create_::name >> space_separator >
76 (x3::eps >> presenceContainerPath |
77 x3::eps >> listInstancePath |
78 x3::eps >> leafListElementPath);
79
Václav Kubernát52b90222022-04-27 11:29:54 +020080auto const delete_rule = x3::rule<delete_class, delete_>{"delete_rule"} =
Václav Kubernát38175022021-11-04 14:58:57 +010081 delete_::name >> space_separator >
82 (x3::eps >> presenceContainerPath |
83 x3::eps >> listInstancePath |
84 x3::eps >> leafListElementPath |
85 x3::eps >> writableLeafPath);
86#else
Václav Kubernát52b90222022-04-27 11:29:54 +020087auto const create = x3::rule<create_class, create_>{"create"} =
Václav Kubernát5b8a8f32020-05-20 00:57:22 +020088 create_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
Václav Kubernátb61336d2018-05-28 17:35:03 +020089
Václav Kubernát52b90222022-04-27 11:29:54 +020090auto const delete_rule = x3::rule<delete_class, delete_>{"delete_rule"} =
Jan Kundrátb7206ad2020-06-18 21:08:14 +020091 delete_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath | writableLeafPath);
Václav Kubernát38175022021-11-04 14:58:57 +010092#endif
Václav Kubernát07204242018-06-04 18:12:09 +020093
Václav Kubernáte066fe22022-04-06 00:32:26 +020094const auto dsTargetSuggestions = staticSuggestions({"running", "startup", "operational"});
95
96struct ds_target_table : x3::symbols<DatastoreTarget> {
97 ds_target_table()
98 {
99 add
100 ("operational", DatastoreTarget::Operational)
101 ("startup", DatastoreTarget::Startup)
102 ("running", DatastoreTarget::Running);
103 }
104} const ds_target_table;
105
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200106auto const get = x3::rule<struct get_class, get_>{"get"} =
Václav Kubernáte066fe22022-04-06 00:32:26 +0200107 get_::name
108 >> -(space_separator >> "-" > staticSuggestions({"-datastore"}) > "-datastore" > space_separator > dsTargetSuggestions > ds_target_table)
Václav Kubernát76bb8c22022-01-19 01:32:31 +0100109 >> -(space_separator > getPath);
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200110
Václav Kubernát52b90222022-04-27 11:29:54 +0200111auto const set = x3::rule<set_class, set_>{"set"} =
Václav Kubernátabf52802020-05-19 01:31:17 +0200112 set_::name >> space_separator > writableLeafPath > space_separator > leaf_data;
Václav Kubernátb61336d2018-05-28 17:35:03 +0200113
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200114auto const commit = x3::rule<struct commit_class, commit_>{"commit"} =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100115 commit_::name >> x3::attr(commit_());
Václav Kubernát812ee282018-08-30 17:10:03 +0200116
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200117auto const discard = x3::rule<struct discard_class, discard_>{"discard"} =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100118 discard_::name >> x3::attr(discard_());
Václav Kubernát6d791432018-10-25 16:00:35 +0200119
Václav Kubernát054cc992019-02-21 14:23:52 +0100120struct command_names_table : x3::symbols<decltype(help_::m_cmd)> {
121 command_names_table()
122 {
123 boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
Václav Kubernát672889c2020-05-27 04:11:36 +0200124 add(commandNamesVisitor(cmd), decltype(help_::m_cmd)(cmd));
Václav Kubernát054cc992019-02-21 14:23:52 +0100125 });
126 }
127} const command_names;
128
Václav Kubernát52b90222022-04-27 11:29:54 +0200129auto const createCommandSuggestions = x3::rule<createCommandSuggestions_class, x3::unused_type>{"createCommandSuggestions"} =
130 x3::eps;
131
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200132auto const help = x3::rule<struct help_class, help_>{"help"} =
aleskucera@cesnet.cz9babcca2023-10-27 23:29:48 +0200133 help_::name >> -(space_separator >> createCommandSuggestions > -(command_names));
Václav Kubernát054cc992019-02-21 14:23:52 +0100134
Václav Kubernát7160a132020-04-03 02:11:01 +0200135struct datastore_symbol_table : x3::symbols<Datastore> {
136 datastore_symbol_table()
137 {
138 add
139 ("running", Datastore::Running)
140 ("startup", Datastore::Startup);
141 }
142} const datastore;
143
Václav Kubernát60b48462022-04-27 12:23:15 +0200144const auto copy_source = x3::rule<struct source, Datastore>{"source datastore"} = datastore;
145const auto copy_destination = x3::rule<struct source, Datastore>{"destination datastore"} = datastore;
Václav Kubernát7160a132020-04-03 02:11:01 +0200146
Václav Kubernát7ff22072022-04-05 10:22:40 +0200147const auto datastoreSuggestions = staticSuggestions({"running", "startup"});
Václav Kubernát7160a132020-04-03 02:11:01 +0200148
149struct copy_args : x3::parser<copy_args> {
150 using attribute_type = copy_;
151 template <typename It, typename Ctx, typename RCtx>
152 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, copy_& attr) const
153 {
154 auto& parserContext = x3::get<parser_context_tag>(ctx);
155 auto iterBeforeDestination = begin;
Václav Kubernát76bb8c22022-01-19 01:32:31 +0100156 auto save_iter = x3::eps[([&iterBeforeDestination](auto& ctx) { iterBeforeDestination = _where(ctx).begin(); })];
Václav Kubernát7160a132020-04-03 02:11:01 +0200157 auto grammar = datastoreSuggestions > copy_source > space_separator > datastoreSuggestions > save_iter > copy_destination;
158
159 try {
160 grammar.parse(begin, end, ctx, rctx, attr);
161 } catch (x3::expectation_failure<It>& ex) {
162 using namespace std::string_literals;
163 parserContext.m_errorMsg = "Expected "s + ex.which() + " here:";
164 throw;
165 }
166
167 if (attr.m_source == attr.m_destination) {
168 begin = iterBeforeDestination; // Restoring the iterator here makes the error caret point to the second datastore
169 parserContext.m_errorMsg = "Source datastore and destination datastore can't be the same.";
170 return false;
171 }
172
173 return true;
174 }
Václav Kubernát21fc5e22020-11-26 04:28:27 +0100175} const copy_args;
Václav Kubernát7160a132020-04-03 02:11:01 +0200176
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200177auto const copy = x3::rule<struct copy_class, copy_>{"copy"} =
Václav Kubernát7160a132020-04-03 02:11:01 +0200178 copy_::name > space_separator > copy_args;
179
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200180auto const describe = x3::rule<struct describe_class, describe_>{"describe"} =
Václav Kubernát3200b862020-12-03 02:34:55 +0100181 describe_::name >> space_separator > anyPath;
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100182
Václav Kubernát162165e2021-02-22 09:46:53 +0100183struct move_mode_table : x3::symbols<MoveMode> {
184 move_mode_table()
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200185 {
186 add
187 ("after", MoveMode::After)
188 ("before", MoveMode::Before)
189 ("begin", MoveMode::Begin)
190 ("end", MoveMode::End);
191 }
Václav Kubernát162165e2021-02-22 09:46:53 +0100192} const move_mode_table;
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200193
194struct move_absolute_table : x3::symbols<yang::move::Absolute> {
195 move_absolute_table()
196 {
197 add
198 ("begin", yang::move::Absolute::Begin)
199 ("end", yang::move::Absolute::End);
200 }
201} const move_absolute_table;
202
203struct move_relative_table : x3::symbols<yang::move::Relative::Position> {
204 move_relative_table()
205 {
206 add
207 ("before", yang::move::Relative::Position::Before)
208 ("after", yang::move::Relative::Position::After);
209 }
210} const move_relative_table;
211
212struct move_args : x3::parser<move_args> {
213 using attribute_type = move_;
214 template <typename It, typename Ctx, typename RCtx>
215 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, move_& attr) const
216 {
217 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
218 dataPath_ movePath;
Václav Kubernát38175022021-11-04 14:58:57 +0100219#if BOOST_VERSION <= 107700
220 auto movePathGrammar = x3::eps >> listInstancePath | x3::eps >> leafListElementPath;
221#else
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200222 auto movePathGrammar = listInstancePath | leafListElementPath;
Václav Kubernát38175022021-11-04 14:58:57 +0100223#endif
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200224 auto res = movePathGrammar.parse(begin, end, ctx, rctx, attr.m_source);
225 if (!res) {
226 parserContext.m_errorMsg = "Expected source path here:";
227 return false;
228 }
229
230 // Try absolute move first.
231 res = (space_separator >> move_absolute_table).parse(begin, end, ctx, rctx, attr.m_destination);
232 if (res) {
233 // Absolute move parsing succeeded, we don't need to parse anything else.
234 return true;
235 }
236
237 // If absolute move didn't succeed, try relative.
238 attr.m_destination = yang::move::Relative{};
239 res = (space_separator >> move_relative_table).parse(begin, end, ctx, rctx, std::get<yang::move::Relative>(attr.m_destination).m_position);
240
241 if (!res) {
242 parserContext.m_errorMsg = "Expected a move position (begin, end, before, after) here:";
243 return false;
244 }
245
246 if (std::holds_alternative<leafListElement_>(attr.m_source.m_nodes.back().m_suffix)) {
247 leaf_data_ value;
248 res = (space_separator >> leaf_data).parse(begin, end, ctx, rctx, value);
249 if (res) {
250 std::get<yang::move::Relative>(attr.m_destination).m_path = {{".", value}};
251 }
252 } else {
253 ListInstance listInstance;
254 // The source list instance will be stored inside the parser context path.
255 // The source list instance will be full data path (with keys included).
256 // However, m_tmpListPath is supposed to store a path with a list without the keys.
257 // So, I pop the last listElement_ (which has the keys) and put in a list_ (which doesn't have the keys).
258 // Example: /mod:cont/protocols[name='ftp'] gets turned into /mod:cont/protocols
259 parserContext.m_tmpListPath = parserContext.currentDataPath();
260 parserContext.m_tmpListPath.m_nodes.pop_back();
261 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 +0200262 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 +0200263
264 res = (space_separator >> listSuffix).parse(begin, end, ctx, rctx, listInstance);
265 if (res) {
266 std::get<yang::move::Relative>(attr.m_destination).m_path = listInstance;
267 }
268 }
269
270 if (!res) {
271 parserContext.m_errorMsg = "Expected a destination here:";
272 }
273
274 return res;
275 }
276} const move_args;
277
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200278auto const move = x3::rule<struct move_class, move_>{"move"} =
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200279 move_::name >> space_separator >> move_args;
280
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200281struct format_table : x3::symbols<DataFormat> {
282 format_table()
283 {
284 add
285 ("xml", DataFormat::Xml)
286 ("json", DataFormat::Json);
287 }
288} const format_table;
289
Václav Kubernát3b5e6522020-06-26 18:36:09 +0200290struct dump_args : x3::parser<dump_args> {
291 using attribute_type = dump_;
292 template <typename It, typename Ctx, typename RCtx>
293 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, dump_& attr) const
294 {
295 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
296 parserContext.m_suggestions = {{"xml"}, {"json"}};
297 parserContext.m_completionIterator = begin;
298 auto res = format_table.parse(begin, end, ctx, rctx, attr);
299 if (!res) {
300 parserContext.m_errorMsg = "Expected a data format (xml, json) here:";
301 }
302 return res;
303 }
304} const dump_args;
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200305
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200306auto const prepare = x3::rule<struct prepare_class, prepare_>{"prepare"} =
Václav Kubernátd8408e02020-12-02 05:13:27 +0100307 prepare_::name > space_separator > as<dataPath_>[RpcActionPath<AllowInput::Yes>{}];
Václav Kubernáte7248b22020-06-26 15:38:59 +0200308
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200309auto const exec = x3::rule<struct exec_class, exec_>{"exec"} =
Václav Kubernátaff1f492021-02-17 10:56:28 +0100310 exec_::name > -(space_separator > -as<dataPath_>[RpcActionPath<AllowInput::No>{}]);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200311
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200312auto const switch_rule = x3::rule<struct switch_class, switch_>{"switch"} =
Václav Kubernát7ff22072022-04-05 10:22:40 +0200313 switch_::name > space_separator > dsTargetSuggestions > ds_target_table;
Václav Kubernát162165e2021-02-22 09:46:53 +0100314
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200315auto const cancel = x3::rule<struct cancel_class, cancel_>{"cancel"} =
Václav Kubernáte7248b22020-06-26 15:38:59 +0200316 cancel_::name >> x3::attr(cancel_{});
317
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200318auto const dump = x3::rule<struct dump_class, dump_>{"dump"} =
Václav Kubernát3b5e6522020-06-26 18:36:09 +0200319 dump_::name > space_separator >> dump_args;
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200320
Václav Kubernátf20f6f92022-04-27 12:13:13 +0200321auto const quit = x3::rule<struct quit_class, quit_>{"quit"} =
Petr Gotthard1c76dea2022-04-13 17:56:58 +0200322 quit_::name >> x3::attr(quit_{});
323
Václav Kubernát52b90222022-04-27 11:29:54 +0200324auto const command = x3::rule<command_class, command_>{"command"} =
Václav Kubernát5b8e2822022-01-14 03:19:54 +0100325#if BOOST_VERSION <= 107800
Václav Kubernátf9533c22021-11-04 15:13:30 +0100326 x3::eps >>
327#endif
Václav Kubernát76bb8c22022-01-19 01:32:31 +0100328 -space_separator >> createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel | switch_rule | quit] >> -space_separator;
Václav Kubernát41378452018-06-06 16:29:40 +0200329
330#if __clang__
331#pragma GCC diagnostic pop
332#endif