请求一个解析器语法,使用boost spirit qi更好

时间:2016-01-07 06:13:20

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

我正在尝试使用boost::spirit::qi来解析表达式。

表达式很简单,可以是

  1. id,例如x
  2. 对象的成员,例如obj.x
  3. 数组的元素,如arr[2]
  4. 函数调用的结果。 func(x, y)
  5. 对象的成员可以是数组或函数类型 x.y[2]x.y()是合法的。

    函数结果可能是数组或对象 所以func(x,y).valuefunc(x)[4]也是合法的。

    数组元素可能是对象或函数类型 因此arr[5].yarr[3](x, y)是合法的。

    结合在一起,以下表达式应该是合法的:

    x[1]().y(x, y, x.z, z[4].y)().q[2][3].fun()[5].x.y.z

    所有这些[...] (...).具有相同的优先顺序,从左到右。

    我的语法就像这样

    expression
        = postfix_expr
        | member_expr
        ;
    
    postfix_expr = elem_expr | call_expr | id;
    elem_expr = postfix_expr >> "[" >> qi::int_ >> "]";
    call_expr = postfix_expr >> "(" >> expression_list >> ")";
    member_expr = id >> *("." >> member_expr);
    
    expression_list
        = -(expression % ",")
    

    但它总是崩溃,我想也许某个地方有无限循环。

    请给我一些关于如何解析这个语法的建议。

    修改 关注问题: 谢谢干部,它有效!

    现在表达式可以正确解析,但我想引入一个新的ref_exp 这也是一个表达式,但不以()结尾,因为函数结果不能放在赋值的左边。

    我的定义是:

        ref_exp
            = id
            | (id >> *postfix_exp >> (memb_exp | elem_exp))
            ;
    
        postfix_exp
            = memb_exp
            | elem_exp
            | call_exp
            ;
    
        memb_exp = "." >> id;
        elem_exp = "[" >> qi::uint_ >> "]";
        call_exp = ("(" >> expression_list >> ")");
    

    boost::spirit::qi无法解析此问题, 我认为原因是(memb_exp | elem_exp)postfix_exp的一部分,如何使其不解析所有内容,并留下最后一部分以匹配(memb_exp | elem_exp)

    ref_exp示例:xx.yx()[12][21]f(x, y, z).x[2] 不是ref_expf()x.y()x[12]()

2 个答案:

答案 0 :(得分:3)

boost::spirit::qi is a descending parser;你的语法一定不能保持递归。

请参阅this question

这里你肯定有一个左递归语法:postfix_expr -> elem_expr -> postfix_expr

编辑解决此问题的一种方法。

在我看来,你的表达式是一串带有可能后缀的ID:[]().

expression = id >> *cont_expr;
cont_expr = elem_expr | call_expr | member_expr
elem_expr = "[" >> qi::int_ >> "]";
call_expr = "(" >> expression_list >> ")";
member_expr = "." >> expression;
expression_list = -(expression % ",")

编辑2 如果您希望能够强制优先 - 例如使用括号:

expression = prefix_expr >> *cont_expr;
prefix_expr = id | par_expr
par_expr = "(" >> expression >> ")"

这样你甚至可以编写像x.(y[3].foo)[5](fun(), foo(bar))这样的表达式 - 如果这是有道理的。

编辑3 我在这里回答您的评论。

您需要分配的左侧不是功能。这意味着你有一个特定的左手表达式后缀。我们在您的评论中调用该规则ref_exp

ref_exp = id >> -( *cont_expr >> cont_ref );
cont_ref = elem_expr | member_expr;

答案 1 :(得分:2)

最后我认为我解决了这个问题,但这个解决方案有副作用,它会改变运营商的关联性。

    lvalue_exp
        = id >> -(ref_exp);
        ;

    ref_exp
        = (postfix_exp >> ref_exp)
        | memb_exp
        | elem_exp
        ;

    postfix_exp
        = call_exp
        | memb_exp
        | elem_exp
        ;

    memb_exp
        = ("." >> id)
        ;

    elem_exp
        = ("[" >> qi::uint_ >> "]")
        ;

    call_exp
        = ("(" >> expression_list >> ")")
        ;

因此表达式f().y()[0]将解析为:

  1. fref_exp - ().y()[0]
  2. ().y()[0]已解析为().y()[0]
  3. ().y()已解析为().y()
  4. .y()已解析为.y()
  5. 如果我不区分左值

    f().y()[0]将解析为:

    1. f().y()[0]
    2. ().y()[0]
    3. .y()[0]
    4. ()[0]
    5. 所以我将使用第二个并在生成ast时检查参考。

      谢谢@cadrian