《编译原理》:词法分析程序设计原理与实现

理论知识

源程序输入与词法分析输出

输入:源程序由字符组成,词法分析程序接收源代码作为输入。

输出:词法分析程序的输出通常是标记化的标记(token)。这些标记是源代码中的识别单元,如关键字、标识符、运算符、常量等。

正则文法及其状态转换图的基本概念

正则文法是一种描述字符串集的形式文法,使用正则表达式来表示字符串模式。

状态转换图(也称为有限状态自动机)是描述正则文法的图形化表示,由节点(状态)和转移(根据输入字符进行状态间的转换)组成。

正则表达式及有限自动机的基本概念

        正则表达式是用于描述字符串模式的表达式,包括字符、操作符和通配符。

        有限自动机是有限状态的机器,接受输入并根据输入进行状态转换。它可以是确定的(DFA)或非确定的(NFA),用于匹配正则表达式定义的模式。

​​​​​​​正规文法构造相应的状态转换图的基本方法

        正规文法是一种描述文法的形式化系统,能够生成正则语言。构建状态转换图的方法包括将正规文法的规则转化为状态及其之间的转移关系,每个状态代表一个模式的识别状态。

​​​​​​​正则表达式构造有限自动机的基本方法及不确定有限自动机确定化的基本方法

        构造有限自动机:正则表达式可直接转换为非确定有限自动机(NFA),NFA 通过状态和转移描述模式的匹配过程。

        不确定有限自动机确定化:从 NFA 到 DFA 的转换过程称为确定化,这包括将非确定性转换为确定性,即对于每个状态和输入字符,有一个明确的下一个状态。


任务要求

​​​​​​​实验项目

        以下为正则文法所描述的C语言子集单词符号的示例,请补充单词符号: ++,--, >>,  <<, += , -= ,*=, /= ,&&(逻辑与),||(逻辑或),!(逻辑非)等等,给出补充后描述C语言子集单词符号的正则文法,设计并实现其词法分析程序。

以下文法为左线性正则文法:

  1. <标识符>→字母︱ <标识符>字母︱ <标识符>d
  2. <无符号整数>→数字︱ <无符号整数>数字
  3. <单字符分界符>→+ ︱- ︱* ︱;︱, ︱(︱) ︱{︱}
  4. <双字符分界符>→<大于>=︱<小于>=︱<小于>>︱<感叹号>=︱<等于>=︱<斜竖>*     
  5. <小于>→<      
  6. <等于>→=      
  7. <大于>→>      
  8. <斜竖> →/   
  9. <感叹号>→!

该语言的保留字 :void、int、float、double、if、else、for、do、while 等等(也可补充)。   

​​​​​​​设计说明

  1. 可将该语言设计成大小写不敏感,也可设计成大小写敏感,用户定义的标识符最长不超过32个字符;
  2. 字母为a-z A-Z,数字为0-9;
  3. 可以对上述文法进行扩充和改造;
  4. “/*……*/”和“//”(一行内)为程序的注释部分。

设计要求

  1. 给出各单词符号的类别编码;
  2. 词法分析程序应能发现输入串中的错误;
  3. 词法分析作为单独一遍编写,词法分析结果为二元式序列组成的中间文件;
  4. 设计至少4个测试用例(尽可能完备),并给出测试结果。

任务实现

​​​​​​​语言与文法说明

  1. 该语言大小写敏感,用户定义的标识符最长不超过32个字符;
  2. 字母为a-z A-Z,数字为0-9;
  3. “/*……*/”和“//”(一行内)为程序的注释部分。

修改后的正则文法:

<标识符>->字母|<标识符>字母|<标识符>数字

<无符号整数>->数字|<无符号整数>数字

<单字符分界符>->;|,|(|)|{|}|!|<斜竖>|<加号>|<减号>|<乘号>|<感叹号>

<双字符分界符>-><大于>=|<大于>>|<小于>=|<小于><|<小于>>|<感叹号>=|<等于>=|<加号>=|<减号>=|<乘号>=|<斜竖>=|<与>&| <或>||<加号>+|<减号>-

