在不回溯的情况下解析 C 赋值表达式的诀窍是什么?

What is the trick to parsing a C assignment-expression with no backtracking?

提问人:Witness Protection ID 44583292 提问时间:11/2/2023 最后编辑:LesWitness Protection ID 44583292 更新时间:11/3/2023 访问量:126

问:

自上而下解析 C 必须在 a 和 a 之间进行选择。(不幸的是,很复杂)BNF 是:assignment-expressionconditional-expressionunary-expression

<assignment-expression>     ::= <conditional-expression>
                              | <unary-expression> <assignment-operator> <assignment-expression>
<conditional-expression>    ::= <logical-or-expression> 
                              | <logical-or-expression> ? <expression> : <conditional-expression>
<logical-or-expression>     ::= <logical-and-expression> 
                              | <logical-or-expression> || <logical-and-expression>
<logical-and-expression>    ::= <inclusive-or-expression> 
                              | <logical-and-expression> && <inclusive-or-expression>
<inclusive-or-expression>   ::= <exclusive-or-expression> 
                              | <inclusive-or-expression> '|' <exclusive-or-expression>
<exclusive-or-expression>   ::= <and-expression> 
                              | <exclusive-or-expression> ^ <and-expression>
<and-expression>            ::= <equality-expression> 
                              | <and-expression> & <equality-expression>
<equality-expression>       ::= <relational-expression> 
                              | <equality-expression> == <relational-expression> 
                              | <equality-expression> != <relational-expression>
<relational-expression>     ::= <shift-expression>
                              | <relational-expression> < <shift-expression>
                              | <relational-expression> > <shift-expression>
                              | <relational-expression> <= <shift-expression>
                              | <relational-expression> >= <shift-expression>
<shift-expression>          ::= <additive-expression>
                              | <shift-expression> << <additive-expression>
                              | <shift-expression> >> <additive-expression>
<additive-expression>       ::= <multiplicative-expression>
                              | <additive-expression> + <multiplicative-expression>
                              | <additive-expression> - <multiplicative-expression>
<multiplicative-expression> ::= <cast-expression>
                              | <multiplicative-expression> * <cast-expression>
                              | <multiplicative-expression> / <cast-expression>
                              | <multiplicative-expression> % <cast-expression>
<cast-expression>           ::= <unary-expression>
                              | ( <type-name> ) <cast-expression>
<unary-expression>          ::= <postfix-expression>
                              | ++ <unary-expression>
                              | -- <unary-expression>
                              | <unary-operator> <cast-expression>
                              | sizeof <unary-expression>
                              | sizeof <type-name>
<postfix-expression>        ::= <primary-expression>
                              | <postfix-expression> [ <expression> ]
                              | <postfix-expression> ( {<assignment-expression>}* )
                              | <postfix-expression> . <identifier>
                              | <postfix-expression> -> <identifier>
                              | <postfix-expression> ++
                              | <postfix-expression> --
<primary-expression>        ::= <identifier>
                              | <constant>
                              | <string>
                              | ( <expression> )

请注意,a 满足该规则。unary-expressioncast-expression

如果输入是,并且解析器首先尝试分支,则输入只需停在 即可满足规则。解析器必须回溯并考虑分支才能发现该部分。X=3conditional-expressionprimary-expressionXunary-expression assignment-operator assignment-expression=3

如果输入是,并且解析器首先尝试分支,则永远不会找到,并且解析器必须回溯并考虑分支以发现规则。X==Yunary-expressionassignment-operatorconditional-expressionequality-expression

我不明白如何在任何地方左分解 BNF 以避免回溯。递归和回溯是不可避免的吗?还是有诀窍?

编辑:为了回应要求简化语法的评论,下面将演示 vs。 问题,但只是为了证明简化的语法如何需要回溯,对于那些不想摸索完整语法的人来说。为避免在这种简化的语法中回溯而进行的任何修复都可能无法使用完整语法。X=3X==Y

<assignment-expression>     ::= <conditional-expression>
                              | <unary-expression> <assignment-operator> <unary-expression>
<conditional-expression>    ::= <unary-expression> == <unary-expression>
<unary-expression>          ::= <identifier>
<assignment_operator>       ::= '='

如果输入是,并且解析器首先查找,则找不到,因此解析器必须回溯,而输入则无需回溯即可工作。X=3conditional-expression==X==Y

如果输入是,并且解析首先查找,则输入是可以接受的,而输入将需要回溯才能匹配。X=3unary-expressionX==Yconditional-expression

c 解析 语法 回溯 bnf

评论

0赞 Ouroborus 11/2/2023
它真的会回溯吗?我觉得,在编译版本中,决定实际上会发生在第一个之后的角色上。它读取第一个,知道这现在是赋值或条件。阅读下一个字符会告诉它哪个。(我不是专家,所以......==
0赞 500 - Internal Server Error 11/2/2023
我不相信将语法重新表述为允许自上而下解析的形式是可能的。C
0赞 n. m. could be an AI 11/2/2023
请考虑制作一个最小的可重现示例。不,真的。我们不需要 C 的 143 个运营商优先事项和他们的岳母来理解这个问题。制作一个简短、完整的语法来证明这一点。
0赞 Witness Protection ID 44583292 11/2/2023
@Ouroborus 如果解析器不使用上述语法回溯,则可能会出错。自上而下的解析器通常不会在确定 .如果选择的起始候选规则是 ,那么解析器认为是 ,并且它根本不会查找,这将是一个错误。如果起始候选项是 ,则规则告诉它查找 next,并且解析器会正确查找 。XXprimary-expressionconditional-expressionXconditional-expression=unary-expressionassignment-expressionassignment-operator=
0赞 Witness Protection ID 44583292 11/2/2023
@500-InternalServerError这是我关心的问题。如果您能找到任何在线证明或讨论,请告诉我。大多数 C 语言可以在不回溯的情况下解析,但是 、 、 、 和 在完整的 C 语法中看起来很有问题。assignment-expressioncompound-statementexternal-declarationparameter-declarationspecifier-qualifier

答: 暂无答案