blob: 67cf792aa0f9c3bfe0702b448da07f8691b67c53 [file] [log] [blame]
/*
* Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
* Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
*
* Written by Václav Kubernát <kubervac@fit.cvut.cz>
*
*/
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "ast_commands.hpp"
#include "ast_handlers.hpp"
#include "common_parsers.hpp"
#include "leaf_data.hpp"
#include "path_parser.hpp"
#if BOOST_VERSION <= 107700
namespace boost::spirit::x3::traits {
// Backport https://github.com/boostorg/spirit/pull/702
// with instructions from https://github.com/boostorg/spirit/issues/701#issuecomment-946743099
template <typename... Types, typename T>
struct variant_find_substitute<boost::variant<Types...>, T>
{
using variant_type = boost::variant<Types...>;
typedef typename variant_type::types types;
typedef typename mpl::end<types>::type end;
typedef typename mpl::find<types, T>::type iter_1;
typedef typename
mpl::eval_if<
is_same<iter_1, end>,
mpl::find_if<types, traits::is_substitute<T, mpl::_1> >,
mpl::identity<iter_1>
>::type
iter;
typedef typename
mpl::eval_if<
is_same<iter, end>,
mpl::identity<T>,
mpl::deref<iter>
>::type
type;
};
}
#endif
#if __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-shift-op-parentheses"
#endif
namespace ascii = boost::spirit::x3::ascii;
struct ls_options_table : x3::symbols<LsOption> {
ls_options_table()
{
add
("--recursive", LsOption::Recursive);
}
} const ls_options;
auto const ls = x3::rule<ls_class, ls_>{"ls"} =
ls_::name >> *(space_separator >> ls_options) >> -(space_separator > (anyPath | (module >> "*")));
auto const cd = x3::rule<cd_class, cd_>{"cd"} =
cd_::name >> space_separator > cdPath;
#if BOOST_VERSION <= 107700
auto const create = x3::rule<create_class, create_>{"create"} =
create_::name >> space_separator >
(x3::eps >> presenceContainerPath |
x3::eps >> listInstancePath |
x3::eps >> leafListElementPath);
auto const delete_rule = x3::rule<delete_class, delete_>{"delete_rule"} =
delete_::name >> space_separator >
(x3::eps >> presenceContainerPath |
x3::eps >> listInstancePath |
x3::eps >> leafListElementPath |
x3::eps >> writableLeafPath);
#else
auto const create = x3::rule<create_class, create_>{"create"} =
create_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath);
auto const delete_rule = x3::rule<delete_class, delete_>{"delete_rule"} =
delete_::name >> space_separator > (presenceContainerPath | listInstancePath | leafListElementPath | writableLeafPath);
#endif
const auto dsTargetSuggestions = staticSuggestions({"running", "startup", "operational"});
struct ds_target_table : x3::symbols<DatastoreTarget> {
ds_target_table()
{
add
("operational", DatastoreTarget::Operational)
("startup", DatastoreTarget::Startup)
("running", DatastoreTarget::Running);
}
} const ds_target_table;
auto const get = x3::rule<get_class, get_>{"get"} =
get_::name
>> -(space_separator >> "-" > staticSuggestions({"-datastore"}) > "-datastore" > space_separator > dsTargetSuggestions > ds_target_table)
>> -(space_separator > getPath);
auto const set = x3::rule<set_class, set_>{"set"} =
set_::name >> space_separator > writableLeafPath > space_separator > leaf_data;
auto const commit = x3::rule<commit_class, commit_>{"commit"} =
commit_::name >> x3::attr(commit_());
auto const discard = x3::rule<discard_class, discard_>{"discard"} =
discard_::name >> x3::attr(discard_());
struct command_names_table : x3::symbols<decltype(help_::m_cmd)> {
command_names_table()
{
boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
add(commandNamesVisitor(cmd), decltype(help_::m_cmd)(cmd));
});
}
} const command_names;
auto const createCommandSuggestions = x3::rule<createCommandSuggestions_class, x3::unused_type>{"createCommandSuggestions"} =
x3::eps;
auto const help = x3::rule<help_class, help_>{"help"} =
help_::name > createCommandSuggestions >> -command_names;
struct datastore_symbol_table : x3::symbols<Datastore> {
datastore_symbol_table()
{
add
("running", Datastore::Running)
("startup", Datastore::Startup);
}
} const datastore;
const auto copy_source = x3::rule<class source, Datastore>{"source datastore"} = datastore;
const auto copy_destination = x3::rule<class source, Datastore>{"destination datastore"} = datastore;
const auto datastoreSuggestions = staticSuggestions({"running", "startup"});
struct copy_args : x3::parser<copy_args> {
using attribute_type = copy_;
template <typename It, typename Ctx, typename RCtx>
bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, copy_& attr) const
{
auto& parserContext = x3::get<parser_context_tag>(ctx);
auto iterBeforeDestination = begin;
auto save_iter = x3::eps[([&iterBeforeDestination](auto& ctx) { iterBeforeDestination = _where(ctx).begin(); })];
auto grammar = datastoreSuggestions > copy_source > space_separator > datastoreSuggestions > save_iter > copy_destination;
try {
grammar.parse(begin, end, ctx, rctx, attr);
} catch (x3::expectation_failure<It>& ex) {
using namespace std::string_literals;
parserContext.m_errorMsg = "Expected "s + ex.which() + " here:";
throw;
}
if (attr.m_source == attr.m_destination) {
begin = iterBeforeDestination; // Restoring the iterator here makes the error caret point to the second datastore
parserContext.m_errorMsg = "Source datastore and destination datastore can't be the same.";
return false;
}
return true;
}
} const copy_args;
auto const copy = x3::rule<copy_class, copy_>{"copy"} =
copy_::name > space_separator > copy_args;
auto const describe = x3::rule<describe_class, describe_>{"describe"} =
describe_::name >> space_separator > anyPath;
struct move_mode_table : x3::symbols<MoveMode> {
move_mode_table()
{
add
("after", MoveMode::After)
("before", MoveMode::Before)
("begin", MoveMode::Begin)
("end", MoveMode::End);
}
} const move_mode_table;
struct move_absolute_table : x3::symbols<yang::move::Absolute> {
move_absolute_table()
{
add
("begin", yang::move::Absolute::Begin)
("end", yang::move::Absolute::End);
}
} const move_absolute_table;
struct move_relative_table : x3::symbols<yang::move::Relative::Position> {
move_relative_table()
{
add
("before", yang::move::Relative::Position::Before)
("after", yang::move::Relative::Position::After);
}
} const move_relative_table;
struct move_args : x3::parser<move_args> {
using attribute_type = move_;
template <typename It, typename Ctx, typename RCtx>
bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, move_& attr) const
{
ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
dataPath_ movePath;
#if BOOST_VERSION <= 107700
auto movePathGrammar = x3::eps >> listInstancePath | x3::eps >> leafListElementPath;
#else
auto movePathGrammar = listInstancePath | leafListElementPath;
#endif
auto res = movePathGrammar.parse(begin, end, ctx, rctx, attr.m_source);
if (!res) {
parserContext.m_errorMsg = "Expected source path here:";
return false;
}
// Try absolute move first.
res = (space_separator >> move_absolute_table).parse(begin, end, ctx, rctx, attr.m_destination);
if (res) {
// Absolute move parsing succeeded, we don't need to parse anything else.
return true;
}
// If absolute move didn't succeed, try relative.
attr.m_destination = yang::move::Relative{};
res = (space_separator >> move_relative_table).parse(begin, end, ctx, rctx, std::get<yang::move::Relative>(attr.m_destination).m_position);
if (!res) {
parserContext.m_errorMsg = "Expected a move position (begin, end, before, after) here:";
return false;
}
if (std::holds_alternative<leafListElement_>(attr.m_source.m_nodes.back().m_suffix)) {
leaf_data_ value;
res = (space_separator >> leaf_data).parse(begin, end, ctx, rctx, value);
if (res) {
std::get<yang::move::Relative>(attr.m_destination).m_path = {{".", value}};
}
} else {
ListInstance listInstance;
// The source list instance will be stored inside the parser context path.
// The source list instance will be full data path (with keys included).
// However, m_tmpListPath is supposed to store a path with a list without the keys.
// So, I pop the last listElement_ (which has the keys) and put in a list_ (which doesn't have the keys).
// Example: /mod:cont/protocols[name='ftp'] gets turned into /mod:cont/protocols
parserContext.m_tmpListPath = parserContext.currentDataPath();
parserContext.m_tmpListPath.m_nodes.pop_back();
auto list = list_{std::get<listElement_>(attr.m_source.m_nodes.back().m_suffix).m_name};
parserContext.m_tmpListPath.m_nodes.emplace_back(attr.m_source.m_nodes.back().m_prefix, list);
res = (space_separator >> listSuffix).parse(begin, end, ctx, rctx, listInstance);
if (res) {
std::get<yang::move::Relative>(attr.m_destination).m_path = listInstance;
}
}
if (!res) {
parserContext.m_errorMsg = "Expected a destination here:";
}
return res;
}
} const move_args;
auto const move = x3::rule<move_class, move_>{"move"} =
move_::name >> space_separator >> move_args;
struct format_table : x3::symbols<DataFormat> {
format_table()
{
add
("xml", DataFormat::Xml)
("json", DataFormat::Json);
}
} const format_table;
struct dump_args : x3::parser<dump_args> {
using attribute_type = dump_;
template <typename It, typename Ctx, typename RCtx>
bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, dump_& attr) const
{
ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
parserContext.m_suggestions = {{"xml"}, {"json"}};
parserContext.m_completionIterator = begin;
auto res = format_table.parse(begin, end, ctx, rctx, attr);
if (!res) {
parserContext.m_errorMsg = "Expected a data format (xml, json) here:";
}
return res;
}
} const dump_args;
auto const prepare = x3::rule<prepare_class, prepare_>{"prepare"} =
prepare_::name > space_separator > as<dataPath_>[RpcActionPath<AllowInput::Yes>{}];
auto const exec = x3::rule<exec_class, exec_>{"exec"} =
exec_::name > -(space_separator > -as<dataPath_>[RpcActionPath<AllowInput::No>{}]);
auto const switch_rule = x3::rule<switch_class, switch_>{"switch"} =
switch_::name > space_separator > dsTargetSuggestions > ds_target_table;
auto const cancel = x3::rule<cancel_class, cancel_>{"cancel"} =
cancel_::name >> x3::attr(cancel_{});
auto const dump = x3::rule<dump_class, dump_>{"dump"} =
dump_::name > space_separator >> dump_args;
auto const quit = x3::rule<quit_class, quit_>{"quit"} =
quit_::name >> x3::attr(quit_{});
auto const command = x3::rule<command_class, command_>{"command"} =
#if BOOST_VERSION <= 107800
x3::eps >>
#endif
-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;
#if __clang__
#pragma GCC diagnostic pop
#endif