Assigning Parsers to Auto Variables

Assigning parsers to auto variables

Spirit Parsers are not designed to be used with auto in Spirit V2.

This is because the underlying Proto expression templates hold references to the temporaries.

You can use

  • qi::copy() (existing in the trunk after boost_1_55_0, not in any released version at this time)
  • boost::proto::deep_copy
  • or BOOST_SPIRIT_AUTO (first coined here)

I've written about these things more often on SO: https://stackoverflow.com/search?q=user%3A85371+deep_copy, specifically, this:

  • boost spirit V2 qi bug associated with optimization level

Boost Spirit X3 will not have this limitation.

Boost Spirit optional parser and backtracking

Firstly, it's UB to use the auto variables to keep the expression templates, because they hold references to the temporaries "a" and "b" [1].

Instead write

expr = +qi::char_("a") >> -(qi::char_("b") >> +qi::char_("a"));

or, if you insist:

auto a = boost::proto::deep_copy(qi::char_("a"));
auto b = boost::proto::deep_copy(qi::char_("b"));
expr = +a >> -(b >> +a);

Now noticing the >> lit("bc") part hiding in the parse call, suggests you may expect backtracking to on succesfully matched tokens when a parse failure happens down the road.

That doesn't happen: Spirit generates PEG grammars, and always greedily matches from left to right.


On to the sample given, ab results, even though backtracking does occur, the effects on the attribute are not rolled back without qi::hold: Live On Coliru

Container attributes are passed along by ref and the effects of previous (successful) expressions is not rolled back, unless you tell Spirit too. This way, you can "pay for what you use" (as copying temporaries all the time would be costly).

See e.g.

  • boost::spirit::qi duplicate parsing on the output
  • Understanding Boost.spirit's string parser
  • Boost spirit revert parsing
<a>
<try>abc</try>
<success>bc</success>
<attributes>[a]</attributes>
</a>
<a>
<try>bc</try>
<fail/>
</a>
<b>
<try>bc</try>
<success>c</success>
<attributes>[b]</attributes>
</b>
<a>
<try>c</try>
<fail/>
</a>
<bc>
<try>bc</try>
<success></success>
<attributes>[]</attributes>
</bc>
Success: 'ab'

[1] see here:

  • Assigning parsers to auto variables
  • Generating Spirit parser expressions from a variadic list of alternative parser expressions
  • boost spirit V2 qi bug associated with optimization level

Boost Qi Composing rules using Functions

Quite simply: using auto with Spirit (or any EDSL based on Boost Proto and Boost Phoenix) is most likely Undefined Behaviour¹

Now, you can usually fix this using

  • BOOST_SPIRIT_AUTO
  • boost::proto::deep_copy
  • the new facility that's coming in the most recent version of Boost (TODO add link)

In this case,

template<typename AttrName, typename Value>
auto attribute(AttrName attrName, Value value) {
return boost::proto::deep_copy(attrName >> ':' >> value);
}

fixes it: Live On Coliru

Alternatively

  1. you could use qi::lazy[] with inherited attributes.

    I do very similar things in the prop_key rule in Reading JSON file with C++ and BOOST.

  2. you could have a look at the Keyword List Operator from the Spirit Repository. It's designed to allow easier construction of grammars like:

    no_constraint_person_rule %=
    kwd("name")['=' > parse_string ]
    / kwd("age") ['=' > int_]
    / kwd("size") ['=' > double_ > 'm']
    ;
  3. This you could potentially combine with the Nabialek Trick. I'd search the answers on SO for examples. (One is Grammar balancing issue)


¹ Except for entirely stateless actors (Eric Niebler on this) and expression placeholders. See e.g.

  • Assigning parsers to auto variables
  • undefined behaviour somewhere in boost::spirit::qi::phrase_parse
  • C++ Boost qi recursive rule construction
  • boost spirit V2 qi bug associated with optimization level

