实验1. 词法分析程序的设计(更符合南邮宝宝的体质)

实验1. 词法分析程序的设计

实验要求就是对于一段代码,去掉空格注释压成一行,并且能识别出各种标识符和错误的语法。想要源码的私信,感谢点赞。

1.实验软件环境

​ 开发平台:Windows 11

​ 编程语言:C++

​ 编译器版本:GCC 11.20 C++20

​ IDE:VS Code

2.实验原理描述

C++语言子集:

(1)关键字:

​ void、int、main、return、if、else

(2)运算符和界限符:

+ 、 − 、 ∗ 、 / 、 % 、 = 、 ; 、 ( 、 ) 、 [ 、 ] 、 { 、 } +、-、*、/、\%、=、;、(、)、[、]、\{、\} +/%=;()[]{}

(3) 整型常数(INT)和标识符(ID)通过正规文法定义

3.功能实现描述

删除注释空格函数:

​ 把读进来的字符串处理压成一行,主要是要识别’\n’转义字符和\字符与*字符

// 处理字符串中的注释和空格
string ShanchuSpace(const string& rawstr) {
    string str;
    for (int i = 0, skipLine = 0, skipOverLine = 0; i < rawstr.size(); i++) {
        char cur = rawstr[i];
        // 处理注释//
        if (skipLine) {
            if (cur == '\n') {
                skipLine = 0;
            }
            continue;
        }

        // 处理跨行注释/*
        if (skipOverLine) {
            if (cur == '/') {
                if (i && rawstr[i - 1] == '*') {
                    skipOverLine = 0;
                }
            }
            continue;
        }
        // 去除空格
        if (cur == '=' || cur == '<' || cur == '>' || cur == '+' ||
            cur == '-' || cur == '(' || cur == '{') {
            while (str.size() && str.back() == ' ')
                str.pop_back();
            str += cur;
            continue;
        }
        if (cur != '\n' && cur != '/' && cur != '*' && cur != ' ' &&
            cur != '=') {
            if (i && rawstr[i - 1] == '/')
                str += '/';
            if (i && rawstr[i - 1] == '*')
                str += '*';
            // 处理流运算符空格
            if (cur == '<' && i && rawstr[i - 1] == '<') {
                str.pop_back();
                while (str.size() && str.back() == ' ')
                    str.pop_back();
                str += '<';
            }
            str += cur;
            continue;
        }
        // 处理多余空格 当前面的字符为字母或数字时 才加入空格
        if (cur == ' ' && (isdigit(str.back()) || isalpha(str.back()))) {
            str += cur;
            continue;
        }

        // 当前字符为/时 前方字符为/时为注释 否则为除号
        if (cur == '/') {
            // 为注释符
            if (i && rawstr[i - 1] == '/') {
                skipLine = 1;
                continue;
            }
            // 为除号
            if (rawstr.size() == i - 1) {
                while (str.size() && str.back() == ' ')
                    str.pop_back();
                str += cur;
            }
            continue;
        }

        // 当前字符为*时 前方字符为/时为注释 否则为乘号
        if (cur == '*') {
            // 为注释符
            if (i && rawstr[i - 1] == '/') {
                skipOverLine = 1;
                continue;
            }
            // 为乘号
            if (rawstr.size() == i - 1) {
                while (str.size() && str.back() == ' ')
                    str.pop_back();
                str += cur;
            }
        }
    }
    return str;
}

work函数:

​ 该函数就是把各个关键词和标识符找到,并且输入到文件中,并且定义了两个哈希表,一个用来看什么字符串对应什么数字,一个是检验这个字符串之前有没有出现过

// 记录出现过的标识符
map<string, int> used;
// 单词映射哈希表 标识符32 十进制整数33 八进制整数34 十六进制整数35
map<string, int> Hash{{"void", 1},   {"main", 2}, {"int", 3},  {"cout", 4},
                      {"return", 5}, {"if", 6},   {"else", 7}, {"break", 8},
                      {"for", 9},    {"(", 10},   {")", 11},   {"{", 12},
                      {"}", 13},     {"+", 14},   {"-", 15},   {"*", 16},
                      {"/", 17},     {"%", 18},   {"=", 19},   {",", 20},
                      {";", 21},     {"<<", 22},  {"<=", 23},  {">=", 24},
                      {"<", 25},     {">", 26},   {"==", 27},  {"[", 28},
                      {"]", 29},     {"++", 30},  {"--", 31}};

