187 lines
6.0 KiB
C++
187 lines
6.0 KiB
C++
/*=============================================================================
|
|
Copyright (c) 2001-2007 Hartmut Kaiser
|
|
http://spirit.sourceforge.net/
|
|
|
|
Use, modification and distribution is subject to the Boost Software
|
|
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt)
|
|
=============================================================================*/
|
|
|
|
#include <boost/detail/lightweight_test.hpp>
|
|
#include <boost/preprocessor/cat.hpp>
|
|
#include <boost/spirit/include/classic_core.hpp>
|
|
#include <boost/spirit/include/classic_ast.hpp>
|
|
#include <boost/spirit/include/classic_tree_to_xml.hpp>
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning(push)
|
|
# pragma warning(disable: 4702) // unreachable code
|
|
#endif
|
|
#include <boost/iostreams/stream.hpp>
|
|
#ifdef _MSC_VER
|
|
# pragma warning(pop)
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
using namespace BOOST_SPIRIT_CLASSIC_NS;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
struct calculator : public grammar<calculator>
|
|
{
|
|
static const int integerID = 1;
|
|
static const int factorID = 2;
|
|
static const int termID = 3;
|
|
static const int expressionID = 4;
|
|
|
|
template <typename ScannerT>
|
|
struct definition
|
|
{
|
|
definition(calculator const& /*self*/)
|
|
{
|
|
// Start grammar definition
|
|
integer = leaf_node_d[ lexeme_d[
|
|
(!ch_p('-') >> +digit_p)
|
|
] ];
|
|
|
|
factor = integer
|
|
| inner_node_d[ch_p('(') >> expression >> ch_p(')')]
|
|
| (root_node_d[ch_p('-')] >> factor);
|
|
|
|
term = factor >>
|
|
*( (root_node_d[ch_p('*')] >> factor)
|
|
| (root_node_d[ch_p('/')] >> factor)
|
|
);
|
|
|
|
expression = term >>
|
|
*( (root_node_d[ch_p('+')] >> term)
|
|
| (root_node_d[ch_p('-')] >> term)
|
|
);
|
|
// End grammar definition
|
|
|
|
// turn on the debugging info.
|
|
BOOST_SPIRIT_DEBUG_RULE(integer);
|
|
BOOST_SPIRIT_DEBUG_RULE(factor);
|
|
BOOST_SPIRIT_DEBUG_RULE(term);
|
|
BOOST_SPIRIT_DEBUG_RULE(expression);
|
|
}
|
|
|
|
rule<ScannerT, parser_context<>, parser_tag<expressionID> > expression;
|
|
rule<ScannerT, parser_context<>, parser_tag<termID> > term;
|
|
rule<ScannerT, parser_context<>, parser_tag<factorID> > factor;
|
|
rule<ScannerT, parser_context<>, parser_tag<integerID> > integer;
|
|
|
|
rule<ScannerT, parser_context<>, parser_tag<expressionID> > const&
|
|
start() const { return expression; }
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/// this is a Boost.IoStreams source device usable to create a istream on
|
|
/// top of a random access container (i.e. vector<>)
|
|
template<typename Container>
|
|
class container_device
|
|
{
|
|
public:
|
|
typedef typename Container::value_type char_type;
|
|
typedef boost::iostreams::sink_tag category;
|
|
|
|
container_device(Container& container)
|
|
: container_(container), pos_(0)
|
|
{}
|
|
|
|
/// Write up to n characters to the underlying data sink into the
|
|
/// buffer s, returning the number of characters written
|
|
std::streamsize write(const char_type* s, std::streamsize n)
|
|
{
|
|
std::streamsize result = 0;
|
|
if (pos_ != container_.size()) {
|
|
std::streamsize amt =
|
|
static_cast<std::streamsize>(container_.size() - pos_);
|
|
result = (std::min)(n, amt);
|
|
std::copy(s, s + result, container_.begin() + pos_);
|
|
pos_ += static_cast<size_type>(result);
|
|
}
|
|
if (result < n) {
|
|
container_.insert(container_.end(), s, s + n);
|
|
pos_ = container_.size();
|
|
}
|
|
return n;
|
|
}
|
|
|
|
Container& container() { return container_; }
|
|
|
|
private:
|
|
typedef typename Container::size_type size_type;
|
|
Container& container_;
|
|
size_type pos_;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#define EXPECTED_XML_OUTPUT "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\
|
|
<!DOCTYPE parsetree SYSTEM \"parsetree.dtd\">\n\
|
|
<!-- 1+2 -->\n\
|
|
<parsetree version=\"1.0\">\n\
|
|
<parsenode>\n\
|
|
<value>+</value>\n\
|
|
<parsenode>\n\
|
|
<value>1</value>\n\
|
|
</parsenode>\n\
|
|
<parsenode>\n\
|
|
<value>2</value>\n\
|
|
</parsenode>\n\
|
|
</parsenode>\n\
|
|
</parsetree>\n"
|
|
|
|
#define EXPECTED_XML_OUTPUT_WIDE BOOST_PP_CAT(L, EXPECTED_XML_OUTPUT)
|
|
|
|
bool test(wchar_t const *text)
|
|
{
|
|
typedef std::basic_string<wchar_t>::iterator iterator_t;
|
|
|
|
std::basic_string<wchar_t> input(text);
|
|
calculator calc;
|
|
tree_parse_info<iterator_t> ast_info =
|
|
ast_parse(iterator_t(input.begin()), iterator_t(input.end()),
|
|
calc >> end_p, space_p);
|
|
|
|
std::basic_string<wchar_t> out;
|
|
{
|
|
typedef container_device<std::basic_string<wchar_t> > device_type;
|
|
boost::iostreams::stream<device_type> outsink(out);
|
|
basic_tree_to_xml<wchar_t>(outsink, ast_info.trees, input);
|
|
}
|
|
return out == EXPECTED_XML_OUTPUT_WIDE;
|
|
}
|
|
|
|
bool test(char const *text)
|
|
{
|
|
typedef std::string::iterator iterator_t;
|
|
|
|
std::string input(text);
|
|
calculator calc;
|
|
tree_parse_info<iterator_t> ast_info =
|
|
ast_parse(iterator_t(input.begin()), iterator_t(input.end()),
|
|
calc >> end_p, space_p);
|
|
|
|
std::string out;
|
|
{
|
|
typedef container_device<std::string> device_type;
|
|
boost::iostreams::stream<device_type> outsink(out);
|
|
basic_tree_to_xml<char>(outsink, ast_info.trees, input);
|
|
}
|
|
return out == EXPECTED_XML_OUTPUT;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
BOOST_TEST(test("1+2"));
|
|
if (std::has_facet<std::ctype<wchar_t> >(std::locale()))
|
|
{
|
|
BOOST_TEST(test(L"1+2"));
|
|
}
|
|
return boost::report_errors();
|
|
}
|