Some examples

  • Define parsers parameterized with sub-parsers in Boost Spirit
  • Generating Spirit parser expressions from a variadic list of alternative parser expressions

Parsing using boost Spirit into STL vector

int_ >> int_ synthesizes a tuple of int, int.¹

If you want to force synthesizing a container, make it repeat(2) [ int_ ] to express that.

Sidenote: As you'll see below, there's room for attribute compatibility even if the bound attribute is a cotainer, so you don't need to.

Sidenote: auto with parser expressions is asking undefined behaviour: Assigning parsers to auto variables

Sidenote: your input uses space delimiter, but the parser doesn't. That will never work.

Your code sample seems to have little to do with the goal, so here's my small example:

1. vector<int>:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
using Vec = std::vector<int>;
namespace qi = boost::spirit::qi;

template <typename It> Vec parse_ivec(It first, It last) {
Vec v;
if (qi::phrase_parse(first, last, qi::int_ >> qi::int_ >> qi::eoi, qi::space, v))
return v;
throw std::runtime_error("Parse failed");
}

Vec parse_ivec(std::string const& r) { return parse_ivec(begin(r), end(r)); }

int main() {
for (int i : parse_ivec("12 13")) {
std::cout << i << "\n";
}
}

Prints

12
13

2. Adapted structs

Alternatively, if you wanted a struct, not std::vector<>:

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
struct Vec { int a, b; };
BOOST_FUSION_ADAPT_STRUCT(Vec, a, b)

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

template <typename It> Vec parse_ivec(It first, It last) {
Vec v;
if (qi::phrase_parse(first, last, qi::int_ >> qi::int_ >> qi::eoi, qi::space, v))
return v;
throw std::runtime_error("Parse failed");
}

Vec parse_ivec(std::string const& r) { return parse_ivec(begin(r), end(r)); }

int main() {
Vec vec = parse_ivec("12 13");
std::cout << vec.a << " " << vec.b << "\n";
}

Prints

12 13

3. std::tuple<int, int>:

Live On Coliru

#include <boost/fusion/adapted/std_tuple.hpp>
using Vec = std::tuple<int, int>;

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

template <typename It> Vec parse_ivec(It first, It last) {
Vec v;
if (qi::phrase_parse(first, last, qi::int_ >> qi::int_ >> qi::eoi, qi::space, v))
return v;
throw std::runtime_error("Parse failed");
}

Vec parse_ivec(std::string const& r) { return parse_ivec(begin(r), end(r)); }

int main() {
Vec vec = parse_ivec("12 13");
std::cout << std::get<0>(vec) << " " << std::get<1>(vec) << "\n";
}

Prints

12 13

¹ Technically, a Fusion sequence, which can be made compatible with a host of types like std::pair, or your own types adapted.

Boost Spirit Qi crashes for memory violation

The problem is the use of auto expressions to capture rules, which deduces the types from the parser expressions. That type is a proto-expression tree, which captures any relations by reference, but that means many of _the intermediates are gone after the end of the enclosing full-expresion (see C++: Life span of temporary arguments?).

This is pretty well-known, as you can see here:

  • Assigning parsers to auto variables
  • boost spirit V2 qi bug associated with optimization level
  • undefined behaviour somewhere in boost::spirit::qi::phrase_parse
  • And some more

Here's the simplest fix:

auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));

If you also fix the missing intialization of the local variables it works correctly:

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

namespace bsq = boost::spirit::qi;

int main()
{
std::cout << "BOOST_VERSION: " << BOOST_VERSION << std::endl;

std::uint16_t major = 0, minor = 0, build = 0, revision = 0;

auto versionParser = bsq::copy(
bsq::uint_
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_)
>> -('.' >> bsq::uint_));

std::string version = "3.5.1";

auto start = version.begin();
if (!bsq::parse(start, version.end(), versionParser, major, minor, build, revision))
{
std::cout << "Error!\n";
}

std::cout << major << "-" << minor << "-" << build << "-" << revision << std::endl;
}

