1.1 获取代码数据结构的函数设计
包括函数接口和伪代码,会用到5中定义的数据结构和抽象语法树的节点类。
1.1.1 获取头文件
函数接口
void inputHeadFileList(string subprogramId);
返回值
void
- 参数列表
无
函数伪代码
void inputHeadFileList(string subprogramId) {
如果mp_subprogramToHeadFile中包含以subprogramId为键值的项 {
mp_headFileShow[mp_subprogramToHeadFile[subprogramId]]=true;
}
}
备注
在获取过程调用或函数调用时调用该函数,该函数可以将被调用的库程序所在的头文件进行标记,以便后续输出完整的头文件
1.1.2 获取常量列表
函数接口
void inputConstList(vector<_Constant *> &constList, vector<string> &constIdList, vector<string> &constTypeList, vector<string> &constValueList, _SymbolTable* symbolTable);
返回值
void
- 参数列表
| 参数 | 描述 |
| —- | —- |
| vector<_Constant *> &constList | AST的常量节点列表,可能是全局常量,也可能是某个子程序的常量 |
| vector
&constIdList | 存储常量标识符 | | vector &constTypeList | 存储常量类型 | | vector &constValueList | 存储常量值 | | _SymbolTable* symbolTable | 符号表指针 |
函数伪代码
void inputConstList(vector<_Constant *> &constList, vector<string> &constIdList, vector<string> &constTypeList, vector<string> &constValueList, _SymbolTable* symbolTable) { 清空constIdList,constTypeList,constValueList; 遍历AST的_Constant节点 { 将常量标识符添加到constIdList中; 将从符号表获取的常量类型添加到constTypeList中; 从符号表获取常量值,要求是string形式,如果是负数,需要在前面添加负号; 将常量值添加到constValueList中; } }
1.1.3 获取变量列表
函数接口
void inputVariantList(vector<_Variant *> variantList, vector<string> &variantIdList, vector<string> &variantTypeList, vector< vector<int> > &arraySizeList, _SymbolTable* symbolTable)
返回值类型
void
参数列表 | 参数 | 描述 | | —- | —- | | vector<_Variant *> variantList | AST的变量节点列表,可能是全局变量,也可能是某个子程序的变量 | | vector
&variantIdList | 存储变量标识符 | | vector &variantTypeList | 存储变量类型 | | vector< vector<int> > &arraySizeList | 存储数组各维大小 | | _SymbolTable* symbolTable | 符号表指针 |
函数伪代码
void inputVariantList(vector<_Variant *> variantList, vector<string> &variantIdList, vector<string> &variantTypeList, vector< vector<int> > &arraySizeList, _SymbolTable* symbolTable) { 清空variantList,variantIdList,variantTypeList; 遍历AST的_Variant节点 { 将变量标识符添加到variantIdList中; 将从符号表获取的变量类型添加到variantTypeList中; if(是数组) 将从符号表获取的数组各维的大小添加到arraySizeList中; } }
1.1.4 获取子程序接口声明列表
函数接口
void inputSubproDecList()
返回值
void
参数列表
无
函数伪代码
void inputSubproDecList() { 查主符号表,遍历所有子程序名 { subproDec.returnType=从符号表获取的返回值类型 id=子程序名 从符号表获取每一个参数 { subproDec.v_paraIdList.push_back(参数标识符) 从符号表获取参数是引用参数还是传值参数; if(参数为传引用) subproDec.v_isParaRef.push_back(true); else subproDec.v_isParaRef.push_back(false); subproDec.v_paraTypeList.push_back(从符号表获得的参数类型); } 将subproDec加入到子程序声明列表中; } }
1.1.5 获取函数调用
函数接口
void inputFunctionCall(_FunctionCall *functionCallNode, string &functionCall, int mode=0)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | _FunctionCall *functionCallNode | AST的_FunctionCall节点,即函数调用 | | string &functionCall | 用于存放函数调用获取结果 | | int mode | mode=0表示按照C语言输出函数调用,mode=1表示按照PASCAL-S语言输出函数调用,后者主要是提供给语义分析报错的接口 |
函数伪代码
void inputFunctionCall(_FunctionCall *functionCallNode, string &functionCall, int mode) { 调用inputHeadFileList标记头文件; functionCall=AST的_FunctionCall节点的函数名信息; if(该函数带参数) functionCall+="("; 遍历AST的_FunctionCall节点的实参列表节点 { if(不是第一个实参) functionCall+=", "; 查符号表获取当前实参对应的形参是否是引用参数,保存在布尔变量isRefered中 调用inputExpression(mode, isRefered)获取当前的实参表达式; functionCall+=当前的实参表达式; } if(该函数带参数) functionCall+=")"; }
备注
伪代码中调用了inputExpression函数,该函数用于获取表达式<br />
1.1.6 获取表达式
函数接口
int inputExpression(_Expression *expressionNode, string &expression, int mode=0, bool isReferedActualPara=false);
返回值
int表示当前获取的表达式的一级运算符的优先级
参数列表 | 参数 | 描述 | | —- | —- | | _Expression *expressionNode | AST的_Expression节点 | | string &expression | 存储表达式 | | int mode | mode=0表示按照C语言输出表达式,mode=1表示按照PASCAL-S语言输出表达式,后者主要是提供给语义分析报错的接口 | | bool isReferedActualPara | true表示当前表达式作为程序调用的实参,且对应的形参为引用参数,false则表示不是上述的情况 |
函数伪代码
//表达式 //需要根据操作符之间的优先级关系添加括号,所以定义了一个flag,用来指示子表达式用到的操作符类型 //变量引用、整数、浮点数、函数调用、复合表达式 int inputExpression(_Expression *expressionNode, string &expression, int mode, bool isReferedActualPara) { //返回值用于表示是否需要加括号 if(是变量标识符) { string variantRef; 调用inputVariantRef获取变量引用(mode, isReferedActualPara),结果保存在variantRef中 将variantRef拼接到原有expression后面 return 0; //无运算符,所以优先级为0 } else if(是整数) { 将整数拼接到原有expression后面; return 0; } else if(是浮点数) { 将浮点数拼接到原有expression后面; return 0; } else if(是函数调用) { string functionCall; 调用inputHeadFileList标记头文件; 调用inputFunctionCall获取函数调用,结果保存在functionCall中 将functionCall拼接到原有expression后面; return 0; } else if(是复合表达式) { if(是单目运算符) { 调用inputExpression(mode)获得子表达式及其优先级 if(是添加括号) { 在子表达式两边添加括号之后,拼接到原有expression后面; return 0; //单目运算符优先级最高,为4 } if(是取相反数) 将"-"拼接到原有expression后面; else if(是取非) 将"!"拼接到原有expression后面; if(mode==0 && 子表达式优先级大于0) 在子表达式两边添加括号之后,拼接到原有expression后面; else 将子表达式拼接到原有expression后面; return 4; //单目运算符优先级最高,为4 } else if(是"mulop") { //"mulop"包括*、/、mod、div、and 调用inputExpression(mode)获取左子表达式及其优先级 if(左子表达式优先级为1或2) 在左子表达式两边添加括号之后,拼接到原有expression后面; else 将左子表达式拼接到原有exprssion后面; 将运算符拼接到原有expression后面; 调用inputExpression(mode)获取右子表达式及其优先级; if(mode == 0 && 右子表达式优先级为1或2) 在右子表达式两边添加括号之后,拼接到原有expression后面; else 将右子表达式拼接到原有exprssion后面; return 3; //"mulop"优先级次于单目运算符,为3 } else if(是"addop") { //"addop"包括+、-、or 调用inputExpression(mode)获取左子表达式及其优先级; if(左子表达式优先级为1) 在左子表达式两边添加括号之后,拼接到原有expression后面; else 将左子表达式拼接到原有exprssion后面; 将运算符拼接到原有expression后面; 调用inputExpression(mode)获取右子表达式及其优先级; if(mode == 0 && 右子表达式优先级为1) 在右子表达式两边添加括号之后,拼接到原有expression后面; else 将右子表达式拼接到原有exprssion后面; return 2; //"addop"优先级次于"mulop",为2 } else if(是"relop") { //"relop"包括=、<>、<、<=、>、>= string tmp; 调用inputExpression(mode)获取左子表达式 将左子表达式拼接到原有expression后面; 将运算符拼接到原有expression后面; tmp=""; 调用inputExpression(mode)获取右子表达式 将右子表达式拼接到原有exprssion后面; return 1; //"relop"优先级次于"addop",为1 } } }
备注
需要根据当前运算符和子表达式运算符的优先级关系,来决定是否需要在子表达式两边添加括号,所以设置了一个flag,用于表示表达式中所包含运算符的优先级。<br /> 变量引用、整数、浮点数、函数调用没有运算符,优先级为0;单目运算符优先级为4,mulop优先级为3,addop优先级为2,relop优先级为1。
1.1.7 获取变量引用
函数接口
void inputVariantRef(_VariantReference *variantRefNode, string &variantRef, int mode=0, bool isReferedActualPara=false);
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | _VariantReference *variantRefNode | AST的_VariantReference节点 | | string &variantRef | 保存变量引用 | | int mode | mode=0表示按照C语言输出变量引用,mode=1表示按照PASCAL-S语言输出变量引用,后者主要是提供给语义分析报错的接口 | | bool isReferedActualPara | true表示当前变量引用作为程序调用的实参,且对应的形参为引用参数,false则表示不是上述的情况 |
- 函数伪代码
void inputVariantRef(_VariantReference *variantRefNode, string &variantRef,int mode, bool isReferedActualPara) { variantRef=_VariantReference节点的变量标识符信息; if (mode == 0 && 变量引用的种类是变量或者数组元素) { 查符号表,检查当前标识符是否是引用参数; bool isRefered = checkIsReferedPara(codeGenerateCurrentSymbolTable, variantRefNode->variantId.first); if (是引用参数) { if (!isReferedActualPara) variantRef前添加"*"; } else { //不是引用参数 if (isReferedActualPara) variantRef前添加"&"; } } //如果是函数,一定是右值,如果是左值,代码逻辑保证了不会调用该函数 //通过了语义分析,保证了该函数形参个数一定为0 if (需要输出目标代码 && _VariantReference的种类是函数调用) 将"()"拼接到variantRef后面; if (_VariantReference的种类不是数组) return; //这里得查符号表,找出数组定义时每一维的下界 if(mode==0) { //生成C代码 查符号表,找到数组定义时每一维的下界 { 调用inputExpression(mode)获取当前维的下标表达式; string delta; //需要添加在下标表达式后面的偏移量部分 if (数组当前维下界>0) delta = " - " + 数组当前维下界; else if (数组当前维下界 < 0) delta = " + " + 数组当前维下界的绝对值; else//如果数组当前维下界刚好等于0 delta = ""; 将delta拼接到原有下标表达式后面; 在表达式两端添加中括号后,拼接到原有variantRef后面; } } else { //生成PASCAL-S代码 将"["拼接到原有variantRef后面; 查符号表,找到数组定义时每一维的下界 { 调用inputExpression(mode)获取当前维的下标表达式; if(不是第一维) 将", "拼接到原有variantRef后面; 将下标表达式拼接到原有variantRef后面; } 将"]"拼接到原有variantRef后面; } }
1.1.8 获取语句列表
函数接口
void inputStatementList(_Statement *statementNode, vector< pair<string,int> > &v_statementList, int retract, int flag=0)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | _Statement statementNode | AST的_Statement节点 | | vector< pair
> &v_statementList | 保存获取的语句列表结果 | | int retract | 当前新增语句应有的缩进(制表符的个数) | | *int flag | 是否需要在compound语句两侧添加花括号,如果flag==0表示需要,否则表示不需要 |
- 函数伪代码
void inputStatementList(_Statement *statementNode, vector< pair<string,int> > &v_statementList, int retract, int flag=0) { if(是"compound"语句) { if(flag==0) 添加"{"到语句列表中,缩进值为retract-1; 遍历_Compound节点的子语句列表 调用inputStatementList获取复合语句列表(retract+1); if(flag==0) 添加"}"到语句列表中,缩进值为retract-1; } else if(是"repeat"语句) { 添加do关键字到语句列表中,缩进值为retract; 调用inputStatementList获取循环体语句(retract+1); 调用inputExpression获取条件表达式; 将条件表达式取非后与while关键字组合成一条语句,添加到语句列表中,缩进值为retract; } else if(是"while"语句) { 调用inputExpression获取条件表达式; 将条件表达式与while关键字组合成一条语句,添加到语句列表中,缩进值为retract; 调用inputStatementList获取循环体语句(retract+1); } else if(是"for"语句) { 调用inputExpression获取初值表达式; 调用inputExpression获取终值表达式; 将初值表达式、终止表达式、for关键字等组合成一条语句,添加到语句列表中,缩进值为retract; 调用inputStatementList获取循环体语句(retract+1); } else if(是"if"语句) { 调用inputExpression获取条件表达式; 将条件表达式和if关键字组合成一条语句,添加到语句列表中,缩进值为retract; 调用inputStatementList获取then语句体(retract+1); if(包含 else部分) { 添加 else关键字到语句列表中,缩进值为retract; 调用inputStatementList获取 else语句体(retract+1); } } else if(是"assign"语句) { 调用inputVariantRef获取左值变量引用; 调用inputExpression获取右值表达式; 将左值变量引用和右值表达式拼接成赋值语句,添加到语句列表中,缩进值为retract; } else if(是"procedure"语句,即过程调用) { 调用inputHeadFileList标记头文件; 调用checkAndInputLibrarySubprogram检查并获取库函数; if(是库函数) return; 遍历所有的实参表达式 { 查符号表获取当前实参对应的形参是否是引用参数,保存在布尔变量isRefered中 调用inputExpression获取实参表达式(mode=0, isRefered); } 将过程名和所有实参表达式拼接成一条过程调用语句,并添加到语句列表中,缩进值为retract; } }
1.1.9 获取子程序定义
函数接口
void inputSubproDef(_FunctionDefinition* functionDefinitionNode)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | _FunctionDefinition* functionDefinitionNode | AST的_FunctionDefinition节点 |
- 函数伪代码
void inputSubproDef(_FunctionDefinition* functionDefinitionNode){ struct subproDef tmp; 调用inputConstList获取常量定义列表,添加到tmp中; 调用inputVariantList获取变量定义列表,添加到tmp中; 调用inputStatementList获取程序体语句列表,添加到tmp中; 将tmp添加到子程序定义列表中; }
1.1.10 获取子程序体列表
函数接口
void inputSubproDefList(_SubProgram* subProgramNode)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | _SubProgram* subProgramNode | AST的_SubProgram节点 |
- 函数伪代码
void inputSubproDefList(_SubProgram* subProgramNode){ 遍历子程序定义列表{ 利用当前子程序名,从符号表获取当前子程序对应的字符号表指针; codeGenerateCurrentSymbolTable定位到当前子程序的子符号表; 调用inputSubproDef获取当前子程序的定义; } }
1.1.11 获取原PASCAL主程序头对应到的C程序头及程序体
函数接口
void inputSubMainFunction(_Program* ASTRoot);//获取原PASCAL主程序头对应到的C程序头,以及主程序体
返回值
无
参数列表 | 参数 | 描述 | | —- | —- | | _Program* ASTRoot | 抽象语法树根节点 |
- 函数伪代码
void inputSubMainFunction(_Program* ASTRoot) { subMainFunctionDeclaration = "void " + ASTRoot->programId.first + "()";//用void关键字和主程序名拼接成一个主函数头 调用inputStatementList获取主函数语句体(retract=1,flag=1即不添加大括号); }
1.1.12 根据类型获取输入输出格式控制符
函数接口
string getOutputFormat(string type);
返回值
string,获得的格式控制符
参数列表
参数 | 描述 |
---|---|
string type | 基本类型 |
- 函数伪代码
string getOutputFormat(string type) { if (type == "integer") return "%d"; if (type == "real") return "%f"; if (type == "char") return "%c"; if (type == "boolean") return "bool"; cout << "[getOutputFormat] type error" << endl; return ""; }
1.1.13 检查并获取库程序
函数接口
bool checkAndInputLibrarySubprogram(_ProcedureCall* procedureCall, vector< pair<string, int> > &v_statementList, int retract);
返回值
bool,如果检查到是库程序,则返回true,表示该函数内部已完成库程序调用的获取,如果检查到不是库程序,则返回false,表示该函数返回后,还需进一步获取程序调用的相关信息
参数列表 | 参数 | 描述 | | —- | —- | | _ProcedureCall* procedureCall | 过程调用节点 | | vector< pair
> &v_statementList | 语句列表 | | int retract | 缩进(制表符个数) |
- 函数伪代码
bool checkAndInputLibrarySubprogram(_ProcedureCall* procedureCall, vector< pair<string, int> > &v_statementList, int retract) { //返回值表示是否是库程序调用 if (是exit过程调用) { 直接将"return;"加入语句列表,缩进值为retract; } else if (是write或writeln过程调用) { if (是writeln过程调用且实参个数为0) { 直接将"printf(\"\\n\");"加入语句列表,缩进值为retract; return true; } for (int i = 0; i < procedureCall->actualParaList.size(); i++) { string expression, typeFormat; 调用inputExpression获取实参表达式; 调用getOutputFormat获取该实参表达式的类型对应的格式控制符; if (实参表达式的类型是bool类型) { 将之前已有的实参表达式及其格式控制符拼成一条printf语句; 将printf语句加入语句列表,缩进值为retract; 将"{"加入语句列表,缩进值为retract; v_statementList.push_back(make_pair("if(" + expression + ")", retract + 1)); v_statementList.push_back(make_pair("printf(\"true\");", retract + 2)); v_statementList.push_back(make_pair("else", retract + 1)); v_statementList.push_back(make_pair("printf(\"false\");", retract + 2)); 将"}"加入语句列表,缩进值为retract; if(当前是最后一个实参 && 是writeln过程) 将输出换行的语句加入到语句列表中,缩进值为retract; } } 将剩余的实参表达式及其格式控制符拼成一条printf语句,如果是“writeln”,还需在最后加入换行符; 将printf语句加入语句列表,缩进值为retract; } else if (是read过程调用) { for (int i = 0; i < procedureCall->actualParaList.size(); i++) { 调用inputExpression获取实参表达式(mode=0, isReferedActualPara=true); 调用getOutputFormat获取该实参表达式的类型对应的格式控制符; if(实参表达式的类型是bool类型) 格式控制符设为"%d"; } 将所有实参表达式、格式控制符拼成一条scanf语句; 将scanf语句加入语句列表,缩进值为retract; } else return false; return true; }