<小于>-><

<大于>->>

<等于>->=

<斜竖>->/

<感叹号>->!

<加号>->+

<减号>->-

<乘号>->*

<与>->&

<或>->|

​​​​​​​单词符号的类别编码

  1. 关键字:1
  2. 标识符:0
  3. char型变量: 5
  4. 字符串变量:4
  5. 数字:6
  6. 单分隔符:2
  7. 双分隔符:3

程序功能描述

        这个程序是一个词法分析器(Lexical Analyzer),用于对输入的源代码文件进行词法分析,提取其中的标识符、运算符、常数、关键字等内容,并将提取的内容按照类型分类后保存到输出文件中。它的主要功能包括:

预处理函数 (pretreatment):

读取输入的源代码文件,去除其中的注释(单行注释 // 和多行注释 /* ... */),将代码进行处理,去除多余的空格和空行。

string pretreatment(const string& p_in) {
        string r = ""; // 用来保存去除注释后的源程序
        try {
            std::ifstream C_txt(p_in); 
            std::stringstream buffer;
            buffer << C_txt.rdbuf();
            std::string line;
            bool InComment = false; // 用于跟踪是否在注释块内

            while (getline(buffer, line)) {
                // 处理多行注释
                if (InComment) {
                    size_t found = line.find("*/");
                    if (found != string::npos)
                    {
                        line = line.substr(found + 2);
                        InComment = false; // 出注释块了
                    }
                    else
                    {
                        continue; // 在多行注释中,跳过当前行
                    }
                }

                // 处理单行注释
                size_t SingleCommentIndex = line.find("//");
                if (SingleCommentIndex != string::npos)
                {
                    line = line.substr(0, SingleCommentIndex);
                }

                // 处理多行注释的开始
                size_t MultiCommentStartIndex = line.find("/*");
                if (MultiCommentStartIndex != std::string::npos)
                {
                    size_t MultiCommentEndIndex = line.find("*/");
                    if (MultiCommentEndIndex != std::string::npos)
                    {
                        // 删除多行注释
                        line = line.substr(0, MultiCommentStartIndex) + line.substr(MultiCommentEndIndex + 2);
                    }
                    else
                    {
                        // 进入多行注释块
                        line = line.substr(0, MultiCommentStartIndex);
                        InComment = true; // 注释块内标识为真
                    }
                }

                r += " " + line; // 将每行用空格隔开
            }

            // 删除多余的空格
            r.erase(std::unique(r.begin(), r.end(), [](char a, char b) { return a == ' ' && b == ' '; }), r.end());
            r.erase(std::remove(r.begin(), r.end(), '\n'), r.end());

            // 去除首尾空格
            size_t start = r.find_first_not_of(" \t\n\r");
            size_t end = r.find_last_not_of(" \t\n\r");

            if (start == string::npos) {
                r = "";  // Empty or all spaces
            }
            else
                r = r.substr(start, end - start + 1);

            cout << r << endl; // 将预处理后的字符串输出
        }
        catch (const exception& e)
        {
            cerr << e.what() << endl;
        }
        return r; // 返回处理好后的字符串
    }

词法分析函数 (scanner):

        对经过预处理的源代码进行逐个字符的扫描,根据字符的特性,将其识别为标识符、运算符、常数、字符串等。

        根据 ASCII 码值将字符进行分类判断,包括空格、字母、数字、双引号、单引号和其他符号。

        通过状态判断和字符串拼接,提取出相应的标识符,并根据其类别将其归入对应的表中,例如关键字表、标识符表、运算符表等。

void scanner(const std::string& path_in, const std::string& path_out) {
        try {
            string result = ""; // 保存token信息
            size_t subscript = 0; // 计数
            int t, status, pre_subscript; // 分别为:记录单个字符的ASCII码,状态,记录上一个字符的下标
            string word; // 形成单词
            string r = path_in; // 经预处理后的文本字符流信息

            while (subscript < r.length()) {
                t = static_cast<int>(r[subscript]); // 将字符转换为ASCII码

                if (t == 32)
                    status = 0; // 空格
                else if ((t > 64 && t < 91) || (t > 96 && t < 123)) // 字母
                    status = 1;
                else if (t >= 48 && t <= 57) // 数字
                    status = 2;
                else if (t == 34) // 双引号是字符串
                    status = 3;
                else if (t == 39) // 单引号是单字符
                    status = 4;
                else if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96) || (t >= 123 && t <= 126))
                    status = 5;
                else {
                    // 非法字符,输出异常信息
                    cout << "出现非法字符:" << endl;
                    cout << r[subscript] << ":";
                    cout << static_cast<int>(r[subscript]) << endl;
                    break;
                }

                switch (status) { // 对于每一种状态
                case 0: // 空格
                    subscript++;
                    break;

                case 1: // 字母(单词)
                    word = "";
                    pre_subscript = subscript;
                    while ((t > 64 && t < 91) || (t > 96 && t < 123) || (t > 47 && t < 58)) {
                        word = word + r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    // 在这里处理关键字、标识符,添加到 result 和对应的列表中
                    if (find(begin(k), end(k), word) != end(k)) // 关键字
                        result = result + "( " + KEY + ", " + word + ")\n";
                    else //标识符 
                    {
                        if (std::find(i.begin(), i.end(), word) == i.end())
                            i.push_back(word);
                        result = result + "( "+ ID + ", " + word + ")\n";
                    }
                    break;

                case 2:  // 数字
                    word = "";
                    pre_subscript = subscript;
                    while (t > 47 && t < 58) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    if (std::find(c.begin(), c.end(), word) == c.end())  // 判断是否为常数
                        c.push_back(word);
                    result = result + "( "+ NUMBER + ", " + word + ")\n";
                    break;

                case 3: {  // 字符串
                    word = "";
                    pre_subscript = subscript;
                    subscript++;
                    t = static_cast<int>(r[subscript]);
                    while (t != 34) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }
                    subscript++;
                    if (find(S.begin(), S.end(), word) == S.end())  // 判断是否为字符串
                        S.push_back(word);
                    result = result + "( " + STRING + ", " + word + ")\n";
                    break;
                }

                case 4:  // 字符常量
                    word = "";
                    pre_subscript = subscript;
                    subscript++;
                    t = static_cast<int>(r[subscript]);
                    while (t != 39) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    subscript++;
                    if (find(C.begin(), C.end(), word) == C.end())  // 判断是否为单字符
                        C.push_back(word);
                    result = result + "( " + CHAR + ", " + word + ")\n";
                    break;

                case 5: { // 符号 如果是符号,直接将符号取出
                    word = r.substr(subscript, 1);

                    if (subscript + 1 < r.length())
                        t = static_cast<int>(r[subscript + 1]);

                    if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96) || (t >= 123 && t <= 126)) {
                        char f_point1 = r[subscript];
                        char f_point2 = r[subscript + 1];
                        if ((f_point1 == '&' && f_point2 == '&') || (f_point1 == '+' && f_point2 == '+') 
                                || (f_point1 == '-' && f_point2 == '-') || (f_point1 == '|' && f_point2 == '|')
                                || (f_point1 == '>' && f_point2 == '=') || (f_point1 == '<' && f_point2 == '=')
                                || (f_point1 == '=' && f_point2 == '=') || (f_point1 == '!' && f_point2 == '=')
                                || (f_point1 == '<' && f_point2 == '<') || (f_point1 == '>' && f_point2 == '>')
                                || (f_point1 == '+' && f_point2 == '=') || (f_point1 == '-' && f_point2 == '=')
                                || (f_point1 == '+' && f_point2 == '+') || (f_point1 == '*' && f_point2 == '=')
                                || (f_point1 == '/' && f_point2 == '=') || (f_point1 == '%' && f_point2 == '=')
                                || (f_point1 == '&' && f_point2 == '=') || (f_point1 == '|' && f_point2 == '=')
                                || (f_point1 == '^' && f_point2 == '=')) {
                            word = r.substr(subscript, 2);
                            subscript++;
                        }
                    }

                    int mm;
                    // 查找p表
                    for (mm = 0; mm < p.size(); mm++) {
                        if (word == p[mm]) {
                            if_point = 1;
                            break;
                        }
                    }

                    if (if_point == 1) 
                    {  // 如果匹配成功
                        if(mm<=24) //单字分界符
                            result = result + "( " + SD +", "+ word + ")\n";
                        else //双字分界符
                            result = result + "( " + SD +", "+ word + ")\n";
                    }
                    else {  // 匹配失败
                        cout << "出现未定义符号: " << word << endl;
                    }
                    subscript++;
                    if_point = 0;
                    break;
                }
                default:
                    cout << "\n字符识别异常:无法识别的状态" << endl;
                    break;
                }
            }
            // 将结果保存在文档中
            saveResultsToFile(result, i, C, S, c, p, k, path_out);
        }
        catch (const std::exception& e)
        {
            cerr << e.what() << endl;
        }
    }

