接下来作语法分析,
其中涉及到的语法规则:
1 fndef: typed_declspecs declarator
{if (! start_function ($1, $2))
YYFAIL;
}
xdecls
{ store_parm_decls (); }
'{' '}'
{finish_function (lineno); }
;
/* Declspecs which contain at least one type specifier or typedef name.
(Just `const' or `volatile' is not enough.)
A typedef'd name following these is taken as a name to be declared. */
2 typed_declspecs:
typespec
{ $$ = tree_cons (NULL_TREE, $1, NULL_TREE); }
;
/* A typespec (but not a type qualifier).
Once we have seen one of these in a declaration,
if a typedef name appears then it is being redeclared. */
3 typespec: TYPESPEC
;
/* Any kind of declarator (thus, all declarators allowed
after an explicit typespec). */
4 declarator:
notype_declarator
;
/* A declarator allowed whether or not there has been
an explicit typespec. These cannot redeclare a typedef-name. */
5 notype_declarator:
notype_declarator '(' parmlist_or_identifiers %prec '.'
{ $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); }
| IDENTIFIER
;
/* This is referred to where either a parmlist or an identifier list is ok.
Its value is a list of ..._TYPE nodes or a list of identifiers. */
6 parmlist_or_identifiers:
{ pushlevel (0); }
parmlist_or_identifiers_1
{ $$ = $2;
poplevel (0, 0, 0); }
;
7 parmlist_or_identifiers_1:
parmlist_2 ')'
;
/* This is what appears inside the parens in a function declarator.
Is value is represented in the format that grokdeclarator expects. */
8 parmlist_2: /* empty */
{ $$ = get_parm_info (0); }
9 xdecls:
/* empty */
;
我们知道bison采用的是从下往上的语法分析方法,
当词法分析器读到void,上一篇词法分析有交待,他会把它识别成 TYPESPEC类型符号,这样就与语法规则3相匹配,同时规约为typespec 在上面的语法规则中只有规则2右边有typespec非终结符,所以可以直接规约到符号typed_declspecs,这时执行
{ $$ = tree_cons (NULL_TREE, $1, NULL_TREE);
tree_cons将生成一个tree_list 的节点,返回类型被记录在tree_list 的list.value上面,这个tree_list将被作为typed_declspecs的值,在规则1中将被引用。
typed_declspecs能否继续规约还要看后面的符号。后面的是main字串,不是
保留字,是IDENTIFIER ,上面出现IDENTIFIER 的只有规则5。这样可规约为notype_declarator非终结符,
右边含有notype_declarator非终结符,只有规则5的另一个分支;再看后面
它是'(',而规则5种这一分支正好有一个'(' ,这个分支后续有parmlist_or_identifiers非终结符。
我们看规则 6,7,8,发现其实parmlist_2对应到一个空串,而规则7,parmlist_2 后面对应')' 与输入相符,
这样与规则5另一分支完全相符,这样就会依次执行:get_parm_info (0);pushlevel (0);poplevel (0, 0, 0);
下面分析get_parm_info函数:
这里参数为0表示没有参数,为1表示有参数。
函数开始取出参数getdecls();
再进行反序排列nreverse();这样parms保存的参数将是第一个参数,他的
下一个参数将通过TREE_CHAIN ()获取,
然后将反序排列的参数进行保存storedecls();
因为函数可以写成这样int main(void) 实际上是没有参数:下面的代码就是处理这种情况:
/* Just `void' (and no ellipsis) is special. There are really no parms. */
if (void_at_end && parms != 0
&& TREE_CHAIN (parms) == 0
&& TREE_TYPE (parms) == void_type_node
&& DECL_NAME (parms) == 0)
{
parms = NULL_TREE;
storedecls (NULL_TREE);
return saveable_tree_cons (NULL_TREE, NULL_TREE,
saveable_tree_cons (NULL_TREE, void_type_node, NULL_TREE));
}
因为这里没参数,我们将在以后再来分析这段代码。
pushlevel和poplevel 表明作用域的变化,进入到参数列表,从全局域变化到函数作用域,pushlevel进入到函数作用域,poplevel跳出函数作用域,进入全局域。
根据规则5,将再次规约到notype_declarator,这时会执行:
{ $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); }
这个函数比较简单:
产生CALL_EXPR节点,
exp.operands[0],保存函数名
exp.operands[1],parmlist_2非终结符值
exp.operands[2],为空。
然后再根据规则4,继续规约到declarator,这个非终结符在产生式右边,只有规则1出现,继续看规则1,根据规则9,xdecls对应到空串,后续输入字符'{' '}' 恰恰与规则1相符,这样会依次执行:
start_function,store_parm_decls (),finish_function (lineno);
接下来讲函数体的处理。