diff options
Diffstat (limited to 'src/bin/netconf/parser_context.h')
-rw-r--r-- | src/bin/netconf/parser_context.h | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/bin/netconf/parser_context.h b/src/bin/netconf/parser_context.h new file mode 100644 index 0000000..2498f83 --- /dev/null +++ b/src/bin/netconf/parser_context.h @@ -0,0 +1,313 @@ +// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef NETCONF_PARSER_CONTEXT_H +#define NETCONF_PARSER_CONTEXT_H +#include <string> +#include <map> +#include <vector> +#include <netconf/netconf_parser.h> +#include <netconf/parser_context_decl.h> +#include <cc/dhcp_config_error.h> +#include <exceptions/exceptions.h> + +// Tell Flex the lexer's prototype ... +#define YY_DECL isc::netconf::NetconfParser::symbol_type netconf_lex (ParserContext& driver) + +// ... and declare it for the parser's sake. +YY_DECL; + +namespace isc { +namespace netconf { + +/// @brief Parser context is a wrapper around flex/bison instances dedicated to +/// Netconf-agent config file parser. +/// +/// It follows the same principle as other components. The primary interface +/// are @ref parseString and @ref parseFile methods. All other methods are +/// public for testing purposes only. This interface allows parsing the +/// whole configuration with syntactic checking (which is by far the most +/// frequent use), but it also allows parsing input as generic JSON or +/// parse only content of the Netconf-agent object, which is a subset +/// of full grammar (this will be very useful for unit-tests to not duplicate +/// unnecessary parts of the config file). +class ParserContext +{ +public: + + /// @brief Defines currently supported scopes + /// + /// NetconfParser is able to parse several types of scope. Usually, + /// when it parses a config file, it expects the data to have a map + /// with Netconf-agent in it and all the parameters within that map. + /// However, sometimes the parser is expected to parse only a subset + /// of that information. + typedef enum { + /// This parser will parse the content as generic JSON. + PARSER_JSON, + + /// This parser will expect the content as Netconf config wrapped + /// in a map (that's the regular config file) + PARSER_NETCONF, + + /// This parser will expect only the content of Netconf. + PARSER_SUB_NETCONF + } ParserType; + + /// @brief Default constructor. + ParserContext(); + + /// @brief destructor + virtual ~ParserContext(); + + /// @brief JSON elements being parsed. + std::vector<isc::data::ElementPtr> stack_; + + /// @brief Method called before scanning starts on a string. + /// + /// @param str string to be parsed + /// @param type specifies expected content + void scanStringBegin(const std::string& str, ParserType type); + + /// @brief Method called before scanning starts on a file. + /// + /// @param f stdio FILE pointer + /// @param filename file to be parsed + /// @param type specifies expected content + void scanFileBegin(FILE* f, const std::string& filename, ParserType type); + + /// @brief Method called after the last tokens are scanned. + void scanEnd(); + + /// @brief Divert input to an include file. + /// + /// @param filename file to be included + void includeFile(const std::string& filename); + + /// @brief Run the parser on the string specified. + /// + /// This method parses specified string. Depending on the value of + /// parser_type, parser may either check only that the input is valid + /// JSON, or may do more specific syntax checking. See @ref ParserType + /// for supported syntax checkers. + /// + /// @param str string to be parsed + /// @param parser_type specifies expected content (usually NETCONF or generic JSON) + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseString(const std::string& str, + ParserType parser_type); + + /// @brief Run the parser on the file specified. + /// + /// This method parses specified file. Depending on the value of + /// parser_type, parser may either check only that the input is valid + /// JSON, or may do more specific syntax checking. See @ref ParserType + /// for supported syntax checkers. + /// + /// @param filename file to be parsed + /// @param parser_type specifies expected content (usually PARSER_NETCONF + /// or PARSER_JSON) + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseFile(const std::string& filename, + ParserType parser_type); + + /// @brief Error handler + /// + /// @note The optional position for an error in a string begins by 1 + /// so the caller should add 1 to the position of the C++ string. + /// + /// @param loc location within the parsed file where the problem was experienced. + /// @param what string explaining the nature of the error. + /// @param pos optional position for in string errors. + /// @throw ParseError + void error(const isc::netconf::location& loc, + const std::string& what, + size_t pos = 0); + + /// @brief Error handler + /// + /// This is a simplified error reporting tool for possible future + /// cases when the NetconfParser is not able to handle the packet. + /// + /// @param what string explaining the nature of the error. + /// @throw ParseError + void error(const std::string& what); + + /// @brief Fatal error handler + /// + /// This is for should not happen but fatal errors. + /// Used by YY_FATAL_ERROR macro so required to be static. + /// + /// @param what string explaining the nature of the error. + /// @throw ParseError + static void fatal(const std::string& what); + + /// @brief Converts bison's position to one understandable by isc::data::Element + /// + /// Convert a bison location into an element position + /// (take the begin, the end is lost) + /// + /// @param loc location in bison format + /// @return Position in format accepted by Element + isc::data::Element::Position loc2pos(isc::netconf::location& loc); + + /// @brief Check if a required parameter is present + /// + /// Check if a required parameter is present in the map at the top + /// of the stack and raise an error when it is not. + /// + /// @param name name of the parameter to check + /// @param open_loc location of the opening curly bracket + /// @param close_loc location of the closing curly bracket + /// @throw ParseError + void require(const std::string& name, + isc::data::Element::Position open_loc, + isc::data::Element::Position close_loc); + + /// @brief Check if a parameter is already present + /// + /// Check if a parameter is already present in the map at the top + /// of the stack and raise an error when it is. + /// + /// @param name name of the parameter to check + /// @param loc location of the current parameter + /// @throw ParseError + void unique(const std::string& name, + isc::data::Element::Position loc); + + /// @brief Warning handler + /// + /// @param loc location within the parsed file where the problem was experienced + /// @param what string explaining the nature of the error + /// + /// @throw ParseError + void warning(const isc::netconf::location& loc, const std::string& what); + + /// @brief Warning for extra commas + /// + /// @param loc location within the parsed file of the extra comma + /// + /// @throw ParseError + void warnAboutExtraCommas(const isc::netconf::location& loc); + + /// @brief Defines syntactic contexts for lexical tie-ins + typedef enum { + ///< This one is used in pure JSON mode. + NO_KEYWORDS, + + ///< Used while parsing top level (that contains Netconf) + CONFIG, + + ///< Used while parsing content of Netconf. + NETCONF, + + /// Used while parsing Netconf/managed-servers. + MANAGED_SERVERS, + + ///< Used while parsing Netconf/managed-servers/*. + SERVER, + + ///< Used while parsing Netconf/manages-servers/*/control-socket + CONTROL_SOCKET, + + ///< Used while parsing Netconf/managed-servers/*/control-socket/socket-type. + SOCKET_TYPE, + + ///< Used while parsing Netconf/hooks-libraries. + HOOKS_LIBRARIES, + + ///< Used while parsing Netconf/loggers structures. + LOGGERS, + + ///< Used while parsing Netconf/loggers/output_options structures. + OUTPUT_OPTIONS + + } LexerContext; + + /// @brief File name + std::string file_; + + /// @brief File name stack + std::vector<std::string> files_; + + /// @brief Location of the current token + /// + /// The lexer will keep updating it. This variable will be useful + /// for logging errors. + isc::netconf::location loc_; + + /// @brief Location stack + std::vector<isc::netconf::location> locs_; + + /// @brief Lexer state stack + std::vector<struct yy_buffer_state*> states_; + + /// @brief sFile (aka FILE) + FILE* sfile_; + + /// @brief sFile (aka FILE) stack + /// + /// This is a stack of files. Typically there's only one file (the + /// one being currently parsed), but there may be more if one + /// file includes another. + std::vector<FILE*> sfiles_; + + /// @brief Current syntactic context + LexerContext ctx_; + + /// @brief Enter a new syntactic context + /// + /// Entering a new syntactic context is useful in several ways. + /// First, it allows the parser to avoid conflicts. Second, it + /// allows the lexer to return different tokens depending on + /// context (e.g. if "renew-timer" string is detected, the lexer + /// will return STRING token if in JSON mode or RENEW_TIMER if + /// in DHCP6 mode. Finally, the syntactic context allows the + /// error message to be more descriptive if the input string + /// does not parse properly. Netconf Agent parser uses simplified + /// contexts: either it recognizes keywords (value set to KEYWORDS) + /// or not (value set to NO_KEYWORDS). + /// + /// Make sure to call @ref leave() once the parsing of your + /// context is complete. + /// + /// @param ctx the syntactic context to enter into + void enter(const LexerContext& ctx); + + /// @brief Leave a syntactic context + /// + /// @ref enter() must be called before (when entering a new scope + /// or context). Once you complete the parsing, this method + /// should be called. + /// + /// @throw isc::Unexpected if unbalanced (more leave() than enter() calls) + void leave(); + + /// @brief Get the syntactic context name + /// + /// @return printable name of the context. + const std::string contextName(); + + private: + /// @brief Flag determining scanner debugging. + bool trace_scanning_; + + /// @brief Flag determining parser debugging. + bool trace_parsing_; + + /// @brief Syntactic context stack + std::vector<LexerContext> cstack_; + + /// @brief Common part of parseXXX + /// + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseCommon(); +}; + +} // end of isc::netconf namespace +} // end of isc namespace + +#endif |