编译原理实践:C++实现词法分析器(学习笔记)
实践说明
【题目要求】
编写C++程序分析当前目录下input.txt文件中的词法,以“(种别码,单词)”的形式输出,单词对应的种别码自拟,输出结果存放到当前目录下的output.txt文件中。
【可分析词法】
程序可识别64种词法,内容如下:
1、关键字:begin, void, int, char, main, if, scanf, printf, while, for, return, end
2、标识符(输出时前后会带上单引号)
3、十进制数字(输出时转换成二进制形式)
4、算术运算符:+, -, *, /, %, ++, –
5、关系运算符:==, !=, >, <, >=, <=
6、逻辑运算符:&&, ||, !
7、位运算符:&, |, ^, >>, <<
8、赋值运算符:=, +=, -=, =, /=, %=, >>=, <<=, &=, ^=, |=
9、注释://, /, */
10、其他:., ->, ', ", , (, ), [, ], {, }, :, , ;, \n
注意,对于不存在于本词法中的输入X,本程序将输出:(-1,‘X’ does not exist in this lexical system)
【操作步骤】
1、在input.txt文件中输入或更改待分析的C源程序段(段末请以#结尾)。
2、双击程序exe文件即可完成词法分析程序的运行。
3、在output.txt文件中查看词法分析结果。
输入举例(input.txt)
begin
/* the main function */
void main()
{
int i = 8 + 244 * 5 / 10 - 2;
char _KEY_WORD_END = '\\';
scanf("%[^end]s", ch); // get the string
if(i == 0)
{
printf("good!, the result is:\n");
for(; i != 128; -- i)
{
while(i % 2 != 0 || (i < 61) && (i > 16))
{
oneWord -> value = ch[i ++];
}
}
}
return 0;
}$
end#
输出举例(output.txt)
Your words:
-------------------------------
begin
/* the main function */
void main()
{
int i = 8 + 244 * 5 / 10 - 2;
char _KEY_WORD_END = '\\';
scanf("%[^end]s", ch); // get the string
if(i == 0)
{
printf("good!, the result is:\n");
for(; i != 128; -- i)
{
while(i % 2 != 0 || (i < 61) && (i > 16))
{
oneWord -> value = ch[i ++];
}
}
}
return 0;
}$
end
-------------------------------
The result of lexical analyzer:
(1,begin)
(20,\n)
(68,/*)
(69,*/)
(20,\n)
(2,void)
(5,main)
(56,()
(57,))
(20,\n)
(60,{)
(20,\n)
(3,int)
(13,'i')
(42,=)
(14,1000)
(21,+)
(14,11110100)
(23,*)
(14,101)
(24,/)
(14,1010)
(22,-)
(14,10)
(64,;)
(20,\n)
(4,char)
(13,'_KEY_WORD_END')
(42,=)
(65,')
(55,\)
(65,')
(64,;)
(20,\n)
(7,scanf)
(56,()
(66,")
(25,%)
(58,[)
(39,^)
(12,end)
(59,])
(13,'s')
(66,")
(63,,)
(13,'ch')
(57,))
(64,;)
(67,//)
(20,\n)
(6,if)
(56,()
(13,'i')
(28,==)
(14,0)
(57,))
(20,\n)
(60,{)
(20,\n)
(8,printf)
(56,()
(66,")
(13,'good')
(36,!)
(63,,)
(13,'the')
(13,'result')
(13,'is')
(62,:)
(20,\n)
(66,")
(57,))
(64,;)
(20,\n)
(10,for)
(56,()
(64,;)
(13,'i')
(29,!=)
(14,10000000)
(64,;)
(27,--)
(13,'i')
(57,))
(20,\n)
(60,{)
(20,\n)
(9,while)
(56,()
(13,'i')
(25,%)
(14,10)
(29,!=)
(14,0)
(35,||)
(56,()
(13,'i')
(31,<)
(14,111101)
(57,))
(34,&&)
(56,()
(13,'i')
(30,>)
(14,10000)
(57,))
(57,))
(20,\n)
(60,{)
(20,\n)
(13,'oneWord')
(54,->)
(13,'value')
(42,=)
(13,'ch')
(58,[)
(13,'i')
(26,++)
(59,])
(64,;)
(20,\n)
(61,})
(20,\n)
(61,})
(20,\n)
(61,})
(20,\n)
(11,return)
(14,0)
(64,;)
(20,\n)
(61,})
(-1,'$' does not exist in this lexical system)
(20,\n)
(12,end)
编程平台
CodeBlocks 10.05
代码实现
基本思路
遍历input.txt文件中的所有字符,使用int型变量inputBufferIndex记录当前遍历到的字符下标,忽略空格和\t(Tab键)这两个字符,通过让inputBufferIndex自增或自减,检查获取到的单词符合可识别词法中哪些类别的特点,并实时地将当前扫描到的单词的识别结果输出到output.txt文件中。
程序环境设置
#include <iostream>
#include <stdio.h> /*包含库所用的某些宏和变量*/
#include <stdlib.h> /*包含库*/
#include <string.h> /*包含字符串处理库*/
#define _KEY_WORD_END "waiting for your expanding" /*定义关键字结束标志*/
using namespace std;
typedef struct /*单词二元组*/
{
int categoryCode; /*单词种别码*/
char *value; /*单词自身的值*/
}WORD;
char inputBuffer[10000]; /*输入缓冲区*/
char wordBuffer[1000] = ""; /*单词缓冲区*/
int inputBufferIndex; /*输入缓冲区下标*/
int wordBufferIndex; /*单词缓冲区下标*/
char ch; /*当前读入字符*/
char *rwtab[] = {"begin", "void", "int", "char", "main", "if", "scanf", "printf", "while", "for", "return", "end", _KEY_WORD_END};
/*关键字数组*/
WORD* scaner(); /*声明词法分析函数,获得一个单词*/
char getChar(); /*声明字符读取函数,从输入缓冲区读取一个字符*/
void removeBlankSymbol(); /*声明去掉空格和\t函数*/
void concatWord(); /*声明拼接单词函数*/
int isLetter(); /*声明判断是否字母函数*/
int isDigit(); /*声明判断是否数字函数*/
int retriveKeyword(); /*声明检索关键字数组函数,在字符数组rwtab中检索*/
void retractChar(); /*声明回退字符函数,回退一个字符*/
char* digitToBinary(); /*声明将十进制转换成二进制函数*/
char* changeWordFormation();/*声明更改单词的输出形式函数*/
主函数定义
int main()
{
freopen("input.txt","r",stdin); // 从input.txt获取输入
freopen("output.txt","w",stdout); // 输出到output.txt里
int over = 1; // 判断是否出现#
inputBufferIndex = 0; // 初始化输入缓冲区下标
WORD *oneWord = new WORD; // 关键字数组
scanf("%[^#]s", inputBuffer); // 读入源程序字符串到缓冲区,以#结束,允许多行输入,#不读入
printf("Your words:\n\n");
printf("-------------------------------\n");
printf("%s\n", inputBuffer);
printf("-------------------------------\n\n");
printf("The result of lexical analyzer:\n\n");
while (over < 1000) // 分析源程序直到出现结束符#
{
oneWord = scaner(); // 获得一个新单词
if (oneWord -> categoryCode < 1000) // 本程序的种别码数量最多不超过1000个
{
printf("(%d,%s)\n", oneWord -> categoryCode, oneWord -> value);
// 打印种别码和单词自身的值
}
over = oneWord -> categoryCode;
}
}
单词获取
/*获取一个单词*/
WORD* scaner()
{
WORD *myWord = new WORD;
myWord -> categoryCode = 13;
myWord -> value = "";
wordBufferIndex = 0;
getChar(); // 从输入缓冲区读取一个字符到ch中
removeBlankSymbol(); // 去掉空格
if(isLetter()) // 如果单词的第一个字符为字母,则说明该单词为变量
{
while(isLetter() || isDigit()) // 变量名可以有数字
{
concatWord(); // 拼接单词
getChar(); // 从输入缓冲区读取下一个字符到ch中
}
retractChar(); // 回退一个字符
if((myWord -> categoryCode = retriveKeyword()) == 13) // 说明不是关键字,需要加上引号后输出
{
myWord -> value = changeWordFormation();
}
else
{
myWord -> value = wordBuffer;
}
return myWord;
}
else if(isDigit()) // 如果单词的第一个字符为数字,则说明该单词为数字
{
while(isDigit())
{
concatWord(); // 拼接单词
getChar(); // 从输入缓冲区读取下一个字符到ch中
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 14;
myWord -> value = digitToBinary(); // 将数字转换成二进制数
return myWord;
}
else // 说明单词为符号
{
switch(ch)
{
case '+': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“+=”符号
{
myWord -> categoryCode = 43;
myWord -> value = "+=";
return myWord;
}
else if(ch == '+') // 是则说明是“++”符号
{
myWord -> categoryCode = 26;
myWord -> value = "++";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 21;
myWord -> value = "+";
return myWord;
break;
case '-': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“-=”符号
{
myWord -> categoryCode = 44;
myWord -> value = "-=";
return myWord;
}
else if(ch == '-') // 是则说明是“--”符号
{
myWord -> categoryCode = 27;
myWord -> value = "--";
return myWord;
}
else if(ch == '>') // 是则说明是“->”符号
{
myWord -> categoryCode = 54;
myWord -> value = "->";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 22;
myWord -> value = "-";
return myWord;
break;
case '*': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“*=”符号
{
myWord -> categoryCode = 45;
myWord -> value = "*=";
return myWord;
}
else if(ch == '/') // 是则说明是“*/”注释符号
{
myWord -> categoryCode = 69;
myWord -> value = "*/";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 23;
myWord -> value = "*";
return myWord;
break;
case '/': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“/=”符号
{
myWord -> categoryCode = 46;
myWord -> value = "/=";
return myWord;
}
else if(ch == '/') // 是则说明是“//”注释符号
{
while(getChar())
{
if(ch == '\n')
{
retractChar(); // 回退一个字符
break;
}
}
myWord -> categoryCode = 67;
myWord -> value = "//";
return myWord;
}
else if(ch == '*') // 是则说明是“/*”注释符号
{
while(getChar())
{
if(ch == '*')
{
getChar();
if(ch == '/')
{
retractChar(); // 回退一个字符
retractChar(); // 回退一个字符
break;
}
}
}
myWord -> categoryCode = 68;
myWord -> value = "/*";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 24;
myWord -> value = "/";
return myWord;
break;
case '%': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“%=”符号
{
myWord -> categoryCode = 47;
myWord -> value = "%=";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 25;
myWord -> value = "%";
return myWord;
break;
case '=': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“==”符号
{
myWord -> categoryCode = 28;
myWord -> value = "==";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 42;
myWord -> value = "=";
return myWord;
break;
case '>': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“>=”符号
{
myWord -> categoryCode = 32;
myWord -> value = ">=";
return myWord;
}
else if(ch == '>') // 是则说明是“>>”符号
{
getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“>>=”符号
{
myWord -> categoryCode = 48;
myWord -> value = ">>=";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 40;
myWord -> value = ">>";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 30;
myWord -> value = ">";
return myWord;
break;
case '<': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“<=”符号
{
myWord -> categoryCode = 33;
myWord -> value = "<=";
return myWord;
}
else if(ch == '<') // 是则说明是“<<”符号
{
getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“<<=”符号
{
myWord -> categoryCode = 49;
myWord -> value = "<<=";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 41;
myWord -> value = "<<";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 31;
myWord -> value = "<";
return myWord;
break;
case '!': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“!=”符号
{
myWord -> categoryCode = 29;
myWord -> value = "!=";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 36;
myWord -> value = "!";
return myWord;
break;
case '^': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“^=”符号
{
myWord -> categoryCode = 52;
myWord -> value = "^=";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 39;
myWord -> value = "^";
return myWord;
break;
case '&': getChar(); // 再读入一个&字符
if(ch == '=') // 是则说明是“&=”符号
{
myWord -> categoryCode = 50;
myWord -> value = "&=";
return myWord;
}
else if(ch == '&') // 是则说明是“&&”符号
{
myWord -> categoryCode = 34;
myWord -> value = "&&";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 37;
myWord -> value = "&";
return myWord;
break;
case '|': getChar(); // 再获取一个字符
if(ch == '=') // 是则说明是“|=”符号
{
myWord -> categoryCode = 51;
myWord -> value = "|=";
return myWord;
}
else if(ch == '|') // 是则说明是“||”符号
{
myWord -> categoryCode = 35;
myWord -> value = "||";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 38;
myWord -> value = "|";
return myWord;
break;
case '.': myWord -> categoryCode = 53;
myWord -> value = ".";
return myWord;
break;
case '\\':getChar(); // 再获取一个字符
if(ch == '\\') // 是则说明是“\”转义符号
{
myWord -> categoryCode = 55;
myWord -> value = "\\";
return myWord;
}
else if(ch == 'n')
{
myWord -> categoryCode = 20; // 换行符
myWord -> value = "\\n";
return myWord;
}
retractChar(); // 回退一个字符
myWord -> categoryCode = 55;
myWord -> value = "\\";
return myWord;
break;
case '(': myWord -> categoryCode = 56;
myWord -> value = "(";
return myWord;
break;
case ')': myWord -> categoryCode = 57;
myWord -> value = ")";
return myWord;
break;
case '[': myWord -> categoryCode = 58;
myWord -> value = "[";
return myWord;
break;
case ']': myWord -> categoryCode = 59;
myWord -> value = "]";
return myWord;
break;
case '{': myWord -> categoryCode = 60;
myWord -> value = "{";
return myWord;
break;
case '}': myWord -> categoryCode = 61;
myWord -> value = "}";
return myWord;
break;
case ':': myWord -> categoryCode = 62;
myWord -> value = ":";
return myWord;
break;
case ',': myWord -> categoryCode = 63;
myWord -> value = ",";
return myWord;
break;
case ';': myWord -> categoryCode = 64;
myWord -> value = ";";
return myWord;
break;
case '\'':myWord -> categoryCode = 65;
myWord -> value = "\'";
return myWord;
break;
case '"': myWord -> categoryCode = 66;
myWord -> value = "\"";
return myWord;
break;
case 10: myWord -> categoryCode = 20; // 换行符
myWord -> value = "\\n";
return myWord;
break;
case '\0':myWord -> categoryCode = 1000; // 此处即到了用户输入的#
myWord -> value = "OVER";
return myWord;
break;
default: myWord -> categoryCode = -1;
concatWord(); // 拼接单词
changeWordFormation(); // 改变单词输出形式
char reportError[] = " does not exist in this lexical system";
for(int i = 0; reportError[i] != '\0' ; ++ i)
{
wordBuffer[wordBufferIndex ++] = reportError[i];
}
wordBuffer[wordBufferIndex] = '\0';
myWord -> value = wordBuffer;
return myWord;
}
}
}
单词检索
/*从输入缓冲区读取一个字符到ch中*/
char getChar()
{
ch = inputBuffer[inputBufferIndex];
++ inputBufferIndex;
return ch;
}
/*去掉空格和\t*/
void removeBlankSymbol()
{
while(ch == ' ' || ch == '\t')
{
ch = inputBuffer[inputBufferIndex];
++ inputBufferIndex;
}
}
/*拼接单词*/
void concatWord()
{
wordBuffer[wordBufferIndex] = ch;
++ wordBufferIndex;
wordBuffer[wordBufferIndex] = '\0';
}
/*判断是否字母*/
int isLetter()
{
if(ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_') // 变量名可以有下划线
{
return 1;
}
return 0;
}
/*判断是否数字*/
int isDigit()
{
if(ch >= '0' && ch <= '9')
{
return 1;
}
return 0;
}
/*检索关键字数组*/
int retriveKeyword()
{
int i = 0;
while(strcmp(rwtab[i], _KEY_WORD_END))
{
if(!strcmp(rwtab[i], wordBuffer)) // 相等时strcmp函数返回0,相等于false
{
return i + 1;
}
++ i;
}
return 13;
}
/*回退一个字符*/
void retractChar()
{
-- inputBufferIndex;
}
特殊词法处理
/*将十进制转换成二进制*/
char* digitToBinary()
{
// 在<stdlib.h>头文件中有两个函数int atoi(char *p)和char *itoa(int value, char *string, int radix);
// 前者将装有整数的char数组转化为整数,后者将整数按位数分解依次存入char数组中。
itoa(atoi(wordBuffer), wordBuffer, 2);
return wordBuffer;
}
/*更改单词的输出形式,让自定义变量或无法识别的单词能以'单词名'的形式输出,与关键字作区分*/
char* changeWordFormation()
{
for(int i = wordBufferIndex - 1; i >= 0; -- i)
{
wordBuffer[i + 1] = wordBuffer[i];
}
wordBuffer[0] = '\'';
wordBuffer[++ wordBufferIndex] = '\'';
wordBuffer[++ wordBufferIndex] = '\0';
return wordBuffer;
}
如果文章内容出错或者您有更好的解决方法,欢迎到评论区指正和讨论!