【编译原理】实现简单的编译器

一、初代编译器功能描述

初代编译器将 C 语言顺序语句序列翻译为等价的汇编程序,所输出的汇编 程序符合 x86 MIPS 汇编语言格式要求,能够被后续的汇编器翻译为可执行程 序运行。如果目标平台为 x86 ,则生成的可执行程序能够在目标机上执行和验证 结果;如果目标平台为 MIPS ,则生成的汇编程序可以在 MIPS 模拟器中运行和 验证结果。

二、初代编译器文法要求

初代编译器能够处理的文法如下所示:
关键字 int, return
标识符 :单个英文字母,如 a b
常量 :十进制整型,如 1 223 10
操作符 = + - * /
分隔符 : ;
语句 :表达式语句、赋值语句

三、初代编译器测试样例

  • 测试样例不用考虑优先级,即表达式中算术操作符优先级相同,且无括号。
  • 所有输入测试样例文件中单词之间均由空格或者回车分隔,但输入文件中可能存在多个连续的空格或者回车。
  • 评分依据 return 的值是否符合预期。生成的 MIPS 汇编代码在 QtSpim 上运 行。

四、设计思路

创建一个初代编译器,使其读取输入文件中的源代码,进行词法分析和语法分析,并生成相应的MIPS指令。

  1. 读取输入文件:在 Compiler 类的构造函数中,打开输入文件,并将其内容存储在字符串变量 content 中。

  2. 使用 alpha[52] 数组来存储标识符的地址,代表相对$fp的偏移量。大写字母的ASCII码范围是65到90,小写字母的ASCII码范围是97到122。我们可以将大写字母映射到数组的后26个位置,小写字母映射到前26个位置。

  3. 词法分析:在 lex 方法中,将源代码分解成单词序列,并将每个单词的类型和值保存到 mipsInstructions 向量中。单词的类型包括关键字、标识符、常量、操作符和分隔符。

  4. 语法分析:在 generateCode 方法中,根据词法分析的结果,生成相应的MIPS指令。这个方法首先遍历词法分析结果,处理变量声明语句和返回语句。然后,对于赋值语句和表达式语句,根据右侧表达式的类型生成相应的MIPS指令,将结果存储到相应的变量中。在处理表达式时,调用了 expression 方法,该方法递归地解析表达式,并生成相应的MIPS指令。

  5. 输出MIPS指令:在 printMipsInstructions 方法中,打印生成的MIPS指令序列。

  6. 主函数:在 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;
}
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值