const int identityNum = 32, decimalNum = 33, octalNum = 34, hexNum = 35;

​ 然后work函数主要就是遍历整个字符串,按照自己设计的状态转移图和有穷自动机的思路来模拟实现,然后输出到文件中,输出函数:

// 输出单词
void ShuChuWord(int num, int type, const string& s) {
    cout << setw(4) << num << ' ' << setw(10) << type << ' ' << setw(12) << s
         << endl;
}

​ work函数:

void work() {
    while (index < str.size()) {
        char cur = str[index];
        if (cur == ' ') {
            index++;
            continue;
        }
        string tmp;
        // 查找关键词或者标识符
        if (isalpha(cur) || cur == '_') {
            while (index < str.size() &&
                   (isalnum(str[index]) || str[index] == '_'))
                tmp += str[index++];
            // 查找到关键词
            if (Hash.contains(tmp)) {
                ShuChuWord(cnt, Hash[tmp], tmp);
                cnt++;
                continue;
            }  // 找到标识符
            else {
                if (used.contains(tmp))
                    continue;
                used[tmp] = cnt;
                ShuChuWord(cnt, identityNum, tmp);
                cnt++;
                continue;
            }
        }
        // 当前找到的是数字 则为整数 直到找到非数字
        else if (isdigit(cur)) {
            // 判断八进制和十六进制
            bool hex = false, oct = false;
            if (cur == '0') {
                // 十六进制
                if (index + 1 < str.size() && str[index + 1] == 'x') {
                    hex = true;
                    tmp += "0x";
                    index += 2;
                } else if (index + 1 < str.size()) {
                    tmp += "0";
                    oct = true;
                    index++;
                }
            }
            // 是否是非法标识符
            bool flag = true;
            while (index < str.size() &&
                   (isdigit(str[index]) || isalpha(str[index]) ||
                    str[index] == '_')) {
                if (isalpha(str[index])) {  // 如果有字符出现 视为违法标识符
                    flag = false;
                }
                tmp += str[index++];
            }
            if (!flag) {
                if (used.contains(tmp))
                    continue;
                used[tmp] = cnt;
                ErrorIDword.emplace_back(tmp, cnt);
            } else {
                if (oct) {
                    ShuChuWord(cnt, octalNum, tmp);
                } else if (hex) {
                    ShuChuWord(cnt, hexNum, tmp);
                } else
                    ShuChuWord(cnt, decimalNum, tmp);
            }
            cnt++;
        }  // 其余符号开头 < > 开头特殊处理
        else {
            if (cur == '<') {
                // 如果有=或<
                if (index + 1 < str.size() &&
                    (str[index + 1] == '=' || str[index + 1] == '<')) {
                    string tmp;
                    tmp += cur;
                    tmp += str[index + 1];
                    ShuChuWord(cnt, Hash[tmp], tmp);
                    cnt++;
                    index++;
                } else {
                    ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
                    cnt++;
                }
            } else if (cur == '>') {
                if (index + 1 < str.size() && str[index + 1] == '=') {
                    string tmp{cur};
                    tmp += "=";
                    ShuChuWord(cnt, Hash[tmp], tmp);
                    cnt++;
                    index++;
                } else {
                    ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
                    cnt++;
                }
            } else if (cur == '+' || cur == '-') {  //++ -- 特殊处理
                if (index + 1 < str.size() && str[index + 1] == cur) {
                    ShuChuWord(cnt, Hash[string(2, cur)], string(2, cur));
                    cnt++;
                    index += 2;
                } else {
                    ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
                    cnt++;
                    index++;
                }
            } else {
                ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
                cnt++;
            }
            index++;
        }
    }
}

然后还要把不合法的标识符输出出来

