Yacc来源如下:
element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL);
if ($$ == NULL) {
ADD_MISSING_ELEMENT (graph, $1);
SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, _("no element \"%s\""), $1);
/* if FATAL_ERRORS flag is set, we don't have to worry about backwards
* compatibility and can continue parsing and check for other missing
* elements */
if ((graph->flags & GST_PARSE_FLAG_FATAL_ERRORS) == 0) {
gst_parse_strfree ($1);
YYERROR;
}
}
gst_parse_strfree ($1);
}
| element ASSIGNMENT { gst_parse_element_set ($2, $1, graph);
$$ = $1;
它被翻译成这样的代码:
{ (yyval.e) = gst_element_factory_make ((yyvsp[(1) - (1)].s), NULL);
if ((yyval.e) == NULL) {
ADD_MISSING_ELEMENT (graph, (yyvsp[(1) - (1)].s));
SET_ERROR (graph->error, GST_PARSE_ERROR_NO_SUCH_ELEMENT, _("no element \"%s\""), (yyvsp[(1) - (1)].s));
/* if FATAL_ERRORS flag is set, we don't have to worry about backwards
* compatibility and can continue parsing and check for other missing
* elements */
if ((graph->flags & GST_PARSE_FLAG_FATAL_ERRORS) == 0) {
gst_parse_strfree ((yyvsp[(1) - (1)].s));
YYERROR;
}
}
gst_parse_strfree ((yyvsp[(1) - (1)].s));
}
break;
其中一个译文转换为:
gst_element_factory_make ($1, NULL)
为:
gst_element_factory_make ((yyvsp[(1) - (1)].s), NULL)
[(1) - (1)]
令我困惑。
为什么[(1) - (1)]
句代表$1
值?
答案 0 :(得分:0)
正如Jonathan Leffler在评论中所说,野牛和yacc以便于代码生成的方式生成正确的代码,而非人类读者。
然而,各种堆栈的处理相当简单。 Bison维持两到三个堆栈:状态堆栈;价值堆栈;以及可选的位置堆栈。这三个堆栈是独立的,否则有必要创建一个包含状态,值和(如果需要)位置的堆栈槽结构。
堆栈总是大小相同,这意味着值堆栈的开头(以及位置堆栈)有一个未使用的槽。如果您参考LR解析算法的经典描述,请参阅Dragon Book的“LR解析算法”部分 - 我的版本中的§4.7 - 您将清楚地看到这一点;堆栈有一个多于语法符号的状态,因此没有X0
:
s0 X1 s1 X2 s2 … Xm sm
(Bison实际上并没有将语法符号保留在堆栈上,因为符号本身可以很容易地计算出来。相反,它保持与符号关联的语义值。)
在代码中,yyvsp
和yyssp
分别是指向值和状态堆栈顶部的指针。 (yylsp
是指向位置堆栈顶部的指针(如果有的话)。)
它们实际指向各自堆栈的顶部元素,而不是指向顶部元素的顶部元素。一般过去的元素指针在一般的堆栈实现中更常见,因为如果堆栈为空,通常可能没有要指向的顶部元素。但是,在这种特殊情况下,堆栈永远不会为空,因此始终存在顶部元素。 (在算法开始时,顶部状态为0
,顶部值和位置是上面提到的未使用的插槽。)与往常一样,堆栈的其余部分从顶部向下,因此倒数第二个元素(如果有的话)位于yyvsp - 1
,而第三个元素位于yyvsp - 2
。
请记住,在C中,*yyvsp
表示与yyvsp[0]
完全相同。或者,通常,yyvsp[k]
与*(yyvsp + k)
相同。即使k
为负数也是如此,因此我们可以编写yyvsp[-1]
来引用堆栈中倒数第二个元素。
您引用的代码实现了制作的缩减操作;减少动作通常计算得到的非终端的语义值。假设减产的是:
A → X1 X2 … Xm
在这种情况下,值堆栈的顶部将精确
V1 V2 … Vm
(其中Vi
是符号Xi
)的语义值。或者,在野牛术语中:
$1 $2 … $m
这意味着$1
为yyvsp[1-m]
,$2
为yyvsp[2-m]
,依此类推,最高为$m
yyvsp[m-m]
。而这恰恰是野牛生成的代码所做的事情;它将$i
表示为yyvsp[(i)-(m)]
,其中m
是缩小右侧的长度(在语法符号中)。