Well, you say that
pred_list = ( '(' >> *literal) >> ')');
compiles, but the following does not:
pred_list = ( '(' >> lit("and") >> (*pred_list) >> ')') | literal;
If you look closely, that makes sense. Since pred_list
has a declared attribute type of std::vector<Literal>
, obviously repeated literals (*literal
) might be compatible with that for automatic attribute propagation.
Now, look at the second rule definition. It parses a bunch of attribute-less literals ('('
, "and"
, ')'
) and then ... *pred_list
. If pred_list
declares a std::vector<Literal>
attribute, surely *pred_list
synthesizes a std::vector<std::vector<Literal> >
. To make matters worse, the "after-thought" | literal
makes the synthesized attribute equivalent of a variant<vector<vector<Literal>>, Literal>
.
Yes. That's a bit of a mess. Your AST simply doesn't reflect the rules, or vice versa.
The Road Ahead
You should probably restate your problem, removing the failed implementation bits and describing the goal instead. If we can know the true grammatic requirements, /then/ we can induce a matching AST that is correct for it.
Interlude
During the intermission, let me simplify that rule for literal
. (See Boost spirit semantic actions on qi::rule for a backgrounder):
literal =
'(' >> matches("not") >> predi >> ')'
| qi::attr(false) >> predi
;
PS It appears that a stray cat also typed an extra unbalanced
>> ')';
at the end there?
A constructive guess
From just looking at the sample input, I wager you just want to parse scheme-like function applications of the form
(function_name arguments)
Where the applications can nest. So, arguments are either atoms or function applications.
Well, let's put an AST on it, quickly:
namespace AST {
using Atom = std::string;
struct Application;
using Expression = boost::variant<Atom, Application>;
struct Application {
Atom function;
std::vector<Expression> arguments;
};
}
That's delightfully simple, right. And here's the simplest grammar that will parse your precondition:
template <typename Iterator>
struct Precondition : qi::grammar<Iterator, AST::Expression()> {
Precondition() : Precondition::base_type(precondition) {
using namespace qi;
atom = +(graph - '(' - ')');
application = '(' >> atom >> *expression >> ')';
expression = atom | application;
precondition = skip(ascii::space) [":precondition" >> expression];
BOOST_SPIRIT_DEBUG_NODES((precondition)(expression)(application)(atom))
}
private:
using Skipper = qi::ascii::space_type;
qi::rule<Iterator, AST::Application(), Skipper> application;
qi::rule<Iterator, AST::Expression(), Skipper> expression;
// lexemes
qi::rule<Iterator, AST::Expression()> precondition;
qi::rule<Iterator, AST::Atom()> atom;
};
Note how each of the rules merely restates the corresponding AST node. precondition
also hides the skipper from the outside world.
The output: Live On Coliru
Prints
Parsed (and (at-pos ?r ?pos) (not (has-pos ?m ?pos)))
And with BOOST_SPIRIT_DEBUG
enabled:
<precondition>
<try>:precondition\n </try>
<expression>
<try>\n </try>
<atom>
<try>(and\n </try>
<fail/>
</atom>
<application>
<try>(and\n </try>
<atom>
<try>and\n </try>
<success>\n </success>
<attributes>[[a, n, d]]</attributes>
</atom>
<expression>
<try>\n </try>
<atom>
<try>(at-pos ?r ?pos)\n </try>
<fail/>
</atom>
<application>
<try>(at-pos ?r ?pos)\n </try>
<atom>
<try>at-pos ?r ?pos)\n </try>
<success> ?r ?pos)\n </success>
<attributes>[[a, t, -, p, o, s]]</attributes>
</atom>
<expression>
<try> ?r ?pos)\n </try>
<atom>
<try>?r ?pos)\n </try>
<success> ?pos)\n </success>
<attributes>[[?, r]]</attributes>
</atom>
<success> ?pos)\n </success>
<attributes>[[?, r]]</attributes>
</expression>
<expression>
<try> ?pos)\n </try>
<atom>
<try>?pos)\n </try>
<success>)\n </success>
<attributes>[[?, p, o, s]]</attributes>
</atom>
<success>)\n </success>
<attributes>[[?, p, o, s]]</attributes>
</expression>
<expression>
<try>)\n </try>
<atom>
<try>)\n </try>
<fail/>
</atom>
<application>
<try>)\n </try>
<fail/>
</application>
<fail/>
</expression>
<success>\n </success>
<attributes>[[[a, t, -, p, o, s], [[?, r], [?, p, o, s]]]]</attributes>
</application>
<success>\n </success>
<attributes>[[[a, t, -, p, o, s], [[?, r], [?, p, o, s]]]]</attributes>
</expression>
<expression>
<try>\n </try>
<atom>
<try>(not (has-pos ?m ?po</try>
<fail/>
</atom>
<application>
<try>(not (has-pos ?m ?po</try>
<atom>
<try>not (has-pos ?m ?pos</try>
<success> (has-pos ?m ?pos))\n</success>
<attributes>[[n, o, t]]</attributes>
</atom>
<expression>
<try> (has-pos ?m ?pos))\n</try>
<atom>
<try>(has-pos ?m ?pos))\n </try>
<fail/>
</atom>
<application>
<try>(has-pos ?m ?pos))\n </try>
<atom>
<try>has-pos ?m ?pos))\n </try>
<success> ?m ?pos))\n </success>
<attributes>[[h, a, s, -, p, o, s]]</attributes>
</atom>
<expression>
<try> ?m ?pos))\n </try>
<atom>
<try>?m ?pos))\n </try>
<success> ?pos))\n </success>
<attributes>[[?, m]]</attributes>
</atom>
<success> ?pos))\n </success>
<attributes>[[?, m]]</attributes>
</expression>
<expression>
<try> ?pos))\n </try>
<atom>
<try>?pos))\n </try>
<success>))\n </success>
<attributes>[[?, p, o, s]]</attributes>
</atom>
<success>))\n </success>
<attributes>[[?, p, o, s]]</attributes>
</expression>
<expression>
<try>))\n </try>
<atom>
<try>))\n </try>
<fail/>
</atom>
<application>
<try>))\n </try>
<fail/>
</application>
<fail/>
</expression>
<success>)\n </success>
<attributes>[[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]</attributes>
</application>
<success>)\n </success>
<attributes>[[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]</attributes>
</expression>
<expression>
<try>)\n </try>
<atom>
<try>)\n </try>
<fail/>
</atom>
<application>
<try>)\n </try>
<fail/>
</application>
<fail/>
</expression>
<success>\n </success>
<attributes>[[[n, o, t], [[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]]]</attributes>
</application>
<success>\n </success>
<attributes>[[[n, o, t], [[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]]]</attributes>
</expression>
<expression>
<try>\n </try>
<atom>
<try>)</try>
<fail/>
</atom>
<application>
<try>)</try>
<fail/>
</application>
<fail/>
</expression>
<success></success>
<attributes>[[[a, n, d], [[[a, t, -, p, o, s], [[?, r], [?, p, o, s]]], [[n, o, t], [[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]]]]]</attributes>
</application>
<success></success>
<attributes>[[[a, n, d], [[[a, t, -, p, o, s], [[?, r], [?, p, o, s]]], [[n, o, t], [[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]]]]]</attributes>
</expression>
<success></success>
<attributes>[[[a, n, d], [[[a, t, -, p, o, s], [[?, r], [?, p, o, s]]], [[n, o, t], [[[h, a, s, -, p, o, s], [[?, m], [?, p, o, s]]]]]]]]</attributes>
</precondition>
What this example shows you is:
- how to represent the variant data-type in your AST
- how to achieve recursion through it (see also documentation for more advanced information)
What it doesn't immediately do is to validate the AST against the PDDL specs. I'm not at all sure how much of it you intend to implement, so I think the more generic starter might be helpful anyways.
Full Listing
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace AST {
using Atom = std::string;
struct Application;
using Expression = boost::variant<Atom, Application>;
struct Application {
Atom function;
std::vector<Expression> arguments;
friend std::ostream& operator<<(std::ostream& os, Application const& a) {
os << "(" << a.function;
for (auto& arg : a.arguments)
os << " " << arg;
return os << ")";
}
};
}
BOOST_FUSION_ADAPT_STRUCT(AST::Application, function, arguments)
namespace pddl_parser {
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct Precondition : qi::grammar<Iterator, AST::Expression()> {
Precondition() : Precondition::base_type(precondition) {
using namespace qi;
atom = +(graph - '(' - ')');
application = '(' >> atom >> *expression >> ')';
expression = atom | application;
precondition = skip(ascii::space) [":precondition" >> expression];
BOOST_SPIRIT_DEBUG_NODES((precondition)(expression)(application)(atom))
}
private:
using Skipper = qi::ascii::space_type;
qi::rule<Iterator, AST::Application(), Skipper> application;
qi::rule<Iterator, AST::Expression(), Skipper> expression;
// lexemes
qi::rule<Iterator, AST::Expression()> precondition;
qi::rule<Iterator, AST::Atom()> atom;
};
}
int main() {
using It = std::string::const_iterator;
for (std::string const& input : {
R"--(:precondition
(and
(at-pos ?r ?pos)
(not (has-pos ?m ?pos))
))--"
})
{
std::cout << "-----\n";
It f = input.begin(), l = input.end();
AST::Expression precondition;
bool ok = parse(f, l, pddl_parser::Precondition<It>{}, precondition);
if (ok) {
std::cout << "Parsed " << precondition << "\n";
} else {
std::cout << "Parse Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
}
}
}