blob: bbacbea8df8570e70c51f95da5a64678d9fe32e2 [file] [log] [blame]
/*
* Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
*
* Written by Václav Kubernát <kubernat@cesnet.cz>
*
*/
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "ast_values.hpp"
#include "ast_handlers.hpp"
#include "common_parsers.hpp"
#include "schema.hpp"
namespace x3 = boost::spirit::x3;
template <yang::LeafDataTypes TYPE>
struct leaf_data_class;
template <yang::LeafDataTypes TYPE>
struct createSetSuggestions_class {
std::set<std::string> getSuggestions(const ParserContext& ctx, const Schema& schema) const;
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
{
auto& parserContext = x3::get<parser_context_tag>(context);
const Schema& schema = parserContext.m_schema;
// Only generate completions if the type is correct so that we don't
// overwrite some other completions.
if (schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node) == TYPE) {
parserContext.m_completionIterator = begin;
auto suggestions = getSuggestions(parserContext, schema);
std::set<Completion> res;
std::transform(suggestions.begin(), suggestions.end(), std::inserter(res, res.end()), [](auto it) { return Completion{it}; });
parserContext.m_suggestions = res;
}
}
};
template <>
struct leaf_data_class<yang::LeafDataTypes::Enum> {
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
{
if (_pass(context) == false)
return;
auto& parserContext = x3::get<parser_context_tag>(context);
auto& schema = parserContext.m_schema;
if (!schema.leafEnumHasValue(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node, ast.m_value)) {
_pass(context) = false;
parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
for (const auto& it : schema.enumValues(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node)) {
parserContext.m_errorMsg += " " + it;
}
}
}
};
template <>
struct leaf_data_class<yang::LeafDataTypes::IdentityRef> {
template <typename T, typename Iterator, typename Context>
void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
{
auto& parserContext = x3::get<parser_context_tag>(context);
auto& schema = parserContext.m_schema;
ModuleValuePair pair;
if (ast.m_prefix) {
pair.first = ast.m_prefix.get().m_name;
}
pair.second = ast.m_value;
if (!schema.leafIdentityIsValid(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node, pair)) {
_pass(context) = false;
}
}
};
x3::rule<leaf_data_class<yang::LeafDataTypes::Enum>, enum_> const leaf_data_enum = "leaf_data_enum";
x3::rule<leaf_data_class<yang::LeafDataTypes::IdentityRef>, identityRef_> const leaf_data_identityRef = "leaf_data_identityRef";
x3::rule<struct leaf_data_class<yang::LeafDataTypes::Binary>, binary_> const leaf_data_binary = "leaf_data_binary";
x3::rule<struct leaf_data_class<yang::LeafDataTypes::Decimal>, double> const leaf_data_decimal = "leaf_data_decimal";
x3::rule<struct leaf_data_class<yang::LeafDataTypes::String>, std::string> const leaf_data_string = "leaf_data_string";
x3::rule<struct leaf_data_class_binary, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
x3::rule<struct leaf_data_identityRef_data_class, identityRef_> const leaf_data_identityRef_data = "leaf_data_identityRef_data";
x3::rule<createSetSuggestions_class<yang::LeafDataTypes::Enum>, x3::unused_type> const createEnumSuggestions = "createEnumSuggestions";
x3::rule<createSetSuggestions_class<yang::LeafDataTypes::IdentityRef>, x3::unused_type> const createIdentitySuggestions = "createIdentitySuggestions";
using x3::char_;
auto const createEnumSuggestions_def =
x3::eps;
auto const leaf_data_enum_def =
createEnumSuggestions >> +char_;
struct bool_symbol_table : x3::symbols<bool> {
bool_symbol_table()
{
add
("true", true)
("false", false);
}
} const bool_symbols;
auto const leaf_data_string_def =
'\'' >> *(char_-'\'') >> '\'' |
'\"' >> *(char_-'\"') >> '\"';
// This intermediate rule is neccessary for coercing to std::string.
// TODO: check if I can do the coercing right in the grammar with `as{}` from
// https://github.com/boostorg/spirit/issues/530#issuecomment-584836532
// This would shave off some more lines.
auto const leaf_data_binary_data_def =
+(x3::alnum | char_('+') | char_('/')) >> -char_('=') >> -char_('=');
auto const leaf_data_binary_def =
leaf_data_binary_data;
auto const leaf_data_identityRef_data_def =
-module >> node_identifier;
auto const createIdentitySuggestions_def =
x3::eps;
auto const leaf_data_identityRef_def =
createIdentitySuggestions >> leaf_data_identityRef_data;
struct LeafData : x3::parser<LeafData> {
using attribute_type = leaf_data_;
// TODO: Can this be placed in a .cpp file?
template <typename It, typename Ctx, typename RCtx, typename Attr>
bool parse(It& first, It last, Ctx const& ctx, RCtx& rctx, Attr& attr) const
{
ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
const Schema& schema = parserContext.m_schema;
auto type = schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node);
std::function<bool(yang::LeafDataTypes)> parse_impl = [&](auto type) {
switch (type) {
case yang::LeafDataTypes::Binary:
return leaf_data_binary.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Bool:
return bool_symbols.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Decimal:
return x3::double_.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Uint8:
return x3::uint8.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Uint16:
return x3::uint16.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Uint32:
return x3::uint32.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Uint64:
return x3::uint64.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Int8:
return x3::int8.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Int16:
return x3::int16.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Int32:
return x3::int32.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Int64:
return x3::int64.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::String:
return leaf_data_string.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::Enum:
return leaf_data_enum.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::IdentityRef:
return leaf_data_identityRef.parse(first, last, ctx, rctx, attr);
case yang::LeafDataTypes::LeafRef:
auto actualType = schema.leafrefBaseType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node);
return parse_impl(actualType);
}
__builtin_unreachable();
};
auto pass = parse_impl(type);
if (!pass) {
if (parserContext.m_errorMsg.empty()) {
parserContext.m_errorMsg = "leaf data type mismatch: Expected " +
leafDataTypeToString(schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node)) + " here:";
}
}
return pass;
}
};
auto const leaf_data = x3::no_skip[std::move(LeafData())];
BOOST_SPIRIT_DEFINE(leaf_data_enum)
BOOST_SPIRIT_DEFINE(leaf_data_string)
BOOST_SPIRIT_DEFINE(leaf_data_binary_data)
BOOST_SPIRIT_DEFINE(leaf_data_binary)
BOOST_SPIRIT_DEFINE(leaf_data_identityRef_data)
BOOST_SPIRIT_DEFINE(leaf_data_identityRef)
BOOST_SPIRIT_DEFINE(createEnumSuggestions)
BOOST_SPIRIT_DEFINE(createIdentitySuggestions)