NetBSD/dist/atf/atf-c++/parser.hpp
jmmv ffe8204c6b Import ATF 0.5
Changes in this release:

* Clauses 3 and 4 of the BSD license used by the project were dropped.
  All the code is now under a 2-clause BSD license compatible with the
  GNU General Public License (GPL).

* Added a C-only binding so that binary test programs do not need to be
  tied to C++ at all.  This binding is now known as the atf-c library.

* Renamed the C++ binding to atf-c++ for consistency with the new atf-c.

* Renamed the POSIX shell binding to atf-sh for consistency with the new
  atf-c and atf-c++.

* Added a -w flag to test programs through which it is possible to specify
  the work directory to be used.  This was possible in prior releases by
  defining the workdir configuration variable (-v workdir=...), but was a
  conceptually incorrect mechanism.

* Test programs now preserve the execution order of test cases when they
  are given in the command line.  Even those mentioned more than once are
  executed multiple times to comply with the user's requests.
2008-05-01 15:22:19 +00:00

525 lines
13 KiB
C++

//
// Automated Testing Framework (atf)
//
// Copyright (c) 2007, 2008 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(_ATF_CXX_PARSER_HPP_)
#define _ATF_CXX_PARSER_HPP_
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace atf {
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();
};
// ------------------------------------------------------------------------
// 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 "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.unget();
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.unget();
} else if (ch == '\n') {
done = true;
if (text.empty())
t = token(m_lineno, m_nl_type, "<<NEWLINE>>");
else
m_is.unget();
} 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&);
};
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;
}
} // namespace parser
} // namespace atf
#endif // !defined(_ATF_CXX_PARSER_HPP_)