blob: e941a8bf9f8bc83be1cf801c4818d58e1abe9c2b [file] [log] [blame]
Václav Kubernát8cd63422018-03-19 17:10:13 +01001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
Václav Kubernátd6662962018-03-22 17:41:33 +01003 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
Václav Kubernát8cd63422018-03-19 17:10:13 +01004 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8#pragma once
9#include <boost/spirit/home/x3.hpp>
Václav Kubernát8cd63422018-03-19 17:10:13 +010010#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
Václav Kubernátb96eef72018-05-04 19:10:22 +020011#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
Václav Kubernátd6662962018-03-22 17:41:33 +010012
Václav Kubernát8cd63422018-03-19 17:10:13 +010013#include <boost/fusion/adapted/struct/adapt_struct.hpp>
14#include <boost/fusion/include/adapt_struct.hpp>
Václav Kubernátb96eef72018-05-04 19:10:22 +020015#include <boost/fusion/include/std_pair.hpp>
16#include <boost/variant.hpp>
17#include <map>
Václav Kubernátd6662962018-03-22 17:41:33 +010018#include <vector>
19
Václav Kubernát8cd63422018-03-19 17:10:13 +010020#include "CTree.hpp"
Václav Kubernát94938b72018-05-04 15:12:24 +020021#include "utils.hpp"
Václav Kubernát8cd63422018-03-19 17:10:13 +010022namespace x3 = boost::spirit::x3;
23namespace ascii = boost::spirit::x3::ascii;
24
25using x3::alpha;
26using x3::alnum;
27using x3::lit;
28using x3::char_;
29using x3::_attr;
30using x3::lexeme;
Václav Kubernátb96eef72018-05-04 19:10:22 +020031using x3::expect;
Václav Kubernát8cd63422018-03-19 17:10:13 +010032using ascii::space;
Václav Kubernátb96eef72018-05-04 19:10:22 +020033using boost::fusion::operator<<;
34
35
36using keyValue_ = std::pair<std::string, std::string>;
37
38class InvalidKeyException : public std::invalid_argument {
39public:
40 using std::invalid_argument::invalid_argument;
41 ~InvalidKeyException() override;
42};
Václav Kubernát8cd63422018-03-19 17:10:13 +010043
Václav Kubernátd6662962018-03-22 17:41:33 +010044struct ParserContext {
Václav Kubernát8cd63422018-03-19 17:10:13 +010045 ParserContext(const CTree& tree);
46 const CTree& m_tree;
Václav Kubernátd6662962018-03-22 17:41:33 +010047 std::string m_curPath;
Václav Kubernátb96eef72018-05-04 19:10:22 +020048 std::string m_errorMsg;
49 std::string m_tmpListName;
50 std::set<std::string> m_tmpListKeys;
51 bool m_errorHandled = false;
Václav Kubernát8cd63422018-03-19 17:10:13 +010052};
53
54struct parser_context_tag;
55
Václav Kubernátd6662962018-03-22 17:41:33 +010056struct container_ {
Václav Kubernátb96eef72018-05-04 19:10:22 +020057 container_() = default;
Václav Kubernátd6662962018-03-22 17:41:33 +010058 container_(const std::string& name);
59
60 bool operator==(const container_& b) const;
61
Václav Kubernát8cd63422018-03-19 17:10:13 +010062 std::string m_name;
63};
64
Václav Kubernátb96eef72018-05-04 19:10:22 +020065BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
66
67struct list_ {
68 std::vector<std::string> m_keys;
69};
70
71struct listElement_ {
72 listElement_() {}
73 listElement_(const std::string& listName, const std::map<std::string, std::string>& keys);
74
75 bool operator==(const listElement_& b) const;
76
77 std::string m_listName;
78 std::map<std::string, std::string> m_keys;
79};
80
81BOOST_FUSION_ADAPT_STRUCT(listElement_, m_listName, m_keys)
Václav Kubernát8cd63422018-03-19 17:10:13 +010082
Václav Kubernátd6662962018-03-22 17:41:33 +010083struct path_ {
84 bool operator==(const path_& b) const;
Václav Kubernátb96eef72018-05-04 19:10:22 +020085 std::vector<boost::variant<container_, listElement_>> m_nodes;
Václav Kubernát8cd63422018-03-19 17:10:13 +010086};
87
88BOOST_FUSION_ADAPT_STRUCT(path_, m_nodes)
89
Václav Kubernátb96eef72018-05-04 19:10:22 +020090struct cd_ : x3::position_tagged {
Václav Kubernátd6662962018-03-22 17:41:33 +010091 bool operator==(const cd_& b) const;
Václav Kubernát8cd63422018-03-19 17:10:13 +010092 path_ m_path;
93};
94
95BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
96
Václav Kubernátb96eef72018-05-04 19:10:22 +020097struct keyValue_class {
98 template <typename T, typename Iterator, typename Context>
99 void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
100 {
101 auto& parserContext = x3::get<parser_context_tag>(context);
102 const CTree& tree = parserContext.m_tree;
103
104 if (parserContext.m_tmpListKeys.find(ast.first) != parserContext.m_tmpListKeys.end()) {
105 _pass(context) = false;
106 parserContext.m_errorMsg = "Key \"" + ast.first + "\" was entered more than once.";
107 } else if (tree.listHasKey(parserContext.m_curPath, parserContext.m_tmpListName, ast.first)) {
108 parserContext.m_tmpListKeys.insert(ast.first);
109 } else {
110 _pass(context) = false;
111 parserContext.m_errorMsg = parserContext.m_tmpListName + " is not indexed by \"" + ast.first + "\".";
112 }
113 }
114};
115struct identifier_class;
116
117struct listPrefix_class {
118 template <typename T, typename Iterator, typename Context>
119 void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
120 {
121 auto& parserContext = x3::get<parser_context_tag>(context);
122 const CTree& tree = parserContext.m_tree;
123
124 if (tree.isList(parserContext.m_curPath, ast)) {
125 parserContext.m_tmpListName = ast;
126 } else {
127 _pass(context) = false;
128 }
129 }
130};
131
132struct listSuffix_class {
133 template <typename T, typename Iterator, typename Context>
134 void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
135 {
136 auto& parserContext = x3::get<parser_context_tag>(context);
137 const CTree& tree = parserContext.m_tree;
138
139 const auto& keysNeeded = tree.listKeys(parserContext.m_curPath, parserContext.m_tmpListName);
140 std::set<std::string> keysSupplied;
141 for (const auto& it : ast)
142 keysSupplied.insert(it.first);
143
144 if (keysNeeded != keysSupplied) {
145 parserContext.m_errorMsg = "Not enough keys for " + parserContext.m_tmpListName + ". " +
146 "These keys were not supplied:";
147 std::set<std::string> missingKeys;
148 std::set_difference(keysNeeded.begin(), keysNeeded.end(),
149 keysSupplied.begin(), keysSupplied.end(),
150 std::inserter(missingKeys, missingKeys.end()));
151
152 for (const auto& it : missingKeys)
153 parserContext.m_errorMsg += " " + it;
154 parserContext.m_errorMsg += ".";
155
156 _pass(context) = false;
157 }
158 }
159};
160struct listElement_class {
161 template <typename T, typename Iterator, typename Context>
162 void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
163 {
164 auto& parserContext = x3::get<parser_context_tag>(context);
165 parserContext.m_curPath = joinPaths(parserContext.m_curPath, ast.m_listName);
166 }
167
168 template <typename Iterator, typename Exception, typename Context>
169 x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& ex, Context const& context)
170 {
171 auto& parserContext = x3::get<parser_context_tag>(context);
172 auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
173 if (parserContext.m_errorHandled) // someone already handled our error
174 return x3::error_handler_result::fail;
175
176 parserContext.m_errorHandled = true;
177
178 std::string message = parserContext.m_errorMsg;
179 error_handler(ex.where(), message);
180 return x3::error_handler_result::fail;
181 }
182};
Václav Kubernát8cd63422018-03-19 17:10:13 +0100183
Václav Kubernátd6662962018-03-22 17:41:33 +0100184struct container_class {
Václav Kubernát8cd63422018-03-19 17:10:13 +0100185 template <typename T, typename Iterator, typename Context>
Václav Kubernátd6662962018-03-22 17:41:33 +0100186 void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
187 {
Václav Kubernátd6662962018-03-22 17:41:33 +0100188 auto& parserContext = x3::get<parser_context_tag>(context);
189 const auto& tree = parserContext.m_tree;
190
191 if (tree.isContainer(parserContext.m_curPath, ast.m_name)) {
Václav Kubernátb96eef72018-05-04 19:10:22 +0200192 parserContext.m_curPath = joinPaths(parserContext.m_curPath, ast.m_name);
Václav Kubernátd6662962018-03-22 17:41:33 +0100193 } else {
Václav Kubernátb96eef72018-05-04 19:10:22 +0200194 _pass(context) = false;
Václav Kubernátd6662962018-03-22 17:41:33 +0100195 }
196 }
Václav Kubernát8cd63422018-03-19 17:10:13 +0100197};
198
Václav Kubernátd6662962018-03-22 17:41:33 +0100199struct path_class {
Václav Kubernát8cd63422018-03-19 17:10:13 +0100200 template <typename T, typename Iterator, typename Context>
Václav Kubernátd6662962018-03-22 17:41:33 +0100201 void on_success(Iterator const&, Iterator const&, T&, Context const&)
202 {
203 }
Václav Kubernát8cd63422018-03-19 17:10:13 +0100204};
205
Václav Kubernátd6662962018-03-22 17:41:33 +0100206struct cd_class {
Václav Kubernát8cd63422018-03-19 17:10:13 +0100207 template <typename T, typename Iterator, typename Context>
Václav Kubernátd6662962018-03-22 17:41:33 +0100208 void on_success(Iterator const&, Iterator const&, T&, Context const&)
209 {
210 }
Václav Kubernátb96eef72018-05-04 19:10:22 +0200211
212 template <typename Iterator, typename Exception, typename Context>
213 x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
214 {
215 auto& parserContext = x3::get<parser_context_tag>(context);
216 auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
217 std::string message = "This isn't a list or a container or nothing.";
218 if (parserContext.m_errorHandled) // someone already handled our error
219 return x3::error_handler_result::fail;
220
221 parserContext.m_errorHandled = true;
222 error_handler(x.where(), message);
223 return x3::error_handler_result::fail;
224 }
Václav Kubernát8cd63422018-03-19 17:10:13 +0100225};
226
227
Václav Kubernátb96eef72018-05-04 19:10:22 +0200228x3::rule<keyValue_class, keyValue_> const keyValue = "keyValue";
229x3::rule<identifier_class, std::string> const identifier = "identifier";
230x3::rule<listPrefix_class, std::string> const listPrefix = "listPrefix";
231x3::rule<listSuffix_class, std::vector<keyValue_>> const listSuffix = "listSuffix";
232x3::rule<listElement_class, listElement_> const listElement = "listElement";
Václav Kubernátd6662962018-03-22 17:41:33 +0100233x3::rule<container_class, container_> const container = "container";
234x3::rule<path_class, path_> const path = "path";
235x3::rule<cd_class, cd_> const cd = "cd";
Václav Kubernát8cd63422018-03-19 17:10:13 +0100236
Václav Kubernát8cd63422018-03-19 17:10:13 +0100237
Václav Kubernátb96eef72018-05-04 19:10:22 +0200238auto const keyValue_def =
239 lexeme[+alnum >> '=' >> +alnum];
240
241auto const identifier_def =
Václav Kubernátd6662962018-03-22 17:41:33 +0100242 lexeme[
243 ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_(".")))
244 ];
Václav Kubernát8cd63422018-03-19 17:10:13 +0100245
Václav Kubernátb96eef72018-05-04 19:10:22 +0200246auto const listPrefix_def =
247 identifier >> '[';
248
249auto const listSuffix_def =
250 +keyValue > ']';
251
252auto const listElement_def =
253 listPrefix > listSuffix;
254
Václav Kubernát8cd63422018-03-19 17:10:13 +0100255auto const container_def =
Václav Kubernátd6662962018-03-22 17:41:33 +0100256 identifier;
257
Václav Kubernát8cd63422018-03-19 17:10:13 +0100258auto const path_def =
Václav Kubernátb96eef72018-05-04 19:10:22 +0200259 (container | listElement) % '/';
Václav Kubernát8cd63422018-03-19 17:10:13 +0100260
261auto const cd_def =
Václav Kubernátb96eef72018-05-04 19:10:22 +0200262 lit("cd") > path >> x3::eoi;
Václav Kubernát8cd63422018-03-19 17:10:13 +0100263
Václav Kubernátb96eef72018-05-04 19:10:22 +0200264BOOST_SPIRIT_DEFINE(keyValue)
265BOOST_SPIRIT_DEFINE(identifier)
266BOOST_SPIRIT_DEFINE(listPrefix)
267BOOST_SPIRIT_DEFINE(listSuffix)
268BOOST_SPIRIT_DEFINE(listElement)
Václav Kubernát8cd63422018-03-19 17:10:13 +0100269BOOST_SPIRIT_DEFINE(container)
270BOOST_SPIRIT_DEFINE(path)
271BOOST_SPIRIT_DEFINE(cd)