The parser
namespace qi = boost::spirit::qi;
template<typename T>
class action
{
public:
action(std::vector<std::shared_ptr<part>>& parts) : m_parts{ parts } {}
void operator()(const std::vector<char>& cc, qi::unused_type, qi::unused_type) const
{
std::string s(cc.begin(), cc.end());
if (s.length() > 0) {
auto p = new T(s);
m_parts.push_back(std::shared_ptr<part>(p));
}
}
private:
std::vector<std::shared_ptr<part>>& m_parts;
};
std::vector<std::shared_ptr<part>> parse(const std::string& source) {
namespace ascii = boost::spirit::ascii;
using ascii::char_;
using qi::lit;
std::vector<std::shared_ptr<part>> parts;
auto prop_g = lit("{{=")
>> *char_(' ')
>> (*(char_ - char_("} ")))[action<property_part>(parts)]
>> *char_(' ')
>> "}}"
;
auto text_g = (+(char_ - '{'))[action<text_part>(parts)];
auto g = -text_g >> +(prop_g >> text_g);
qi::parse(source.begin(), source.end(), g);
return parts;
}
causes a fault on the qi::parse call when testing on a Kitkat device. The fault happens before any semantic action is called. The same code works on Xcode 6/iOS 8.4 and VS 2015. We're using Boost 1.59.
We could replace Spirit with Bison implying an extra build step, or use Clang with the Android NDK, getting us off the beaten path.
Can this fault be fixed with build configuration or are there other options we could explore?
Of course errors can be fixed. You do not show the grammar See Update You show a lot of parser expressions with unknowns, though, so there's no way we could even start to reason about your code.
One thing that does stand out is the (ab)use of auto
, though.
A quick google should point you at ~6 questions on SO that warn about this.
You can not use
auto
with Spirit's expression templates. In all but the very very trivial cases (unparameterized stateless terminals) this leads directly to Undefined Behaviour
Try using
qi::rule<>
to contain/group the expressionsboost::spirit::copy
or boost::proto::deep_copy
UPDATE
To the edited question: indeed the grammar crashed due to undefined behaviour (if this ever "appeared to work", you were just (un)lucky!).
Here's a fixed version, as a bonus I removed the action<>
thing replacing it with existing Phoenix mechanisms and attribute propagation.
You may want to study the changes side-by-side to find out exactly what I changed.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
struct part {
part(std::string s="") : _value(std::move(s)) {}
virtual ~part() { }
virtual void do_print(std::ostream& os) const = 0;
protected:
std::string _value;
};
struct property_part : part {
using part::part;
void do_print(std::ostream& os) const { os << "{{=" << _value << "}}"; }
};
struct text_part : part {
using part::part;
void do_print(std::ostream& os) const { os << "'" << _value << "'"; }
};
std::vector<std::shared_ptr<part>> parse(const std::string& source) {
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using boost::spirit::ascii::char_;
using qi::lit;
typedef std::shared_ptr<part> pptr;
qi::rule<std::string::const_iterator, pptr()> prop_g, text_g;
// this is ok: purely stateless expression template
prop_g = lit("{{=")
>> *char_(' ')
>> qi::as_string [ +~char_("} ") ] [ qi::_val = px::construct<pptr>(px::new_<property_part>(qi::_1)) ]
>> *char_(' ')
>> "}}"
;
text_g = qi::as_string [ +~char_('{') ] [ qi::_val = px::construct<pptr>(px::new_<text_part>(qi::_1)) ];
std::vector<pptr> parts;
qi::parse(source.begin(), source.end(), -text_g >> +(prop_g >> text_g), parts);
return parts;
}
int main() {
auto result = parse("My book is about {{= this-is-a-(bogus)-property-part }} else entirely {{=byebye}}");
assert(result.size() == 4);
for(auto item : result)
item->do_print(std::cout);
}
Prints
'My book is about '{{=this-is-a-(bogus)-property-part}}' else entirely '{{=byebye}}