525 lines
13 KiB
C++
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_)
|