tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / atf / dist / atf-c++ / detail / parser.cpp
blob7e7f680c573fa4a9c375c3cfc89f3b633a64e444
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.
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.
30 #include <sstream>
32 #include "parser.hpp"
33 #include "sanity.hpp"
34 #include "text.hpp"
36 namespace impl = atf::parser;
37 #define IMPL_NAME "atf::parser"
39 // ------------------------------------------------------------------------
40 // The "parse_error" class.
41 // ------------------------------------------------------------------------
43 impl::parse_error::parse_error(size_t line, std::string msg) :
44 std::runtime_error(msg),
45 std::pair< size_t, std::string >(line, msg)
49 impl::parse_error::~parse_error(void)
50 throw()
54 const char*
55 impl::parse_error::what(void)
56 const throw()
58 try {
59 std::ostringstream oss;
60 oss << "LONELY PARSE ERROR: " << first << ": " << second;
61 m_msg = oss.str();
62 return m_msg.c_str();
63 } catch (...) {
64 return "Could not format message for parsing error.";
68 impl::parse_error::operator std::string(void)
69 const
71 return atf::text::to_string(first) + ": " + second;
74 // ------------------------------------------------------------------------
75 // The "parse_errors" class.
76 // ------------------------------------------------------------------------
78 impl::parse_errors::parse_errors(void) :
79 std::runtime_error("No parsing errors yet")
81 m_msg.clear();
84 impl::parse_errors::~parse_errors(void)
85 throw()
89 const char*
90 impl::parse_errors::what(void)
91 const throw()
93 try {
94 m_msg = atf::text::join(*this, "\n");
95 return m_msg.c_str();
96 } catch (...) {
97 return "Could not format messages for parsing errors.";
101 // ------------------------------------------------------------------------
102 // The "format_error" class.
103 // ------------------------------------------------------------------------
105 impl::format_error::format_error(const std::string& w) :
106 std::runtime_error(w.c_str())
110 // ------------------------------------------------------------------------
111 // The "token" class.
112 // ------------------------------------------------------------------------
114 impl::token::token(void) :
115 m_inited(false)
119 impl::token::token(size_t p_line,
120 const token_type& p_type,
121 const std::string& p_text) :
122 m_inited(true),
123 m_line(p_line),
124 m_type(p_type),
125 m_text(p_text)
129 size_t
130 impl::token::lineno(void)
131 const
133 return m_line;
136 const impl::token_type&
137 impl::token::type(void)
138 const
140 return m_type;
143 const std::string&
144 impl::token::text(void)
145 const
147 return m_text;
150 impl::token::operator bool(void)
151 const
153 return m_inited;
156 bool
157 impl::token::operator!(void)
158 const
160 return !m_inited;
163 // ------------------------------------------------------------------------
164 // The "header_entry" class.
165 // ------------------------------------------------------------------------
167 impl::header_entry::header_entry(void)
171 impl::header_entry::header_entry(const std::string& n, const std::string& v,
172 attrs_map as) :
173 m_name(n),
174 m_value(v),
175 m_attrs(as)
179 const std::string&
180 impl::header_entry::name(void) const
182 return m_name;
185 const std::string&
186 impl::header_entry::value(void) const
188 return m_value;
191 const impl::attrs_map&
192 impl::header_entry::attrs(void) const
194 return m_attrs;
197 bool
198 impl::header_entry::has_attr(const std::string& n) const
200 return m_attrs.find(n) != m_attrs.end();
203 const std::string&
204 impl::header_entry::get_attr(const std::string& n) const
206 attrs_map::const_iterator iter = m_attrs.find(n);
207 PRE(iter != m_attrs.end());
208 return (*iter).second;
211 // ------------------------------------------------------------------------
212 // The header tokenizer.
213 // ------------------------------------------------------------------------
215 namespace header {
217 static const impl::token_type eof_type = 0;
218 static const impl::token_type nl_type = 1;
219 static const impl::token_type text_type = 2;
220 static const impl::token_type colon_type = 3;
221 static const impl::token_type semicolon_type = 4;
222 static const impl::token_type dblquote_type = 5;
223 static const impl::token_type equal_type = 6;
225 class tokenizer : public impl::tokenizer< std::istream > {
226 public:
227 tokenizer(std::istream& is, size_t curline) :
228 impl::tokenizer< std::istream >
229 (is, true, eof_type, nl_type, text_type, curline)
231 add_delim(';', semicolon_type);
232 add_delim(':', colon_type);
233 add_delim('=', equal_type);
234 add_quote('"', dblquote_type);
238 static
239 impl::parser< header::tokenizer >&
240 read(impl::parser< header::tokenizer >& p, impl::header_entry& he)
242 using namespace header;
244 impl::token t = p.expect(text_type, nl_type, "a header name");
245 if (t.type() == nl_type) {
246 he = impl::header_entry();
247 return p;
249 std::string hdr_name = t.text();
251 t = p.expect(colon_type, "`:'");
253 t = p.expect(text_type, "a textual value");
254 std::string hdr_value = t.text();
256 impl::attrs_map attrs;
258 for (;;) {
259 t = p.expect(eof_type, semicolon_type, nl_type,
260 "eof, `;' or new line");
261 if (t.type() == eof_type || t.type() == nl_type)
262 break;
264 t = p.expect(text_type, "an attribute name");
265 std::string attr_name = t.text();
267 t = p.expect(equal_type, "`='");
269 t = p.expect(text_type, "word or quoted string");
270 std::string attr_value = t.text();
271 attrs[attr_name] = attr_value;
274 he = impl::header_entry(hdr_name, hdr_value, attrs);
276 return p;
279 static
280 std::ostream&
281 write(std::ostream& os, const impl::header_entry& he)
283 std::string line = he.name() + ": " + he.value();
284 impl::attrs_map as = he.attrs();
285 for (impl::attrs_map::const_iterator iter = as.begin(); iter != as.end();
286 iter++) {
287 PRE((*iter).second.find('\"') == std::string::npos);
288 line += "; " + (*iter).first + "=\"" + (*iter).second + "\"";
291 os << line << "\n";
293 return os;
296 } // namespace header
298 // ------------------------------------------------------------------------
299 // Free functions.
300 // ------------------------------------------------------------------------
302 std::pair< size_t, impl::headers_map >
303 impl::read_headers(std::istream& is, size_t curline)
305 using impl::format_error;
307 headers_map hm;
310 // Grammar
312 // header = entry+ nl
313 // entry = line nl
314 // line = text colon text
315 // (semicolon (text equal (text | dblquote string dblquote)))*
316 // string = quoted_string
319 header::tokenizer tkz(is, curline);
320 impl::parser< header::tokenizer > p(tkz);
322 bool first = true;
323 for (;;) {
324 try {
325 header_entry he;
326 if (!header::read(p, he).good() || he.name().empty())
327 break;
329 if (first && he.name() != "Content-Type")
330 throw format_error("Could not determine content type");
331 else
332 first = false;
334 hm[he.name()] = he;
335 } catch (const impl::parse_error& pe) {
336 p.add_error(pe);
337 p.reset(header::nl_type);
341 if (!is.good())
342 throw format_error("Unexpected end of stream");
344 return std::pair< size_t, headers_map >(tkz.lineno(), hm);
347 void
348 impl::write_headers(const impl::headers_map& hm, std::ostream& os)
350 PRE(!hm.empty());
351 headers_map::const_iterator ct = hm.find("Content-Type");
352 PRE(ct != hm.end());
353 header::write(os, (*ct).second);
354 for (headers_map::const_iterator iter = hm.begin(); iter != hm.end();
355 iter++) {
356 if ((*iter).first != "Content-Type")
357 header::write(os, (*iter).second);
359 os << "\n";
362 void
363 impl::validate_content_type(const impl::headers_map& hm, const std::string& fmt,
364 int version)
366 using impl::format_error;
368 headers_map::const_iterator iter = hm.find("Content-Type");
369 if (iter == hm.end())
370 throw format_error("Could not determine content type");
372 const header_entry& he = (*iter).second;
373 if (he.value() != fmt)
374 throw format_error("Mismatched content type: expected `" + fmt +
375 "' but got `" + he.value() + "'");
377 if (!he.has_attr("version"))
378 throw format_error("Could not determine version");
379 const std::string& vstr = atf::text::to_string(version);
380 if (he.get_attr("version") != vstr)
381 throw format_error("Mismatched version: expected `" +
382 vstr + "' but got `" +
383 he.get_attr("version") + "'");