blob: 3cc74f2461581048998a46b6962e89c2001927bd [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át60d6f292018-05-25 09:45:32 +020018
Václav Kubernát6d791432018-10-25 16:00:35 +020019x3::rule<discard_class, discard_> const discard = "discard";
Václav Kubernát11afac72018-07-18 14:59:53 +020020x3::rule<ls_class, ls_> const ls = "ls";
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020021x3::rule<cd_class, cd_> const cd = "cd";
Václav Kubernát07204242018-06-04 18:12:09 +020022x3::rule<set_class, set_> const set = "set";
Václav Kubernátb6ff0b62018-08-30 16:14:53 +020023x3::rule<get_class, get_> const get = "get";
Václav Kubernátb61336d2018-05-28 17:35:03 +020024x3::rule<create_class, create_> const create = "create";
25x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
Václav Kubernát812ee282018-08-30 17:10:03 +020026x3::rule<commit_class, commit_> const commit = "commit";
Václav Kubernát9cfcd872020-02-18 12:34:02 +010027x3::rule<describe_class, describe_> const describe = "describe";
Václav Kubernát054cc992019-02-21 14:23:52 +010028x3::rule<help_class, help_> const help = "help";
Václav Kubernát7160a132020-04-03 02:11:01 +020029x3::rule<copy_class, copy_> const copy = "copy";
Václav Kubernátbf65dd72020-05-28 02:32:31 +020030x3::rule<move_class, move_> const move = "move";
Václav Kubernátb61336d2018-05-28 17:35:03 +020031x3::rule<command_class, command_> const command = "command";
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020032
Václav Kubernát57272422019-02-08 12:48:24 +010033x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
Václav Kubernát5c75b252018-10-10 18:33:47 +020034
Václav Kubernát41378452018-06-06 16:29:40 +020035#if __clang__
36#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
38#endif
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020039
Václav Kubernát509ce652019-05-29 19:46:44 +020040namespace ascii = boost::spirit::x3::ascii;
41
Václav Kubernáte7d4aea2018-09-11 18:15:48 +020042struct ls_options_table : x3::symbols<LsOption> {
43 ls_options_table()
44 {
Václav Kubernátbf083ec2019-02-19 13:58:09 +010045 add
46 ("--recursive", LsOption::Recursive);
Václav Kubernáte7d4aea2018-09-11 18:15:48 +020047 }
48} const ls_options;
49
Václav Kubernát11afac72018-07-18 14:59:53 +020050auto const ls_def =
Václav Kubernát4a58ce62020-05-14 17:58:10 +020051 ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (anyPath | (module >> "*")));
Václav Kubernát11afac72018-07-18 14:59:53 +020052
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020053auto const cd_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +010054 cd_::name >> space_separator > dataPath;
Václav Kubernát0a2a2e82018-05-11 13:59:12 +020055
Václav Kubernátb61336d2018-05-28 17:35:03 +020056auto const create_def =
Václav Kubernát5b8a8f32020-05-20 00:57:22 +020057 create_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
Václav Kubernátb61336d2018-05-28 17:35:03 +020058
59auto const delete_rule_def =
Jan Kundrátb7206ad2020-06-18 21:08:14 +020060 delete_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath | writableLeafPath);
Václav Kubernát07204242018-06-04 18:12:09 +020061
Václav Kubernátb6ff0b62018-08-30 16:14:53 +020062auto const get_def =
Václav Kubernát9456b5c2019-10-02 21:14:52 +020063 get_::name >> -(space_separator >> ((dataPathListEnd | dataPath) | (module >> "*")));
Václav Kubernátb6ff0b62018-08-30 16:14:53 +020064
Václav Kubernát07204242018-06-04 18:12:09 +020065auto const set_def =
Václav Kubernátabf52802020-05-19 01:31:17 +020066 set_::name >> space_separator > writableLeafPath > space_separator > leaf_data;
Václav Kubernátb61336d2018-05-28 17:35:03 +020067
Václav Kubernát812ee282018-08-30 17:10:03 +020068auto const commit_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +010069 commit_::name >> x3::attr(commit_());
Václav Kubernát812ee282018-08-30 17:10:03 +020070
Václav Kubernát6d791432018-10-25 16:00:35 +020071auto const discard_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +010072 discard_::name >> x3::attr(discard_());
Václav Kubernát6d791432018-10-25 16:00:35 +020073
Václav Kubernát054cc992019-02-21 14:23:52 +010074struct command_names_table : x3::symbols<decltype(help_::m_cmd)> {
75 command_names_table()
76 {
77 boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
Václav Kubernát672889c2020-05-27 04:11:36 +020078 add(commandNamesVisitor(cmd), decltype(help_::m_cmd)(cmd));
Václav Kubernát054cc992019-02-21 14:23:52 +010079 });
80 }
81} const command_names;
82
83auto const help_def =
84 help_::name > createCommandSuggestions >> -command_names;
85
Václav Kubernát7160a132020-04-03 02:11:01 +020086struct datastore_symbol_table : x3::symbols<Datastore> {
87 datastore_symbol_table()
88 {
89 add
90 ("running", Datastore::Running)
91 ("startup", Datastore::Startup);
92 }
93} const datastore;
94
95auto copy_source = x3::rule<class source, Datastore>{"source datastore"} = datastore;
96auto copy_destination = x3::rule<class source, Datastore>{"destination datastore"} = datastore;
97
98const auto datastoreSuggestions = x3::eps[([](auto& ctx) {
99 auto& parserContext = x3::get<parser_context_tag>(ctx);
100 parserContext.m_suggestions = {Completion{"running", " "}, Completion{"startup", " "}};
101 parserContext.m_completionIterator = _where(ctx).begin();
102})];
103
104struct copy_args : x3::parser<copy_args> {
105 using attribute_type = copy_;
106 template <typename It, typename Ctx, typename RCtx>
107 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, copy_& attr) const
108 {
109 auto& parserContext = x3::get<parser_context_tag>(ctx);
110 auto iterBeforeDestination = begin;
111 auto save_iter = x3::no_skip[x3::eps[([&iterBeforeDestination](auto& ctx) {iterBeforeDestination = _where(ctx).begin();})]];
112 auto grammar = datastoreSuggestions > copy_source > space_separator > datastoreSuggestions > save_iter > copy_destination;
113
114 try {
115 grammar.parse(begin, end, ctx, rctx, attr);
116 } catch (x3::expectation_failure<It>& ex) {
117 using namespace std::string_literals;
118 parserContext.m_errorMsg = "Expected "s + ex.which() + " here:";
119 throw;
120 }
121
122 if (attr.m_source == attr.m_destination) {
123 begin = iterBeforeDestination; // Restoring the iterator here makes the error caret point to the second datastore
124 parserContext.m_errorMsg = "Source datastore and destination datastore can't be the same.";
125 return false;
126 }
127
128 return true;
129 }
130} copy_args;
131
132auto const copy_def =
133 copy_::name > space_separator > copy_args;
134
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100135auto const describe_def =
Václav Kubernát4618fa42020-05-07 01:33:19 +0200136 describe_::name >> space_separator > (dataPathListEnd | anyPath);
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100137
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200138struct mode_table : x3::symbols<MoveMode> {
139 mode_table()
140 {
141 add
142 ("after", MoveMode::After)
143 ("before", MoveMode::Before)
144 ("begin", MoveMode::Begin)
145 ("end", MoveMode::End);
146 }
147} const mode_table;
148
149struct move_absolute_table : x3::symbols<yang::move::Absolute> {
150 move_absolute_table()
151 {
152 add
153 ("begin", yang::move::Absolute::Begin)
154 ("end", yang::move::Absolute::End);
155 }
156} const move_absolute_table;
157
158struct move_relative_table : x3::symbols<yang::move::Relative::Position> {
159 move_relative_table()
160 {
161 add
162 ("before", yang::move::Relative::Position::Before)
163 ("after", yang::move::Relative::Position::After);
164 }
165} const move_relative_table;
166
167struct move_args : x3::parser<move_args> {
168 using attribute_type = move_;
169 template <typename It, typename Ctx, typename RCtx>
170 bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, move_& attr) const
171 {
172 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
173 dataPath_ movePath;
174 auto movePathGrammar = listInstancePath | leafListElementPath;
175 auto res = movePathGrammar.parse(begin, end, ctx, rctx, attr.m_source);
176 if (!res) {
177 parserContext.m_errorMsg = "Expected source path here:";
178 return false;
179 }
180
181 // Try absolute move first.
182 res = (space_separator >> move_absolute_table).parse(begin, end, ctx, rctx, attr.m_destination);
183 if (res) {
184 // Absolute move parsing succeeded, we don't need to parse anything else.
185 return true;
186 }
187
188 // If absolute move didn't succeed, try relative.
189 attr.m_destination = yang::move::Relative{};
190 res = (space_separator >> move_relative_table).parse(begin, end, ctx, rctx, std::get<yang::move::Relative>(attr.m_destination).m_position);
191
192 if (!res) {
193 parserContext.m_errorMsg = "Expected a move position (begin, end, before, after) here:";
194 return false;
195 }
196
197 if (std::holds_alternative<leafListElement_>(attr.m_source.m_nodes.back().m_suffix)) {
198 leaf_data_ value;
199 res = (space_separator >> leaf_data).parse(begin, end, ctx, rctx, value);
200 if (res) {
201 std::get<yang::move::Relative>(attr.m_destination).m_path = {{".", value}};
202 }
203 } else {
204 ListInstance listInstance;
205 // The source list instance will be stored inside the parser context path.
206 // The source list instance will be full data path (with keys included).
207 // However, m_tmpListPath is supposed to store a path with a list without the keys.
208 // So, I pop the last listElement_ (which has the keys) and put in a list_ (which doesn't have the keys).
209 // Example: /mod:cont/protocols[name='ftp'] gets turned into /mod:cont/protocols
210 parserContext.m_tmpListPath = parserContext.currentDataPath();
211 parserContext.m_tmpListPath.m_nodes.pop_back();
212 auto list = list_{std::get<listElement_>(attr.m_source.m_nodes.back().m_suffix).m_name};
213 parserContext.m_tmpListPath.m_nodes.push_back(dataNode_{attr.m_source.m_nodes.back().m_prefix, list});
214
215 res = (space_separator >> listSuffix).parse(begin, end, ctx, rctx, listInstance);
216 if (res) {
217 std::get<yang::move::Relative>(attr.m_destination).m_path = listInstance;
218 }
219 }
220
221 if (!res) {
222 parserContext.m_errorMsg = "Expected a destination here:";
223 }
224
225 return res;
226 }
227} const move_args;
228
229auto const move_def =
230 move_::name >> space_separator >> move_args;
231
Václav Kubernát57272422019-02-08 12:48:24 +0100232auto const createCommandSuggestions_def =
Václav Kubernátbf083ec2019-02-19 13:58:09 +0100233 x3::eps;
Václav Kubernát57272422019-02-08 12:48:24 +0100234
Václav Kubernátb61336d2018-05-28 17:35:03 +0200235auto const command_def =
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200236 createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move];
Václav Kubernát41378452018-06-06 16:29:40 +0200237
238#if __clang__
239#pragma GCC diagnostic pop
240#endif
Václav Kubernátb61336d2018-05-28 17:35:03 +0200241
Václav Kubernát07204242018-06-04 18:12:09 +0200242BOOST_SPIRIT_DEFINE(set)
Václav Kubernát812ee282018-08-30 17:10:03 +0200243BOOST_SPIRIT_DEFINE(commit)
Václav Kubernátb6ff0b62018-08-30 16:14:53 +0200244BOOST_SPIRIT_DEFINE(get)
Václav Kubernát11afac72018-07-18 14:59:53 +0200245BOOST_SPIRIT_DEFINE(ls)
Václav Kubernát6d791432018-10-25 16:00:35 +0200246BOOST_SPIRIT_DEFINE(discard)
Václav Kubernát0a2a2e82018-05-11 13:59:12 +0200247BOOST_SPIRIT_DEFINE(cd)
Václav Kubernátb61336d2018-05-28 17:35:03 +0200248BOOST_SPIRIT_DEFINE(create)
249BOOST_SPIRIT_DEFINE(delete_rule)
Václav Kubernát9cfcd872020-02-18 12:34:02 +0100250BOOST_SPIRIT_DEFINE(describe)
Václav Kubernát054cc992019-02-21 14:23:52 +0100251BOOST_SPIRIT_DEFINE(help)
Václav Kubernát7160a132020-04-03 02:11:01 +0200252BOOST_SPIRIT_DEFINE(copy)
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200253BOOST_SPIRIT_DEFINE(move)
Václav Kubernátb61336d2018-05-28 17:35:03 +0200254BOOST_SPIRIT_DEFINE(command)
Václav Kubernát57272422019-02-08 12:48:24 +0100255BOOST_SPIRIT_DEFINE(createCommandSuggestions)