4

I have a qi::symbol<char, std::string> escapedDoubleQuote that converts a double "" into \".

I try to use this into a more complex parser, and want the result still to be a single string. But didn't succeed. I tried with and without qi::lexeme, qi::as_string and qi::as<std::string>.

#include <iostream>
#include <boost/spirit/home/qi.hpp>
#include <vector>
#include <string>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

struct escapedDoubleQuote_ : qi::symbols<char, std::string >
{
    escapedDoubleQuote_()
    {
        add("\"\"", "\\\"");
    }
} escapedDoubleQuote;

template<typename Iterator>
struct MyGrammar : qi::grammar<Iterator, std::string()>
{
    MyGrammar() : MyGrammar::base_type(escapedField)
    {
        subField = +(~qi::char_("\""));
        escapedField =
                qi::lexeme[  // or qi::as<std::string> ... both seems to do nothing.
                    subField
                    >> -(escapedDoubleQuote >> escapedField)
                ];
    }

    qi::rule<Iterator, std::string()> subField;
    qi::rule<Iterator, std::string()> escapedField;
};

int main()
{
    typedef std::string::const_iterator iterator_type;
    typedef MyGrammar<iterator_type> parser_type;
    parser_type parser;

    std::string input = "123\"\"456\"\"789";
    std::string output;
    iterator_type it = input.begin();
    bool parsed = parse(
            it,
            input.cend(),
            parser,
            output
    );

    if(parsed && it == input.end())
    {
        std::cout << output << std::endl;
        // expected: 123\"456\"789
        // actual  : \"789
    }

    return 0;
}

In the end, I want to use the parser and have as output a single std::string. In the example 123\"\"456\"\"789 should output 123\"456\"789.


Note: I know I can define the escapedDoubleQuote as qi::symbols<char, std::vector<char> > so that the result is achieved, but I want to understand if and how one can combine the strings.

mr_georg
  • 3,635
  • 5
  • 35
  • 52
  • When you are "assigning to a container from a value" and both the container and value are the same type, Spirit overwrites the container with the value (see "boost/spirit/home/qi/detail/assign_to.hpp" searching for `struct assign_to_container_from_value`). If you use `vector` in the `symbols` the types are not the same and another path is chosen reaching `void append_to_string(Attribute& attr, Iterator begin, Iterator end)` that does what you expect. [This hack](http://coliru.stacked-crooked.com/a/97d43835f2dc1123) sidesteps the problem but may break something else. – llonesmiz Oct 26 '14 at 10:46
  • @cv_and_he Yeah that looks overly hacky. I wouldn't touch the traits for builtin types, personally. Good spot on the include though /cc OP: take note! – sehe Oct 26 '14 at 19:39

1 Answers1

0

As @cv_and_he suggested, the "atomic" assignment behaviour stems from the transformation of the exposed attribute type (std::vector<char>) to std::string.

Specifically, it's about the declared attribute of escapedField, because when it is recursively invoked, it creates incompatible synthesized attribute combinations: [std::vector<char>, std::string, std::string]

If you can keep the attributes "equal" you fix the issue:

MyGrammar() : MyGrammar::base_type(start)
{
    subField     = +(~qi::char_("\""));
    escapedField = subField >> -(escapedDoubleQuote >> escapedField);
    start        = escapedField;
}

qi::rule<Iterator, std::vector<char>()> subField, escapedField;
qi::rule<Iterator, std::string()> start;

See it Live On Coliru

(I removed lexeme because you had no skipper in the sample code, see Boost spirit skipper issues)

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Notice that if you make the attribute of the `symbols` be `vector` [it does not work again](http://coliru.stacked-crooked.com/a/ddffd0e029be59c1). So it only works when the attributes in each concatenation are different (in your case you have `vector` that concatenates with the result of `string` and `vector`, which is `string`). I think this is a bug, but I'm really not sure, it probably is needed in another use case. – llonesmiz Oct 26 '14 at 19:47
  • @cv_and_he ooh. You're quite right. That's... Annoying. I'll leave it to the OP whether he wants to report this at the [spirit-general] mailing list. – sehe Oct 26 '14 at 19:52
  • hm, so there is no real solution (yet)? I do not understand the internals of boost::spirit enough for saying that it is a bug or intended behavior. – mr_georg Oct 27 '14 at 14:13
  • I'd say it is a lack of an expectable feature. Expectable because similar constellations of parser expressions (as opposed to rules or transformed sub-attributes) do work as expected. – sehe Oct 27 '14 at 14:18