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