一、 实验目的
设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。
二、 实验要求
1、实现预处理功能
源程序中可能包含有对程序执行无意义的符号,要求将其剔除。
首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。
2、实现词法分析功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。其中,
syn为单词种别码。
Token为存放的单词自身字符串。
Sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。
3、待分析的C语言子集的词法
1)关键字
main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。
2)运算符和界符
+ - * / : := < <> <= > >= = ; ( ) #
3)其他标记ID和NUM
通过以下正规式定义其他标记:
ID→letter(letter|digit)*
NUM→digit digit*
letter→a|…|z|A|…|Z
digit→0|…|9…
4)空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
4、各种单词符号对应的种别码
表1 各种单词符号的种别码
单词符号 种别码 单词符号 种别码 main 1 ; 41 if 2 ( 42 then 3 ) 43 while 4 int 7 do 5 double 8 static 6 struct 9 ID 25 break 10 NUM 26 else 11 + 27 long 12 - 28 switch 13 * 29 case 14 / 30 typedef 15 ** 31 char 16 == 32 return 17 < 33 const 18 <> 34 float 19 <= 35 short 20 > 36 continue 21 >= 37 for 22 = 38 void 23 [ 39 sizeof 24 ] 40 # 0
5、 词法分析程序的主要算法思想
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到的单词符号的第一个字符的种类,拼出相应的单词符号。
1. 主程序示意图
主程序示意图如图1所示。
图1 词法分析主程序示意图
其中初值包括如下两方面:
(1)关键字表初值
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下:
char *rwtab[27]={“main”,”if”,”then”,”while”,”do”,” static”,”int”,” double”,”struct”,”break”,”else”,”long”,”switch”,”case”,”typedef”,”char”,”return”,”const”,”float”,”short”,”continue”,”for”,”void”,”default”,”sizeof”,”do”};
(2) 程序中需要用到的主要变量:syn,token和sum。
2. 扫描子程序的算法思想
首先设置三个变量:token用来存放构成单词符号的字符串;sum用来存放整型单词;syn用来存放单词符号的种别编码。扫描子程序主要部分流程如图2所示。
在这里插入代码片
图2 词法分析程序流程
三、实验报告要求
1.写出编程思路、源代码(或流程图);
2.写出上机调试时发现的问题,以及解决的过程;
3.写出你所使用的测试数据及结果;
4.谈谈你的体会。
5.上机6小时,完成实验报告2小时。
测试数据及结果
代码
#include <bits/stdc++.h> using namespace std; int len; // 经过预处理后的文本长度 char proBuffer[20000]; //经过预处理后代码的全局缓冲区 int syn; //单词种别码 char Token[200] = {}; //字符串 int Sum; //整型常量 string rwtab[46]={"#","main","if","then","while","do","static","int","double","struct", "break","else","long","switch","case","typedef","char","return", "const","float","short","continue","for","void","sizeof","ID", "NUM","+","-","*","/","**","==","<","<>","<=",">",">=","=", "[","]",";","(",")","{","}"}; bool isDigit(string s, bool &point) { // 对数字开头的串额外判断 for (int i = 0; i < s.size(); i++) { if (s[i] == 'x' || s[i] == 'X') { // 十六进制 if (s[i - 1] == '0') continue; else return false; } else if (s[i] == 'e' || s[i] == 'E') { // 指数 if (i + 1 < s.size() && s[i + 1] == '+' || s[i + 1] == '-' || (s[i + 1] >= '0' && s[i + 1] <= '9')) continue; else return false; } else if (s[i] >= '0' && s[i] <= '9') { // 是数字则处理下一位 continue; } else if (s[i] == '.' && !point) { // 出错情况连续两个小数点 point = true; } else return false; } return true; } bool isSeparative(char ch) { // 判断是否为分隔符 if (ch == rwtab[0][0]) {//#的种别码为0 return true; } for (int i = 27; i < sizeof(rwtab) / sizeof(rwtab[0]); i++) { if (ch == rwtab[i][0]) { return true; } } return false; } void pretreatment()//预处理 { string temp; bool isNote = false; // 判断是否为注释 bool empty = false; // 判断是否为空格 while (getline(cin, temp)) { // 每次读入一行存入临时变量空间temp,不保存换行符 int i = 0; // 指针,指示当前处理字符 if (isNote) { // 多行注释内容 /* ... */ // 当前行未结束,且不是注释结束标志,指针后移 while (temp[i + 1] && !(temp[i] == '*' && temp[i + 1] == '/')) { i++; } // 是注释标志,注释部分结束 if (temp[i] == '*' && temp[i + 1] && temp[i + 1] == '/') { isNote = false; i += 2; } } // 当前行存在非注释内容 while (!isNote && temp[i]) { // 有多余空格直接跳过 while (empty) { // 需要放在最前面 if (temp[i] == ' ') i++; else empty = false; } if (temp[i] == ' ') empty = true; // 标记空格 if (temp[i] == '"') { // 存入""之内的内容,当做字符串 do { proBuffer[len++] = temp[i++]; } while (temp[i] != '"'); proBuffer[len++] = temp[i++]; // 存入后" } if (temp[i] == '\'') { // ' 代表之后有一个字符,例如'a' proBuffer[len++] = temp[i++]; proBuffer[len++] = temp[i++]; proBuffer[len++] = temp[i]; } if (temp[i] == '/' && temp[i + 1]) { // 判断是 / 还是 // if (temp[i + 1] == '/') { // 是 // 则当前行之后都是注释内容 break; } if (temp[i + 1] == '*') { // 是 /* 则进行标记且跳过该行 isNote = true; break; } } proBuffer[len++] = temp[i++]; // 将当前字符存入 } if (!empty) proBuffer[len++] = ' '; // 在最后若无空格,则加上空格与下一行隔开 empty = true; } } void scaner()//扫描子程序 { int cnt = 0; // 当前指针位置 while (cnt < len) { string token; int sum = 0, syn = -1; // 定义标识变量 char ch; // 存储当前所指示的字符 string temp; // 存储分出来的字符串 ch = proBuffer[cnt]; if (ch == ' ') { // 不考虑空格 cnt++; continue; } temp += ch; if (ch >= '0' && ch <= '9') { // 以数字开头 while ((++cnt) < len) { if (proBuffer[cnt] == ' ' || (proBuffer[cnt] != '.' && isSeparative(proBuffer[cnt]))) // 是空格或者分隔符,但不是小数点 break; else temp += proBuffer[cnt]; } bool point = false; if (isDigit(temp, point)) { // 判断是否符合数字规则 syn = 26; token = "NUM"; if (point) // 是否是小数 token = temp; else sum = atoi(temp.c_str()); // 是整数转换为整型存储 } else token = temp; // 不是数字串,syn默认为1,token存储该错误串 } else if ((ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A')) { // 判断是都是文本 bool keywords = false; while ((++cnt) < len) { if ((proBuffer[cnt] >= '0' && proBuffer[cnt] <= '9') || (proBuffer[cnt] >= 'a' && proBuffer[cnt] <= 'z') || (proBuffer[cnt] >= 'A' && proBuffer[cnt] <= 'Z') || proBuffer[cnt] == '_') temp += proBuffer[cnt]; else break; } for (int i = 1; i < 25; i++) { // 拼串完成,判断是否是关键字 if (temp == rwtab[i]) { syn = i ; token = temp; keywords = true; } } // for (int i = 0; i < sizeof(rwtab) / sizeof(rwtab[0]); // i++) { // 拼串完成,判断是否是关键字 // if (temp == rwtab[i]) { // syn = -2; // token = temp; // keywords = true; // } // } if (!keywords) { // 不是关键字,则为letter syn = 25; token = temp; } } else if ((ch >= '!' && ch <= '/') || (ch >= ':' && ch <= '?') || //判断是否是分隔符或运算符 ch == ']' || ch == '[' || ch == '{' || ch == '}' || ch == '#') { temp = ch; for (int i = 0; i < sizeof(rwtab) / sizeof(rwtab[0]); i++) if (temp == rwtab[i] ) { token = temp; syn = (i + 0) % (sizeof(rwtab) / sizeof(rwtab[0]) + 0); } if (token == "*" || token == "=" || token == "<" || token == ">" || token == "+" || token == "|" || token == "&") { // 对一些含有两部分的运算符特判 temp += proBuffer[cnt + 1]; for (int i = 27; i < sizeof(rwtab) / sizeof(rwtab[0]); i++) if (temp == rwtab[i]) { token = temp; syn = (i + 0) % (sizeof(rwtab) / sizeof(rwtab[0]) + 0); cnt++; } } cnt++; } else { // 不以数字开头,不以英文字母开头,不是分隔符或运算符,则为错误串 cout << '(' << syn << '\t' << ch << ')' << endl; cnt++; continue; } if (token == "NUM") // 是整型数字串 cout << '(' << syn << '\t' << sum << ')' << endl; else cout << '(' << syn << '\t' << token << ')' << endl; } } int main(){ FILE *stream; freopen_s(&stream, "Compiler/project1/test.txt", "r", stdin); freopen_s(&stream, "Compiler/project1/result.txt", "w", stdout); ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); pretreatment(); //预处理 cout << "预处理:\n"<<proBuffer <<endl; cout <<"词法分析器的结果如下:"<<endl; scaner(); return 0; }