// 输出不合法标识符
void ShuChuErrorID(const string& rawstr, vector<pair<string, int>>& vec) {
    int index = 0, column = 1, row = 1;
    for (auto [str, pos] : vec) {  //结构化绑定
        while (index < rawstr.size()) {
            char cur = rawstr[index];
            if (cur == '\n')
                row++, column = 1;
            if (rawstr[index] == str[0] &&
                rawstr.substr(index, str.size()) == str) {
                index += str.size();
                cout << "发现非法标识符:" << str << '\n';
                cout << "位于源程序第" << pos << "个单词\n";
                cout << "位于源程序第" << row << "行,第" << column << "列\n\n";
                column += str.size();
                break;
            } else
                index++, column++;
        }
    }
}

4.实验结果展示与分析

在这里插入图片描述

​ 首先初始有 1. t x t 1.txt 1.txt文档,里面有我写的一段代码。

在这里插入图片描述

​ 在运行后,在终端中输入文件路径,因为在同一个文件夹中,就只需要输入相对路径,输入好了按回车

在这里插入图片描述

​ 运行好后,在左侧,多出了 S h u C h u . t x t 和 Y u C h u L i . t x t ShuChu.txt和YuChuLi.txt ShuChu.txtYuChuLi.txt两个txt文件,代表运行成功了,在当前文件夹中生成了好了两个需要的txt文件。

在这里插入图片描述

​ 可以看出,在 Y u C h u L i . t x t YuChuLi.txt YuChuLi.txt中,已经把代码过滤了空格和注释,全部压成一行。

在这里插入图片描述

​ 在 S h u C h u . t x t ShuChu.txt ShuChu.txt中,根据预处理的结果识别的各种变量,标识符输出到这个文件里,在最后输出了非法标识符,查看后是因为定义了int类型,但用浮点数类型给它赋值。

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
词法分析是编译器的第一个阶段,它的主要任务是将源程序中的字符序列分解成有意义的词素序列(即单词),并将每个词素分类为一种词法单元。词法分析程序通常由两部分组成: 1. 词法规则定义:定义每种词法单元的模式或正则表达式,例如标识符、数字、运算符等。 2. 词法分析器实现:根据词法规则定义,将输入的源程序逐个字符扫描,并匹配识别出各个词法单元。 下面是一个简单的词法分析程序设计实验实验要求:设计一个词法分析程序,对以下语言进行词法分析:变量名只能由字母和数字组成,必须以字母开头;数字只包含整数,不包含小数;运算符包括加、减、乘、除,分号用于表示语句结束。 样例输入: x = 10 + y * 2; 样例输出: 变量名:x 赋值运算符:= 数字:10 加运算符:+ 变量名:y 乘运算符:* 数字:2 分号:; 实验步骤: 1. 定义词法规则,包括标识符、数字、运算符和分号的正则表达式。 ``` ID = [a-zA-Z][a-zA-Z0-9]* NUM = [0-9]+ OP = [+|\-|\*|\/] SEMICOLON = [\;] ``` 2. 实现词法分析器,逐个字符扫描输入的源程序,并匹配识别出各个词法单元。 ```python import re def lexer(input_string): tokens = [] while input_string: # 匹配标识符 if re.match(ID, input_string): match = re.match(ID, input_string) tokens.append(('变量名', match.group(0))) input_string = input_string[len(match.group(0)):] # 匹配数字 elif re.match(NUM, input_string): match = re.match(NUM, input_string) tokens.append(('数字', match.group(0))) input_string = input_string[len(match.group(0)):] # 匹配运算符 elif re.match(OP, input_string): match = re.match(OP, input_string) tokens.append(('运算符', match.group(0))) input_string = input_string[len(match.group(0)):] # 匹配分号 elif re.match(SEMICOLON, input_string): match = re.match(SEMICOLON, input_string) tokens.append(('分号', match.group(0))) input_string = input_string[len(match.group(0)):] else: # 无法匹配的字符,跳过 input_string = input_string[1:] return tokens ``` 3. 对样例输入进行词法分析,并输出结果。 ```python input_string = 'x = 10 + y * 2;' tokens = lexer(input_string) for token in tokens: print(token[0] + ':', token[1]) ``` 输出结果: ``` 变量名: x 赋值运算符: = 数字: 10 加运算符: + 变量名: y 乘运算符: * 数字: 2 分号: ; ``` 至此,我们完成了一个简单的词法分析程序设计实验

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值