输出函数 (saveResultsToFile):

        将识别的结果按照类型分别保存到输出文件中。它将分别输出 Token 序列、标识符表、单字符表、字符串表、常数表、运算符表和关键字表。

 void saveResultsToFile(const string& result, vector<string> i, vector<string> C, vector<string> S, vector<string> c, vector<string> p, vector<string> k, const string& path_out) {
        ofstream outputFile(path_out);
        if (outputFile.is_open()) {
            outputFile << result;
            outputFile.close();
        }
    }

主要数据结构描述

LexicalAnalyzer 类:

  1. vector<string> k - 存储关键字表
  2. vector<string> p - 存储运算符和界符表
  3. vector<string> i - 存储标识符表
  4. vector<string> C - 存储单字符表
  5. vector<string> S - 存储多字符表
  6. vector<string> c - 存储常数表
  7. int if_point - 用于标记某个字符是否为操作符或界符

​​​​​​​程序结构描述

  1. startLexicalAnalysis 方法:开始词法分析,进行预处理并调用 scanner 方法进行分析。
  2. pretreatment 方法:预处理,去除注释和多余的空格。
  3. scanner 方法:扫描器,用于将经过预处理的字符串转换为 token 序列。
  4. getListAsString 方法:将存储的字符串向量转换为单个字符串以便保存到输出文件。
  5. saveResultsToFile 方法:保存结果到输出文件。

