1.1 获取代码数据结构的函数设计

包括函数接口和伪代码,会用到5中定义的数据结构和抽象语法树的节点类。

1.1.1 获取头文件

  • 函数接口

    1. void inputHeadFileList(string subprogramId);
  • 返回值

void

  • 参数列表

  • 函数伪代码

    1. void inputHeadFileList(string subprogramId) {
    2. 如果mp_subprogramToHeadFile中包含以subprogramId为键值的项 {
    3. mp_headFileShow[mp_subprogramToHeadFile[subprogramId]]=true;
    4. }
    5. }
  • 备注

在获取过程调用或函数调用时调用该函数,该函数可以将被调用的库程序所在的头文件进行标记,以便后续输出完整的头文件

1.1.2 获取常量列表

  • 函数接口

    1. 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;  
    }