使用 Boost::Spirit 解析前提条件和递归

Parsing preconditions and recursion with Boost::Spirit

本文关键字:条件 前提 递归 Boost Spirit 使用      更新时间:2023-10-16

我正在尝试使用 Boost::Spirit 解析 PDDL 文件,并且在将前提条件解析为结构时遇到了一些麻烦。我正在努力理解有关如何将条件放入我的结构和递归的 Boost 手册。

我在下面给出了一段代码,应该可以很好地说明问题。必须解析如下所示的字符串:

:precondition
(and
(at-pos ?r ?pos)
(not (has-pos ?m ?pos))
)

到目前为止,我的代码看起来像这样,但我几乎可以肯定我不明白at_c是如何工作的,因为还没有使用Boost::P hoenix的经验。

predi_param = '?' >> name_type;
predi = '(' 
>> name_type
>> +predi_param
>> ')';
literal = ( 
( '(' >> lit("not") >>
predi       [at_c<0>(_val) = false]
>> ')'
)
| predi       [at_c<0>(_val) = true]
)
>> ')';
pred_list = ( '(' >> lit("and") >> (*pred_list) >> ')')
| literal;
preconditions = lit(":precondition") >> pred_list;
qi::rule<Iterator, std::string(), ascii::space_type> predi_param;
qi::rule<Iterator, Predicate(), ascii::space_type> predi;
qi::rule<Iterator, Literal(), ascii::space_type> literal;
qi::rule<Iterator, std::vector<Literal>(), ascii::space_type> preconditions, pred_list;

我的 AST 看起来像这样:

struct Predicate
{
std::string name;
std::vector<std::string> predicate_params;
};  
struct Literal
{
bool condition;
Predicate predicate;
};
BOOST_FUSION_ADAPT_STRUCT(
pddl_parser::Literal,
(bool, condition)
(pddl_parser::Predicate, predicate)
)
BOOST_FUSION_ADAPT_STRUCT(
pddl_parser::Predicate,
(std::string, name)
(std::vector<std::string>, predicate_params)
)

编译此方法会导致编译错误:

parser.cpp:67:17:   required from ‘pddl_parser::domain_parser<Iterator>::domain_parser() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
parser.cpp:136:10:   required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:153:20: error: no matching function for call to ‘pddl_parser::Literal::Literal(const std::vector<pddl_parser::Literal>&)’
attr = static_cast<Attribute>(val);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from parser.cpp:11:0:
./pddlast.h:23:10: note: candidate: pddl_parser::Literal::Literal()
struct Literal
^~~~~~~
./pddlast.h:23:10: note:   candidate expects 0 arguments, 1 provided
./pddlast.h:23:10: note: candidate: pddl_parser::Literal::Literal(const pddl_parser::Literal&)
./pddlast.h:23:10: note:   no known conversion for argument 1 from ‘const std::vector<pddl_parser::Literal>’ to ‘const pddl_parser::Literal&’
./pddlast.h:23:10: note: candidate: pddl_parser::Literal::Literal(pddl_parser::Literal&&)
./pddlast.h:23:10: note:   no known conversion for argument 1 from ‘const std::vector<pddl_parser::Literal>’ to ‘pddl_parser::Literal&&’

如果我将用于测试目的的pred_list重新格式化为pred_list = ( '(' >> *literal) >> ')');代码编译,但仍然没有成功,尽管我删除了(and )。我的印象是我完全错了,但找不到任何东西。这是我第一次尝试使用Boost::Spirit。

好吧,你说

pred_list = ( '(' >> *literal) >> ')');

编译,但以下内容不编译:

pred_list = ( '(' >> lit("and") >> (*pred_list) >> ')') | literal;

如果你仔细观察,这是有道理的。由于pred_list的声明属性类型为std::vector<Literal>,显然重复的文字(*literal)可能与自动属性传播的文字兼容。

现在,看一下第二个规则定义。它解析一堆无属性的文本('('"and"')'),然后...*pred_list.如果pred_list声明了一个std::vector<Literal>属性,*pred_list肯定合成了一个std::vector<std::vector<Literal> >。更糟糕的是,"事后的想法"| literal使合成的属性等同于variant<vector<vector<Literal>>, Literal>

是的。这有点乱。您的 AST 根本不反映规则,反之亦然。

前方的道路

您可能应该重述您的问题,删除失败的实现位并描述目标。如果我们能知道真正的语法要求,/then/我们可以诱导出一个匹配的AST,这是正确的。

插曲

在中场休息期间,让我为literal简化这条规则。(请参阅提升 qi 上的精神语义操作::后台程序的规则):

literal = 
'(' >> matches("not") >> predi >> ')'
| qi::attr(false) >> predi
;

PS看来一只流浪猫也打了一个额外的不平衡

>> ')';

最后呢?

一个建设性的猜测

仅查看示例输入,我敢打赌您只想解析表单的类似方案的函数应用程序

(function_name arguments)

应用程序可以嵌套的位置。因此,参数要么是原子,要么是函数应用程序。

好吧,让我们快速添加 AST:

namespace AST {
using Atom = std::string;
struct Application;
using Expression = boost::variant<Atom, Application>;
struct Application {
Atom function;
std::vector<Expression> arguments;
};
}

这很简单,对吧。这是最简单的语法,可以解析你的前提条件:

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;
};

请注意,每个规则如何仅重述相应的 AST 节点。precondition也向外界隐藏了船长。

输出:住在科里鲁

指纹

Parsed (and (at-pos ?r ?pos) (not (has-pos ?m ?pos)))

启用BOOST_SPIRIT_DEBUG后:

<precondition>
<try>:preconditionn      </try>
<expression>
<try>n                   </try>
<atom>
<try>(andn               </try>
<fail/>
</atom>
<application>
<try>(andn               </try>
<atom>
<try>andn                </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>

此示例显示的是:

  • 如何在 AST 中表示变量数据类型
  • 如何通过 IT 实现递归(有关更高级的信息,另请参阅文档)

它不会立即根据 PDDL 规范验证 AST。我完全不确定您打算实现多少,所以我认为更通用的启动器无论如何都会有所帮助。

完整列表

住在科里鲁

#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 Failedn";
}
if (f != l) {
std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'n";
}
}
}