gcc 源码分析-从一个最简单的程序说起2

接下来作语法分析,

其中涉及到的语法规则:

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

接下来讲函数体的处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值