完整代码

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cctype>
using namespace std;
#define ID '0'            // 标识符
#define KEY '1'         // 关键字
#define SD '2'           // 单字分界符
#define DD '3'           // 双字分界符
#define STRING '4'       // 字符串常量
#define CHAR '5'         //字符常量
#define NUMBER '6'        //数字



class LexicalAnalyzer {
private:
    vector<string> k = { "begin","end","then","int", "for","while", "void", "if", "char", "return", "short", "long", "short", "signed", "unsigned",
        "restrict", "struct", "union", "enum", "typedef", "sizeof", "auto", "static", "register", "extern", "const",
        "volatile", "break", "goto", "else", "switch", "case", "default", "continue", "do", "double", "float" }; // 关键字表

    vector<string> p = { "+", "-", "*", "/", "%", ".",  ">", "<", "=", "(", ")", "{", "}", ";", "[", "]", ":", "?", "'",
        "\"", ",", "&", "|", "~", "^", "++", "--", "==", ">=", "<=", "!=",  "<<", ">>", "!", "&&", "||", "+=", "-=", "*=", "/=",
        "%=", "&=", "|=", "^=", ">>=", "<<=", "*", "&" }; // 运算符和界符表

    vector<std::string> i; // 标识符表
    vector<std::string> C; // 字符常量表
    vector<std::string> S; // 字符串常量表
    vector<std::string> c; // 常数表
    int if_point = 0;

public:
    void startLexicalAnalysis(const std::string& path_in, const std::string& path_out) {
        string p_in = pretreatment(path_in);
        scanner(p_in, path_out);
    }

