编译原理实践: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;
}

如果文章内容出错或者您有更好的解决方法,欢迎到评论区指正和讨论!

猜你喜欢

转载自blog.csdn.net/weixin_40589192/article/details/106927940