1 // Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef PARSER_CONTEXT_H 8 #define PARSER_CONTEXT_H 9 #include <string> 10 #include <map> 11 #include <vector> 12 #include <agent/agent_parser.h> 13 #include <agent/parser_context_decl.h> 14 #include <exceptions/exceptions.h> 15 16 // Tell Flex the lexer's prototype ... 17 #define YY_DECL isc::agent::AgentParser::symbol_type agent_lex (ParserContext& driver) 18 19 // ... and declare it for the parser's sake. 20 YY_DECL; 21 22 namespace isc { 23 namespace agent { 24 25 /// @brief Parser context is a wrapper around flex/bison instances dedicated to 26 /// Control-agent config file parser. 27 /// 28 /// It follows the same principle as other components. The primary interface 29 /// are @ref parseString and @ref parseFile methods. All other methods are 30 /// public for testing purposes only. This interface allows parsing the 31 /// whole configuration with syntactic checking (which is by far the most 32 /// frequent use), but it also allows parsing input as generic JSON or 33 /// parse only content of the Control-agent object, which is a subset 34 /// of full grammar (this will be very useful for unit-tests to not duplicate 35 /// unnecessary parts of the config file). 36 class ParserContext 37 { 38 public: 39 40 /// @brief Defines currently supported scopes 41 /// 42 /// AgentParser is able to parse several types of scope. Usually, 43 /// when it parses a config file, it expects the data to have a map 44 /// with Control-agent in it and all the parameters within that map. 45 /// However, sometimes the parser is expected to parse only a subset 46 /// of that information. 47 typedef enum { 48 /// This parser will parse the content as generic JSON. 49 PARSER_JSON, 50 51 /// This parser will expect the content as Control-agent config wrapped 52 /// in a map (that's the regular config file) 53 PARSER_AGENT, 54 55 /// This parser will expect only the content of Control-agent. 56 PARSER_SUB_AGENT 57 } ParserType; 58 59 /// @brief Default constructor. 60 ParserContext(); 61 62 /// @brief destructor 63 virtual ~ParserContext(); 64 65 /// @brief JSON elements being parsed. 66 std::vector<isc::data::ElementPtr> stack_; 67 68 /// @brief Method called before scanning starts on a string. 69 /// 70 /// @param str string to be parsed 71 /// @param type specifies expected content 72 void scanStringBegin(const std::string& str, ParserType type); 73 74 /// @brief Method called before scanning starts on a file. 75 /// 76 /// @param f stdio FILE pointer 77 /// @param filename file to be parsed 78 /// @param type specifies expected content 79 void scanFileBegin(FILE* f, const std::string& filename, ParserType type); 80 81 /// @brief Method called after the last tokens are scanned. 82 void scanEnd(); 83 84 /// @brief Divert input to an include file. 85 /// 86 /// @param filename file to be included 87 void includeFile(const std::string& filename); 88 89 /// @brief Run the parser on the string specified. 90 /// 91 /// This method parses specified string. Depending on the value of 92 /// parser_type, parser may either check only that the input is valid 93 /// JSON, or may do more specific syntax checking. See @ref ParserType 94 /// for supported syntax checkers. 95 /// 96 /// @param str string to be parsed 97 /// @param parser_type specifies expected content (usually AGENT or generic JSON) 98 /// @return Element structure representing parsed text. 99 isc::data::ElementPtr parseString(const std::string& str, 100 ParserType parser_type); 101 102 /// @brief Run the parser on the file specified. 103 /// 104 /// This method parses specified file. Depending on the value of 105 /// parser_type, parser may either check only that the input is valid 106 /// JSON, or may do more specific syntax checking. See @ref ParserType 107 /// for supported syntax checkers. 108 /// 109 /// @param filename file to be parsed 110 /// @param parser_type specifies expected content (usually PARSER_AGENT or 111 /// PARSER_JSON) 112 /// @return Element structure representing parsed text. 113 isc::data::ElementPtr parseFile(const std::string& filename, 114 ParserType parser_type); 115 116 /// @brief Error handler 117 /// 118 /// @note The optional position for an error in a string begins by 1 119 /// so the caller should add 1 to the position of the C++ string. 120 /// 121 /// @param loc location within the parsed file when experienced a problem. 122 /// @param what string explaining the nature of the error. 123 /// @param pos optional position for in string errors. 124 /// @throw ParseError 125 void error(const isc::agent::location& loc, 126 const std::string& what, 127 size_t pos = 0); 128 129 /// @brief Error handler 130 /// 131 /// This is a simplified error reporting tool for possible future 132 /// cases when the AgentParser is not able to handle the packet. 133 /// 134 /// @param what string explaining the nature of the error. 135 /// @throw ParseError 136 void error(const std::string& what); 137 138 /// @brief Fatal error handler 139 /// 140 /// This is for should not happen but fatal errors. 141 /// Used by YY_FATAL_ERROR macro so required to be static. 142 /// 143 /// @param what string explaining the nature of the error. 144 /// @throw ParseError 145 static void fatal(const std::string& what); 146 147 /// @brief Converts bison's position to one understandable by isc::data::Element 148 /// 149 /// Convert a bison location into an element position 150 /// (take the begin, the end is lost) 151 /// 152 /// @param loc location in bison format 153 /// @return Position in format accepted by Element 154 isc::data::Element::Position loc2pos(isc::agent::location& loc); 155 156 /// @brief Check if a required parameter is present 157 /// 158 /// Check if a required parameter is present in the map at the top 159 /// of the stack and raise an error when it is not. 160 /// 161 /// @param name name of the parameter to check 162 /// @param open_loc location of the opening curly bracket 163 /// @param close_loc location of the closing curly bracket 164 /// @throw ParseError 165 void require(const std::string& name, 166 isc::data::Element::Position open_loc, 167 isc::data::Element::Position close_loc); 168 169 /// @brief Check if a parameter is already present 170 /// 171 /// Check if a parameter is already present in the map at the top 172 /// of the stack and raise an error when it is. 173 /// 174 /// @param name name of the parameter to check 175 /// @param loc location of the current parameter 176 /// @throw ParseError 177 void unique(const std::string& name, 178 isc::data::Element::Position loc); 179 180 /// @brief Defines syntactic contexts for lexical tie-ins 181 typedef enum { 182 ///< This one is used in pure JSON mode. 183 NO_KEYWORDS, 184 185 ///< Used while parsing top level (that contains Control-agent) 186 CONFIG, 187 188 ///< Used while parsing content of Agent. 189 AGENT, 190 191 ///< Used while parsing Control-agent/Authentication. 192 AUTHENTICATION, 193 194 ///< Used while parsing Control-agent/Authentication/type. 195 AUTH_TYPE, 196 197 ///< Used while parsing Control-agent/Authentication/clients. 198 CLIENTS, 199 200 ///< Used while parsing Control-agent/control-sockets. 201 CONTROL_SOCKETS, 202 203 ///< Used while parsing Control-agent/control-socket/*-server. 204 SERVER, 205 206 ///< Used while parsing Control-agent/control-socket/*-server/socket-type. 207 SOCKET_TYPE, 208 209 ///< Used while parsing Control-agent/hooks-libraries. 210 HOOKS_LIBRARIES, 211 212 ///< Used while parsing Control-agent/loggers structures. 213 LOGGERS, 214 215 ///< Used while parsing Control-agent/loggers/output_options structures. 216 OUTPUT_OPTIONS 217 } LexerContext; 218 219 /// @brief File name 220 std::string file_; 221 222 /// @brief File name stack 223 std::vector<std::string> files_; 224 225 /// @brief Location of the current token 226 /// 227 /// The lexer will keep updating it. This variable will be useful 228 /// for logging errors. 229 isc::agent::location loc_; 230 231 /// @brief Location stack 232 std::vector<isc::agent::location> locs_; 233 234 /// @brief Lexer state stack 235 std::vector<struct yy_buffer_state*> states_; 236 237 /// @brief sFile (aka FILE) 238 FILE* sfile_; 239 240 /// @brief sFile (aka FILE) stack 241 /// 242 /// This is a stack of files. Typically there's only one file (the 243 /// one being currently parsed), but there may be more if one 244 /// file includes another. 245 std::vector<FILE*> sfiles_; 246 247 /// @brief Current syntactic context 248 LexerContext ctx_; 249 250 /// @brief Enter a new syntactic context 251 /// 252 /// Entering a new syntactic context is useful in several ways. 253 /// First, it allows the parser to avoid conflicts. Second, it 254 /// allows the lexer to return different tokens depending on 255 /// context (e.g. if "renew-timer" string is detected, the lexer 256 /// will return STRING token if in JSON mode or RENEW_TIMER if 257 /// in DHCP6 mode. Finally, the syntactic context allows the 258 /// error message to be more descriptive if the input string 259 /// does not parse properly. Control Agent parser uses simplified 260 /// contexts: either it recognizes keywords (value set to KEYWORDS) 261 /// or not (value set to NO_KEYWORDS). 262 /// 263 /// Make sure to call @ref leave() once the parsing of your 264 /// context is complete. 265 /// 266 /// @param ctx the syntactic context to enter into 267 void enter(const LexerContext& ctx); 268 269 /// @brief Leave a syntactic context 270 /// 271 /// @ref enter() must be called before (when entering a new scope 272 /// or context). Once you complete the parsing, this method 273 /// should be called. 274 /// 275 /// @throw isc::Unexpected if unbalanced (more leave() than enter() calls) 276 void leave(); 277 278 /// @brief Get the syntactic context name 279 /// 280 /// @return printable name of the context. 281 const std::string contextName(); 282 283 private: 284 /// @brief Flag determining scanner debugging. 285 bool trace_scanning_; 286 287 /// @brief Flag determining parser debugging. 288 bool trace_parsing_; 289 290 /// @brief Syntactic context stack 291 std::vector<LexerContext> cstack_; 292 293 /// @brief Common part of parseXXX 294 /// 295 /// @return Element structure representing parsed text. 296 isc::data::ElementPtr parseCommon(); 297 }; 298 299 } // end of isc::eval namespace 300 } // end of isc namespace 301 302 #endif 303