cpp_reader中的域op_stack用于多次包含优化(multiple-include optimization,即将#include指示加入#if !defined和#endif对中),它将缓存#if或#elseif表达式的符号。
cpp_create_reader (continue)
199 /* The expression parser stack. */
200 _cpp_expand_op_stack (pfile);
200
201 /* Initialize the buffer obstack. */
202 _obstack_begin (&pfile->buffer_ob, 0, 0,
203 (void *(*) (long)) xmalloc,
204 (void (*) (void *)) free);
205
206 _cpp_init_files (pfile);
207
208 _cpp_init_hashtable (pfile, table);
209
210 return pfile;
211 }
这个栈由_cpp_expand_op_stack初始化及扩展。
970 struct op *
971 _cpp_expand_op_stack (cpp_reader *pfile) in cppexp.c
972 {
973 size_t old_size = (size_t) (pfile->op_limit - pfile->op_stack);
974 size_t new_size = old_size * 2 + 20;
975
976 pfile->op_stack = xrealloc (pfile->op_stack, new_size * sizeof (struct op));
977 pfile->op_limit = pfile->op_stack + new_size;
978
979 return pfile->op_stack + old_size;
980 }
这个栈的初始大小是20。而op的定义如下。
32 {
33 const cpp_token *token; /* The token forming op (for diagnostics). */
34 cpp_num value; /* The value logically "right" of op. */
35 enum cpp_ttype op;
36 };
Op中的域token保存用于诊断的符号信息。域value记录了符号的值(如果有的话),它是语义处理的关键。域op保存符号的类型,它也是语义分析的关键。
进一步深入op定义。
165 #define PREV_WHITE (1 << 0) /* If whitespace before this token. */ in cpplib.h
166 #define DIGRAPH (1 << 1) /* If it was a digraph. */
167 #define STRINGIFY_ARG (1 << 2) /* If macro argument to be stringified. */
168 #define PASTE_LEFT (1 << 3) /* If on LHS of a ## operator. */
169 #define NAMED_OP (1 << 4) /* C++ named operators. */
170 #define NO_EXPAND (1 << 5) /* Do not macro-expand this token. */
171 #define BOL (1 << 6) /* Token at beginning of line. */
上面的宏用于cpp_token的flags域,这个域表示了符号的特性。而cpp_token的定义如下所示。
175 struct cpp_token in cpplib.h
176 {
177 fileline line; /* Logical line of first char of token. */
178 unsigned short col; /* Column of first char of token. */
179 ENUM_BITFIELD(cpp_ttype) type : CHAR_BIT; /* token type */
180 unsigned char flags; /* flags - see above */
181
183 {
184 cpp_hashnode *node; /* An identifier. */
185 const cpp_token *source; /* Inherit padding from this token. */
186 struct cpp_string str; /* A string, or number. */
187 unsigned int arg_no; /* Argument no. for a CPP_MACRO_ARG. */
188 } val;
189 };
op的域value则定义在下面。注意到结构中的cpp_num_part是机器上最宽的整型。
4929 struct cpp_num in cpplib.h
4930 {
4931 cpp_num_part high;
4932 cpp_num_part low;
4933 bool unsignedp; /* True if value should be treated as unsigned. */
4934 bool overflow; /* True if the most recent calculation overflowed. */
4935 };
而上面的179行,cpp_ttype是这样定义的。同时注意伴随的OP及TK的定义。
143 #define OP(e, s) e, in cpplib.h
144 #define TK(e, s) e,
146 {
147 TTYPE_TABLE
148 N_TTYPES
149 };
150 #undef OP
151 #undef TK
在147行的TTYPE_TABLE的内容见如下,它包含了所有可能的符号类型。
60 #define TTYPE_TABLE / in cpplib.h
61 OP(CPP_EQ = 0, "=") /
62 OP (CPP_NOT, "!") /
63 OP (CPP_GREATER, ">") /* compare */ /
64 OP (CPP_LESS, "<") /
65 OP (CPP_PLUS, "+") /* math */ /
66 OP (CPP_MINUS, "-") /
67 OP (CPP_MULT, "*") /
68 OP (CPP_DIV, "/") /
69 OP (CPP_MOD, "%") /
70 OP (CPP_AND, "&") /* bit ops */ /
71 OP (CPP_OR, "|") /
72 OP (CPP_XOR, "^") /
73 OP (CPP_RSHIFT, ">>") /
74 OP (CPP_LSHIFT, "<<") /
75 OP (CPP_MIN, "<?") /* extension */ /
76 OP (CPP_MAX, ">?") /
77 /
78 OP (CPP_COMPL, "~") /
79 OP (CPP_AND_AND, "&&") /* logical */ /
80 OP (CPP_OR_OR, "||") /
81 OP (CPP_QUERY, "?") /
82 OP (CPP_COLON, ":") /
83 OP (CPP_COMMA, ",") /* grouping */ /
84 OP (CPP_OPEN_PAREN, "(") /
85 OP (CPP_CLOSE_PAREN, ")") /
86 TK(CPP_EOF, SPELL_NONE) /
87 OP (CPP_EQ_EQ, "==") /* compare */ /
88 OP (CPP_NOT_EQ, "!=") /
89 OP (CPP_GREATER_EQ, ">=") /
90 OP (CPP_LESS_EQ, "<=") /
91 /
92 /* These two are unary + / - in preprocessor expressions. */ /
93 OP (CPP_PLUS_EQ, "+=") /* math */ /
94 OP (CPP_MINUS_EQ, "-=") /
95 /
96 OP (CPP_MULT_EQ, "*=") /
97 OP (CPP_DIV_EQ, "/=") /
98 OP (CPP_MOD_EQ, "%=") /
99 OP (CPP_AND_EQ, "&=") /* bit ops */ /
100 OP (CPP_OR_EQ, "|=") /
101 OP (CPP_XOR_EQ, "^=") /
102 OP (CPP_RSHIFT_EQ, ">>=") /
103 OP (CPP_LSHIFT_EQ, "<<=") /
104 OP (CPP_MIN_EQ, "<?=") /* extension */ /
105 OP (CPP_MAX_EQ, ">?=") /
106 /* Digraphs together, beginning with CPP_FIRST_DIGRAPH. */ /
107 OP (CPP_HASH, "#") /* digraphs */ /
108 OP (CPP_PASTE, "##") /
109 OP (CPP_OPEN_SQUARE, "[") /
110 OP (CPP_CLOSE_SQUARE, "]") /
111 OP (CPP_OPEN_BRACE, "{") /
112 OP (CPP_CLOSE_BRACE, "}") /
113 /* The remainder of the punctuation. Order is not significant. */ /
114 OP (CPP_SEMICOLON, ";") /* structure */ /
115 OP (CPP_ELLIPSIS, "...") /
116 OP (CPP_PLUS_PLUS, "++") /* increment */ /
117 OP (CPP_MINUS_MINUS, "--") /
118 OP (CPP_DEREF, "->") /* accessors */ /
119 OP (CPP_DOT, ".") /
120 OP (CPP_SCOPE, "::") /
121 OP (CPP_DEREF_STAR, "->*") /
122 OP (CPP_DOT_STAR, ".*") /
123 OP (CPP_ATSIGN, "@") /* used in Objective-C */ /
124 /
125 TK (CPP_NAME, SPELL_IDENT) /* word */ /
126 TK (CPP_AT_NAME, SPELL_IDENT) /* @word - Objective-C */ /
127 TK (CPP_NUMBER, SPELL_LITERAL) /* 34_be+ta */ /
128 /
129 TK (CPP_CHAR, SPELL_LITERAL) /* 'char' */ /
130 TK (CPP_WCHAR, SPELL_LITERAL) /* L'char' */ /
131 TK (CPP_OTHER, SPELL_LITERAL) /* stray punctuation */ /
132 /
133 TK (CPP_STRING, SPELL_LITERAL) /* "string" */ /
134 TK (CPP_WSTRING, SPELL_LITERAL) /* L"string" */ /
135 TK (CPP_OBJC_STRING, SPELL_LITERAL) /* @"string" - Objective-C */ /
136 TK (CPP_HEADER_NAME, SPELL_LITERAL) /* <stdio.h> in #include */ /
137 /
138 TK (CPP_COMMENT, SPELL_LITERAL) /* Only if output comments. */ /
139 /* SPELL_LITERAL happens to DTRT. */ /
140 TK (CPP_MACRO_ARG, SPELL_NONE) /* Macro argument. */ /
141 TK (CPP_PADDING, SPELL_NONE) /* Whitespace for cpp0. */
其中OP应该是operator的缩写,TK应该是token的缩写。
cpp_reader使用哈希表来管理编译的文件及头文件的查找目录。接下来,就是初始化这些哈希表。
930 void
931 _cpp_init_files (cpp_reader *pfile) in cppfiles.c
932 {
933 pfile->file_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
934 NULL, xcalloc, free);
935 pfile->dir_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
936 NULL, xcalloc, free);
937 allocate_file_hash_entries (pfile);
938 }
cpp_create_reader的最后一步是调用下面的_cpp_init_hashtable。
这里,我们看到在调用时,ident_hash被传入做第二个参数。它保存了所有的标识符(identifier):由#define定义的宏(符号类型NT_MACRO),由#assert声明的断言(符号类型NT_ASSERTION),及其他(符号类型NT_VOID)。内建的宏(builtin macro),例如__LINE__,由NODE_BUILTIN标识。中毒(poisoned)的标识符由NODE_POISONED标记。NODE_OPERATOR(仅对于C++)表示标识符的代表操作符,比如“xor”。 NODE_DIAGNOSTIC用于加速词法分析:它表示该符号可能需要诊断(diagnostic)。目前,这个标识仅用于__VA_ARGS__及中毒的标识符。
47 void
48 _cpp_init_hashtable (cpp_reader *pfile, hash_table *table) in cpphash.c
49 {
50 struct spec_nodes *s;
51
52 if (table == NULL)
53 {
54 pfile->our_hashtable = 1;
55 table = ht_create (13); /* 8K (=2^13) entries. */
56 table->alloc_node = (hashnode (*) (hash_table *)) alloc_node;
57
58 _obstack_begin (&pfile->hash_ob, 0, 0,
59 (void *(*) (long)) xmalloc,
60 (void (*) (void *)) free);
61 }
62
63 table->pfile = pfile;
64 pfile->hash_table = table;
65
66 /* Now we can initialize things that use the hash table. */
67 _cpp_init_directives (pfile);
68 _cpp_init_internal_pragmas (pfile);
69
70 s = &pfile->spec_nodes;
71 s->n_defined = cpp_lookup (pfile, DSC("defined"));
72 s->n_true = cpp_lookup (pfile, DSC ("true"));
73 s->n_false = cpp_lookup (pfile, DSC ("false"));
74 s->n__VA_ARGS__ = cpp_lookup (pfile, DSC ("__VA_ARGS__"));
75 s->n__VA_ARGS__->flags |= NODE_DIAGNOSTIC;
76 }
在C/C++语言里,定义了一系列的指示(directive)。_cpp_init_directives确保指示的hashnode都已出现在cpp_reader中的hash_table哈希表中。
1983 void
1984 _cpp_init_directives (cpp_reader *pfile) in cpplib.c
1985 {
1986 unsigned int i;
1987 cpp_hashnode *node;
1988
1989 for (i = 0; i < (unsigned int) N_DIRECTIVES; i++)
1990 {
1991 node = cpp_lookup (pfile, dtable[i].name, dtable[i].length);
1992 node->is_directive = 1;
1993 node->directive_index = i;
1994 }
1995 }
上面的1991行,dtable依照DIRECTIVE_TABLE的内容,以以下的方式初始化。
179 #define D(name, t, origin, flags) / in cpplib.c
180 { do_##name, (const uchar *) #name, /
181 sizeof #name - 1, origin, flags },
182 static const directive dtable[] =
183 {
184 DIRECTIVE_TABLE
185 };
186 #undef D
187 #undef DIRECTIVE_TABLE
记录指示细节的节点具有如下定义。其中的handler是指向处理函数的函数指针。
84 struct directive in cpplib.c
85 {
86 directive_handler handler; /* Function to handle directive. */
87 const uchar *name; /* Name of directive. */
88 unsigned short length; /* Length of name. */
89 unsigned char origin; /* Origin of directive. */
90 unsigned char flags; /* Flags describing this directive. */
91 };
而DIRECTIVE_TABLE由宏D展开。D的定义在上面的179行,以第一行为例,展开后为:do_define, (const unchar*) “define”, sizeof “define” -1, KANDR, IN_I
143 #define DIRECTIVE_TABLE / in cpplib.c
144 D(define, T_DEFINE = 0, KANDR, IN_I) /* 270554 */ /
145 D(include, T_INCLUDE, KANDR, INCL | EXPAND) /* 52262 */ /
146 D(endif, T_ENDIF, KANDR, COND) /* 45855 */ /
147 D(ifdef, T_IFDEF, KANDR, COND | IF_COND) /* 22000 */ /
148 D(if, T_IF, KANDR, COND | IF_COND | EXPAND) /* 18162 */ /
149 D(else, T_ELSE, KANDR, COND) /* 9863 */ /
150 D(ifndef, T_IFNDEF, KANDR, COND | IF_COND) /* 9675 */ /
151 D(undef, T_UNDEF, KANDR, IN_I) /* 4837 */ /
152 D(line, T_LINE, KANDR, EXPAND) /* 2465 */ /
153 D(elif, T_ELIF, STDC89, COND | EXPAND) /* 610 */ /
154 D(error, T_ERROR, STDC89, 0) /* 475 */ /
155 D(pragma, T_PRAGMA, STDC89, IN_I) /* 195 */ /
156 D(warning, T_WARNING, EXTENSION, 0) /* 22 */ /
157 D(include_next, T_INCLUDE_NEXT, EXTENSION, INCL | EXPAND) /* 19 */ /
158 D(ident, T_IDENT, EXTENSION, IN_I) /* 11 */ /
159 D(import, T_IMPORT, EXTENSION, INCL | EXPAND) /* 0 ObjC */ /
160 D(assert, T_ASSERT, EXTENSION, 0) /* 0 SVR4 */ /
161 D(unassert, T_UNASSERT, EXTENSION, 0) /* 0 SVR4 */ /
162 D(sccs, T_SCCS, EXTENSION, 0) /* 0 SVR4? */
上面代码中的第二列在cpplib.c的其他地方,以类似的方式展开为一个枚举类型。在第三及第四列,是已定义的宏。第三列的宏表示指示的来源,目前我们已有以下宏定义。
KANDR:指示来自传统(K&R)C
STDC89:指示来自1989 C标准
EXTENSION:指示是扩展的(extension)
在第四列的宏表示了指示的特性,我们已有如下宏定义。
COND:表示一个条件指示
IF_COND:表示一个引导的(opening)条件指示
INCL:表示分别将"..."和<...> 作为q-char及h-char序列来处理
IN_I:表示该指示即便使用了-fpreprocessed(只做预处理即退出),也要被处理(这些都是设置了回调钩子的指示)。
EXPAND:在需要宏展开的指示中设置
接着,_cpp_init_directives会注册预处理器所要处理的#pragma。[4]给出了详细的解释。
#pragma GCC dependency
#pragma GCC dependency允许你检查当前文件和其他文件的相对日期。如果其他文件比当前文件更新,给出一个警告。如果当前文件从其他文件导出,需要重新生成,这#pragma是有用的。其他文件使用普通的包含文件查找路径来查找。可选的,尾随字符串可用于在警告消息中给出更多信息。
#pragma GCC dependency “parse.y”
#pragma GCC dependency “/usr/include/time.h” rerun fixincludes
#pragma GCC poison
有时,你希望从你的程序中完全移除一个标识符(identifier),以确保它不会悄悄地回来。为了达到这个目的,你可以使用这个pragma毒化这个标识符。#pragma GCC poison 后面跟着要毒化的标识符列表。如果其中的任一标识符出现在以后的代码中,它是个错误。例如,
#pragma GCC poison printf sprint fprintf
sprint (some_string, “hello”);
将产生一个错误。
如果被毒化的标识符出现在展开的宏里,并且这个宏定义在毒化之前,那么它不会导致错误。这使得你可以毒化一个标识符,而不需要担心使用它,定义在系统头文件中的宏。例如,
#define strrchr rindex
#pragma GCC poison rindex
strrchr (some_string, ‘h’);
将不会产生错误。
#pragma GCC system_header
这个pragma不带参数。它使得当前文件余下的代码,按系统头文件来处理(当GCC处理一个系统头文件时,所有的警告,除了#warning产生的,都被压制。在系统头文件中定义的宏,在展开时也对一些警告免疫。当我们发现,因为系统头文件定义的宏,一个警告产生大量的误报(false positives)时,这个免疫被特别地授予)。
#pragma once不是标准支持的,但它被广泛支持。它的效果与利用#ifdef宏来防止重复包含相同。http://en.wikipedia.org/wiki/Pragma_once上有更详细的介绍。
1048 void
1049 _cpp_init_internal_pragmas (cpp_reader *pfile) in cpplib.c
1050 {
1051 /* Pragmas in the global namespace. */
1052 cpp_register_pragma (pfile, 0, "once", do_pragma_once);
1053
1054 /* New GCC-specific pragmas should be put in the GCC namespace. */
1055 cpp_register_pragma (pfile, "GCC", "poison", do_pragma_poison);
1056 cpp_register_pragma (pfile, "GCC", "system_header", do_pragma_system_header);
1057 cpp_register_pragma (pfile, "GCC", "dependency", do_pragma_dependency);
1058 }
GCC为#pragma定义了pragma_entry。
49 typedef void (*pragma_cb) (cpp_reader *); in cpplib.c
51 {
52 struct pragma_entry *next;
53 const cpp_hashnode *pragma; /* Name and length. */
54 int is_nspace;
55 union {
56 pragma_cb handler;
57 struct pragma_entry *space;
58 } u;
59 };
对于后跟参数的#pragma,使用在56行的handler,它是一个函数指针,提供这个#pragma的功能。而对于构成一个空间的#pragma,使用57行的space把出现于它的空间中的其他#pragma链起来,同时设置54行的is_nspace。
编译器使用结构pragma_entry来记录#pragma,所有的#pragma都被保存在parse_in的pragmas域,这样可以尽早开始处理#pragma。
1005 void
1006 cpp_register_pragma (cpp_reader *pfile, const char *space, in cpplib.c
1007 const char *name, pragma_cb handler)
1008 {
1009 struct pragma_entry **chain = &pfile->pragmas;
1010 struct pragma_entry *entry;
1011 const cpp_hashnode *node;
1012
1013 if (!handler)
1014 abort ();
1015
1016 if (space)
1017 {
1018 node = cpp_lookup (pfile, U space, strlen (space));
1019 entry = lookup_pragma_entry (*chain, node);
1020 if (!entry)
1021 entry = insert_pragma_entry (pfile, chain, node, NULL);
1022 else if (!entry->is_nspace)
1023 goto clash;
1024 chain = &entry->u.space;
1025 }
1026
1027 /* Check for duplicates. */
1028 node = cpp_lookup (pfile, U name, strlen (name));
1029 entry = lookup_pragma_entry (*chain, node);
1030 if (entry)
1031 {
1032 if (entry->is_nspace)
1033 clash:
1034 cpp_error (pfile, CPP_DL_ICE,
1035 "registering /"%s/" as both a pragma and a pragma namespace",
1036 NODE_NAME (node));
1037 else if (space)
1038 cpp_error (pfile, CPP_DL_ICE, "#pragma %s %s is already registered",
1039 space, name);
1040 else
1041 cpp_error (pfile, CPP_DL_ICE, "#pragma %s is already registered", name);
1042 }
1043 else
1044 insert_pragma_entry (pfile, chain, node, handler);
1045 }
因为#pragma的种类有限,以nul结尾的简单链表已经足够。注意到对于类似#pragma GCC dependency等指示,GCC构成了一个空间,dependency,posion及system_header都在这个空间中。因此对应于GCC的节点是一个分枝,其中的节点对应所包含的内容。
965 static struct pragma_entry *
966 lookup_pragma_entry (struct pragma_entry *chain, const cpp_hashnode *pragma) in cpplib.c
967 {
968 while (chain && chain->pragma != pragma)
969 chain = chain->next;
970
971 return chain;
972 }
For insert_pragma_entry, notice that argument pragma is of type cpp_hashnode which is the identifier for the directive in ident_hash table.
977 static struct pragma_entry *
978 insert_pragma_entry (cpp_reader *pfile, struct pragma_entry **chain, in cpplib.c
979 const cpp_hashnode *pragma, pragma_cb handler)
980 {
981 struct pragma_entry *new;
982
983 new = (struct pragma_entry *)
984 _cpp_aligned_alloc (pfile, sizeof (struct pragma_entry));
985 new->pragma = pragma;
986 if (handler)
987 {
988 new->is_nspace = 0;
989 new->u.handler = handler;
990 }
991 else
992 {
993 new->is_nspace = 1;
994 new->u.space = NULL;
995 }
996
997 new->next = *chain;
998 *chain = new;
999 return new;
1000 }
回到_cpp_init_hashtable,cpp_reader的spec_nodes记录了语言中特殊的标识符。这里是“defined”,“true”,“false”和“__VAR_ARGS”。它们必须是全局唯一的,因此我们在以下spec_nodes的定义中使用指针引用它们。
247 struct spec_nodes in cpphash.h
248 {
249 cpp_hashnode *n_defined; /* defined operator */
250 cpp_hashnode *n_true; /* C++ keyword true */
251 cpp_hashnode *n_false; /* C++ keyword false */
252 cpp_hashnode *n__VA_ARGS__; /* C99 vararg macros */
253 };
587 #define DSC(str) (const uchar *)str, sizeof str – 1 in cpphash.h
连同上面DSC的定义,在函数最后“defined”,“true”,“false”及“__VAR_ARGS”的唯一节点被创建。