最简单的c程序gcc源码编译流程分析
第1章 c代码“int a;”语法分析
GCC如何解析c代码“int a;”函数c_parse_file()是C语法分析的入口函数,我们从此函数开始来分析一段简单的c代码是如何从产生式(推导式、文法)来推到出来的。
在gcc源码中大多函数之前都有一段对应的产生式,通过这个产生式能够明白对应的函数是怎么实现的、实现了什么功能。(俞峰老师强调一定要看懂产生式在看函数的实现)。
1.1 入口函数c_parse_file
C语法分析的入口函数源码:
/* Parse a single source file. */
void
c_parse_file (void)
……
c_parser_translation_unit (the_parser);
the_parser= NULL;
}
1.2 翻译单元解析函数c_parser_translation_unit
函数c_parser_translation_unit (the_parser)用来解析翻译单元。什么是翻译单元呢?
语言叙述:
翻译单元(translation-unit)可以推导出多个外部声明(external-declarations);
那什么是外部声明呢?多个外部声明(external-declarations)可以推导出一个外部声明(external-declaration)或者多个外部声明(external-declarations)和一个外部声明(external-declaration)。(由于中文翻译的歧义,请看形式化表示。)
形式化表示:
translation-unit→external-declarations
external-declarations→external-declaration
external-declarations→external-declarationsexternal-declaration
也就是说如果将translation-unit 用S表示,将external-declarations用A表示,将external-declaration用B表示;S→A;A→B;A→AB。那么S可以推导出由任意非0个B组成的单元,例如S→B、S→BB、S→BBB……
那么我们由此来分析翻译单元是如何推导出“int a;”的,容易想到“int a;” 仅仅是一条声明,那么选择translation-unit→external-declarations→external-declaration;
还需要研究源码,gcc怎么知道是一条声明的,如何选择分支的?
源码:
/* Parse a translation unit (C90 6.7, C99 6.9).
translation-unit://翻译单元
external-declarations
external-declaration
external-declarations external-declaration
GNUextensions://GUN扩展语法
translation-unit:
empty
*/
static void
c_parser_translation_unit (c_parser *parser)
{
……
do
{
ggc_collect ();
c_parser_external_declaration (parser);
obstack_free (&parser_obstack,obstack_position);
}
while(c_parser_next_token_is_not (parser, CPP_EOF));
……
}
1.3 外部声明解析函数c_parser_external_declaration
3.2中提到的对多的就是外部声明external_declaration,那么什么是外部声明呢,当然外部声明也是文法组成的,它不是终结符。
注:终结符,通俗的说就是不能单独出现在推导式左边的符号,也就是说终结符不能再进行推导。不是终结符的都是非终结符。非终结符可理解为一个可拆分元素,而终结符是不可拆分的最小元素。如:有α→β ,则α 必然是个非终结符。
如果我们知道了外部声明(external_declaration)的文法,我们就明白什么是外部声明(external_declaration)。那么c_parser_external_declaration就是来处理外部声明的函数,当然这个函数由3.2中函数c_parser_translation_unit来调用,详见3.2中c_parser_translation_unit源码。
external declaration文法源码:
/* Parse an external declaration (C90 6.7, C996.9).
function-definition
external-declaration:
asm-definition
;
__extension__ external-declaration
external-declaration:
objc-class-definition
objc-class-declaration
objc-alias-declaration
objc-protocol-definition
objc-method-definition
@end
*/
external-declaration→function-definition
external-declaration→declaration
(注:为简化起见GNU extensions、Objective-C部分未做分析)
由此看出外部声明(external-declaration)可以推导出函数定义(function-definition)或者声明(declaration);
继续按照3.2的方式推导c代码“int a;” translation-unit→external-declarations→external-declaration→declaration
那么declaration是有谁解析的呢?declaration的文法是什么呢?通过函数c_parser_external_declaration()的源代码我们发现此函数调用了函数c_parser_declaration_or_fndef(),gcc正是通过c_parser_declaration_or_fndef()来解析declaration的。
c_parser_external_declaration源码:
static void
c_parser_external_declaration(c_parser *parser)
{
……
c_parser_declaration_or_fndef (parser,true, true, true, false, true,NULL, vNULL);
break;
}
}
1.4 解析声明和函数定义函数c_parser_declaration_or_fndef
下面我们来分析函数c_parser_declaration_or_fndef是怎么实现的。同理先理解此函数的产生式。这里为只分析“int a;”所以我们按照上述的translation-unit→external-declarations→external-declaration→declaration继续推导,我们分析declaration的描述;从下文c_parser_declaration_or_fndef产生式源码中我们可以得到declaration的相关描述。
c_parser_declaration_or_fndef产生式源码:
declaration-specifiersinit-declarator-list[opt] ;
static_assert-declaration
function-definition:
declaration-specifiers[opt]declarator declaration-list[opt]
compound-statement
declaration-list:
declaration
declaration-list declaration
init-declarator
init-declarator-list , init-declarator
init-declarator:
declarator simple-asm-expr[opt] attributes[opt]
declarator simple-asm-expr[opt] attributes[opt] = initializer
形式化表述:
-
declaration→declaration-specifiers init-declarator-list[opt] ;
-
declaration→static_assert-declaration
-
init-declarator-list→init-declarator
-
init-declarator-list→init-declarator-list , init-declarator
-
init-declarator→declarator simple-asm-expr[opt] attributes[opt] = initializer
注意:上述分号是推导式(文法)的一部分。
这里我们能够得到一条声明(declaration)能够推导出声明符(declaration-specifiers) 初始化说明符(init-declarator-list[opt])(这里的opt意思是可选的意思。)和一个“;”分号。到此不难得出“int a;”的推导式为
translation-unit→external-declarations→external-declaration→declaration→declaration-specifiers init-declarator-list; (1-1)
到此“int a;”中的分号就被从文法推导式的角度推导出来了。
按照gcc文法的规则,它将自左向右依次解析符号,直到解析到终结符为止,分号“;”即为一个终结符。由于这里c_parser_declaration_or_fndef函数给出了init-declarator-list的文法,我们先分析init-declarator-list,稍后补充上declaration-specifiers;值得注意的是gcc文法规则是自左向右先分析元素declaration-specifiers的。
由于“int a;”中声明的的变量只有a一个符号,所以不难推导此时的文法推导为3.init-declarator-list→init-declarator 。如果解析C代码为“int a,b;”这时的文法推导就是上述4.init-declarator-list→init-declarator-list , init-declarator。很明显这个4.式有个逗号“,”分隔,可以解析形如“int a,b,c;”“int a,b,c,d;”等C源码。
至此,我们推导出(1-1)中的init-declarator-list:
init-declarator-list→init-declarator (式1-2)
又由5.式不难得到可以得到:
init-declarator-list→init-declarator→declarator (式1-3)
c_parser_declaration_or_fndef源码:
static void
c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
bool static_assert_ok, bool empty_ok,
bool nested, bool start_attr_ok,
tree *objc_foreach_object_declaration,
vec<c_token>omp_declare_simd_clauses)
{
……
c_parser_declspecs(parser, specs, true, true, start_attr_ok,
true, true, cla_nonabstract_decl);
while (true)
{
……
declarator = c_parser_declarator (parser,
specs->typespec_kind!= ctsk_none,
C_DTR_NORMAL,&dummy);
if (declarator == NULL)
}
……
我们继续分析declarator的文法,我们可以通过函数c_parser_declaration_or_fndef()中调用的实现文法declarator的函数c_parser_declspecs ()来定位到文法declarator。
1.5 标识符解析函数c_parser_declarator
declarator文法源码:
pointer[opt] direct-declarator
direct-declarator:
identifier
( attributes[opt] declarator )
direct-declarator array-declarator
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list[opt] )
形式化定义:
declarator→pointer[opt] direct-declarator
direct-declarator→identifier
direct-declarator→direct-declarator array-declarator
direct-declarator→direct-declarator( parameter-type-list )
direct-declarator→direct-declarator( identifier-list[opt] )
由于pointer是可选元素,那么容易推导出:
declarator→direct-declarator→identifier (式1-4)
这里的识别符identifier就是一个终结符,在源码“int a;”中指的就是a这个标识符。由(1-3)、(1-4)得到:
init-declarator-list→init-declarator→declarator→direct-declarator→identifier
(式1-5)
1.6 声明符解析函数c_parser_declspecs
部分文法源码:
storage-class-specifierdeclaration-specifiers[opt]
type-specifier declaration-specifiers[opt]
type-qualifier declaration-specifiers[opt]
function-specifierdeclaration-specifiers[opt]
alignment-specifier declaration-specifiers[opt]
C90 6.5.2, C99 6.7.2:
type-specifier:
void
char
short
int
long
float
double
signed
unsigned
_Bool
_Complex
通过文法不难推导出“int a;”的declaration-specifiers的文法推导为:
declaration-specifiers→type-specifier→int (式1-6)
由(式1-1)、(式1-5)和(1-6)可得到“int a;”的文法推导树如下图所示:
至此,我们完成了最简单的c语言代码“inta;”的文法推导,只有在理解gcc文法的基础上在去分析对应的函数才能理清思路。