【问题标题】:parsing identifiers except keywords解析除关键字之外的标识符
【发布时间】:2016-06-26 13:59:39
【问题描述】:

我正在努力编写一个标识符解析器,它解析一个不是关键字的字母数字字符串。 关键字都在一个表中:

struct keywords_t : x3::symbols<x3::unused_type> {
    keywords_t() {
        add("for", x3::unused)
                ("in", x3::unused)
                ("while", x3::unused);
    }
} const keywords;

标识符的解析器应该是这样的:

auto const identifier_def =       
            x3::lexeme[
                (x3::alpha | '_') >> *(x3::alnum | '_')
            ];

现在我尝试将这些组合起来,因此标识符解析器无法解析关键字。 我试过这样:

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_')
                ]-keywords;

还有这个:

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_') - keywords
                ];

它适用于大多数输入,但如果字符串以 int, whilefoo, forbar 之类的关键字开头,则解析器无法解析此字符串。 我怎样才能让这个解析器正确?

【问题讨论】:

标签: c++ parsing c++14 boost-spirit boost-spirit-x3


【解决方案1】:

您的问题是由 Spirit 中差异运算符的语义引起的。当您拥有a - b Spirit 时,会执行以下操作:

  • 检查b是否匹配:
    • 如果是这样,a - b 会失败并且不会解析任何内容。
    • 如果b 失败,则检查a 是否匹配:
      • 如果a 失败,a - b 失败并且没有任何内容被解析。
      • 如果a 成功,则a - b 成功并解析a 解析的任何内容。

在您的情况下 (unchecked_identifier - keyword),只要标识符以关键字开头,keyword 就会匹配,并且您的解析器将失败。因此,您需要将keyword 与在传递不同关键字时匹配的东西交换,但只要关键字后面跟着其他东西就会失败。 not predicate (!) 可以提供帮助。

auto const distinct_keyword = x3::lexeme[ keyword >> !(x3::alnum | '_') ];

完整样本 (Running on Coliru):

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>

namespace parser {
    namespace x3 = boost::spirit::x3;

    struct keywords_t : x3::symbols<x3::unused_type> {
        keywords_t() {
            add("for", x3::unused)
                    ("in", x3::unused)
                    ("while", x3::unused);
        }
    } const keywords;

    x3::rule<struct identifier_tag,std::string>  const identifier ("identifier");

    auto const distinct_keyword = x3::lexeme[ keywords >> !(x3::alnum | '_') ];
    auto const unchecked_identifier = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];


    auto const identifier_def = unchecked_identifier - distinct_keyword;

    //This should also work:
    //auto const identifier_def = !distinct_keyword >> unchecked_identifier


    BOOST_SPIRIT_DEFINE(identifier);

    bool is_identifier(const std::string& input)
    {
        auto iter = std::begin(input), end= std::end(input);

        bool result = x3::phrase_parse(iter,end,identifier,x3::space);

        return result && iter==end;
    }
}



int main() {

    std::cout << parser::is_identifier("fortran") << std::endl;
    std::cout << parser::is_identifier("for") << std::endl;
    std::cout << parser::is_identifier("integer") << std::endl;
    std::cout << parser::is_identifier("in") << std::endl;
    std::cout << parser::is_identifier("whileechoyote") << std::endl;
    std::cout << parser::is_identifier("while") << std::endl;
}

【讨论】:

    【解决方案2】:

    问题是,它在没有词法分析器的情况下运行,也就是说,如果你写

    keyword >> *char_
    

    输入whilefoo,它会将while解析为keyword,将foo解析为*char_

    您可以通过两种方式防止这种情况:要么要求在关键字后有一个空格,即

    auto keyword_rule = (keyword >> x3::space);
    //or if you use phrase_parse
    auto keyword_rule = x3::lexeme[keyword >> x3::space];
    

    您描述的其他方式也是可能的,即从字符串中显式删除关键字(我会这样做):

    auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')];
    

    您的定义的问题是,它将第一组字符解释为关键字,从而选择根本不解析它。 'x-y' 运算符的意思是,解析 x,但不解析 y。但是如果你传递 'whilefoo' 它会将 'while' 解释为关键字,因此根本不会解析。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-06
      相关资源
      最近更新 更多