用C ++创建自定义编译器的第一步之一就是开发编译器的预处理器引擎。 通常,C ++编译器具有单独的预处理引擎,可以完成以下四个基本任务:
- 定义和重新定义宏(#
#define
,##undef
)。 - 支持头文件(
#include
指令)。 - 支持条件编译(
#ifdef
,#ifndef
,#else
,#endif
)。 - 从输入源清单中删除所有注释。
通常,预处理器引擎的输出然后馈送到C ++词法分析器/解析器组合。 本文讨论使用Antlr的C ++预处理器的设计。 您应该对Antlr和自上而下的递归下降解析器的概念有所了解。 众所周知,本文讨论的所有代码都可在Antlr-2.7.2上运行,并使用gcc-3.4.4进行编译。
为预处理器创建一个解析器
将Antlr用作创建C ++编译器的首选解析器生成器工具的一个显着好处是不需要单独的预处理器引擎。 预处理器引擎可以集成为词法分析器的一部分。 要了解此策略,请回顾词法分析器和解析器通常是如何工作的。 词法分析器处理原始数据(在本例中为.h / .cpp文件),从该数据创建令牌,然后将令牌传递给解析器。 解析器依次处理令牌并针对语法进行验证,进行语义检查并最终生成汇编代码。
使用单个编译器和预处理器引擎的关键在于对词法分析器的适当修改。 Antlr词法分析器已扩展为可以预处理C ++源,并且仅将相关令牌传递给解析器。 解析器永远不会意识到预处理部分; 它仅与它从词法分析器收到的令牌有关。 考虑以下代码段:
#define USE_STREAMS
#ifdef USE_STREAMS
#include <iostream>
#else
# include <stdio.h>
#endif
假定此代码段已声明为C ++源文件的一部分。 词法分析器需要通过包含iostream
标头将其找到的第一个令牌传递给解析器。 #define
映射和#ifdef
条件评估是词法分析器的附加职责。
通过定义新标记来增强Antlr词法分析器
使用C ++语言的词法分析器根据C ++语言标准定义令牌。 例如,您通常会找到表示平均lexer文件中定义的变量名称和语言关键字的标记。
要将预处理引擎与词法分析器结合使用,必须定义与预处理器构造相对应的新令牌类型。 在遇到这些构造时,词法分析器将采取必要的措施。 词法分析器还必须从源代码中删除注释。 在这种情况下,词法分析器在遇到注释标记时,需要忽略该标记并继续寻找下一个可用标记。 重要的是要注意,这些标记没有被定义为语言标准的一部分。 它们本质上是实现定义的令牌。
在词法分析器中定义新标记
该词法分析器必须包含以下标记以及语言要求的标记: COMMENT_TOKEN
, INCLUDE_TOKEN
, DEFINE_TOKEN
, UNDEF_TOKEN
, IFDEF_TOKEN
, ELSE_TOKEN
和ENDIF_TOKEN
。 本文讨论了一个原型词法分析器,它支持前面提到的预处理器宏以及整数和长数据类型的变量声明。 清单1显示了处理这些令牌的准系统词法分析器/解析器组合。
清单1.支持预处理器标记和long / int声明的准系统词法分析器/解析器
header {
#include "Main.hpp"
#include <string>
#include <iostream>
}
options {
language = Cpp;
}
class cppParser extends Parser;
start: ( declaration )+ ;
declaration: type a:IDENTIFIER (COMMA b:IDENTIFIER)* SEMI;
type: INT | LONG;
{
#include <fstream>
#include "cppParser.hpp"
}
class cppLexer extends Lexer;
options {
charVocabulary = '\3'..'\377';
k=5;
}
tokens {
INT="int";
LONG="long";
}
DEFINE_TOKEN: "#define" WS macroText:IDENTIFIER WS macroArgs:MACRO_TEXT;
UNDEF_TOKEN: "#undef" IDENTIFIER;
IFDEF_TOKEN: ("ifdef" | "#ifndef") WS IDENTIFIER;
ELSE_TOKEN: ("else" | "elsif&