【问题标题】:How to combine skipping and non-skipping (lexeme) rules?如何结合跳过和非跳过(词位)规则?
【发布时间】:2020-03-25 09:12:32
【问题描述】:

我的解析器快要工作了 :) (仍然对 Spirit 功能集(和编译时间)以及堆栈溢出时非常受欢迎的社区感到惊讶)

在线试用小样: http://coliru.stacked-crooked.com/a/1c1bf88909dce7e3

所以我学会了使用更多的词位规则并尝试防止 no_skip - 结果我的规则更小更好读,但现在我坚持 结合 lexeme-rules 和 skipping-rules 似乎不可能的事情(编译时错误,警告不能转换为 Skipper)

我的问题是订阅中的逗号分隔列表 它不会跳过表达式周围的空格

解析:

"a.b[a,b]"

失败:

"a.b[ a , b ]"

这是我的规则:

qi::rule<std::string::const_iterator, std::string()> identifier_chain;

qi::rule<std::string::const_iterator, std::string()>
    expression_list = identifier_chain >> *(qi::char_(',') >> identifier_chain);

qi::rule < std::string::const_iterator, std::string() >
    subscription = qi::char_('[') >> expression_list >> qi::char_(']');

qi::rule<std::string::const_iterator, std::string()>
    identifier = qi::ascii::alpha >> *(qi::ascii::alnum | '_');

identifier_chain = identifier >> *(('.' >> identifier) | subscription);

您可以看到所有规则都是“词素”,我认为订阅规则应该是 ascii::space_type 跳过器,但它不会编译

我应该在 expression_list 中 identifier_chains 的前后添加空间吞噬者吗?

感觉就像在写一个正则表达式:(

expression_list = *qi::blank >> identifier_chain >> *(*qi::blank >> qi::char_(',') >> *qi::blank >> identifier_chain >> *qi::blank);

它有效,但我读到这最终会让我得到一个更大的解析器(自己处理所有跳过的空间)

感谢任何建议

顺便说一句:如果用qi::char_('.')包围indentifier_chain中的'.',知道为什么我无法编译

identifier_chain = identifier >> *(('.' >> identifier) | subscription);

更新

我已经按照 sehe 的建议更新了我的表情列表

qi::rule<std::string::const_iterator, spirit::ascii::blank_type, std::string()>
expression_list = identifier_chain >> *(qi::char_(',') >> identifier_chain);

qi::rule < std::string::const_iterator, std::string() >
subscription = qi::char_('[') >> qi::skip(qi::blank)[expression_list] >> qi::char_(']');

但由于不可转换的船长仍然会出现编译错误:http://coliru.stacked-crooked.com/a/adcf665742b055dd

我也尝试将 identifer_chain 更改为

identifier_chain = identifier >> *(('.' >> identifier) | qi::skip(qi::blank)[subscription]);

但我仍然无法编译示例

【问题讨论】:

  • 使用char_() 将字符公开为合成属性序列中的元素,破坏属性传播,因为它需要字符串,而不是字符串和字符的元组/序列。 lit() 根本不公开属性,只是匹配所需的输入。

标签: c++ boost-spirit boost-spirit-qi


【解决方案1】:

我之前链接的答案描述了所有组合(如果我没记错的话):Boost spirit skipper issues

简而言之:

  • 任何声明船长的规则(如rule&lt;It, Skipper[, Attr()]&gt;rule&lt;It, Attr(), Skipper&gt;)都必须使用兼容的船长(可以分配给Skipper 类型的表达式)调用。

  • 任何未声明跳过者的规则(形式为rule&lt;It[, Attr()]&gt;)都将隐含地表现得像一个词素,这意味着不会跳过任何输入字符。

    李>

就是这样。稍微微妙的后果是给出了两个规则:

rule<It, blank_type> a;
rule<It> b; // b is implicitly lexeme

可以a调用b

a = "test" >> b;

但是当你想从b调用a时你会发现你必须提供skipper:

b = "oops" >> a; // DOES NOT COMPILE
b = "okay" >> qi::skip(qi::blank) [ a ];

这就是它的全部内容。 Qi 中还有一些关于skippers 和lexemes 的指令,请再次查看上面链接的答案。

附加问题:

我应该在 expression_list 中的 identifier_chains 的前后添加空间吞噬者吗?

如果您仔细查看此处Parse a '.' chained identifier list, with qi::lexeme and prevent space skipping 的答案示例,您会发现它已经正确地进行前后跳过,因为我使用了phrase_parse

" a.b " OK: ( "a" "b" ) 
----
"a . b" Failed
Remaining unparsed: "a . b"
----

您也可以将整个事情包装在“外部”规则中:

rule<std::string::const_iterator> main_rule = 
     qi::skip(qi::blank) [ identifier_chain ];

同样如此,但允许用户在不指定船长的情况下调用parse

【讨论】:

  • 也回答了一个附带问题
  • 再次感谢 - Spirit 的灵活性是如此之高,以至于我只是看不到所有同等可附加的策略
  • 是的。我喜欢精神,因为它可以让您非常有效率当您知道便利功能的最佳点时。在那之前,它会像沼泽一样让你慢下来,至少和节省时间一样多(比如测试构建模块)
  • 我开始手动编写我的表达式解析器 - 一个朋友强迫我(没有像样的精神经验)使用 Spirit - 所以现在我深陷沼泽,顺便说一句:你的例子不适合我- 见更新
  • @ilm 如果您可以共享代码(即使它当前未编译),它将帮助我更快地发现问题 - 现在我必须做出许多假设(我确实注意到你有char_(",") 是故意的吗?)
猜你喜欢
  • 2019-03-17
  • 1970-01-01
  • 1970-01-01
  • 2012-09-21
  • 2012-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多