命令行调用参数说明

我们最终生成的编译器采用命令行调用方式。调用时,可选的参数及其功能如下表所示:

参数接口 参数 参数功能 参数默认值
-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结构:是参数名字符串到其解释信息字符串的映射

  1. 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,则编译器也要终止运行。
  • 更详细的运行逻辑可以参考主函数流程图