]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/atf/atf-c++/detail/parser.hpp
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / contrib / atf / atf-c++ / detail / parser.hpp
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 //    notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 //    notice, this list of conditions and the following disclaimer in the
14 //    documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 #if !defined(_ATF_CXX_PARSER_HPP_)
31 #define _ATF_CXX_PARSER_HPP_
32
33 #include <istream>
34 #include <map>
35 #include <ostream>
36 #include <stdexcept>
37 #include <string>
38 #include <utility>
39 #include <vector>
40
41 namespace atf {
42 namespace parser {
43
44 // ------------------------------------------------------------------------
45 // The "parse_error" class.
46 // ------------------------------------------------------------------------
47
48 class parse_error : public std::runtime_error,
49                     public std::pair< size_t, std::string > {
50     mutable std::string m_msg;
51
52 public:
53     parse_error(size_t, std::string);
54     ~parse_error(void) throw();
55
56     const char* what(void) const throw();
57
58     operator std::string(void) const;
59 };
60
61 // ------------------------------------------------------------------------
62 // The "parse_errors" class.
63 // ------------------------------------------------------------------------
64
65 class parse_errors : public std::runtime_error,
66                      public std::vector< parse_error > {
67     std::vector< parse_error > m_errors;
68     mutable std::string m_msg;
69
70 public:
71     parse_errors(void);
72     ~parse_errors(void) throw();
73
74     const char* what(void) const throw();
75 };
76
77 // ------------------------------------------------------------------------
78 // The "format_error" class.
79 // ------------------------------------------------------------------------
80
81 class format_error : public std::runtime_error {
82 public:
83     format_error(const std::string&);
84 };
85
86 // ------------------------------------------------------------------------
87 // The "token" class.
88 // ------------------------------------------------------------------------
89
90 typedef int token_type;
91
92 //!
93 //! \brief Representation of a read token.
94 //!
95 //! A pair that contains the information of a token read from a stream.
96 //! It contains the token's type and its associated data, if any.
97 //!
98 struct token {
99     bool m_inited;
100     size_t m_line;
101     token_type m_type;
102     std::string m_text;
103
104 public:
105     token(void);
106     token(size_t, const token_type&, const std::string& = "");
107
108     size_t lineno(void) const;
109     const token_type& type(void) const;
110     const std::string& text(void) const;
111
112     operator bool(void) const;
113     bool operator!(void) const;
114 };
115
116 // ------------------------------------------------------------------------
117 // The "tokenizer" class.
118 // ------------------------------------------------------------------------
119
120 //!
121 //! \brief A stream tokenizer.
122 //!
123 //! This template implements an extremely simple, line-oriented stream
124 //! tokenizer.  It is only able to recognize one character-long delimiters,
125 //! random-length keywords, skip whitespace and, anything that does not
126 //! match these rules is supposed to be a word.
127 //!
128 //! Parameter IS: The input stream's type.
129 //!
130 template< class IS >
131 class tokenizer {
132     IS& m_is;
133     size_t m_lineno;
134     token m_la;
135
136     bool m_skipws;
137     token_type m_eof_type, m_nl_type, m_text_type;
138
139     std::map< char, token_type > m_delims_map;
140     std::string m_delims_str;
141
142     char m_quotech;
143     token_type m_quotetype;
144
145     std::map< std::string, token_type > m_keywords_map;
146
147     token_type alloc_type(void);
148
149     template< class TKZ >
150     friend
151     class parser;
152
153 public:
154     tokenizer(IS&, bool, const token_type&, const token_type&,
155               const token_type&, size_t = 1);
156
157     size_t lineno(void) const;
158
159     void add_delim(char, const token_type&);
160     void add_keyword(const std::string&, const token_type&);
161     void add_quote(char, const token_type&);
162
163     token next(void);
164     std::string rest_of_line(void);
165 };
166
167 template< class IS >
168 tokenizer< IS >::tokenizer(IS& p_is,
169                            bool p_skipws,
170                            const token_type& p_eof_type,
171                            const token_type& p_nl_type,
172                            const token_type& p_text_type,
173                            size_t p_lineno) :
174     m_is(p_is),
175     m_lineno(p_lineno),
176     m_skipws(p_skipws),
177     m_eof_type(p_eof_type),
178     m_nl_type(p_nl_type),
179     m_text_type(p_text_type),
180     m_quotech(-1)
181 {
182 }
183
184 template< class IS >
185 size_t
186 tokenizer< IS >::lineno(void)
187     const
188 {
189     return m_lineno;
190 }
191
192 template< class IS >
193 void
194 tokenizer< IS >::add_delim(char delim, const token_type& type)
195 {
196     m_delims_map[delim] = type;
197     m_delims_str += delim;
198 }
199
200 template< class IS >
201 void
202 tokenizer< IS >::add_keyword(const std::string& keyword,
203                              const token_type& type)
204 {
205     m_keywords_map[keyword] = type;
206 }
207
208 template< class IS >
209 void
210 tokenizer< IS >::add_quote(char ch, const token_type& type)
211 {
212     m_quotech = ch;
213     m_quotetype = type;
214 }
215
216 template< class IS >
217 token
218 tokenizer< IS >::next(void)
219 {
220     if (m_la) {
221         token t = m_la;
222         m_la = token();
223         if (t.type() == m_nl_type)
224             m_lineno++;
225         return t;
226     }
227
228     char ch;
229     std::string text;
230
231     bool done = false, quoted = false;
232     token t(m_lineno, m_eof_type, "<<EOF>>");
233     while (!done && m_is.get(ch).good()) {
234         if (ch == m_quotech) {
235             if (text.empty()) {
236                 bool escaped = false;
237                 while (!done && m_is.get(ch).good()) {
238                     if (!escaped) {
239                         if (ch == '\\')
240                             escaped = true;
241                         else if (ch == '\n') {
242                             m_la = token(m_lineno, m_nl_type, "<<NEWLINE>>");
243                             throw parse_error(t.lineno(),
244                                               "Missing double quotes before "
245                                               "end of line");
246                         } else if (ch == m_quotech)
247                             done = true;
248                         else
249                             text += ch;
250                     } else {
251                         text += ch;
252                         escaped = false;
253                     }
254                 }
255                 if (!m_is.good())
256                     throw parse_error(t.lineno(),
257                                       "Missing double quotes before "
258                                       "end of file");
259                 t = token(m_lineno, m_text_type, text);
260                 quoted = true;
261             } else {
262                 m_is.unget();
263                 done = true;
264             }
265         } else {
266             typename std::map< char, token_type >::const_iterator idelim;
267             idelim = m_delims_map.find(ch);
268             if (idelim != m_delims_map.end()) {
269                 done = true;
270                 if (text.empty())
271                     t = token(m_lineno, (*idelim).second,
272                                    std::string("") + ch);
273                 else
274                     m_is.unget();
275             } else if (ch == '\n') {
276                 done = true;
277                 if (text.empty())
278                     t = token(m_lineno, m_nl_type, "<<NEWLINE>>");
279                 else
280                     m_is.unget();
281             } else if (m_skipws && (ch == ' ' || ch == '\t')) {
282                 if (!text.empty())
283                     done = true;
284             } else
285                 text += ch;
286         }
287     }
288
289     if (!quoted && !text.empty()) {
290         typename std::map< std::string, token_type >::const_iterator ikw;
291         ikw = m_keywords_map.find(text);
292         if (ikw != m_keywords_map.end())
293             t = token(m_lineno, (*ikw).second, text);
294         else
295             t = token(m_lineno, m_text_type, text);
296     }
297
298     if (t.type() == m_nl_type)
299         m_lineno++;
300
301     return t;
302 }
303
304 template< class IS >
305 std::string
306 tokenizer< IS >::rest_of_line(void)
307 {
308     std::string str;
309     while (m_is.good() && m_is.peek() != '\n')
310         str += m_is.get();
311     return str;
312 }
313
314 // ------------------------------------------------------------------------
315 // The "parser" class.
316 // ------------------------------------------------------------------------
317
318 template< class TKZ >
319 class parser {
320     TKZ& m_tkz;
321     token m_last;
322     parse_errors m_errors;
323     bool m_thrown;
324
325 public:
326     parser(TKZ& tkz);
327     ~parser(void);
328
329     bool good(void) const;
330     void add_error(const parse_error&);
331     bool has_errors(void) const;
332
333     token next(void);
334     std::string rest_of_line(void);
335     token reset(const token_type&);
336
337     token
338     expect(const token_type&,
339            const std::string&);
340
341     token
342     expect(const token_type&,
343            const token_type&,
344            const std::string&);
345
346     token
347     expect(const token_type&,
348            const token_type&,
349            const token_type&,
350            const std::string&);
351
352     token
353     expect(const token_type&,
354            const token_type&,
355            const token_type&,
356            const token_type&,
357            const std::string&);
358
359     token
360     expect(const token_type&,
361            const token_type&,
362            const token_type&,
363            const token_type&,
364            const token_type&,
365            const token_type&,
366            const token_type&,
367            const std::string&);
368
369     token
370     expect(const token_type&,
371            const token_type&,
372            const token_type&,
373            const token_type&,
374            const token_type&,
375            const token_type&,
376            const token_type&,
377            const token_type&,
378            const std::string&);
379 };
380
381 template< class TKZ >
382 parser< TKZ >::parser(TKZ& tkz) :
383     m_tkz(tkz),
384     m_thrown(false)
385 {
386 }
387
388 template< class TKZ >
389 parser< TKZ >::~parser(void)
390 {
391     if (!m_errors.empty() && !m_thrown)
392         throw m_errors;
393 }
394
395 template< class TKZ >
396 bool
397 parser< TKZ >::good(void)
398     const
399 {
400     return m_tkz.m_is.good();
401 }
402
403 template< class TKZ >
404 void
405 parser< TKZ >::add_error(const parse_error& pe)
406 {
407     m_errors.push_back(pe);
408 }
409
410 template< class TKZ >
411 bool
412 parser< TKZ >::has_errors(void)
413     const
414 {
415     return !m_errors.empty();
416 }
417
418 template< class TKZ >
419 token
420 parser< TKZ >::next(void)
421 {
422     token t = m_tkz.next();
423
424     m_last = t;
425
426     if (t.type() == m_tkz.m_eof_type) {
427         if (!m_errors.empty()) {
428             m_thrown = true;
429             throw m_errors;
430         }
431     }
432
433     return t;
434 }
435
436 template< class TKZ >
437 std::string
438 parser< TKZ >::rest_of_line(void)
439 {
440     return m_tkz.rest_of_line();
441 }
442
443 template< class TKZ >
444 token
445 parser< TKZ >::reset(const token_type& stop)
446 {
447     token t = m_last;
448
449     while (t.type() != m_tkz.m_eof_type && t.type() != stop)
450         t = next();
451
452     return t;
453 }
454
455 template< class TKZ >
456 token
457 parser< TKZ >::expect(const token_type& t1,
458                       const std::string& textual)
459 {
460     token t = next();
461
462     if (t.type() != t1)
463         throw parse_error(t.lineno(),
464                           "Unexpected token `" + t.text() +
465                           "'; expected " + textual);
466
467     return t;
468 }
469
470 template< class TKZ >
471 token
472 parser< TKZ >::expect(const token_type& t1,
473                       const token_type& t2,
474                       const std::string& textual)
475 {
476     token t = next();
477
478     if (t.type() != t1 && t.type() != t2)
479         throw parse_error(t.lineno(),
480                           "Unexpected token `" + t.text() +
481                           "'; expected " + textual);
482
483     return t;
484 }
485
486 template< class TKZ >
487 token
488 parser< TKZ >::expect(const token_type& t1,
489                       const token_type& t2,
490                       const token_type& t3,
491                       const std::string& textual)
492 {
493     token t = next();
494
495     if (t.type() != t1 && t.type() != t2 && t.type() != t3)
496         throw parse_error(t.lineno(),
497                           "Unexpected token `" + t.text() +
498                           "'; expected " + textual);
499
500     return t;
501 }
502
503 template< class TKZ >
504 token
505 parser< TKZ >::expect(const token_type& t1,
506                       const token_type& t2,
507                       const token_type& t3,
508                       const token_type& t4,
509                       const std::string& textual)
510 {
511     token t = next();
512
513     if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
514         t.type() != t4)
515         throw parse_error(t.lineno(),
516                           "Unexpected token `" + t.text() +
517                           "'; expected " + textual);
518
519     return t;
520 }
521
522 template< class TKZ >
523 token
524 parser< TKZ >::expect(const token_type& t1,
525                       const token_type& t2,
526                       const token_type& t3,
527                       const token_type& t4,
528                       const token_type& t5,
529                       const token_type& t6,
530                       const token_type& t7,
531                       const std::string& textual)
532 {
533     token t = next();
534
535     if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
536         t.type() != t4 && t.type() != t5 && t.type() != t6 &&
537         t.type() != t7)
538         throw parse_error(t.lineno(),
539                           "Unexpected token `" + t.text() +
540                           "'; expected " + textual);
541
542     return t;
543 }
544
545 template< class TKZ >
546 token
547 parser< TKZ >::expect(const token_type& t1,
548                       const token_type& t2,
549                       const token_type& t3,
550                       const token_type& t4,
551                       const token_type& t5,
552                       const token_type& t6,
553                       const token_type& t7,
554                       const token_type& t8,
555                       const std::string& textual)
556 {
557     token t = next();
558
559     if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
560         t.type() != t4 && t.type() != t5 && t.type() != t6 &&
561         t.type() != t7 && t.type() != t8)
562         throw parse_error(t.lineno(),
563                           "Unexpected token `" + t.text() +
564                           "'; expected " + textual);
565
566     return t;
567 }
568
569 #define ATF_PARSER_CALLBACK(parser, func) \
570     do { \
571         if (!(parser).has_errors()) \
572             func; \
573     } while (false)
574
575 // ------------------------------------------------------------------------
576 // Header parsing.
577 // ------------------------------------------------------------------------
578
579 typedef std::map< std::string, std::string > attrs_map;
580
581 class header_entry {
582     std::string m_name;
583     std::string m_value;
584     attrs_map m_attrs;
585
586 public:
587     header_entry(void);
588     header_entry(const std::string&, const std::string&,
589                  attrs_map = attrs_map());
590
591     const std::string& name(void) const;
592     const std::string& value(void) const;
593     const attrs_map& attrs(void) const;
594     bool has_attr(const std::string&) const;
595     const std::string& get_attr(const std::string&) const;
596 };
597
598 typedef std::map< std::string, header_entry > headers_map;
599
600 std::pair< size_t, headers_map > read_headers(std::istream&, size_t);
601 void write_headers(const headers_map&, std::ostream&);
602 void validate_content_type(const headers_map&, const std::string&, int);
603
604 } // namespace parser
605 } // namespace atf
606
607 #endif // !defined(_ATF_CXX_PARSER_HPP_)