Prints

BOOST_VERSION: 106600
3-5-1-0

Additional Notes

  1. To avoid the whole "unitialized attribute" situation, let's make it so the parser assigns to all elements, even if unspecified in the input text:

        >> ('.' >> bsq::uint_ | bsq::attr(0))
    >> ('.' >> bsq::uint_ | bsq::attr(0))
    >> ('.' >> bsq::uint_ | bsq::attr(0))
  2. To diagnose errors where there is trailing "garbage" (like with "3.4bogus"), you could add a check that the full input is parsed:

    auto versionParser = bsq::copy(
    bsq::uint_
    >> ('.' >> bsq::uint_ | bsq::attr(0))
    >> ('.' >> bsq::uint_ | bsq::attr(0))
    >> ('.' >> bsq::uint_ | bsq::attr(0))
    >> bsq::eoi);
  3. Because a version is semantically a tuple, why not represent it as such?

    using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>;
    Version parsed;

    if (!bsq::parse(version.begin(), version.end(), versionParser, parsed))
    std::cout << "Error!\n";

    That way you can even say:

    using boost::fusion::operator<<;

    auto obsolete = parsed < Version(3, 4, 0, 0);
    std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";

Combining those:

Live On Coliru

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>

namespace bsq = boost::spirit::qi;

int main() {
auto versionParser = bsq::copy(
bsq::uint_
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> ('.' >> bsq::uint_ | bsq::attr(0))
>> bsq::eoi);

std::string version = "3.5.1";

using Version = std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>;
Version parsed;

if (!bsq::parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";

using boost::fusion::operator<<;

auto obsolete = parsed < Version(3, 4, 0, 0);
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}

Prints

Version (3 5 1 0) supported

std::tuple sucks?

I agree. So, equivalently write your own struct:

Live On Coliru

struct Version {
uint16_t major, minor, revision, build;

auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};

BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)

Gettin' With The Times

Note that Spirit X3 (Getting into boost spirit; Qi or X3?) doesn't have the auto-issues that you ran into:

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>

#include <boost/fusion/include/io.hpp>
#include <iostream>

namespace bsx = boost::spirit::x3;

struct Version {
uint16_t major, minor, revision, build;

auto key() const { return std::tie(major, minor, revision, build); }
bool operator<(Version const& b) const { return key() < b.key(); }
};

BOOST_FUSION_ADAPT_STRUCT(Version, major, minor, revision, build)

int main() {
auto versionParser = bsx::uint_
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> ('.' >> bsx::uint_ | bsx::attr(0))
>> bsx::eoi;

std::string version = "3.5.1";

Version parsed;

if (!parse(version.begin(), version.end(), versionParser, parsed))
std::cout << "Error!\n";

using boost::fusion::operator<<;

auto obsolete = parsed < Version{3, 4, 0, 0};
std::cout << "Version " << parsed << " " << (obsolete? "too old" : "supported") << "\n";
}

Also printing the same.

boost spirit qi parser failed in release and pass in debug

You can't use auto with Spirit v2:

  • Assigning parsers to auto variables

You have Undefined Behaviour

DEMO

I tried to make (more) sense of the rest of the code. There were various instances that would never work:

  1. txt('=') is an invalid Qi expression. I assumed you wanted txt >> ('=') instead

  2. qi::char_("a-zA-Z0-9_.\\:$\\-{}[]+/()") doesn't do what you think because $-{ is actually the character "range" \x24-\x7b... Escape the - (or put it at the very end/start of the set like in the other char_ call).

  3. qi::char_('-','.','_') can't work. Did you mean qi::char_("-._")?

  4. specialtxt and anytxt were unused...

  5. prefer const_iterator

  6. prefer namespace aliases above using namespace to prevent hard-to-detect errors

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;

