Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/ |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 3 | * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/ |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 4 | * |
| 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át | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 10 | #include <boost/spirit/home/x3/support/ast/position_tagged.hpp> |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 11 | #include <boost/spirit/home/x3/support/utility/error_reporting.hpp> |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 12 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 13 | #include <boost/fusion/adapted/struct/adapt_struct.hpp> |
| 14 | #include <boost/fusion/include/adapt_struct.hpp> |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 15 | #include <boost/fusion/include/std_pair.hpp> |
| 16 | #include <boost/variant.hpp> |
| 17 | #include <map> |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 18 | #include <vector> |
| 19 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 20 | #include "CTree.hpp" |
Václav Kubernát | 94938b7 | 2018-05-04 15:12:24 +0200 | [diff] [blame] | 21 | #include "utils.hpp" |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 22 | namespace x3 = boost::spirit::x3; |
| 23 | namespace ascii = boost::spirit::x3::ascii; |
| 24 | |
| 25 | using x3::alpha; |
| 26 | using x3::alnum; |
| 27 | using x3::lit; |
| 28 | using x3::char_; |
| 29 | using x3::_attr; |
| 30 | using x3::lexeme; |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 31 | using x3::expect; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 32 | using ascii::space; |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 33 | using boost::fusion::operator<<; |
| 34 | |
| 35 | |
| 36 | using keyValue_ = std::pair<std::string, std::string>; |
| 37 | |
| 38 | class InvalidKeyException : public std::invalid_argument { |
| 39 | public: |
| 40 | using std::invalid_argument::invalid_argument; |
| 41 | ~InvalidKeyException() override; |
| 42 | }; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 43 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 44 | struct ParserContext { |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 45 | ParserContext(const CTree& tree); |
| 46 | const CTree& m_tree; |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 47 | std::string m_curPath; |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 48 | std::string m_errorMsg; |
| 49 | std::string m_tmpListName; |
| 50 | std::set<std::string> m_tmpListKeys; |
| 51 | bool m_errorHandled = false; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 52 | }; |
| 53 | |
| 54 | struct parser_context_tag; |
| 55 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 56 | struct container_ { |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 57 | container_() = default; |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 58 | container_(const std::string& name); |
| 59 | |
| 60 | bool operator==(const container_& b) const; |
| 61 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 62 | std::string m_name; |
| 63 | }; |
| 64 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 65 | BOOST_FUSION_ADAPT_STRUCT(container_, m_name) |
| 66 | |
| 67 | struct list_ { |
| 68 | std::vector<std::string> m_keys; |
| 69 | }; |
| 70 | |
| 71 | struct 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 | |
| 81 | BOOST_FUSION_ADAPT_STRUCT(listElement_, m_listName, m_keys) |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 82 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 83 | struct path_ { |
| 84 | bool operator==(const path_& b) const; |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 85 | std::vector<boost::variant<container_, listElement_>> m_nodes; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 86 | }; |
| 87 | |
| 88 | BOOST_FUSION_ADAPT_STRUCT(path_, m_nodes) |
| 89 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 90 | struct cd_ : x3::position_tagged { |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 91 | bool operator==(const cd_& b) const; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 92 | path_ m_path; |
| 93 | }; |
| 94 | |
| 95 | BOOST_FUSION_ADAPT_STRUCT(cd_, m_path) |
| 96 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 97 | struct 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 | }; |
| 115 | struct identifier_class; |
| 116 | |
| 117 | struct 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 | |
| 132 | struct 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 | }; |
| 160 | struct 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át | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 183 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 184 | struct container_class { |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 185 | template <typename T, typename Iterator, typename Context> |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 186 | void on_success(Iterator const&, Iterator const&, T& ast, Context const& context) |
| 187 | { |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 188 | 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át | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 192 | parserContext.m_curPath = joinPaths(parserContext.m_curPath, ast.m_name); |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 193 | } else { |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 194 | _pass(context) = false; |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 195 | } |
| 196 | } |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 197 | }; |
| 198 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 199 | struct path_class { |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 200 | template <typename T, typename Iterator, typename Context> |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 201 | void on_success(Iterator const&, Iterator const&, T&, Context const&) |
| 202 | { |
| 203 | } |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 204 | }; |
| 205 | |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 206 | struct cd_class { |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 207 | template <typename T, typename Iterator, typename Context> |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 208 | void on_success(Iterator const&, Iterator const&, T&, Context const&) |
| 209 | { |
| 210 | } |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 211 | |
| 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át | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 225 | }; |
| 226 | |
| 227 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 228 | x3::rule<keyValue_class, keyValue_> const keyValue = "keyValue"; |
| 229 | x3::rule<identifier_class, std::string> const identifier = "identifier"; |
| 230 | x3::rule<listPrefix_class, std::string> const listPrefix = "listPrefix"; |
| 231 | x3::rule<listSuffix_class, std::vector<keyValue_>> const listSuffix = "listSuffix"; |
| 232 | x3::rule<listElement_class, listElement_> const listElement = "listElement"; |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 233 | x3::rule<container_class, container_> const container = "container"; |
| 234 | x3::rule<path_class, path_> const path = "path"; |
| 235 | x3::rule<cd_class, cd_> const cd = "cd"; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 236 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 237 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 238 | auto const keyValue_def = |
| 239 | lexeme[+alnum >> '=' >> +alnum]; |
| 240 | |
| 241 | auto const identifier_def = |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 242 | lexeme[ |
| 243 | ((alpha | char_("_")) >> *(alnum | char_("_") | char_("-") | char_("."))) |
| 244 | ]; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 245 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 246 | auto const listPrefix_def = |
| 247 | identifier >> '['; |
| 248 | |
| 249 | auto const listSuffix_def = |
| 250 | +keyValue > ']'; |
| 251 | |
| 252 | auto const listElement_def = |
| 253 | listPrefix > listSuffix; |
| 254 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 255 | auto const container_def = |
Václav Kubernát | d666296 | 2018-03-22 17:41:33 +0100 | [diff] [blame] | 256 | identifier; |
| 257 | |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 258 | auto const path_def = |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 259 | (container | listElement) % '/'; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 260 | |
| 261 | auto const cd_def = |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 262 | lit("cd") > path >> x3::eoi; |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 263 | |
Václav Kubernát | b96eef7 | 2018-05-04 19:10:22 +0200 | [diff] [blame^] | 264 | BOOST_SPIRIT_DEFINE(keyValue) |
| 265 | BOOST_SPIRIT_DEFINE(identifier) |
| 266 | BOOST_SPIRIT_DEFINE(listPrefix) |
| 267 | BOOST_SPIRIT_DEFINE(listSuffix) |
| 268 | BOOST_SPIRIT_DEFINE(listElement) |
Václav Kubernát | 8cd6342 | 2018-03-19 17:10:13 +0100 | [diff] [blame] | 269 | BOOST_SPIRIT_DEFINE(container) |
| 270 | BOOST_SPIRIT_DEFINE(path) |
| 271 | BOOST_SPIRIT_DEFINE(cd) |