summaryrefslogtreecommitdiffstats
path: root/tools/parser.hpp
diff options
context:
space:
mode:
authorjmmv <jmmv@FreeBSD.org>2014-02-14 14:41:25 +0000
committerjmmv <jmmv@FreeBSD.org>2014-02-14 14:41:25 +0000
commita817576754c6af0869b83f2f3efc4659963962a1 (patch)
treea986bed64ee725de3eb52c5f0838f6c59b8aa7d9 /tools/parser.hpp
parentb849e3606cf0dc725ff02712cd92907131c0188c (diff)
downloadFreeBSD-src-a817576754c6af0869b83f2f3efc4659963962a1.zip
FreeBSD-src-a817576754c6af0869b83f2f3efc4659963962a1.tar.gz
Import atf-0.19:
Experimental version released on February 7th, 2014. This is the last release to bundle the code for the deprecated tools. The next release will drop their code and will stop worrying about backwards compatibility between the ATF libraries and what the old tools may or may not support. If you still require the old tools for some reason, grab a copy of the 'tools' directory now. The code in this directory is standalone and does not depend on any internal details of atf-c++ any longer. * Various fixes and improvements to support running as part of the FreeBSD test suite. * Project hosting moved from Google Code (as a subproject of Kyua) to GitHub (as a first-class project). The main reason for the change is the suppression of binary downloads in Google Code on Jan 15th, 2014. See https://github.com/jmmv/atf/ * Removed builtin help from atf-sh(1) and atf-check(1) for simplicity reasons. In other words, their -h option is gone. * Moved the code of the deprecated tools into a 'tools' directory and completely decoupled their code from the internals of atf-c++. The reason for this is to painlessly allow a third-party to maintain a copy of these tools after we delete them because upcoming changes to atf-c++ would break the stale tools.
Diffstat (limited to 'tools/parser.hpp')
-rw-r--r--tools/parser.hpp607
1 files changed, 607 insertions, 0 deletions
diff --git a/tools/parser.hpp b/tools/parser.hpp
new file mode 100644
index 0000000..eb00cb4
--- /dev/null
+++ b/tools/parser.hpp
@@ -0,0 +1,607 @@
+//
+// Automated Testing Framework (atf)
+//
+// Copyright (c) 2007 The NetBSD Foundation, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#if !defined(TOOLS_PARSER_HPP)
+#define TOOLS_PARSER_HPP
+
+#include <istream>
+#include <map>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace tools {
+namespace parser {
+
+// ------------------------------------------------------------------------
+// The "parse_error" class.
+// ------------------------------------------------------------------------
+
+class parse_error : public std::runtime_error,
+ public std::pair< size_t, std::string > {
+ mutable std::string m_msg;
+
+public:
+ parse_error(size_t, std::string);
+ ~parse_error(void) throw();
+
+ const char* what(void) const throw();
+
+ operator std::string(void) const;
+};
+
+// ------------------------------------------------------------------------
+// The "parse_errors" class.
+// ------------------------------------------------------------------------
+
+class parse_errors : public std::runtime_error,
+ public std::vector< parse_error > {
+ std::vector< parse_error > m_errors;
+ mutable std::string m_msg;
+
+public:
+ parse_errors(void);
+ ~parse_errors(void) throw();
+
+ const char* what(void) const throw();
+};
+
+// ------------------------------------------------------------------------
+// The "format_error" class.
+// ------------------------------------------------------------------------
+
+class format_error : public std::runtime_error {
+public:
+ format_error(const std::string&);
+};
+
+// ------------------------------------------------------------------------
+// The "token" class.
+// ------------------------------------------------------------------------
+
+typedef int token_type;
+
+//!
+//! \brief Representation of a read token.
+//!
+//! A pair that contains the information of a token read from a stream.
+//! It contains the token's type and its associated data, if any.
+//!
+struct token {
+ bool m_inited;
+ size_t m_line;
+ token_type m_type;
+ std::string m_text;
+
+public:
+ token(void);
+ token(size_t, const token_type&, const std::string& = "");
+
+ size_t lineno(void) const;
+ const token_type& type(void) const;
+ const std::string& text(void) const;
+
+ operator bool(void) const;
+ bool operator!(void) const;
+};
+
+// ------------------------------------------------------------------------
+// The "tokenizer" class.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief A stream tokenizer.
+//!
+//! This template implements an extremely simple, line-oriented stream
+//! tokenizer. It is only able to recognize one character-long delimiters,
+//! random-length keywords, skip whitespace and, anything that does not
+//! match these rules is supposed to be a word.
+//!
+//! Parameter IS: The input stream's type.
+//!
+template< class IS >
+class tokenizer {
+ IS& m_is;
+ size_t m_lineno;
+ token m_la;
+
+ bool m_skipws;
+ token_type m_eof_type, m_nl_type, m_text_type;
+
+ std::map< char, token_type > m_delims_map;
+ std::string m_delims_str;
+
+ char m_quotech;
+ token_type m_quotetype;
+
+ std::map< std::string, token_type > m_keywords_map;
+
+ token_type alloc_type(void);
+
+ template< class TKZ >
+ friend
+ class parser;
+
+public:
+ tokenizer(IS&, bool, const token_type&, const token_type&,
+ const token_type&, size_t = 1);
+
+ size_t lineno(void) const;
+
+ void add_delim(char, const token_type&);
+ void add_keyword(const std::string&, const token_type&);
+ void add_quote(char, const token_type&);
+
+ token next(void);
+ std::string rest_of_line(void);
+};
+
+template< class IS >
+tokenizer< IS >::tokenizer(IS& p_is,
+ bool p_skipws,
+ const token_type& p_eof_type,
+ const token_type& p_nl_type,
+ const token_type& p_text_type,
+ size_t p_lineno) :
+ m_is(p_is),
+ m_lineno(p_lineno),
+ m_skipws(p_skipws),
+ m_eof_type(p_eof_type),
+ m_nl_type(p_nl_type),
+ m_text_type(p_text_type),
+ m_quotech(-1)
+{
+}
+
+template< class IS >
+size_t
+tokenizer< IS >::lineno(void)
+ const
+{
+ return m_lineno;
+}
+
+template< class IS >
+void
+tokenizer< IS >::add_delim(char delim, const token_type& type)
+{
+ m_delims_map[delim] = type;
+ m_delims_str += delim;
+}
+
+template< class IS >
+void
+tokenizer< IS >::add_keyword(const std::string& keyword,
+ const token_type& type)
+{
+ m_keywords_map[keyword] = type;
+}
+
+template< class IS >
+void
+tokenizer< IS >::add_quote(char ch, const token_type& type)
+{
+ m_quotech = ch;
+ m_quotetype = type;
+}
+
+template< class IS >
+token
+tokenizer< IS >::next(void)
+{
+ if (m_la) {
+ token t = m_la;
+ m_la = token();
+ if (t.type() == m_nl_type)
+ m_lineno++;
+ return t;
+ }
+
+ char ch;
+ std::string text;
+
+ bool done = false, quoted = false;
+ token t(m_lineno, m_eof_type, "<<EOF>>");
+ while (!done && m_is.get(ch).good()) {
+ if (ch == m_quotech) {
+ if (text.empty()) {
+ bool escaped = false;
+ while (!done && m_is.get(ch).good()) {
+ if (!escaped) {
+ if (ch == '\\')
+ escaped = true;
+ else if (ch == '\n') {
+ m_la = token(m_lineno, m_nl_type, "<<NEWLINE>>");
+ throw parse_error(t.lineno(),
+ "Missing double quotes before "
+ "end of line");
+ } else if (ch == m_quotech)
+ done = true;
+ else
+ text += ch;
+ } else {
+ text += ch;
+ escaped = false;
+ }
+ }
+ if (!m_is.good())
+ throw parse_error(t.lineno(),
+ "Missing double quotes before "
+ "end of file");
+ t = token(m_lineno, m_text_type, text);
+ quoted = true;
+ } else {
+ m_is.putback(ch);
+ done = true;
+ }
+ } else {
+ typename std::map< char, token_type >::const_iterator idelim;
+ idelim = m_delims_map.find(ch);
+ if (idelim != m_delims_map.end()) {
+ done = true;
+ if (text.empty())
+ t = token(m_lineno, (*idelim).second,
+ std::string("") + ch);
+ else
+ m_is.putback(ch);
+ } else if (ch == '\n') {
+ done = true;
+ if (text.empty())
+ t = token(m_lineno, m_nl_type, "<<NEWLINE>>");
+ else
+ m_is.putback(ch);
+ } else if (m_skipws && (ch == ' ' || ch == '\t')) {
+ if (!text.empty())
+ done = true;
+ } else
+ text += ch;
+ }
+ }
+
+ if (!quoted && !text.empty()) {
+ typename std::map< std::string, token_type >::const_iterator ikw;
+ ikw = m_keywords_map.find(text);
+ if (ikw != m_keywords_map.end())
+ t = token(m_lineno, (*ikw).second, text);
+ else
+ t = token(m_lineno, m_text_type, text);
+ }
+
+ if (t.type() == m_nl_type)
+ m_lineno++;
+
+ return t;
+}
+
+template< class IS >
+std::string
+tokenizer< IS >::rest_of_line(void)
+{
+ std::string str;
+ while (m_is.good() && m_is.peek() != '\n')
+ str += m_is.get();
+ return str;
+}
+
+// ------------------------------------------------------------------------
+// The "parser" class.
+// ------------------------------------------------------------------------
+
+template< class TKZ >
+class parser {
+ TKZ& m_tkz;
+ token m_last;
+ parse_errors m_errors;
+ bool m_thrown;
+
+public:
+ parser(TKZ& tkz);
+ ~parser(void);
+
+ bool good(void) const;
+ void add_error(const parse_error&);
+ bool has_errors(void) const;
+
+ token next(void);
+ std::string rest_of_line(void);
+ token reset(const token_type&);
+
+ token
+ expect(const token_type&,
+ const std::string&);
+
+ token
+ expect(const token_type&,
+ const token_type&,
+ const std::string&);
+
+ token
+ expect(const token_type&,
+ const token_type&,
+ const token_type&,
+ const std::string&);
+
+ token
+ expect(const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const std::string&);
+
+ token
+ expect(const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const std::string&);
+
+ token
+ expect(const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const token_type&,
+ const std::string&);
+};
+
+template< class TKZ >
+parser< TKZ >::parser(TKZ& tkz) :
+ m_tkz(tkz),
+ m_thrown(false)
+{
+}
+
+template< class TKZ >
+parser< TKZ >::~parser(void)
+{
+ if (!m_errors.empty() && !m_thrown)
+ throw m_errors;
+}
+
+template< class TKZ >
+bool
+parser< TKZ >::good(void)
+ const
+{
+ return m_tkz.m_is.good();
+}
+
+template< class TKZ >
+void
+parser< TKZ >::add_error(const parse_error& pe)
+{
+ m_errors.push_back(pe);
+}
+
+template< class TKZ >
+bool
+parser< TKZ >::has_errors(void)
+ const
+{
+ return !m_errors.empty();
+}
+
+template< class TKZ >
+token
+parser< TKZ >::next(void)
+{
+ token t = m_tkz.next();
+
+ m_last = t;
+
+ if (t.type() == m_tkz.m_eof_type) {
+ if (!m_errors.empty()) {
+ m_thrown = true;
+ throw m_errors;
+ }
+ }
+
+ return t;
+}
+
+template< class TKZ >
+std::string
+parser< TKZ >::rest_of_line(void)
+{
+ return m_tkz.rest_of_line();
+}
+
+template< class TKZ >
+token
+parser< TKZ >::reset(const token_type& stop)
+{
+ token t = m_last;
+
+ while (t.type() != m_tkz.m_eof_type && t.type() != stop)
+ t = next();
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const token_type& t2,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1 && t.type() != t2)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const token_type& t2,
+ const token_type& t3,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1 && t.type() != t2 && t.type() != t3)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const token_type& t2,
+ const token_type& t3,
+ const token_type& t4,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
+ t.type() != t4)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const token_type& t2,
+ const token_type& t3,
+ const token_type& t4,
+ const token_type& t5,
+ const token_type& t6,
+ const token_type& t7,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
+ t.type() != t4 && t.type() != t5 && t.type() != t6 &&
+ t.type() != t7)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+template< class TKZ >
+token
+parser< TKZ >::expect(const token_type& t1,
+ const token_type& t2,
+ const token_type& t3,
+ const token_type& t4,
+ const token_type& t5,
+ const token_type& t6,
+ const token_type& t7,
+ const token_type& t8,
+ const std::string& textual)
+{
+ token t = next();
+
+ if (t.type() != t1 && t.type() != t2 && t.type() != t3 &&
+ t.type() != t4 && t.type() != t5 && t.type() != t6 &&
+ t.type() != t7 && t.type() != t8)
+ throw parse_error(t.lineno(),
+ "Unexpected token `" + t.text() +
+ "'; expected " + textual);
+
+ return t;
+}
+
+#define ATF_PARSER_CALLBACK(parser, func) \
+ do { \
+ if (!(parser).has_errors()) \
+ func; \
+ } while (false)
+
+// ------------------------------------------------------------------------
+// Header parsing.
+// ------------------------------------------------------------------------
+
+typedef std::map< std::string, std::string > attrs_map;
+
+class header_entry {
+ std::string m_name;
+ std::string m_value;
+ attrs_map m_attrs;
+
+public:
+ header_entry(void);
+ header_entry(const std::string&, const std::string&,
+ attrs_map = attrs_map());
+
+ const std::string& name(void) const;
+ const std::string& value(void) const;
+ const attrs_map& attrs(void) const;
+ bool has_attr(const std::string&) const;
+ const std::string& get_attr(const std::string&) const;
+};
+
+typedef std::map< std::string, header_entry > headers_map;
+
+std::pair< size_t, headers_map > read_headers(std::istream&, size_t);
+void write_headers(const headers_map&, std::ostream&);
+void validate_content_type(const headers_map&, const std::string&, int);
+
+} // namespace parser
+} // namespace tools
+
+#endif // !defined(TOOLS_PARSER_HPP)
OpenPOWER on IntegriCloud