命令行调用参数说明
我们最终生成的编译器采用命令行调用方式。调用时,可选的参数及其功能如下表所示:
参数接口 | 参数 | 参数功能 | 参数默认值 |
---|---|---|---|
-inname | [file name] | 指定输入文件名 | PascalProgram.pas |
-outname | [file name] | 指定输出文件名 | CProgram.c |
-compiler | [compiler name] | 指定c编译器名,并将c程序编译成可执行文件 | gcc |
-exename | [exe name] | 指定可执行文件名,自动编译 | CProcess.exe |
-execute | 无 | 自动执行生成的可执行文件,如果未出现-e、-exename参数,则均按照默认方式进行操作 | |
-errorbound | [n] | 指定错误上限,即编译器发现了指定个数的错误后,立即停止运行 | INF |
-developer | 无 | 输出开发者信息 | |
-version | 无 | 输出版本信息 | |
-help | 无 | 输出所有命令行参数的帮助信息 |
注:中括号表示该参数可有可无
如果出现了-developer –version –help 或者 其它非法参数,则不运行编译器,只显示对应信息。
相关函数设计
参数帮助文档的初始化
首先需要引入一个存储帮助信息的map结构:是参数名字符串到其解释信息字符串的映射
map<string, string> argumentsExplanation;
函数接口
void argumentsExplanationInit();
伪代码
void argumentsExplanationInit() {
下面的映射全部添加到map argumentsExplanation中;
添加"-inname" 到 "-inname [file name]:\t\tdesignate the name of input pascal program, default is \"PascalProgram.pas\"." 的映射;
添加"-outname" 到 "-outname [file name]:\t\tdesignate the name of output C program, default is \"CProgram.c\"." 的映射;
添加"-compiler" 到 "-complier [complier name]:\tdesignate the name of C compiler, default is \"gcc\"." 的映射;
添加"-exename" 到 "-exename [exe name]:\t\tdesignate the name of exe file, default is \"CProcess.exe\"." 的映射;
添加"-execute" 到 "-execute:\t\t\tautomatically run the exe file." 的映射;
添加"-errorbound" 到 "-errorbound [n]:\t\tdesignate the up bound of error number as n, if the pascal2c compiler finds n errors, the compile process will abort, default is INF." 的映射;
添加"-developer" 到 "-developer:\t\t\tinformation about developers." 的映射;
添加"-version" 到 "-version:\t\t\tinformation about version of pascal2c compiler." 的映射;
添加"-help" 到 "-help:\t\t\t\toutput all the explanation abount command line arguments." 的映射;
}
输出参数帮助文档
函数接口
void outputArgumentsExplanation();
伪代码
void outputArgumentsExplanation() {
遍历map argumentExplanations {
输出参数说明和换行;
}
}
将char*指向的字符串保存到string中
函数接口
string char2str(char* chs);
返回值
string,转化后的字符串
参数列表
参数 | 描述 |
---|---|
char* chs | chs指向待转化的字符串 |
代码
string char2str(char* chs) {
string res;
for (; *chs != 0; chs++)
res += *chs;
return res;
}
将char*指向的字符串转化为整型
函数接口
bool chs2int(char* chs, int &num);
返回值
bool,true表示转化成功,false表示转化失败
参数列表
参数 | 描述 |
---|---|
char* chs | chs指向待转化的字符串 |
int &num | num保存转化后的整型数字 |
代码
bool chs2int(char* chs, int &num) {
将num备份在tmp中;
num=0;//初始化为0
遍历chs指向的字符串的字符 {
if (当前字符是数字) {
num *= 10;
num += *chs - '0';
}
else {
num=tmp;//转化失败则num不变;
return false;
}
}
return true;
}
获取命令行参数
函数接口
void getRunArguments(int argc, char **argv, string &inName, string &outName, string &compilerName, string &exeName, int &errorBound, bool &willCompile, bool &willExecute);
参数列表
参数 | 描述 |
---|---|
int argc | 参数及其取值总个数 |
char **argv | 存储了所有的参数 |
string &inName | 输入PASCAL-S源程序的文件名 |
string &outName | 输出C程序的文件名 |
string &compilerName | C程序编译器名 |
string &exeName | 可执行文件名 |
int &errorBound | 错误上限 |
bool &willCompile | 是否编译C程序 |
bool &willExecute | 是否运行可执行文件 |
伪代码
void getRunArguments(int argc, char **argv, string &inName, string &outName, string &compilerName, string &exeName, int &errorBound, bool &willCompile, bool &willExecute) {
for (int i = 1; i < argc; i++) {
if (当前串是"-inname") {
if (没有下一个串 || 下一个串仍是参数名)
continue;
inName = 调用char2str将第i+1个串转化为string;
if (不能找到或打开名为inName的文件) {
输出报错信息;
退出程序;
}
i++;
} else if (当前串是"-outname") {
if (没有下一个串 || 下一个串仍是参数名)
continue;
outName = 调用char2str将第i+1个串转化为string;
i++;
} else if (当前串是"-compiler") {
willCompile = true;
if (没有下一个串 || 下一个串仍是参数名)
continue;
compilerName = 调用char2str将第i+1个串转化为string;
i++;
} else if (当前串是"-exename") {
if (没有下一个串 || 下一个串仍是参数名)
continue;
exeName = 调用char2str将第i+1个串转化为string;
i++;
willCompile = true;
} else if (当前串是"-execute") {
willCompile = true;
willExecute = true;
} else if (当前串是"-errorbound") {
if (没有下一个串 || 下一个串仍是参数名)
continue;
调用chs2int将下一个串转化为数字,保存到errorBound中;
if (下一个串不能转化为数字) {
输出报错信息;
退出程序;
}
i++;
} else if (当前串是"-developer") {
cout << "MilesGO BUPT" << endl;
退出程序;
} else if (当前串是"-version") {
cout << "Version 1.0" << endl;
退出程序;
} else if (当前串是"-help") {
调用outputArgumentsExplanation输出帮助文档;
退出程序;
} else {
报非法参数错误;
退出程序;
}
}
}
输出错误
该函数涉及到四个存储错误、警告信息的列表:
extern vector<string> lexicalErrorInformation;
extern vector<string> syntaxErrorInformation;
extern vector<string> semanticErrorInformation;
extern vector<string> semanticWarningInformation;
函数接口
void outputErrors();
伪代码
void outputErrors() {
if (有词法错误)
输出词法错误;
if (有语法错误)
输出语法错误;
if (有语义警告)
输出语义警告;
if (有语义错误)
输出语义错误;
}
统计和检测错误的宏
该宏用于所有错误总个数的统计,需要在词法分析添加错误、语法分析添加错误、语义分析添加错误的函数尾部调用该宏。
宏定义如下:
#define CHECK_ERROR_BOUND errorCount++;\
if(errorCount>=errorBound){\
cout << "There have been more than " << errorBound << " errors, compiler abort." << endl;\
outputErrors();\
exit(0);\
}
涉及的函数和变量解释如下:
设计的函数或变量 | 描述 |
---|---|
errorCount | 忽略错误类型,统计错误个数的变量,初始化为0, |
errorBound | 通过命令行参数指定的错误上限 |
outputErrors() | 4.2.6中介绍的输出错误的函数 |
exit(0) | 编译器终止运行 |
即每次新增错误时,调用该宏,错误个数+1,然后判断是否打到了errorBound规定的上限,如果达到了,则输出当前已经发现的错误,并终止编译器的运行。
运行逻辑
源程序的错误情况 | 编译器的运行情况 | |||||
---|---|---|---|---|---|---|
词法情况 | 语法情况 | 语义情况 | 词法分析 | 语法分析 | 语义分析 | 代码生成 |
× | \ | \ | √ | √ | × | × |
√ | × | \ | √ | √ | × | × |
√ | √ | × | √ | √ | √ | × |
√ | √ | √ | √ | √ | √ | √ |
×:源程序包含对应类型的错误 √:源程序不包含对应类型的错误 \:源程序包含或不包含对应类型的错误 |
×:没有运行对应模块 √:运行了对应模块 |
- 注:编译器运行情况中,第一种情况,语法分析的运行情况为√。语法分析和词法分析的关系为,语法分析调用词法分析程序,每次返回一个记号序列。即语法和词法分析可以说是同时进行的,如果词法分析中遇到的错误可恢复,那么语法分析程序就可以正确运行,此时词法错误对语法分析程序是透明的;如果遇到词法错误不可恢复的情况,那么语法分析程序和词法分析程序同时停止运行。
- 另外,各部分的错误处理实时的统计错误个数,如果错误个数超过指定的errorBound,则编译器也要终止运行。
- 更详细的运行逻辑可以参考主函数流程图