    string pretreatment(const string& p_in) {
        string r = ""; // 用来保存去除注释后的源程序
        try {
            std::ifstream C_txt(p_in); 
            std::stringstream buffer;
            buffer << C_txt.rdbuf();
            std::string line;
            bool InComment = false; // 用于跟踪是否在注释块内

            while (getline(buffer, line)) {
                // 处理多行注释
                if (InComment) {
                    size_t found = line.find("*/");
                    if (found != string::npos)
                    {
                        line = line.substr(found + 2);
                        InComment = false; // 出注释块了
                    }
                    else
                    {
                        continue; // 在多行注释中,跳过当前行
                    }
                }

                // 处理单行注释
                size_t SingleCommentIndex = line.find("//");
                if (SingleCommentIndex != string::npos)
                {
                    line = line.substr(0, SingleCommentIndex);
                }

                // 处理多行注释的开始
                size_t MultiCommentStartIndex = line.find("/*");
                if (MultiCommentStartIndex != std::string::npos)
                {
                    size_t MultiCommentEndIndex = line.find("*/");
                    if (MultiCommentEndIndex != std::string::npos)
                    {
                        // 删除多行注释
                        line = line.substr(0, MultiCommentStartIndex) + line.substr(MultiCommentEndIndex + 2);
                    }
                    else
                    {
                        // 进入多行注释块
                        line = line.substr(0, MultiCommentStartIndex);
                        InComment = true; // 注释块内标识为真
                    }
                }

                r += " " + line; // 将每行用空格隔开
            }

            // 删除多余的空格
            r.erase(std::unique(r.begin(), r.end(), [](char a, char b) { return a == ' ' && b == ' '; }), r.end());
            r.erase(std::remove(r.begin(), r.end(), '\n'), r.end());

            // 去除首尾空格
            size_t start = r.find_first_not_of(" \t\n\r");
            size_t end = r.find_last_not_of(" \t\n\r");

            if (start == string::npos) {
                r = "";  // Empty or all spaces
            }
            else
                r = r.substr(start, end - start + 1);

            cout << r << endl; // 将预处理后的字符串输出
        }
        catch (const exception& e)
        {
            cerr << e.what() << endl;
        }
        return r; // 返回处理好后的字符串
    }

