#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <cmath>
using namespace boost::spirit;
namespace client
{
struct format_data
{
char flag;
char fill;
int width;
int precision;
char type;
};
}
BOOST_FUSION_ADAPT_STRUCT(
client::format_data,
(char, flag)
(char, fill)
(int, width)
(int, precision)
(char, type)
)
namespace client
{
template <typename Iterator>
struct format_grammar : qi::grammar<Iterator, format_data()>
{
format_grammar() : format_grammar::base_type(format)
{
using qi::uint_;
using qi::attr;
using ascii::char_;
using ascii::no_case;
format %= '%' >> flags >> fill >> width >> prec >> type;
flags = char_('+') | char_('-') | attr(' ');
fill = char_('0') | attr(' '); width = uint_ | attr(-1);
prec = '.' >> uint_ | attr(3); type = no_case[char_('e')] | char_('f') | no_case[char_('g')];
};
qi::rule<Iterator, format_data()> format;
qi::rule<Iterator, char()> flags;
qi::rule<Iterator, char()> fill;
qi::rule<Iterator, int()> width;
qi::rule<Iterator, int()> prec;
qi::rule<Iterator, char()> type;
};
}
template <typename T>
struct format_policies : karma::real_policies<T>
{
typedef karma::real_policies<T> base_policy_type;
typedef boost::mpl::int_<
karma::generator_properties::countingbuffer
> properties;
format_policies(char const* fmt = "%f")
{
char const* last = fmt;
while (*last)
last++;
client::format_grammar<char const*> g;
if (!qi::parse(fmt, last, g, format_))
throw std::runtime_error("bad format string");
}
int floatfield(T n) const
{
if (format_.type == 'e' || format_.type == 'E')
return base_policy_type::fmtflags::scientific;
if (format_.type == 'f')
return base_policy_type::fmtflags::fixed;
BOOST_ASSERT(format_.type == 'g' || format_.type == 'G');
return this->base_policy_type::floatfield(n);
}
bool const force_sign(T) const
{
return format_.flag == '+';
}
unsigned precision(T) const
{
return format_.precision;
}
template <typename OutputIterator>
static bool dot (OutputIterator& sink, T n, unsigned precision)
{
if (precision == 0)
return true;
return base_policy_type::dot(sink, n, precision);
}
template <typename CharEncoding, typename Tag, typename OutputIterator>
bool exponent (OutputIterator& sink, long n) const
{
if (format_.type == 'E' || format_.type == 'G') {
return this->base_policy_type::
template exponent<char_encoding::ascii, tag::upper>(sink, n);
}
return this->base_policy_type::
template exponent<CharEncoding, Tag>(sink, n);
}
template <typename Inserter, typename OutputIterator, typename Policies>
bool call (OutputIterator& sink, T n, Policies const& p) const
{
bool r = false;
if (format_.flag == '-') { karma::detail::enable_counting<OutputIterator> counting(sink);
r = Inserter::call_n(sink, n, p);
while(r && int(counting.count()) < format_.width)
r = karma::generate(sink, ' ');
}
else { karma::detail::enable_buffering<OutputIterator> buffering(
sink, format_.width);
{
karma::detail::disable_counting<OutputIterator> nocounting(sink);
r = Inserter::call_n(sink, n, p);
}
buffering.disable();
karma::detail::enable_counting<OutputIterator> counting(
sink, buffering.buffer_size());
while(r && int(counting.count()) < format_.width)
r = karma::generate(sink, format_.fill);
if (r)
buffering.buffer_copy();
}
return r;
}
client::format_data format_;
};
typedef karma::real_generator<double, format_policies<double> > real;
int main()
{
std::cout << "/////////////////////////////////////////////////////////////\n\n";
std::cout << "A format driven floating point number generator for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a printf style format\n";
std::cout << "Type [enter] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty())
break;
try {
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
if (!karma::generate(sink, real(str.c_str()), 4*std::atan(1.0)))
{
std::cout << "-------------------------\n";
std::cout << "Generating failed\n";
std::cout << "-------------------------\n";
}
else
{
std::cout << ">" << generated << "<\n";
}
}
catch (std::runtime_error const&) {
std::cout << "-------------------------\n";
std::cout << "Invalid format specified!\n";
std::cout << "-------------------------\n";
}
}
return 0;
}