求一个parser grammar,用boost spirit qi比较好

ask for a parser grammar, using boost spirit qi is better

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

表达式简单,可以

  1. id,喜欢x
  2. 对象的成员,例如 obj.x
  3. 数组的一个元素,例如arr[2]
  4. 函数调用的结果。 func(x, y)

对象的成员可以是数组或函数类型所以 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_exp : f(), x.y(), x[12]()

boost::spirit::qiis a descending parser;你的语法 一定不能 保留递归。

this question

这里肯定有left-recursive语法: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 % ",")

EDIT 2 如果你想强制优先——例如带括号:

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

这样你甚至可以写出像 x.(y[3].foo)[5](fun(), foo(bar)) 这样的表达式——如果这有意义的话。

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

您需要赋值的左侧不是函数。这意味着您有 left-hand 表达式的特定后缀。让我们在您的评论中称该规则为 ref_exp

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

最后我想我解决了这个问题,但是这个解决方案有一个side-effects,它会改变运算符的结合性。

    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()

如果我不区分左值

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

  1. f().y()[0]
  2. ().y()[0]
  3. .y()[0]
  4. ()[0]

所以我将使用第二个并在生成 ast 时检查参考。

感谢@cadrian