// Copyright (C) 2015-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 PARSER_CONTEXT_H #define PARSER_CONTEXT_H #include #include #include #include #include #include // Tell Flex the lexer's prototype ... #define YY_DECL isc::dhcp::Dhcp4Parser::symbol_type parser4_lex (Parser4Context& driver) // ... and declare it for the parser's sake. YY_DECL; namespace isc { namespace dhcp { /// @brief Evaluation error exception raised when trying to parse. /// /// @todo: This probably should be common for Dhcp4 and Dhcp6. class Dhcp4ParseError : public isc::Exception { public: Dhcp4ParseError(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @brief Evaluation context, an interface to the expression evaluation. class Parser4Context { public: /// @brief Defines currently supported scopes /// /// Dhcp4Parser is able to parse several types of scope. Usually, /// when it parses a config file, it expects the data to have a map /// with Dhcp4 in it and all the parameters within that Dhcp4 map. /// However, sometimes the parser is expected to parse only a subset /// of that information. For example, it may be asked to parse /// a structure that is host-reservation only, without the global /// 'Dhcp4' or 'reservations' around it. In such case the parser /// is being told to start parsing as PARSER_HOST_RESERVATION4. typedef enum { /// This parser will parse the content as generic JSON. PARSER_JSON, /// This parser will parse the content as Dhcp4 config wrapped in a map /// (that's the regular config file) PARSER_DHCP4, /// This parser will parse the content of Dhcp4 (without outer { } and /// without "Dhcp4"). It is mostly used in unit-tests as most of the /// unit-tests do not define the outer map and Dhcp4 entity, just the /// contents of it. SUBPARSER_DHCP4, /// This will parse the input as interfaces content. PARSER_INTERFACES, /// This will parse the input as Subnet4 content. PARSER_SUBNET4, /// This will parse the input as pool4 content. PARSER_POOL4, /// This will parse the input as host-reservation. PARSER_HOST_RESERVATION, /// This will parse the input option definitions (for tests). PARSER_OPTION_DEFS, /// This will parse the input as option definition. PARSER_OPTION_DEF, /// This will parse the input as option data. PARSER_OPTION_DATA, /// This will parse the input as hooks-library. PARSER_HOOKS_LIBRARY, /// This will parse the input as dhcp-ddns. PARSER_DHCP_DDNS, /// This will parse the input as config-control. PARSER_CONFIG_CONTROL, } ParserType; /// @brief Default constructor. Parser4Context(); /// @brief destructor virtual ~Parser4Context(); /// @brief JSON elements being parsed. std::vector 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 DHCP4 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 DHCP4 or generic 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 Dhcp4ParseError void error(const isc::dhcp::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 Dhcp4Parser is not able to handle the packet. /// /// @param what string explaining the nature of the error. /// @throw Dhcp4ParseError 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 Dhcp4ParseError 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::dhcp::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 Dhcp4ParseError 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 Dhcp4ParseError 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::dhcp::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::dhcp::location& loc); /// @brief Defines syntactic contexts for lexical tie-ins typedef enum { ///< This one is used in pure JSON mode. NO_KEYWORD, ///< Used while parsing top level (that contains Dhcp4) CONFIG, ///< Used while parsing content of Dhcp4. DHCP4, /// Used while parsing Dhcp4/interfaces structures. INTERFACES_CONFIG, /// Sanity checks. SANITY_CHECKS, /// Used while parsing Dhcp4/interfaces/dhcp-socket-type structures. DHCP_SOCKET_TYPE, /// Used while parsing Dhcp4/interfaces/outbound-interface structures. OUTBOUND_INTERFACE, /// Used while parsing Dhcp4/lease-database structures. LEASE_DATABASE, /// Used while parsing Dhcp4/hosts-database[s] structures. HOSTS_DATABASE, /// Used while parsing Dhcp4/*-database/type. DATABASE_TYPE, /// Used while parsing Dhcp4/*-database/on-fail. DATABASE_ON_FAIL, /// Used while parsing Dhcp4/host-reservation-identifiers. HOST_RESERVATION_IDENTIFIERS, /// Used while parsing Dhcp4/hooks-libraries. HOOKS_LIBRARIES, /// Used while parsing Dhcp4/Subnet4 structures. SUBNET4, /// Used while parsing shared-networks structures. SHARED_NETWORK, /// Used while parsing Dhcp4/reservation-mode. RESERVATION_MODE, /// Used while parsing Dhcp4/option-def structures. OPTION_DEF, /// Used while parsing Dhcp4/option-data, Dhcp4/subnet4/option-data /// or anywhere option-data is present (client classes, host /// reservations and possibly others). OPTION_DATA, /// Used while parsing Dhcp4/client-classes structures. CLIENT_CLASSES, /// Used while parsing Dhcp4/expired-leases-processing. EXPIRED_LEASES_PROCESSING, /// Used while parsing Dhcp4/server-id structures. SERVER_ID, /// Used while parsing Dhcp4/control-socket structures. CONTROL_SOCKET, /// Used while parsing Dhcp4/dhcp-queue-control structures. DHCP_QUEUE_CONTROL, /// Used while parsing Dhcp4/multi-threading structures. DHCP_MULTI_THREADING, /// Used while parsing Dhcp4/subnet4/pools structures. POOLS, /// Used while parsing Dhcp4/reservations structures. RESERVATIONS, /// Used while parsing Dhcp4/subnet4relay structures. RELAY, /// Used while parsing Dhcp4/loggers structures. LOGGERS, /// Used while parsing Dhcp4/loggers/output_options structures. OUTPUT_OPTIONS, /// Used while parsing Dhcp4/dhcp-ddns. DHCP_DDNS, /// Used while parsing Dhcp4/dhcp-ddns/ncr-protocol NCR_PROTOCOL, /// Used while parsing Dhcp4/dhcp-ddns/ncr-format NCR_FORMAT, /// Used while parsing Dhcp4/dhcp-ddns/replace-client-name. REPLACE_CLIENT_NAME, /// Used while parsing Dhcp4/config-control CONFIG_CONTROL, /// Used while parsing config-control/config-databases CONFIG_DATABASE, /// Used while parsing compatibility parameters COMPATIBILITY, } ParserContext; /// @brief File name std::string file_; /// @brief File name stack std::vector files_; /// @brief Location of the current token /// /// The lexer will keep updating it. This variable will be useful /// for logging errors. isc::dhcp::location loc_; /// @brief Location stack std::vector locs_; /// @brief Lexer state stack std::vector 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 sfiles_; /// @brief Current syntactic context ParserContext 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 DHCP4 mode. Finally, the syntactic context allows the /// error message to be more descriptive if the input string /// does not parse properly. /// /// @param ctx the syntactic context to enter into void enter(const ParserContext& ctx); /// @brief Leave a syntactic context /// /// @throw isc::Unexpected if unbalanced 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 cstack_; /// @brief Common part of parseXXX /// /// @return Element structure representing parsed text. isc::data::ElementPtr parseCommon(); }; }; // end of isc::eval namespace }; // end of isc namespace #endif