int main() {
std::string const s = "abc=[];";

auto specialtxt = qi::copy(*(qi::char_("-._")));
auto anytxt = qi::copy(*(qi::char_("a-zA-Z0-9_.\\:$\\-{}[]+/()")));
(void) specialtxt;
(void) anytxt;

auto txt = qi::copy(qi::no_skip[*(qi::char_("a-zA-Z0-9_.\\:$\'-"))]);

qi::rule<std::string::const_iterator, qi::space_type> rule2 = txt >> '=' >> '[' >> ']';

auto begin = s.begin();
auto end = s.end();

if (qi::phrase_parse(begin, end, rule2, qi::space)) {
std::cout << "MATCH" << std::endl;
} else {
std::cout << "NO MATCH" << std::endl;
}

if (begin != end) {
std::cout << "Trailing unparsed: '" << std::string(begin, end) << "'\n";
}

}

Printing

MATCH
Trailing unparsed: ';'

Boost.spirit segmentation fault when parsing with composite grammar

Giving you some free advice beyond the duplicate: Assigning parsers to auto variables

Let's parse the simple FNT sample:

info face="Arial" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0
common lineHeight=32 base=26 scaleW=256 scaleH=256 pages=0 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0
chars count=0

Notes:

  1. use a skipper to allow for the insignificant whitespace. In this case, use qi::blank because it keeps eol significant
  2. allow for multivalue ("property=0,0,0" style) by doing double % ','
  3. use BOOST_SPIRIT_DEBUG with rules
  4. declare skipperless rules for implicit lexemes (Boost spirit skipper issues)

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <fstream>

namespace qi = boost::spirit::qi;

int main() {
using It = boost::spirit::istream_iterator;
qi::rule<It>
word = +qi::alnum,
literal = '"' >> *(qi::char_ - '"') >> '"';
qi::rule<It, std::string()>
value = qi::raw[literal | qi::double_ % ','];

BOOST_SPIRIT_DEBUG_NODES((word)(literal)(value))

const auto pair = qi::copy(word >> '=' >> value);
const auto line = qi::copy(word >> ( + pair ) >> qi::eol);
const auto document = qi::copy(+ line);

std::ifstream in("input.fnt");
in.unsetf(std::ios::skipws);
It begin(in), end;

bool ok = qi::phrase_parse(begin, end, document, qi::blank);
std::cout << std::boolalpha << ok << '\n';
}

Prints:

true

With debug info:

