/*=============================================================================
    Copyright (c) 2001-2011 Hartmut Kaiser
    http://spirit.sourceforge.net/

    Distributed under 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)
=============================================================================*/

//[reference_karma_includes
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/proto/deep_copy.hpp>
#include <iostream>
#include <string>
//]

//[reference_karma_includes_simple
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <string>
//]

//[reference_karma_output_iterator
typedef std::back_insert_iterator<std::string> output_iterator_type;
//]

//[reference_karma_test
template <typename G>
void test_generator(char const* expected, G const& g)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate(out, g) && s == expected)
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_test_attr
template <typename G, typename T>
void test_generator_attr(char const* expected, G const& g, T const& attr)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate(out, g, attr) && s == expected)
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_test_attr2
template <typename G, typename T1, typename T2>
void test_generator_attr(char const* expected, G const& g, T1 const& attr1, 
    T2 const& attr2)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate(out, g, attr1, attr2) && s == expected)
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_test_attr_delim
template <typename G, typename Delimiter, typename T>
void test_generator_attr_delim(char const* expected, G const& g, Delimiter const& d, T const& attr)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate_delimited(out, g, d, attr) && s == expected)
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_binary_test
template <typename G>
void test_binary_generator(char const* expected, std::size_t size, G const& g)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate(out, g) && !std::memcmp(s.c_str(), expected, size))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_binary_test_attr
template <typename G, typename T>
void test_binary_generator_attr(char const* expected, std::size_t size, G const& g, T const& attr)
{
    std::string s;
    std::back_insert_iterator<std::string> out(s);
    if (boost::spirit::karma::generate(out, g, attr) && !std::memcmp(s.c_str(), expected, size))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}
//]

//[reference_karma_complex
// a simple complex number representation z = a + bi
struct complex
{
    complex (double a, double b)
      : a(a), b(b)
    {}

    double a;
    double b;
};
//]

//[reference_karma_stream_complex
// define streaming operator for the type complex
std::ostream& 
operator<< (std::ostream& os, complex const& z)
{
    os << "{" << z.a << "," << z.b << "}";
    return os;
}
//]

//[reference_karma_auto_complex
/*`The following construct is required to allow the `complex` data structure
   to be utilized as a __fusion__ sequence. This is required as we will 
   emit output for this data structure with a __karma__ sequence:
   `'{' << karma::double_ << ',' << karma::double_ << '}'`.
*/
BOOST_FUSION_ADAPT_STRUCT(
    complex,
    (double, a)
    (double, b)
)

/*`We add a specialization for the create_generator customization point
   defining a custom output format for the complex type. Generally, any 
   specialization for create_generator is expected to return the proto 
   expression to be used to generate output for the type the customization 
   point has been specialized for.
 */
/*`We need to utilize `proto::deep_copy` as the expression contains literals 
   (the `'{'`, `','`, and `'}'`) which normally get embedded in the proto 
   expression by reference only. The deep copy converts the proto tree to 
   hold this by value. The deep copy operation can be left out for simpler 
   proto expressions (not containing references to temporaries). Alternatively
   you could use the `proto::make_expr` facility to build the required
   proto expression.
*/
namespace boost { namespace spirit { namespace traits
{
    template <>
    struct create_generator<complex>
    {
        typedef proto::result_of::deep_copy<
            BOOST_TYPEOF('{' << karma::double_ << ',' << karma::double_ << '}')
        >::type type;

        static type call()
        {
            return proto::deep_copy(
                '{' << karma::double_ << ',' << karma::double_ << '}');
        }
    };
}}}
//]

//[reference_karma_auxiliary_attr_cast_data1
// this is just a test structure we want to use in place of an int
struct int_data
{
    int i;
};

// we provide a custom attribute transformation to allow its use as an int
namespace boost { namespace spirit { namespace traits
{
    template <>
    struct transform_attribute<int_data const, int, karma::domain>
    {
        typedef int type;
        static int pre(int_data const& d) { return d.i; }
    };
}}}
//]

namespace client
{
    using boost::spirit::karma::grammar;
    using boost::spirit::karma::rule;
    using boost::spirit::ascii::space_type;

    //[karma_reference_grammar_definition
    /*`Basic grammar usage:
     */
    struct num_list : grammar<output_iterator_type, space_type, std::vector<int>()>
    {
        num_list() : base_type(start)
        {
            using boost::spirit::int_;
            num = int_;
            start = num << *(',' << num);
        }

