一、初代编译器功能描述
初代编译器将
C
语言顺序语句序列翻译为等价的汇编程序,所输出的汇编 程序符合 x86
或
MIPS
汇编语言格式要求,能够被后续的汇编器翻译为可执行程 序运行。如果目标平台为 x86
,则生成的可执行程序能够在目标机上执行和验证 结果;如果目标平台为 MIPS
,则生成的汇编程序可以在
MIPS
模拟器中运行和 验证结果。
二、初代编译器文法要求
初代编译器能够处理的文法如下所示:
关键字
:
int, return
标识符
:单个英文字母,如
a
、
b
等
常量
:十进制整型,如
1
、
223
、
10
等
操作符
:
=
、
+
、
-
、
*
、
/
分隔符
: ;
语句
:表达式语句、赋值语句
三、初代编译器测试样例
- 测试样例不用考虑优先级,即表达式中算术操作符优先级相同,且无括号。
- 所有输入测试样例文件中单词之间均由空格或者回车分隔,但输入文件中可能存在多个连续的空格或者回车。
- 评分依据 return 的值是否符合预期。生成的 MIPS 汇编代码在 QtSpim 上运 行。
四、设计思路
创建一个初代编译器,使其读取输入文件中的源代码,进行词法分析和语法分析,并生成相应的MIPS指令。
-
读取输入文件:在
Compiler
类的构造函数中,打开输入文件,并将其内容存储在字符串变量content
中。 -
使用
alpha[52]
数组来存储标识符的地址,代表相对$fp的偏移量。大写字母的ASCII码范围是65到90,小写字母的ASCII码范围是97到122。我们可以将大写字母映射到数组的后26个位置,小写字母映射到前26个位置。 -
词法分析:在
lex
方法中,将源代码分解成单词序列,并将每个单词的类型和值保存到mipsInstructions
向量中。单词的类型包括关键字、标识符、常量、操作符和分隔符。 -
语法分析:在
generateCode
方法中,根据词法分析的结果,生成相应的MIPS指令。这个方法首先遍历词法分析结果,处理变量声明语句和返回语句。然后,对于赋值语句和表达式语句,根据右侧表达式的类型生成相应的MIPS指令,将结果存储到相应的变量中。在处理表达式时,调用了expression
方法,该方法递归地解析表达式,并生成相应的MIPS指令。 -
输出MIPS指令:在
printMipsInstructions
方法中,打印生成的MIPS指令序列。 -
主函数:在
main
函数中,检查命令行参数,创建Compiler
对象,进行词法分析和语法分析,最后输出生成的MIPS指令序列。
五、源代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <ctype.h>
using namespace std;
int alpha[52]={0};
// Token 类型枚举
enum class TokenType {
KEYWORD,
IDENTIFIER,
CONSTANT,
OPERATOR,
DELIMITER,
END_OF_FILE
};
// Token 结构体
struct Token {
TokenType type;
string value;
};
// 符号表项结构体
struct SymbolTableEntry {
string name;
int offset; // 偏移量,用于变量在栈中的位置
};
// 初代编译器类
class Compiler {
private:
string content;
int sizeoflex;
unordered_map<string, SymbolTableEntry> symbolTable; // 符号表
vector<string> mipsInstructions; // 保存生成的MIPS指令
public:
Compiler(const string& inputFileName);
~Compiler();
void lex();
void generateCode();
void printMipsInstructions();
int expression(int i,int o);
// 其他辅助函数可以根据需要添加
};
Compiler::Compiler(const string& inputFileName) {
// 这里可以添加读取输入文件并进行初始化的逻辑
// 对于本示例,暂时不做处理
// 你可以在这里打开输入文件,进行词法分析和语法分析的初始化工作
// 例如,可以初始化符号表,设置输入文件等
// 示例中暂时不做处理,你可以根据需求进行扩展
string line;
ifstream in(inputFileName);
while (getline(in, line))
{content+=line;
content+=" ";}
in.close();
}
Compiler::~Compiler() {
// 这里可以添加释放资源的逻辑
// 对于本示例,暂时不做处理
// 你可以在这里关闭文件,释放动态分配的内存等
// 示例中暂时不做处理,你可以根据需求进行扩展
}
void Compiler::lex() {
string line=content;
{ // 从标准输入逐行读取输入
size_t pos = 0;
while (pos < line.length()) {
char currentChar = line[pos];
// 跳过空格
if (isspace(currentChar)) {
pos++;
continue;
}
// 关键字和标识符
if (isalpha(currentChar)) {
string tokenValue = "";
while (pos < line.length() && isalnum(line[pos])) {
tokenValue += line[pos];
pos++;
}
if (tokenValue == "int" || tokenValue == "return") {
mipsInstructions.push_back(tokenValue); // 添加关键字到MIPS指令
continue;
} else {
mipsInstructions.push_back("Identifier: " + tokenValue); // 添加标识符到MIPS指令
continue;
}
}
// 常量
if (isdigit(currentChar)) {
string tokenValue = "";
while (pos < line.length() && isdigit(line[pos])) {
tokenValue += line[pos];
pos++;
}
mipsInstructions.push_back("Constant: " + tokenValue); // 添加常量到MIPS指令
continue;
}
// 操作符
if (currentChar == '=' || currentChar == '+' || currentChar == '-' || currentChar == '*' || currentChar == '/') {
string tokenValue = "";
tokenValue += currentChar;
mipsInstructions.push_back("Operator: " + tokenValue); // 添加操作符到MIPS指令
pos++;
continue;
}
// 分隔符
if (currentChar == ';') {
string tokenValue = "";
tokenValue += currentChar;
mipsInstructions.push_back("Delimiter: " + tokenValue); // 添加分隔符到MIPS指令
pos++;
continue;
}
// 非法字符
cerr << "Error: Invalid character encountered: " << currentChar << endl;
exit(1);
}
}
}
int Compiler::expression(int i,int o){
int j=i,pos=0,o1,o2;
string s1 = "",s2="";
//-----vs-----
string str = mipsInstructions[i];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char firstChar = afterColon[0];
if(islower(firstChar)) {
o1=alpha[firstChar - 'a' + 26];
s1+=("lw $t"+to_string(pos)+","+ to_string(o1) + "($fp)");
} else if(isupper(firstChar)) {
o1=alpha[firstChar - 'A'];
s1+=("lw $t"+to_string(pos)+","+ to_string(o1) + "($fp)");
}
else{
s1+=("li $t"+to_string(pos)+","+afterColon);
}
//-----op-----
str = mipsInstructions[i+1];
colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char op = afterColon[0];
pos++;
//-----vt-----
str = mipsInstructions[i+2];
colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
firstChar = afterColon[0];
if(islower(firstChar)) {
o2=alpha[firstChar - 'a' + 26];
s2+=("lw $t"+to_string(pos)+","+ to_string(o2) + "($fp)");
} else if(isupper(firstChar)) {
o2=alpha[firstChar - 'A'];
s2+=("lw $t"+to_string(pos)+","+ to_string(o2) + "($fp)");
}
else{
s2+=("li $t"+to_string(pos)+","+afterColon);
}
mipsInstructions.push_back(s1);
mipsInstructions.push_back(s2);
switch(op){
case '+':{mipsInstructions.push_back("add $t"+to_string(pos-1)+","+"$t"+to_string(pos-1)+","+"$t"+to_string(pos));}break;
case '-':{mipsInstructions.push_back("sub $t"+to_string(pos-1)+","+"$t"+to_string(pos-1)+","+"$t"+to_string(pos));}break;
case '*':{mipsInstructions.push_back("mul $t"+to_string(pos-1)+","+"$t"+to_string(pos-1)+","+"$t"+to_string(pos));}break;
case '/':{mipsInstructions.push_back("div $t"+to_string(pos-1)+","+"$t"+to_string(pos-1)+","+"$t"+to_string(pos));}break;
}
if(o1>200) o1=0;
// 跳过标识符
j += 3;
while(mipsInstructions[j] != "Delimiter: ;"){
//-----op-----
str = mipsInstructions[j];
colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char op = afterColon[0];
//-----vt-----
s2="";
pos++;
str = mipsInstructions[j+1];
colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
firstChar = afterColon[0];
if(islower(firstChar)) {
o2=alpha[firstChar - 'a' + 26];
s2+=("lw $t"+to_string(1)+","+ to_string(o2) + "($fp)");
} else if(isupper(firstChar)) {
o2=alpha[firstChar - 'A'];
s2+=("lw $t"+to_string(1)+","+ to_string(o2) + "($fp)");
}
else{
s2+=("li $t"+to_string(1)+","+afterColon);
}
mipsInstructions.push_back(s2);
switch(op){
case '+':{mipsInstructions.push_back("add $t"+to_string(0)+","+"$t"+to_string(0)+","+"$t"+to_string(1));}break;
case '-':{mipsInstructions.push_back("sub $t"+to_string(0)+","+"$t"+to_string(0)+","+"$t"+to_string(1));}break;
case '*':{mipsInstructions.push_back("mul $t"+to_string(0)+","+"$t"+to_string(0)+","+"$t"+to_string(1));}break;
case '/':{mipsInstructions.push_back("div $t"+to_string(0)+","+"$t"+to_string(0)+","+"$t"+to_string(1));}break;
}
j+=2;
}
mipsInstructions.push_back("sw $t"+to_string(0)+","+ to_string(o) + "($fp)");
i=j;
return i;
}
void Compiler::generateCode() {
sizeoflex=mipsInstructions.size();
int offset=0;
for (size_t i = 0; i < mipsInstructions.size(); ++i) {
//cout<<mipsInstructions[i]<<" "<<i<<endl;
if (mipsInstructions[i] == "int") {
// 变量声明语句
// 生成存储指令,假设栈帧中已经为变量留出空间
// 对应的偏移量在符号表中已经设定
// 示例中使用 $zero 寄存器存储常量 0
offset-=4;
symbolTable[mipsInstructions[i + 1]].offset=offset;
string str = mipsInstructions[i+1];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char firstChar = afterColon[0];
// cout<<firstChar<<"==="<<endl;
if(islower(firstChar)) {
alpha[firstChar - 'a' + 26] = offset;
} else if(isupper(firstChar)) {
alpha[firstChar - 'A'] = offset;
} else {
// 处理非字母字符的情况,如果有必要的话
}
mipsInstructions.push_back("sw $zero," + to_string(symbolTable[mipsInstructions[i + 1]].offset) + "($fp)");
// 跳过变量名
++i;
continue;
}
if (mipsInstructions[i] == "return") {
// return 语句
// 将返回值存储到 $v0 寄存器中
string str = mipsInstructions[i+1];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char firstChar = afterColon[0];
if(islower(firstChar)) {
symbolTable[mipsInstructions[i + 1]].offset=alpha[firstChar - 'a' + 26];
} else if(isupper(firstChar)) {
symbolTable[mipsInstructions[i + 1]].offset=alpha[firstChar - 'A'];
}
mipsInstructions.push_back("lw $v0," + to_string(symbolTable[mipsInstructions[i + 1]].offset) + "($fp)");
// 结束循环
break;
}
string str = mipsInstructions[i];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
// 处理赋值语句或表达式语句
if (beforeColon == "Identifier") {
int o;
// 获取变量名
string variableName = afterColon;
char firstChar = variableName[0];
if(islower(firstChar)) {
o =alpha[firstChar - 'a' + 26];
} else if(isupper(firstChar)) {
o =alpha[firstChar - 'A'];
}
// 检查下一个指令是否为赋值操作符
if (mipsInstructions[i + 1] == "Operator: =") {
// 跳过赋值操作符
++i;
// 处理右侧表达式
// 右侧表达式可以是标识符或常量
if(mipsInstructions[i+2] == "Delimiter: ;"){
int value = 0;
string str = mipsInstructions[i+1];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
//cout<<str<<endl;
if (beforeColon == "Identifier") {
string str = mipsInstructions[i+1];
string beforeColon,afterColon;
size_t colonPos = str.find(':');
if (colonPos != string::npos) {
// 提取冒号之前的部分
beforeColon = str.substr(0, colonPos);
afterColon=str.substr(colonPos+2,sizeof(str));
}
char firstChar = afterColon[0];
if(islower(firstChar)) {
symbolTable[mipsInstructions[i + 1]].offset=alpha[firstChar - 'a' + 26];
} else if(isupper(firstChar)) {
symbolTable[mipsInstructions[i + 1]].offset=alpha[firstChar - 'A'];
}
mipsInstructions.push_back("lw $v0," + to_string(symbolTable[mipsInstructions[i+1]].offset) + "($fp)");
// 跳过标识符
i += 2;
} else if (beforeColon == "Constant") {
//cout<<afterColon;
// 如果是常量,直接将常量值作为右侧值
value = stoi(afterColon);
// 跳过常量
mipsInstructions.push_back("li $v0," + to_string(value));
i += 2;
}
// 生成存储指令,将右侧值存储到对应偏移量处
mipsInstructions.push_back("sw $v0," + to_string(o) + "($fp)");
}
else{
i=expression(i+1,o);
}
}
}
}
// for(int i=0;i<52;i++) cout<<alpha[i]<<" ---"<<endl;
}
void Compiler::printMipsInstructions() {
for (size_t i = sizeoflex; i < mipsInstructions.size(); ++i){
cout << mipsInstructions[i] << endl;
}
}
// 主函数
int main(int argc, char* argv[]) {
if (argc != 2) {
cout << "Usage: " << argv[0] << " <input_file>" << endl;
return 1;
}
string inputFileName = argv[1];
Compiler compiler(inputFileName);
compiler.lex();
compiler.generateCode();
compiler.printMipsInstructions();
return 0;
}