1 // Copyright (C) 2017-2021 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 <d2/d2_parser.h>
13 #include <d2/parser_context_decl.h>
14 #include <exceptions/exceptions.h>
15 
16 // Tell Flex the lexer's prototype ...
17 #define YY_DECL isc::d2::D2Parser::symbol_type d2_parser_lex (D2ParserContext& driver)
18 
19 // ... and declare it for the parser's sake.
20 YY_DECL;
21 
22 namespace isc {
23 namespace d2 {
24 
25 /// @brief Evaluation error exception raised when trying to parse.
26 ///
27 /// @todo: This probably should be common for Dhcp4 and Dhcp6.
28 class D2ParseError : public isc::Exception {
29 public:
D2ParseError(const char * file,size_t line,const char * what)30     D2ParseError(const char* file, size_t line, const char* what) :
31         isc::Exception(file, line, what) { };
32 };
33 
34 /// @brief Evaluation context, an interface to the expression evaluation.
35 class D2ParserContext
36 {
37 public:
38 
39     /// @brief Defines currently supported scopes
40     ///
41     /// D2Parser may eventually support multiple levels of parsing scope.
42     /// Currently it supports only the D2 module scope which expects the data
43     /// to be parsed to be a map containing the DhcpDdns element and its
44     /// constituents.
45     ///
46     typedef enum {
47         /// This parser will parse the content as generic JSON.
48         PARSER_JSON,
49 
50         ///< Used for parsing top level (contains DhcpDdns)
51         PARSER_DHCPDDNS,
52 
53         ///< Used for parsing content of DhcpDdns.
54         PARSER_SUB_DHCPDDNS,
55 
56         ///< Used for parsing content of a TSIG key.
57         PARSER_TSIG_KEY,
58 
59         ///< Used for parsing a list of TSIG Keys.
60         PARSER_TSIG_KEYS,
61 
62         ///< Used for parsing content of a DDNS Domain.
63         PARSER_DDNS_DOMAIN,
64 
65         ///< Used for parsing a list a DDNS Domains.
66         PARSER_DDNS_DOMAINS,
67 
68         ///< Used for parsing content of a DNS Server.
69         PARSER_DNS_SERVER,
70 
71         ///< Used for parsing a list of DNS servers.
72         PARSER_DNS_SERVERS,
73 
74         ///< Used for parsing content of hooks libraries.
75         PARSER_HOOKS_LIBRARY
76     } ParserType;
77 
78     /// @brief Default constructor.
79     D2ParserContext();
80 
81     /// @brief destructor.
82     virtual ~D2ParserContext();
83 
84     /// @brief JSON elements being parsed.
85     std::vector<isc::data::ElementPtr> stack_;
86 
87     /// @brief Method called before scanning starts on a string.
88     ///
89     /// @param str string to be parsed
90     /// @param type specifies expected content
91     void scanStringBegin(const std::string& str, ParserType type);
92 
93     /// @brief Method called before scanning starts on a file.
94     ///
95     /// @param f stdio FILE pointer
96     /// @param filename file to be parsed
97     /// @param type specifies expected content
98     void scanFileBegin(FILE* f, const std::string& filename, ParserType type);
99 
100     /// @brief Method called after the last tokens are scanned.
101     void scanEnd();
102 
103     /// @brief Divert input to an include file.
104     ///
105     /// @param filename file to be included
106     void includeFile(const std::string& filename);
107 
108     /// @brief Run the parser on the string specified.
109     ///
110     /// This method parses specified string. Depending on the value of
111     /// parser_type, parser may either check only that the input is valid
112     /// JSON, or may do more specific syntax checking. See @ref ParserType
113     /// for supported syntax checkers.
114     ///
115     /// @param str string to be parsed
116     /// @param parser_type specifies expected content
117     /// @return Element structure representing parsed text.
118     isc::data::ElementPtr parseString(const std::string& str,
119                                       ParserType parser_type);
120 
121     /// @brief Run the parser on the file specified.
122     ///
123     /// This method parses specified file. Depending on the value of
124     /// parser_type, parser may either check only that the input is valid
125     /// JSON, or may do more specific syntax checking. See @ref ParserType
126     /// for supported syntax checkers.
127     ///
128     /// @param filename file to be parsed
129     /// @param parser_type specifies expected content
130     /// @return Element structure representing parsed text.
131     isc::data::ElementPtr parseFile(const std::string& filename,
132                                     ParserType parser_type);
133 
134     /// @brief Error handler
135     ///
136     /// @note The optional position for an error in a string begins by 1
137     /// so the caller should add 1 to the position of the C++ string.
138     ///
139     /// @param loc location within the parsed file when experienced a problem.
140     /// @param what string explaining the nature of the error.
141     /// @param pos optional position for in string errors.
142     /// @throw D2ParseError
143     void error(const isc::d2::location& loc,
144                const std::string& what,
145                size_t pos = 0);
146 
147     /// @brief Error handler
148     ///
149     /// This is a simplified error reporting tool for reporting
150     /// parsing errors.
151     ///
152     /// @param what string explaining the nature of the error.
153     /// @throw D2ParseError
154     void error(const std::string& what);
155 
156     /// @brief Fatal error handler
157     ///
158     /// This is for should not happen but fatal errors.
159     /// Used by YY_FATAL_ERROR macro so required to be static.
160     ///
161     /// @param what string explaining the nature of the error.
162     /// @throw D2ParseError
163     static void fatal(const std::string& what);
164 
165     /// @brief Converts bison's position to one understood by isc::data::Element
166     ///
167     /// Convert a bison location into an element position
168     /// (take the begin, the end is lost)
169     ///
170     /// @param loc location in bison format
171     /// @return Position in format accepted by Element
172     isc::data::Element::Position loc2pos(isc::d2::location& loc);
173 
174     /// @brief Check if a required parameter is present
175     ///
176     /// Check if a required parameter is present in the map at the top
177     /// of the stack and raise an error when it is not.
178     ///
179     /// @param name name of the parameter to check
180     /// @param open_loc location of the opening curly bracket
181     /// @param close_loc location of the closing curly bracket
182     /// @throw D2ParseError
183     void require(const std::string& name,
184                  isc::data::Element::Position open_loc,
185                  isc::data::Element::Position close_loc);
186 
187     /// @brief Check if a parameter is already present
188     ///
189     /// Check if a parameter is already present in the map at the top
190     /// of the stack and raise an error when it is.
191     ///
192     /// @param name name of the parameter to check
193     /// @param loc location of the current parameter
194     /// @throw D2ParseError
195     void unique(const std::string& name,
196                 isc::data::Element::Position loc);
197 
198     /// @brief Defines syntactic contexts for lexical tie-ins
199     typedef enum {
200         ///< This one is used in pure JSON mode.
201         NO_KEYWORD,
202 
203         ///< Used while parsing top level (contains DhcpDdns).
204         CONFIG,
205 
206         ///< Used while parsing content of DhcpDdns.
207         DHCPDDNS,
208 
209         ///< Used while parsing content of a tsig-key
210         TSIG_KEY,
211 
212         ///< Used while parsing a list of tsig-keys
213         TSIG_KEYS,
214 
215         ///< Used while parsing content of DhcpDdns/tsig-keys/algorithm
216         ALGORITHM,
217 
218         ///< Used while parsing content of DhcpDdns/tsig-keys/digest-bits
219         DIGEST_BITS,
220 
221         ///< Used while parsing content of DhcpDdns/tsig-keys/secret
222         SECRET,
223 
224         ///< Used while parsing content of DhcpDdns/forward-ddns
225         FORWARD_DDNS,
226 
227         ///< Used while parsing content of DhcpDdns/reverse-ddns
228         REVERSE_DDNS,
229 
230         ///< Used while parsing content of a ddns-domain
231         DDNS_DOMAIN,
232 
233         ///< Used while parsing a list of ddns-domains
234         DDNS_DOMAINS,
235 
236         ///< Used while parsing content of a dns-server
237         DNS_SERVER,
238 
239         ///< Used while parsing content of list of dns-servers
240         DNS_SERVERS,
241 
242         ///< Used while parsing content of a control-socket
243         CONTROL_SOCKET,
244 
245         /// Used while parsing DhcpDdns/loggers structures.
246         LOGGERS,
247 
248         /// Used while parsing DhcpDdns/loggers/output_options structures.
249         OUTPUT_OPTIONS,
250 
251         /// Used while parsing DhcpDdns/ncr-protocol
252         NCR_PROTOCOL,
253 
254         /// Used while parsing DhcpDdns/ncr-format
255         NCR_FORMAT,
256 
257         /// Used while parsing DhcpDdns/hooks-libraries.
258         HOOKS_LIBRARIES
259 
260     } ParserContext;
261 
262     /// @brief File name
263     std::string file_;
264 
265     /// @brief File name stack
266     std::vector<std::string> files_;
267 
268     /// @brief Location of the current token
269     ///
270     /// The lexer will keep updating it. This variable will be useful
271     /// for logging errors.
272     isc::d2::location loc_;
273 
274     /// @brief Location stack
275     std::vector<isc::d2::location> locs_;
276 
277     /// @brief Lexer state stack
278     std::vector<struct yy_buffer_state*> states_;
279 
280     /// @brief sFile (aka FILE)
281     FILE* sfile_;
282 
283     /// @brief sFile (aka FILE) stack
284     ///
285     /// This is a stack of files. Typically there's only one file (the
286     /// one being currently parsed), but there may be more if one
287     /// file includes another.
288     std::vector<FILE*> sfiles_;
289 
290     /// @brief Current syntactic context
291     ParserContext ctx_;
292 
293     /// @brief Enter a new syntactic context
294     ///
295     /// Entering a new syntactic context is useful in several ways.
296     /// First, it allows the parser to avoid conflicts. Second, it
297     /// allows the lexer to return different tokens depending on
298     /// context (e.g. if "name" string is detected, the lexer
299     /// will return STRING token if in JSON mode or NAME if
300     /// in TSIG_KEY mode. Finally, the syntactic context allows the
301     /// error message to be more descriptive if the input string
302     /// does not parse properly.
303     ///
304     /// @param ctx the syntactic context to enter into
305     void enter(const ParserContext& ctx);
306 
307     /// @brief Leave a syntactic context
308     ///
309     /// @throw isc::Unexpected if unbalanced
310     void leave();
311 
312     /// @brief Get the syntax context name
313     ///
314     /// @return printable name of the context.
315     const std::string contextName();
316 
317  private:
318     /// @brief Flag determining scanner debugging.
319     bool trace_scanning_;
320 
321     /// @brief Flag determining parser debugging.
322     bool trace_parsing_;
323 
324     /// @brief Syntactic context stack
325     std::vector<ParserContext> cstack_;
326 
327     /// @brief Common part of parseXXX
328     ///
329     /// @return Element structure representing parsed text.
330     isc::data::ElementPtr parseCommon();
331 };
332 
333 } // end of isc::eval namespace
334 } // end of isc namespace
335 
336 #endif
337