我正在阅读书籍 - “Flex and Bison”以了解解析器生成器的工作方式,并且有例子:
calclist: /* nothing */
| calclist exp EOL { printf("= %d\n", $1); }
;
exp: factor
| exp ADD factor { $$ = $1 + $3; }
| exp SUB factor { $$ = $1 - $3; }
;
factor: term
| factor MUL term { $$ = $1 * $3; }
| factor DIV term { $$ = $1 / $3; }
;
term: NUMBER
| ABS term { $$ = $2 >= 0? $2 : - $2; }
;
并且在书中说上面的语法通过使用单独的非终结符号具有隐含的优先级。但它是如何工作的?假设我们有以下示例:1 + 3 * 2
(空格只是跳过)我们读取第一个令牌1
它将被推送到堆栈为NUMBER
或term
或{{1}通过语法“冒泡”多长时间?将检查哪个语法规则下一个标记?为什么这个语法乘法的优先级高于加法?
答案 0 :(得分:2)
这有一个“隐含的”优先级(而不是显式)的原因确实正如文本所说,由于因式语法(单独的非终结符号)。
完成1 + 3 * 2
的示例,将自己想象为正在进行解析的计算机,按照每个指令“按字母”进行操作。为了找到“exp”(表达式),您必须首先找到一个因子。 (你的其他选择是从找到“exp”开始,但必须找到一个“因子”。)所以你必须找到一个因素......但要这样做,你必须找到一个“术语”,因为一个因素是术语,或者本身以术语开头的因素。现在,您必须找到一个术语,即NUMBER
或关键字ABS
。因此,您可以“接受”(在语法术语中)1
,实际上是NUMBER
,并且您在解析的第一部分取得了成功 - 找到一个术语。 (您现在从令牌流中删除1
,留下+
作为下一个令牌。)
既然你有一个术语,你也有一个因素(按照定义),但是为了“完成一个因子的动作”,你需要尝试更长的匹配:一个因素然后是MUL
或DIV
,然后是其他内容。你的下一个标记是+
:它不是MUL而且它不是DIV。因此,您被迫停止解析因子并返回整个解析链 - 因为您的因素:1
是一个因素,下一个令牌仍为+
。
既然你有一个因子,你有一个exp(根据定义),但为了“完成exp的操作”,你再次被要求尝试更长的匹配:一个exp后跟{{ 1}}或ADD
,后跟一些东西。下一个令牌仍为SUB
,实际上是ADD ...因此您必须继续使用+
规则。
此时,你(作为解析器)将整个东西推到堆栈上并再次开始寻找合适的非终结符 - 在这种情况下,是另一个“因子”。所以你现在开始考虑一个因素的规则。您必须查找“字词”,如果找到“字词”,请尝试执行包含exp ADD factor { $$ = $1 + $3 };
或MUL
的规则的较长版本。当您完成此部分时,您将看到DIV
令牌确实是MUL,您将不得不采用更长的规则,使“因子”结果使用规则的*
部分。这将接受,即吃掉/用完factor MUL term { $$ = $1 * $3; }
序列,并返回值3 * 2
以获取“因子”,让您完成推送到解析堆栈的规则。
返回到推送状态后,通过添加1并接受(吃)完整表达式来完成“1 +”的解析。当然1 + 6是7,所以你的语法会返回正确的值。
答案 1 :(得分:2)
优先级是ADD和SUB的操作数是因子的结果,只有因子包含MUL和DIV运算符。 ADD不与MUL竞争,因为MUL被封装在一个术语中。
从解析器的角度考虑这个问题:在解析器考虑其与ADD运算符的关系之前必须减少术语表达式,并且该减少会消除MUL运算符。
给定A + X * Y
,X * Y
表达式减少为term
,这是一个不再表达*
运算符的单个语法符号,因此没有任何内容+
运营商有优先权问题;它现在只是A + term
。
顺便说一句,语法使用非常规的反向术语。
这些是术语:A + B + C
这些是因素:A * B * C
添加术语(“系列术语”),乘以因子(“整数或多项式的因子”)。
这真的来自这本教科书吗?无论如何,请尝试Aho,Sethi,Ullman的编译器:原理,技术和工具。 (1988年,但经典)。