blob: b33d4c1c6d832317ac2d5e0aabdd5af523f9438b [file] [log] [blame]
Václav Kubernát9ae8cc42020-03-25 19:17:41 +01001/*
2 * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Václav Kubernát <kubernat@cesnet.cz>
5 *
6*/
7
8#pragma once
9
10#include <boost/spirit/home/x3.hpp>
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010011#include "ast_handlers.hpp"
12#include "common_parsers.hpp"
Václav Kubernát3a99f002020-03-31 02:27:41 +020013#include "leaf_data_type.hpp"
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010014#include "schema.hpp"
15namespace x3 = boost::spirit::x3;
16
Václav Kubernát3a99f002020-03-31 02:27:41 +020017template <typename TYPE>
Václav Kubernát882174d2020-03-25 21:31:46 +010018struct leaf_data_class;
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010019
Václav Kubernát3a99f002020-03-31 02:27:41 +020020x3::rule<struct leaf_data_class<yang::Enum>, enum_> const leaf_data_enum = "leaf_data_enum";
21x3::rule<struct leaf_data_class<yang::IdentityRef>, identityRef_> const leaf_data_identityRef = "leaf_data_identityRef";
22x3::rule<struct leaf_data_class<yang::Binary>, binary_> const leaf_data_binary = "leaf_data_binary";
23x3::rule<struct leaf_data_class<yang::Decimal>, double> const leaf_data_decimal = "leaf_data_decimal";
24x3::rule<struct leaf_data_class<yang::String>, std::string> const leaf_data_string = "leaf_data_string";
Václav Kubernát882174d2020-03-25 21:31:46 +010025x3::rule<struct leaf_data_class_binary, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
26x3::rule<struct leaf_data_identityRef_data_class, identityRef_> const leaf_data_identityRef_data = "leaf_data_identityRef_data";
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010027
Václav Kubernát882174d2020-03-25 21:31:46 +010028using x3::char_;
29
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010030auto const leaf_data_enum_def =
Václav Kubernát3a99f002020-03-31 02:27:41 +020031 +char_;
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010032
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010033struct bool_symbol_table : x3::symbols<bool> {
34 bool_symbol_table()
35 {
Václav Kubernát3a99f002020-03-31 02:27:41 +020036 add
37 ("true", true)
38 ("false", false);
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010039 }
Václav Kubernát882174d2020-03-25 21:31:46 +010040} const bool_symbols;
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010041
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010042auto const leaf_data_string_def =
43 '\'' >> *(char_-'\'') >> '\'' |
44 '\"' >> *(char_-'\"') >> '\"';
45
46// This intermediate rule is neccessary for coercing to std::string.
Václav Kubernát882174d2020-03-25 21:31:46 +010047// TODO: check if I can do the coercing right in the grammar with `as{}` from
48// https://github.com/boostorg/spirit/issues/530#issuecomment-584836532
49// This would shave off some more lines.
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010050auto const leaf_data_binary_data_def =
51 +(x3::alnum | char_('+') | char_('/')) >> -char_('=') >> -char_('=');
52
53auto const leaf_data_binary_def =
54 leaf_data_binary_data;
55
56auto const leaf_data_identityRef_data_def =
Jan Kundrát0d8abd12020-05-07 02:00:14 +020057 -module >> node_identifier;
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010058
Václav Kubernát3a99f002020-03-31 02:27:41 +020059// TODO: get rid of this and use leaf_data_identityRef_data directly
Václav Kubernát9ae8cc42020-03-25 19:17:41 +010060auto const leaf_data_identityRef_def =
Václav Kubernát3a99f002020-03-31 02:27:41 +020061 leaf_data_identityRef_data;
62
63template <typename It, typename Ctx, typename RCtx, typename Attr>
64struct impl_LeafData {
65 It& first;
66 It last;
67 Ctx const& ctx;
68 RCtx& rctx;
69 Attr& attr;
70 ParserContext& parserContext;
71
72 bool operator()(const yang::Binary&) const
73 {
74 return leaf_data_binary.parse(first, last, ctx, rctx, attr);
75 }
76 bool operator()(const yang::Bool&) const
77 {
78 return bool_symbols.parse(first, last, ctx, rctx, attr);
79 }
80 bool operator()(const yang::Decimal&) const
81 {
82 return x3::double_.parse(first, last, ctx, rctx, attr);
83 }
84 bool operator()(const yang::Uint8&) const
85 {
86 return x3::uint8.parse(first, last, ctx, rctx, attr);
87 }
88 bool operator()(const yang::Uint16&) const
89 {
90 return x3::uint16.parse(first, last, ctx, rctx, attr);
91 }
92 bool operator()(const yang::Uint32&) const
93 {
94 return x3::uint32.parse(first, last, ctx, rctx, attr);
95 }
96 bool operator()(const yang::Uint64&) const
97 {
98 return x3::uint64.parse(first, last, ctx, rctx, attr);
99 }
100 bool operator()(const yang::Int8&) const
101 {
102 return x3::int8.parse(first, last, ctx, rctx, attr);
103 }
104 bool operator()(const yang::Int16&) const
105 {
106 return x3::int16.parse(first, last, ctx, rctx, attr);
107 }
108 bool operator()(const yang::Int32&) const
109 {
110 return x3::int32.parse(first, last, ctx, rctx, attr);
111 }
112 bool operator()(const yang::Int64&) const
113 {
114 return x3::int64.parse(first, last, ctx, rctx, attr);
115 }
116 bool operator()(const yang::String&) const
117 {
118 return leaf_data_string.parse(first, last, ctx, rctx, attr);
119 }
120 template <typename Type>
121 void createSetSuggestions(const Type& type) const
122 {
123 parserContext.m_suggestions.clear();
124 std::transform(type.m_allowedValues.begin(),
125 type.m_allowedValues.end(),
126 std::inserter(parserContext.m_suggestions, parserContext.m_suggestions.end()),
Václav Kubernát549b08f2020-05-14 22:19:36 +0200127 [](auto it) {
128 std::string res;
129 if constexpr (std::is_same<Type, yang::IdentityRef>()) {
130 res = it.m_prefix ? it.m_prefix->m_name + ":" : "";
131 }
132 res += it.m_value;
133 return Completion{res};
134 });
Václav Kubernát3a99f002020-03-31 02:27:41 +0200135 parserContext.m_completionIterator = first;
136 }
137 bool operator()(const yang::Enum& type) const
138 {
139 createSetSuggestions(type);
140 // leaf_data_enum will advance the iterator if it succeeds, so I have
141 // to save the iterator here, to roll it back in case the enum is
142 // invalid.
143 auto saveIter = first;
144 auto pass = leaf_data_enum.parse(first, last, ctx, rctx, attr);
145 if (!pass) {
146 return false;
147 }
148 auto isValidEnum = type.m_allowedValues.count(boost::get<enum_>(attr)) != 0;
149 if (!isValidEnum) {
150 first = saveIter;
151 parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
152 for (const auto& it : type.m_allowedValues) {
153 parserContext.m_errorMsg += " " + it.m_value;
154 }
155 }
156 return isValidEnum;
157 }
158 bool operator()(const yang::IdentityRef& type) const
159 {
160 createSetSuggestions(type);
161 // leaf_data_identityRef will advance the iterator if it succeeds, so I have
162 // to save the iterator here, to roll it back in case the enum is
163 // invalid.
164 auto saveIter = first;
165 auto pass = leaf_data_identityRef.parse(first, last, ctx, rctx, attr);
166 if (!pass) {
167 return false;
168 }
169 identityRef_ pair{boost::get<identityRef_>(attr)};
170 if (!pair.m_prefix) {
171 pair.m_prefix = module_{parserContext.currentSchemaPath().m_nodes.front().m_prefix.get().m_name};
172 }
173 auto isValidIdentity = type.m_allowedValues.count(pair) != 0;
174 if (!isValidIdentity) {
175 first = saveIter;
176 }
177 return isValidIdentity;
178 }
179 bool operator()(const yang::LeafRef& leafRef) const
180 {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200181 return std::visit(*this, leafRef.m_targetType->m_type);
Václav Kubernát3a99f002020-03-31 02:27:41 +0200182 }
Václav Kubernát2984f442020-02-20 17:43:35 +0100183 bool operator()(const yang::Union& unionInfo) const
184 {
185 return std::any_of(unionInfo.m_unionTypes.begin(), unionInfo.m_unionTypes.end(), [this](const auto& type) {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200186 return std::visit(*this, type.m_type);
Václav Kubernát2984f442020-02-20 17:43:35 +0100187 });
188 }
Václav Kubernát3a99f002020-03-31 02:27:41 +0200189};
Václav Kubernát9ae8cc42020-03-25 19:17:41 +0100190
Václav Kubernát882174d2020-03-25 21:31:46 +0100191struct LeafData : x3::parser<LeafData> {
192 using attribute_type = leaf_data_;
Václav Kubernát9ae8cc42020-03-25 19:17:41 +0100193
Václav Kubernát882174d2020-03-25 21:31:46 +0100194 // TODO: Can this be placed in a .cpp file?
195 template <typename It, typename Ctx, typename RCtx, typename Attr>
196 bool parse(It& first, It last, Ctx const& ctx, RCtx& rctx, Attr& attr) const
197 {
198 ParserContext& parserContext = x3::get<parser_context_tag>(ctx);
199 const Schema& schema = parserContext.m_schema;
Václav Kubernát13b23d72020-04-16 21:49:51 +0200200 auto type = schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node).m_type;
Václav Kubernát882174d2020-03-25 21:31:46 +0100201
Václav Kubernát3a99f002020-03-31 02:27:41 +0200202 auto pass = std::visit(impl_LeafData<It, Ctx, RCtx, Attr>{first, last, ctx, rctx, attr, parserContext}, type);
Václav Kubernát882174d2020-03-25 21:31:46 +0100203
204 if (!pass) {
205 if (parserContext.m_errorMsg.empty()) {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200206 parserContext.m_errorMsg = "leaf data type mismatch: Expected " + leafDataTypeToString(type) + " here:";
Václav Kubernát882174d2020-03-25 21:31:46 +0100207 }
208 }
209 return pass;
210 }
211};
212
213auto const leaf_data = x3::no_skip[std::move(LeafData())];
214
Václav Kubernát9ae8cc42020-03-25 19:17:41 +0100215BOOST_SPIRIT_DEFINE(leaf_data_enum)
Václav Kubernát9ae8cc42020-03-25 19:17:41 +0100216BOOST_SPIRIT_DEFINE(leaf_data_string)
217BOOST_SPIRIT_DEFINE(leaf_data_binary_data)
218BOOST_SPIRIT_DEFINE(leaf_data_binary)
219BOOST_SPIRIT_DEFINE(leaf_data_identityRef_data)
220BOOST_SPIRIT_DEFINE(leaf_data_identityRef)