Addressing the questions:
- My idea was to only adapt part of the structure with
BOOST_FUSION_ADAPT_STRUCT, and fill the rest with semantic actions. Stupid idea, or am I just doing it wrong?
Not unthinkable. Not my recommendation (see Boost Spirit: "Semantic actions are evil"?). But yeah you're doing it wrong:
You want lit("$") instead of char_("$") if you don't want it to be part of the exposed attribute. In fact, '$' will do here
Is there a way to bind a semantic action and still get the output? Like
char_[doSomething] // Can this both call doSomething, and parse a char?
Yes. You're doing it right now, and it works because of operator%= you used instead of operator= (see the docs: http://www.boost.org/doc/libs/1_61_0/libs/spirit/doc/html/spirit/qi/reference/nonterminal/rule.html#spirit.qi.reference.nonterminal.rule.expression_semantics).
However, it seems you are really trying to use the same input twice (raw as fromFile and "cooked" as name?) it backfires, because the auto-rule attribute propagation also overwrites name with the value you want for fromFile.
The only quick way out, here, is to use SA only. I'd suggest making VariableHolder's constructor responsible for details though.
Side note: it looks a bit as if the optional parentheses suggest an expression grammar. If so, make that explicit in the grammar, instead of hardcoding a special case in the rule for a variableName. If not, carry on :)
Here's an attempted fix:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
struct VariableHolder {
std::string name; // contains "varName"
std::string fromFile; // contains "$(varName)" or "$varName"
};
template <typename It, typename Skipper = qi::ascii::space_type> struct P : qi::grammar<It, VariableHolder(), Skipper> {
P() : P::base_type(start) {
auto _name = phoenix::bind(&VariableHolder::name, qi::_val);
auto _fromFile = phoenix::bind(&VariableHolder::fromFile, qi::_val);
variableName = qi::alpha >> +qi::alnum;
variable = '$' >> (variableName | '(' >> variableName >> ')');
start = qi::as_string [ qi::raw [
variable [ _name = qi::_1 ]]
] [ _fromFile = qi::_1 ];
BOOST_SPIRIT_DEBUG_NODES((start)(variable)(variableName))
}
private:
qi::rule<It, std::string(), Skipper> variable;
qi::rule<It, VariableHolder(), Skipper> start;
// lexemes
qi::rule<It, std::string()> variableName;
};
int main() {
using It = std::string::const_iterator;
P<It> const p;
for (std::string const input : {
"$foo1",
"$(bar2)"
})
{
It f = input.begin(), l = input.end();
VariableHolder data;
bool ok = qi::phrase_parse(f, l, p, qi::ascii::space, data);
if (ok) {
std::cout << "Parse success: " << data.name << " (source: '" << data.fromFile << "')\n";
} else {
std::cout << "Parse failure ('" << input << "')\n";
}
if (f != l) {
std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
}
}
}
Prints
Parse success: foo1 (source: '$foo1')
Parse success: bar2 (source: '$(bar2)')