C++的语法可以以树的形式显示(当然,我们需要把父节点的递归处理为子节点,绝大部分这样的情况,我们在图中标记为红色)。树的根及起点是translation-unit(编译单元)。在下面的图中,可以看到构建C++解析器不是一件容易的工作。更有甚者,标准提供的语法,本质上,是左递归的。为了能够使用解析器构建器(parser builder)来构建解析器,需要把它转换为右递归形式,来顾及由底向上的LR解析器。即便有了右递归的语法,构建LR(1)解析器依然相当的困难。因此GCC采用了手写的尝试性LL(n)解析器——它支持回溯。毫无疑问,浏览这个解析器将是一个漫长的旅途。让我们出发!
图43:声明的语法树
图44:类语法树
图45:语句语法树
c_parser_file的主体是cp_parser_translation_unit。根据【3】,编译单元的简要语法树如下:
translation-unit
Ⅼ declaration-seq [opt] —— declaration-seq declaration
Ⅼ declaration
在这个简要的视图中,一个编译单元仅包含一系列的声明。在C++中,编译单元通常包括一个源文件及多个头文件。从这个编译单元编译器将产生出目标文件,而后链接器将这些目标文件链接起来形成目的文件(target file)。
2319 static bool
2320 cp_parser_translation_unit (cp_parser* parser) in parser.c
2321 {
2322 while (true)
2323 {
2324 cp_parser_declaration_seq_opt (parser);
2325
2326 /* If there are no tokens left then all went well. */
2327 if (cp_lexer_next_token_is (parser->lexer, CPP_EOF))
2328 break;
2329
2330 /* Otherwise, issue an error message. */
2331 cp_parser_error (parser, "expected declaration");
2332 return false;
2333 }
2334
2335 /* Consume the EOF token. */
2336 cp_parser_require (parser, CPP_EOF, "end-of-file");
2337
2338 /* Finish up. */
2339 finish_translation_unit ();
2340
2341 /* All went well. */
2342 return true;
2343 }
注意上面的WHILE,正常的处理是经2328行的break跳到2336行,这样就可以避免使用goto。