lur*_*her 9 c++ parsing boost-spirit boost-spirit-qi
我试图像下面的树表达式一样解析C函数(使用Spirit Parser Framework):
F( A() , B( GREAT( SOME , NOT ) ) , C( YES ) )
Run Code Online (Sandbox Code Playgroud)
为此,我试图使用以下语法的三个规则:
template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> {
InputGrammar() : InputGrammar::base_type( ) {
tag = ( qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9") )[ push_back( at_c<0>(qi::_val) , qi::_1 ) ];
command = tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",")
[ push_back( at_c<1>(qi::_val) , qi::_1 ) ] >> ")";
instruction = ( command | tag ) [qi::_val = qi::_1];
}
qi::rule< Iterator , ExpressionAST() , space_type > tag;
qi::rule< Iterator , ExpressionAST() , space_type > command;
qi::rule< Iterator , ExpressionAST() , space_type > instruction;
};
Run Code Online (Sandbox Code Playgroud)
请注意,我的标记规则只是尝试捕获表达式中使用的标识符("函数"名称).另请注意,标记规则的签名会返回a ExpressionAST而不是a std::string,就像在大多数示例中一样.我想这样做的原因实际上非常简单:我讨厌使用变体,如果可能的话我会避免它们.我想,保持蛋糕和吃它也会很棒.
命令应该以标记(当前节点的名称,AST节点的第一个字符串字段)和括号括起来的可变数量的参数开始,每个参数可以是标记本身或另一个命令.
但是,这个例子根本不起作用.它编译和一切,但在运行时它无法解析我的所有测试字符串.而真正让我烦恼的是我无法弄清楚如何修复它,因为我无法真正调试上面的代码,至少在这个词的传统意义上.基本上我认为我可以解决上述代码的唯一方法是知道我做错了什么.
所以,问题是我不知道上面的代码有什么问题.你会如何定义上述语法?
ExpressionAST我使用的类型是:
struct MockExpressionNode {
std::string name;
std::vector< MockExpressionNode > operands;
typedef std::vector< MockExpressionNode >::iterator iterator;
typedef std::vector< MockExpressionNode >::const_iterator const_iterator;
iterator begin() { return operands.begin(); }
const_iterator begin() const { return operands.begin(); }
iterator end() { return operands.end(); }
const_iterator end() const { return operands.end(); }
bool is_leaf() const {
return ( operands.begin() == operands.end() );
}
};
BOOST_FUSION_ADAPT_STRUCT(
MockExpressionNode,
(std::string, name)
(std::vector<MockExpressionNode>, operands)
)
Run Code Online (Sandbox Code Playgroud)
aca*_*bot 12
至于调试,它可以使用正常的休息和观看方法.尽管如何格式化规则使这变得困难.如果您根据精神示例进行格式化(每行〜一个解析器,每行一个凤凰语句),断点将提供更多信息.
你的数据结构没有办法区分A()来自SOME于他们都是叶(让我知道如果我想的东西).从您的变体注释中,我不认为这是您的意图,因此为了区分这两种情况,我将一个bool commandFlag成员变量添加到MockExpressionNode(true for A()和false for SOME),以及相应的融合适配器行.
具体来说,您需要将开始规则传递给基础构造函数,即:
InputGrammar() : InputGrammar::base_type(instruction) {...}
Run Code Online (Sandbox Code Playgroud)
这是语法的入口点,也是您没有获得任何数据解析的原因.我很惊讶没有它编译,我认为语法类型需要匹配第一个规则的类型.即便如此,这也是一个方便的惯例.
对于tag规则,因而实际上有两个解析器qi::char_("a-zA-Z_"),其被_1与类型char和*qi::char_("a-zA-Z_0-9")其_2与类型(基本上)vector<char>.不可能将这些强制转换为没有autorule的字符串,但可以通过将规则附加到每个已解析的char来完成:
tag = qi::char_("a-zA-Z_")
[ at_c<0>(qi::_val) = qi::_1 ];
>> *qi::char_("a-zA-Z_0-9") //[] has precedence over *, so _1 is
[ at_c<0>(qi::_val) += qi::_1 ]; // a char rather than a vector<char>
Run Code Online (Sandbox Code Playgroud)
然而,让精神做这种转换更加清洁.因此,定义一个新规则:
qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
Run Code Online (Sandbox Code Playgroud)
并且不要担心;).然后标签变成了
tag = identifier
[
at_c<0>(qi::_val) = qi::_1,
ph::at_c<2>(qi::_val) = false //commandFlag
]
Run Code Online (Sandbox Code Playgroud)
对于命令,第一部分很好,但是有几个问题(*instruction >> ",")[ push_back( at_c<1>(qi::_val) , qi::_1 ) ].这将解析零或多个指令规则,后跟",".它也尝试push_back一个vector<MockExpressionNode>(不知道为什么这个编译,也许没有实例化,因为缺少启动规则?).我想你想要以下(标识符修改):
command =
identifier
[
ph::at_c<0>(qi::_val) = qi::_1,
ph::at_c<2>(qi::_val) = true //commandFlag
]
>> "("
>> -(instruction % ",")
[
ph::at_c<1>(qi::_val) = qi::_1
]
>> ")";
Run Code Online (Sandbox Code Playgroud)
这使用可选运算符-和列表运算符%,后者相当于instruction >> *("," >> instruction).然后,phoenix表达式只是将向量直接赋给结构成员,但您也可以将操作直接附加到指令匹配并使用push_back.
指令规则很好,我只是提到它相当于instruction %= (command|tag).
最后一件事,如果A()和之间没有区别SOME(即你的原始结构没有commandFlag),你可以只使用autorules来编写这个解析器:
template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> {
InputGrammar() : InputGrammar::base_type( command ) {
identifier %=
qi::char_("a-zA-Z_")
>> *qi::char_("a-zA-Z_0-9");
command %=
identifier
>> -(
"("
>> -(command % ",")
>> ")");
}
qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command;
};
Run Code Online (Sandbox Code Playgroud)
这是使用融合包裹结构的最大好处,该结构可以密切地模拟输入.