    void scanner(const std::string& path_in, const std::string& path_out) {
        try {
            string result = ""; // 保存token信息
            size_t subscript = 0; // 计数
            int t, status, pre_subscript; // 分别为:记录单个字符的ASCII码,状态,记录上一个字符的下标
            string word; // 形成单词
            string r = path_in; // 经预处理后的文本字符流信息

            while (subscript < r.length()) {
                t = static_cast<int>(r[subscript]); // 将字符转换为ASCII码

                if (t == 32)
                    status = 0; // 空格
                else if ((t > 64 && t < 91) || (t > 96 && t < 123)) // 字母
                    status = 1;
                else if (t >= 48 && t <= 57) // 数字
                    status = 2;
                else if (t == 34) // 双引号是字符串
                    status = 3;
                else if (t == 39) // 单引号是单字符
                    status = 4;
                else if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96) || (t >= 123 && t <= 126))
                    status = 5;
                else {
                    // 非法字符,输出异常信息
                    cout << "出现非法字符:" << endl;
                    cout << r[subscript] << ":";
                    cout << static_cast<int>(r[subscript]) << endl;
                    break;
                }

                switch (status) { // 对于每一种状态
                case 0: // 空格
                    subscript++;
                    break;

                case 1: // 字母(单词)
                    word = "";
                    pre_subscript = subscript;
                    while ((t > 64 && t < 91) || (t > 96 && t < 123) || (t > 47 && t < 58)) {
                        word = word + r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    // 在这里处理关键字、标识符,添加到 result 和对应的列表中
                    if (find(begin(k), end(k), word) != end(k)) // 关键字
                        result = result + "( " + KEY + ", " + word + ")\n";
                    else //标识符 
                    {
                        if (std::find(i.begin(), i.end(), word) == i.end())
                            i.push_back(word);
                        result = result + "( "+ ID + ", " + word + ")\n";
                    }
                    break;

                case 2:  // 数字
                    word = "";
                    pre_subscript = subscript;
                    while (t > 47 && t < 58) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    if (std::find(c.begin(), c.end(), word) == c.end())  // 判断是否为常数
                        c.push_back(word);
                    result = result + "( "+ NUMBER + ", " + word + ")\n";
                    break;

                case 3: {  // 字符串
                    word = "";
                    pre_subscript = subscript;
                    subscript++;
                    t = static_cast<int>(r[subscript]);
                    while (t != 34) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }
                    subscript++;
                    if (find(S.begin(), S.end(), word) == S.end())  // 判断是否为字符串
                        S.push_back(word);
                    result = result + "( " + STRING + ", " + word + ")\n";
                    break;
                }

                case 4:  // 字符常量
                    word = "";
                    pre_subscript = subscript;
                    subscript++;
                    t = static_cast<int>(r[subscript]);
                    while (t != 39) {
                        word += r[subscript];
                        subscript++;
                        if (subscript < r.length())
                            t = static_cast<int>(r[subscript]);
                        else
                            break;
                    }

                    subscript++;
                    if (find(C.begin(), C.end(), word) == C.end())  // 判断是否为单字符
                        C.push_back(word);
                    result = result + "( " + CHAR + ", " + word + ")\n";
                    break;

                case 5: { // 符号 如果是符号,直接将符号取出
                    word = r.substr(subscript, 1);

                    if (subscript + 1 < r.length())
                        t = static_cast<int>(r[subscript + 1]);

                    if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96) || (t >= 123 && t <= 126)) {
                        char f_point1 = r[subscript];
                        char f_point2 = r[subscript + 1];
                        if ((f_point1 == '&' && f_point2 == '&') || (f_point1 == '+' && f_point2 == '+') 
                                || (f_point1 == '-' && f_point2 == '-') || (f_point1 == '|' && f_point2 == '|')
                                || (f_point1 == '>' && f_point2 == '=') || (f_point1 == '<' && f_point2 == '=')
                                || (f_point1 == '=' && f_point2 == '=') || (f_point1 == '!' && f_point2 == '=')
                                || (f_point1 == '<' && f_point2 == '<') || (f_point1 == '>' && f_point2 == '>')
                                || (f_point1 == '+' && f_point2 == '=') || (f_point1 == '-' && f_point2 == '=')
                                || (f_point1 == '+' && f_point2 == '+') || (f_point1 == '*' && f_point2 == '=')
                                || (f_point1 == '/' && f_point2 == '=') || (f_point1 == '%' && f_point2 == '=')
                                || (f_point1 == '&' && f_point2 == '=') || (f_point1 == '|' && f_point2 == '=')
                                || (f_point1 == '^' && f_point2 == '=')) {
                            word = r.substr(subscript, 2);
                            subscript++;
                        }
                    }

                    int mm;
                    // 查找p表
                    for (mm = 0; mm < p.size(); mm++) {
                        if (word == p[mm]) {
                            if_point = 1;
                            break;
                        }
                    }

                    if (if_point == 1) 
                    {  // 如果匹配成功
                        if(mm<=24) //单字分界符
                            result = result + "( " + SD +", "+ word + ")\n";
                        else //双字分界符
                            result = result + "( " + SD +", "+ word + ")\n";
                    }
                    else {  // 匹配失败
                        cout << "出现未定义符号: " << word << endl;
                    }
                    subscript++;
                    if_point = 0;
                    break;
                }
                default:
                    cout << "\n字符识别异常:无法识别的状态" << endl;
                    break;
                }
            }
            // 将结果保存在文档中
            saveResultsToFile(result, i, C, S, c, p, k, path_out);
        }
        catch (const std::exception& e)
        {
            cerr << e.what() << endl;
        }
    }

    // 将vector<String>转换成String用于保存信息写入文档
    string getListAsString(const vector<string>& t) {
        std::stringstream ss;
        for (const auto& item : t) {
            ss << item << " ";
        }
        return ss.str();
    }

    void saveResultsToFile(const string& result, vector<string> i, vector<string> C, vector<string> S, vector<string> c, vector<string> p, vector<string> k, const string& path_out) {
        ofstream outputFile(path_out);
        if (outputFile.is_open()) {
            outputFile << result;
            outputFile.close();
        }
    }
};