        rule<output_iterator_type, space_type, std::vector<int>()> start;
        rule<output_iterator_type, space_type, int()> num;
    };
    //]
}

int main()
{
    ///////////////////////////////////////////////////////////////////////////
    // Operators
    ///////////////////////////////////////////////////////////////////////////
    {
        //[reference_karma_using_declarations_sequence
        using boost::spirit::karma::double_;
        //]

        //[reference_karma_sequence
        test_generator_attr("1.0,2.0", double_ << ',' << double_, std::make_pair(1.0, 2.0));
        //]
    }

    {
        //[reference_karma_using_declarations_alternative
        using boost::spirit::karma::double_;
        using boost::spirit::karma::ascii::string;
        //]

        //[reference_karma_alternative1
        boost::variant<std::string, double> v1(1.0);
        test_generator_attr("1.0", string | double_, v1);
        test_generator_attr("2.0", string | double_, 2.0);
        //]

        //[reference_karma_alternative2
        boost::variant<std::string, double> v2("example");
        test_generator_attr("example", string | double_, v2);
        test_generator_attr("example", string | double_, "example");
        //]
    }

    {
        //[reference_karma_using_declarations_kleene
        using boost::spirit::karma::double_;
        using boost::spirit::karma::space;
        //]

        //[reference_karma_kleene
        std::vector<double> v;
        v.push_back(1.0);
        v.push_back(2.0);
        v.push_back(3.0);
        test_generator_attr_delim("1.0 2.0 3.0 ", *double_, space, v);
        //]
    }

    {
        //[reference_karma_using_declarations_plus
        using boost::spirit::karma::double_;
        using boost::spirit::karma::space;
        //]

        //[reference_karma_plus1
        std::vector<double> v1;
        v1.push_back(1.0);
        v1.push_back(2.0);
        v1.push_back(3.0);
        test_generator_attr_delim("1.0 2.0 3.0 ", +double_, space, v1);
        //]

        //[reference_karma_plus2
        std::vector<double> v2;               // empty container
        test_generator_attr("empty", +double_ | "empty", v2);
        //]
    }

    {
        //[reference_karma_using_declarations_list
        using boost::spirit::karma::double_;
        //]

        //[reference_karma_list
        std::vector<double> v1;
        v1.push_back(1.0);
        test_generator_attr("1.0", double_ % ',', v1);

        v1.push_back(2.0);
        test_generator_attr("1.0,2.0", double_ % ',', v1);
        //]
    }

    {
        //[reference_karma_using_declarations_optional
        using boost::spirit::karma::double_;
        //]

        //[reference_karma_optional1
        boost::optional<double> val(1.0);
        test_generator_attr("1.0", -double_, val);
        test_generator_attr("2.0", -double_, 2.0);
        //]
    }
    {
        using boost::spirit::karma::double_;

        //[reference_karma_optional2
        boost::optional<double> val;          // empty optional
        test_generator_attr("", -double_, val);
        //]
    }

    {
        //[reference_karma_using_declarations_and_predicate
        using boost::spirit::karma::double_;
        using boost::spirit::karma::ascii::char_;
        using boost::spirit::karma::ascii::string;
        using boost::phoenix::ref;
        //]

        //[reference_karma_and_predicate
        test_generator_attr("b", &char_('a') << 'b' | 'c', 'a');
        test_generator_attr("c", &char_('a') << 'b' | 'c', 'x');

        test_generator_attr("abc", &string("123") << "abc" | "def", "123");
        test_generator_attr("def", &string("123") << "abc" | "def", "456");
        //]
    }

    {
        //[reference_karma_using_declarations_not_predicate
        using boost::spirit::karma::double_;
        using boost::spirit::karma::ascii::char_;
        using boost::spirit::karma::ascii::string;
        using boost::phoenix::ref;
        //]

        //[reference_karma_not_predicate
        test_generator_attr("c", !char_('a') << 'b' | 'c', 'a');
        test_generator_attr("b", !char_('a') << 'b' | 'c', 'x');

        test_generator_attr("def", !string("123") << "abc" | "def", "123");
        test_generator_attr("abc", !string("123") << "abc" | "def", "456");
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    //  Directives
    ///////////////////////////////////////////////////////////////////////////
    {
        //[reference_karma_using_declarations_alignment
        using boost::spirit::karma::double_;
        using boost::spirit::karma::left_align;
        using boost::spirit::karma::center;
        using boost::spirit::karma::right_align;
        //]

        //[reference_karma_alignment
        std::pair<double, double> p (1.0, 2.0);
        test_generator_attr("1.0     |2.0", left_align(8)[double_] << '|' << double_, p);
        test_generator_attr("   1.0  |2.0", center(8)[double_] << '|' << double_, p);
        test_generator_attr("     1.0|2.0", right_align(8)[double_] << '|' << double_, p);
        //]
    }

    {
        //[reference_karma_using_declarations_repeat
        using boost::spirit::karma::double_;
        using boost::spirit::karma::repeat;
        //]

        //[reference_karma_repeat
        std::vector<double> v;
        v.push_back(1.0);
        v.push_back(2.0);
        v.push_back(3.0);

        test_generator_attr("[1.0][2.0][3.0]", repeat['[' << double_ << ']'], v);
        test_generator_attr("[1.0][2.0]", repeat(2)['[' << double_ << ']'], v);

        // fails because of insufficient number of items
        test_generator_attr("", repeat(4)['[' << double_ << ']'], v);
        //]
    }

    {
        //[reference_karma_using_declarations_delimit
        using boost::spirit::karma::double_;
        using boost::spirit::karma::delimit;
        using boost::spirit::karma::verbatim;
        //]

        //[reference_karma_delimit
        test_generator_attr("[ 2.0 , 4.3 ] ", 
            delimit['[' << double_ << ',' << double_ << ']'], 2.0, 4.3);
        test_generator_attr("[*2.0*,*4.3*]*", 
            delimit('*')['[' << double_ << ',' << double_ << ']'], 2.0, 4.3);
        test_generator_attr("[2.0, 4.3 ] ", 
            delimit[verbatim['[' << double_ << ','] << double_ << ']'], 2.0, 4.3);
        //]
    }

    {
        //[reference_karma_using_declarations_upperlower
        using boost::spirit::karma::double_;
        using boost::spirit::ascii::upper;
        using boost::spirit::ascii::lower;
        //]

        //[reference_karma_upperlower
        test_generator_attr("abc:2.0e-06", lower["ABC:" << double_], 2e-6);
        test_generator_attr("ABC:2.0E-06", upper["abc:" << double_], 2e-6);
        //]
    }

    {
        //[reference_karma_using_declarations_maxwidth
        using boost::spirit::karma::double_;
        using boost::spirit::karma::maxwidth;
        using boost::spirit::karma::left_align;
        using boost::spirit::karma::right_align;
        //]

        //[reference_karma_maxwidth
        test_generator("01234", maxwidth(5)["0123456789"]);
        test_generator("  012", maxwidth(5)[right_align(12)["0123456789"]]);
        test_generator("0123    ", maxwidth(8)[left_align(8)["0123"]]);
        //]
    }

    {
        //[reference_karma_using_declarations_buffer
        using boost::spirit::karma::double_;
        using boost::spirit::karma::buffer;
        //]

        //[reference_karma_buffer
        std::vector<double> v;                // empty container
        test_generator_attr("", -buffer['[' << +double_ << ']'], v);

        v.push_back(1.0);                     // now, fill the container
        v.push_back(2.0);
        test_generator_attr("[1.02.0]", buffer['[' << +double_ << ']'], v);
        //]
    }

    {
        //[reference_karma_using_declarations_omit
        using boost::spirit::karma::double_;
        using boost::spirit::karma::omit;
        //]

        //[reference_karma_omit
        std::pair<double, double> p (1.0, 2.0);
        test_generator_attr("2.0", omit[double_] << double_, p);
        //]
    }

    {
        //[reference_karma_using_declarations_duplicate
        using boost::spirit::karma::double_;
        using boost::spirit::karma::duplicate;
        using boost::spirit::karma::space;
        //]

        //[reference_karma_duplicate
        test_generator_attr("2.02.0", duplicate[double_ << double_], 2.0);
        test_generator_attr_delim("2.0 2.0 ", duplicate[double_ << double_], space, 2.0);
        //]
    }

    {
        //[reference_karma_using_declarations_columns
        using boost::spirit::karma::double_;
        using boost::spirit::karma::columns;
        using boost::spirit::karma::space;
        //]

        //[reference_karma_columns
        std::vector<double> v;
        v.push_back(1.0);
        v.push_back(2.0);
        v.push_back(3.0);
        test_generator_attr("1.0\n2.0\n3.0\n", columns(1)[*double_], v);
        test_generator_attr_delim("1.0 2.0 \n3.0 \n", columns(2)[*double_], space, v);
        //]
    }

    {
        //[reference_karma_using_declarations_bool
        using boost::spirit::karma::bool_;
        using boost::spirit::karma::lit;
        //]

        //[reference_karma_bool
        test_generator("true", lit(true));
        test_generator("false", bool_(false));
        test_generator_attr("true", bool_(true), true);
        test_generator_attr("", bool_(true), false);    // fails (as true != false)!
        test_generator_attr("false", bool_, false);
        //]
    }

    {
        //[reference_karma_using_declarations_int
        using boost::spirit::karma::int_;
        using boost::spirit::karma::lit;
        //]

        //[reference_karma_int
        test_generator("-2", lit(-2));
        test_generator("-2", int_(-2));
        test_generator_attr("-2", int_(-2), -2);
        test_generator_attr("", int_(-2), 3);    // fails (as -2 != 3)!
        test_generator_attr("-2", int_, -2);
        //]
    }

    {
        //[reference_karma_using_declarations_uint
        using boost::spirit::karma::uint_;
        using boost::spirit::karma::lit;
        //]

        //[reference_karma_uint
        test_generator("2", lit(2U));
        test_generator("2", uint_(2));
        test_generator_attr("2", uint_(2), 2);
        test_generator_attr("", uint_(2), 3);    // fails (as 2 != 3)!
        test_generator_attr("2", uint_, 2);
        //]
    }

    {
        //[reference_karma_using_declarations_real
        using boost::spirit::karma::double_;
        using boost::spirit::karma::lit;
        //]

        //[reference_karma_real
        test_generator("2.0", lit(2.0));
        test_generator("2.0", double_(2));
        test_generator_attr("2.0", double_(2.0), 2.0);
        test_generator_attr("", double_(2.0), 3.0);    // fails (as 2.0 != 3.0)!
        test_generator_attr("-2.0", double_, -2.0);

        test_generator_attr("1.234e05", double_, 1234.0e2);
        test_generator_attr("1.234e-06", double_, 0.000001234);
        //]
    }

    {
        //[reference_karma_using_declarations_char
        using boost::spirit::karma::lit;
        using boost::spirit::ascii::char_;
        //]

        //[reference_karma_char
        test_generator("A", 'A');
        test_generator("A", lit('A'));

        test_generator_attr("a", char_, 'a');
        test_generator("A", char_('A'));
        test_generator_attr("A", char_('A'), 'A');
        test_generator_attr("", char_('A'), 'B');         // fails (as 'A' != 'B')

        test_generator_attr("A", char_('A', 'Z'), 'A');
        test_generator_attr("", char_('A', 'Z'), 'a');    // fails (as 'a' does not belong to 'A'...'Z')

        test_generator_attr("k", char_("a-z0-9"), 'k');
        test_generator_attr("", char_("a-z0-9"), 'A');    // fails (as 'A' does not belong to "a-z0-9")
        //]
    }

    {
        //[reference_karma_using_declarations_char_class
        using boost::spirit::karma::alpha;
        using boost::spirit::karma::upper;
        //]

        //[reference_karma_char_class
        test_generator_attr("a", alpha, 'a');
        test_generator_attr("A", alpha, 'A');
        test_generator_attr("", alpha, '1');          // fails (as isalpha('1') is false)
        test_generator_attr("A", upper[alpha], 'A');
        test_generator_attr("", upper[alpha], 'a');   // fails (as isupper('a') is false)
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    // string
    {
        //[reference_karma_using_declarations_string
        using boost::spirit::karma::lit;
        using boost::spirit::ascii::string;
        //]

        //[reference_karma_string
        test_generator("abc", "abc");
        test_generator("abc", lit("abc"));
        test_generator("abc", lit(std::string("abc")));

        test_generator_attr("abc", string, "abc");
        test_generator("abc", string("abc"));
        test_generator("abc", string(std::string("abc")));

        test_generator_attr("abc", string("abc"), "abc");
        test_generator_attr("", string("abc"), "cba");     // fails (as "abc" != "cba")
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    // auxiliary
    {
        //[reference_karma_using_declarations_eol
        using boost::spirit::karma::eol;
        //]

        //[reference_karma_eol
        test_generator("\n", eol);
        test_generator("abc\n", "abc" << eol);
        //]
    }

    {
        //[reference_karma_using_declarations_attr_cast
        using boost::spirit::karma::int_;
        //]

        //[reference_karma_attr_cast1
        int_data d = { 1 };
        test_generator_attr("1", boost::spirit::karma::attr_cast(int_), d);
        //]
    }

    {
        //[reference_karma_using_declarations_eps
        using boost::spirit::karma::eps;
        using boost::phoenix::val;
        //]

        //[reference_karma_eps
        test_generator("abc", eps[std::cout << val("starting eps example")] << "abc");
        test_generator("abc", eps(true) << "abc");
        test_generator("", eps(false) << "abc");      // fails as eps expression is 'false'
        //]
    }

    {
        //[reference_karma_using_declarations_lazy
        namespace karma = boost::spirit::karma;
        using boost::spirit::karma::_1;
        using boost::spirit::ascii::string;
        using boost::phoenix::val;
        //]

        //[reference_karma_lazy
        test_generator_attr("abc", karma::lazy(val(string)), "abc");
        test_generator("abc", karma::lazy(val(string))[_1 = "abc"]);
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    // stream module
    {
        //[reference_karma_using_declarations_stream
        using boost::spirit::karma::stream;
        //]

        //[reference_karma_stream
        test_generator_attr("abc", stream, "abc");
        test_generator("abc", stream("abc"));
        test_generator_attr("{1.2,2.4}", stream, complex(1.2, 2.4));
        test_generator("{1.2,2.4}", stream(complex(1.2, 2.4)));
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    // auto module
    {
        //[reference_karma_using_declarations_auto
        using boost::spirit::karma::auto_;
        //]

        //[reference_karma_auto
        /*`Emit a simple string using the `karma::string` generator:
         */
        test_generator_attr("abc", auto_, "abc");
        test_generator("abc", auto_("abc"));

        /*`Emit instances of the `complex` data type as defined above using the
           generator defined by the customization point for `complex`:
         */
        test_generator_attr("{1.2,2.4}", auto_, complex(1.2, 2.4));
        test_generator("{1.2,2.4}", auto_(complex(1.2, 2.4)));
        //]
    }

    ///////////////////////////////////////////////////////////////////////////
    // binary module
    {
        //[reference_karma_using_declarations_native_binary
        using boost::spirit::karma::byte_;
        using boost::spirit::karma::word;
        using boost::spirit::karma::dword;
        using boost::spirit::karma::qword;
        //]

        //[reference_karma_native_binary_little
        test_binary_generator("\x01", 1, byte_(0x01));
        test_binary_generator("\x01\x02", 2, word(0x0201));
        test_binary_generator("\x01\x02\x03\x04", 4, dword(0x04030201));
        test_binary_generator("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword(0x0807060504030201LL));

        test_binary_generator_attr("\x01", 1, byte_, 0x01);
        test_binary_generator_attr("\x01\x02", 2, word, 0x0201);
        test_binary_generator_attr("\x01\x02\x03\x04", 4, dword, 0x04030201);
        test_binary_generator_attr("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, 0x0807060504030201LL);
        //]

        //[reference_karma_native_binary_big
        test_binary_generator("\x01", 1, byte_(0x01));
        test_binary_generator("\x02\x01", 2, word(0x0201));
        test_binary_generator("\x04\x03\x02\x01", 4, dword(0x04030201));
        test_binary_generator("\x08\x07\x06\x05\x04\x03\x02\x01", 8, qword(0x0807060504030201LL));

        test_binary_generator_attr("\x01", 1, byte_, 0x01);
        test_binary_generator_attr("\x02\x01", 2, word, 0x0201);
        test_binary_generator_attr("\x04\x03\x02\x01", 4, dword, 0x04030201);
        test_binary_generator_attr("\x08\x07\x06\x05\x04\x03\x02\x01", 8, qword, 0x0807060504030201LL);
        //]
    }

    {
        //[reference_karma_using_declarations_little_binary
        using boost::spirit::karma::little_word;
        using boost::spirit::karma::little_dword;
        using boost::spirit::karma::little_qword;
        //]

        //[reference_karma_little_binary
        test_binary_generator("\x01\x02", 2, little_word(0x0201));
        test_binary_generator("\x01\x02\x03\x04", 4, little_dword(0x04030201));
        test_binary_generator("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword(0x0807060504030201LL));

        test_binary_generator_attr("\x01\x02", 2, little_word, 0x0201);
        test_binary_generator_attr("\x01\x02\x03\x04", 4, little_dword, 0x04030201);
        test_binary_generator_attr("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword, 0x0807060504030201LL);
        //]
    }

    {
        //[reference_karma_using_declarations_big_binary
        using boost::spirit::karma::big_word;
        using boost::spirit::karma::big_dword;
        using boost::spirit::karma::big_qword;
        //]

        //[reference_karma_big_binary
        test_binary_generator("\x02\x01", 2, big_word(0x0201));
        test_binary_generator("\x04\x03\x02\x01", 4, big_dword(0x04030201));
        test_binary_generator("\x08\x07\x06\x05\x04\x03\x02\x01", 8, big_qword(0x0807060504030201LL));

        test_binary_generator_attr("\x02\x01", 2, big_word, 0x0201);
        test_binary_generator_attr("\x04\x03\x02\x01", 4, big_dword, 0x04030201);
        test_binary_generator_attr("\x08\x07\x06\x05\x04\x03\x02\x01", 8, big_qword, 0x0807060504030201LL);
        //]
    }

    // action
    {
        //[reference_karma_using_declarations_action
        using boost::spirit::karma::int_;
        using boost::spirit::karma::string;
        using boost::spirit::karma::_1;
        using boost::phoenix::ref;
        using boost::phoenix::val;
        //]

        //[reference_karma_action
        int i = 42;
        test_generator("42", int_[_1 = ref(i)]);
        test_generator("abc", string[_1 = val("abc")]);
        //]
    }

    // rule
    {
        //[karma_reference_rule
        //`Some using declarations:
        using boost::spirit::karma::rule;
        using boost::spirit::karma::int_;
        using boost::spirit::ascii::space;
        using boost::spirit::ascii::space_type;

        /*`Basic rule:
         */
        rule<output_iterator_type> r;
        r = int_(123);
        test_generator("123", r);

        /*`Rule with consumed attribute:
         */
        rule<output_iterator_type, int()> ra;
        ra = int_;
        test_generator_attr("123", ra, 123);

        /*`Rule with delimiter and consumed attribute:
         */
        rule<output_iterator_type, std::vector<int>(), space_type> rs;
        rs = *int_;
        std::vector<int> v;
        v.push_back(123);
        v.push_back(456);
        v.push_back(789);
        test_generator_attr_delim("123 456 789", rs, space, v);
        //]
    }

    // grammar
    {
        using client::num_list;

        //[karma_reference_grammar_using
        //`Some using declarations:
        using boost::spirit::ascii::space_type;
        using boost::spirit::ascii::space;
        using boost::spirit::int_;
        using boost::spirit::karma::grammar;
        using boost::spirit::karma::rule;
        //]

        //[karma_reference_grammar
        //`How to use the example grammar:
        num_list nlist;
        std::vector<int> v;
        v.push_back(123);
        v.push_back(456);
        v.push_back(789);
        test_generator_attr_delim("123 , 456 , 789", nlist, space, v);
        //]
    }

    // symbols
    {
        //[reference_karma_using_declarations_symbols
        using boost::spirit::karma::symbols;
        //]

        //[reference_karma_symbols
        symbols<char, char const*> sym;

        sym.add
            ('a', "Apple")
            ('b', "Banana")
            ('o', "Orange")
        ;

        test_generator_attr("Banana", sym, 'b');
        //]
    }

    // as
    {
        //[reference_karma_using_declarations_as
        using boost::spirit::utree;
        using boost::spirit::utree_type;
        using boost::spirit::utf8_symbol_type; 
        using boost::spirit::karma::as;
        using boost::spirit::karma::as_string;
        using boost::spirit::karma::char_;
        using boost::spirit::karma::double_;
        //]
        
        //[reference_karma_as
        /*`To properly handle string concatenation with __utree__, we 
           make use of `as_string[]`. We also use `as<T>` to explicitly extract
           a __utree__ symbol node.*/
        
        typedef as<utf8_symbol_type> as_symbol_type;
        as_symbol_type const as_symbol = as_symbol_type();

        utree ut;
        ut.push_back("xyz");
        ut.push_back(1.23);

        test_generator_attr("xyz1.23", as_string[*char_] << double_, ut);
        test_generator_attr("xyz1.23", as<std::string>()[*char_] << double_, ut);
        
        ut.clear();

        ut.push_back(utf8_symbol_type("xyz"));
        ut.push_back(1.23);

        test_generator_attr("xyz1.23", as_symbol[*char_] << double_, ut);
        test_generator_attr("xyz1.23", as<utf8_symbol_type>()[*char_] << double_, ut);
        //]
    }

    return 0;
}