<word>
<try>info face="Arial" si</try>
<success> face="Arial" size=3</success>
<attributes>[]</attributes>
</word>
<word>
<try>face="Arial" size=32</try>
<success>="Arial" size=32 bol</success>
<attributes>[]</attributes>
</word>
<value>
<try>"Arial" size=32 bold</try>
<literal>
<try>"Arial" size=32 bold</try>
<success> size=32 bold=0 ital</success>
<attributes>[]</attributes>
</literal>
<success> size=32 bold=0 ital</success>
<attributes>[[", A, r, i, a, l, "]]</attributes>
</value>
<word>
<try>size=32 bold=0 itali</try>
<success>=32 bold=0 italic=0 </success>
<attributes>[]</attributes>
</word>
<value>
<try>32 bold=0 italic=0 c</try>
<literal>
<try>32 bold=0 italic=0 c</try>
<fail/>
</literal>
<success> bold=0 italic=0 cha</success>
<attributes>[[3, 2]]</attributes>
</value>
<word>
<try>bold=0 italic=0 char</try>
<success>=0 italic=0 charset=</success>
<attributes>[]</attributes>
</word>
<value>
<try>0 italic=0 charset="</try>
<literal>
<try>0 italic=0 charset="</try>
<fail/>
</literal>
<success> italic=0 charset=""</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>italic=0 charset="" </try>
<success>=0 charset="" unicod</success>
<attributes>[]</attributes>
</word>
<value>
<try>0 charset="" unicode</try>
<literal>
<try>0 charset="" unicode</try>
<fail/>
</literal>
<success> charset="" unicode=</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>charset="" unicode=1</try>
<success>="" unicode=1 stretc</success>
<attributes>[]</attributes>
</word>
<value>
<try>"" unicode=1 stretch</try>
<literal>
<try>"" unicode=1 stretch</try>
<success> unicode=1 stretchH=</success>
<attributes>[]</attributes>
</literal>
<success> unicode=1 stretchH=</success>
<attributes>[[", "]]</attributes>
</value>
<word>
<try>unicode=1 stretchH=1</try>
<success>=1 stretchH=100 smoo</success>
<attributes>[]</attributes>
</word>
<value>
<try>1 stretchH=100 smoot</try>
<literal>
<try>1 stretchH=100 smoot</try>
<fail/>
</literal>
<success> stretchH=100 smooth</success>
<attributes>[[1]]</attributes>
</value>
<word>
<try>stretchH=100 smooth=</try>
<success>=100 smooth=1 aa=1 p</success>
<attributes>[]</attributes>
</word>
<value>
<try>100 smooth=1 aa=1 pa</try>
<literal>
<try>100 smooth=1 aa=1 pa</try>
<fail/>
</literal>
<success> smooth=1 aa=1 paddi</success>
<attributes>[[1, 0, 0]]</attributes>
</value>
<word>
<try>smooth=1 aa=1 paddin</try>
<success>=1 aa=1 padding=0,0,</success>
<attributes>[]</attributes>
</word>
<value>
<try>1 aa=1 padding=0,0,0</try>
<literal>
<try>1 aa=1 padding=0,0,0</try>
<fail/>
</literal>
<success> aa=1 padding=0,0,0,</success>
<attributes>[[1]]</attributes>
</value>
<word>
<try>aa=1 padding=0,0,0,0</try>
<success>=1 padding=0,0,0,0 s</success>
<attributes>[]</attributes>
</word>
<value>
<try>1 padding=0,0,0,0 sp</try>
<literal>
<try>1 padding=0,0,0,0 sp</try>
<fail/>
</literal>
<success> padding=0,0,0,0 spa</success>
<attributes>[[1]]</attributes>
</value>
<word>
<try>padding=0,0,0,0 spac</try>
<success>=0,0,0,0 spacing=1,1</success>
<attributes>[]</attributes>
</word>
<value>
<try>0,0,0,0 spacing=1,1 </try>
<literal>
<try>0,0,0,0 spacing=1,1 </try>
<fail/>
</literal>
<success> spacing=1,1 outline</success>
<attributes>[[0, ,, 0, ,, 0, ,, 0]]</attributes>
</value>
<word>
<try>spacing=1,1 outline=</try>
<success>=1,1 outline=0\ncommo</success>
<attributes>[]</attributes>
</word>
<value>
<try>1,1 outline=0\ncommon</try>
<literal>
<try>1,1 outline=0\ncommon</try>
<fail/>
</literal>
<success> outline=0\ncommon li</success>
<attributes>[[1, ,, 1]]</attributes>
</value>
<word>
<try>outline=0\ncommon lin</try>
<success>=0\ncommon lineHeight</success>
<attributes>[]</attributes>
</word>
<value>
<try>0\ncommon lineHeight=</try>
<literal>
<try>0\ncommon lineHeight=</try>
<fail/>
</literal>
<success>\ncommon lineHeight=3</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>\ncommon lineHeight=3</try>
<fail/>
</word>
<word>
<try>common lineHeight=32</try>
<success> lineHeight=32 base=</success>
<attributes>[]</attributes>
</word>
<word>
<try>lineHeight=32 base=2</try>
<success>=32 base=26 scaleW=2</success>
<attributes>[]</attributes>
</word>
<value>
<try>32 base=26 scaleW=25</try>
<literal>
<try>32 base=26 scaleW=25</try>
<fail/>
</literal>
<success> base=26 scaleW=256 </success>
<attributes>[[3, 2]]</attributes>
</value>
<word>
<try>base=26 scaleW=256 s</try>
<success>=26 scaleW=256 scale</success>
<attributes>[]</attributes>
</word>
<value>
<try>26 scaleW=256 scaleH</try>
<literal>
<try>26 scaleW=256 scaleH</try>
<fail/>
</literal>
<success> scaleW=256 scaleH=2</success>
<attributes>[[2, 6]]</attributes>
</value>
<word>
<try>scaleW=256 scaleH=25</try>
<success>=256 scaleH=256 page</success>
<attributes>[]</attributes>
</word>
<value>
<try>256 scaleH=256 pages</try>
<literal>
<try>256 scaleH=256 pages</try>
<fail/>
</literal>
<success> scaleH=256 pages=0 </success>
<attributes>[[2, 5, 6]]</attributes>
</value>
<word>
<try>scaleH=256 pages=0 p</try>
<success>=256 pages=0 packed=</success>
<attributes>[]</attributes>
</word>
<value>
<try>256 pages=0 packed=0</try>
<literal>
<try>256 pages=0 packed=0</try>
<fail/>
</literal>
<success> pages=0 packed=0 al</success>
<attributes>[[2, 5, 6]]</attributes>
</value>
<word>
<try>pages=0 packed=0 alp</try>
<success>=0 packed=0 alphaChn</success>
<attributes>[]</attributes>
</word>
<value>
<try>0 packed=0 alphaChnl</try>
<literal>
<try>0 packed=0 alphaChnl</try>
<fail/>
</literal>
<success> packed=0 alphaChnl=</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>packed=0 alphaChnl=1</try>
<success>=0 alphaChnl=1 redCh</success>
<attributes>[]</attributes>
</word>
<value>
<try>0 alphaChnl=1 redChn</try>
<literal>
<try>0 alphaChnl=1 redChn</try>
<fail/>
</literal>
<success> alphaChnl=1 redChnl</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>alphaChnl=1 redChnl=</try>
<success>=1 redChnl=0 greenCh</success>
<attributes>[]</attributes>
</word>
<value>
<try>1 redChnl=0 greenChn</try>
<literal>
<try>1 redChnl=0 greenChn</try>
<fail/>
</literal>
<success> redChnl=0 greenChnl</success>
<attributes>[[1]]</attributes>
</value>
<word>
<try>redChnl=0 greenChnl=</try>
<success>=0 greenChnl=0 blueC</success>
<attributes>[]</attributes>
</word>
<value>
<try>0 greenChnl=0 blueCh</try>
<literal>
<try>0 greenChnl=0 blueCh</try>
<fail/>
</literal>
<success> greenChnl=0 blueChn</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>greenChnl=0 blueChnl</try>
<success>=0 blueChnl=0\nchars </success>
<attributes>[]</attributes>
</word>
<value>
<try>0 blueChnl=0\nchars c</try>
<literal>
<try>0 blueChnl=0\nchars c</try>
<fail/>
</literal>
<success> blueChnl=0\nchars co</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>blueChnl=0\nchars cou</try>
<success>=0\nchars count=0\n</success>
<attributes>[]</attributes>
</word>
<value>
<try>0\nchars count=0\n</try>
<literal>
<try>0\nchars count=0\n</try>
<fail/>
</literal>
<success>\nchars count=0\n</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>\nchars count=0\n</try>
<fail/>
</word>
<word>
<try>chars count=0\n</try>
<success> count=0\n</success>
<attributes>[]</attributes>
</word>
<word>
<try>count=0\n</try>
<success>=0\n</success>
<attributes>[]</attributes>
</word>
<value>
<try>0\n</try>
<literal>
<try>0\n</try>
<fail/>
</literal>
<success>\n</success>
<attributes>[[0]]</attributes>
</value>
<word>
<try>\n</try>
<fail/>
</word>
<word>
<try></try>
<fail/>
</word>


Related Topics



Leave a reply



Submit