int main() {
    std::string path_in = "./lab3_4.txt";
    std::string path_out = "./lab3_4_out.txt";

    LexicalAnalyzer lexicalAnalyzer;
    lexicalAnalyzer.startLexicalAnalysis(path_in, path_out);

    return 0;
}


测试

测试用例(部分)如下:

终端运行结果:

词法分析结果(部分):

【问题描述】 请根据给定的文法设计并实现词法分析程序,从源程序中识别出单词,记录其单词类别和单词值,输入输出及处理要求如下: (1)数据结构和与语法分析程序的接口请自行定义;类别码需按下表格式统一定义; (2)为了方便进行自动评测,输入的被编译源文件统一命名为testfile.txt(注意不要写错文件名);输出的结果文件统一命名为output.txt(注意不要写错文件名),结果文件中每行按如下方式组织: 单词类别码 单词的字符/字符串形式(中间仅用一个空格间隔) 单词的类别码请统一按如下形式定义: 单词名称 类别码 单词名称 类别码 单词名称 类别码 单词名称 类别码 标识符 IDENFR else ELSETK - MINU = ASSIGN 整形常量 INTCON switch SWITCHTK * MULT ; SEMICN 字符常量 CHARCON case CASETK / DIV , COMMA 字符串 STRCON default DEFAULTTK < LSS ( LPARENT const CONSTTK while WHILETK GRE [ LBRACK char CHARTK scanf SCANFTK >= GEQ ] RBRACK void VOIDTK printf PRINTFTK == EQL { LBRACE main MAINTK return RETURNTK != NEQ } RBRACE if IFTK + PLUS : COLON 【输入形式】testfile.txt中的符合文法要求的测试程序。 【输出形式】要求将词法分析结果输出至output.txt中。 【特别提醒】(1)读取的字符串要原样保留着便于输出,特别是数字,这里输出的并不是真正的单词值,其实是读入的字符串,单词值需另行记录。 (2)本次作业只考核对正确程序的处理,但需要为今后可能出现的错误情况预留接口。 (3)在今后的错误处理作业中,需要输出错误的行号,在词法分析的时候注意记录该信息。 (4)单词的类别和单词值以及其他关注的信息,在词法分析阶段获取后,后续的分析阶段会使用,请注意记录;当前要求的输出只是为了便于评测,完成编译器中无需出现这些信息,请设计为方便打开/关闭这些输出的方案。 【文法定义】请到“2020年文法定义(用于查看文法,不计入成绩)”作业中查看文法 【样例输入】 coNst int cONst1 = 001, const2 = -100; const char const3 = '_'; int change1; char change3; int gets1(int var1,int var2){ change1 = var1 + var2; return (change1); } void main(){ printf("Hello World"); printf(gets1(10, 20)); } 【样例输出】 CONSTTK coNst INTTK int IDENFR cONst1 ASSIGN = INTCON 001 COMMA , IDENFR const2 ASSIGN = MINU - INTCON 100 SEMICN ; CONSTTK const CHARTK char IDENFR const3 ASSIGN = CHARCON _ SEMICN ; INTTK int IDENFR change1 SEMICN ; CHARTK char IDENFR change3 SEMICN ; INTTK int IDENFR gets1 LPARENT ( INTTK int IDENFR var1 COMMA , INTTK int IDENFR var2 RPARENT ) LBRACE { IDENFR change1 ASSIGN = IDENFR var1 PLUS + IDENFR var2 SEMICN ; RETURNTK return LPARENT ( IDENFR change1 RPARENT ) SEMICN ; RBRACE } VOIDTK void MAINTK main LPARENT ( RPARENT ) LBRACE { PRINTFTK printf LPARENT ( STRCON Hello World RPARENT ) SEMICN ; PRINT
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天之交子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值