编译原理实验-简单词法分析器

一.实验内容

C++计算机语言的编译程序的词法分析部分实现。

从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。

为了简化程序的编写,有具体的要求如下:

  1. 空白符仅仅是空格、回车符、制表符。
  2. 代码是自由格式。
  3. 注释应放在花括号之内,并且不允许嵌套

保留字

特殊符号

if

+

else

-

while

*

扫描二维码关注公众号,回复: 14677997 查看本文章

do

/

main

=

int

<

float

{

double

}

return

;

const

void

continue

break

char

unsigned

enum

==

long

!=

switch

&&

case

||

unsigned

>

auto

>=

static

<=

其他

标识符

(首符号为字母或下划线,其他由一个或更多的字母或数字或下划线构成)

数字的属性包括三种:类型属性(整型、浮点型等)、进制属性(十进制、十六进制)

二.要求实现编译器的以下功能:

  1. 按规则拼单词,并转换成二元式形式
  2. 删除注释行
  3. 删除空白符 (空格、回车符、制表符)
  4. 显示源程序,在每行的前面加上行号,并且打印出每行包含的记号的二元形式
  5. 发现并定位错误。

词法分析进行具体的要求:

  1. 详细给出标识符、数字、字符和字符串的状态转换图
  2. 画出空白间隔符的状态转换图、换行符的状态装换图、注释(//、/*  */)的状态转换图。

(3) 词法分析的具体功能实现是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。

(4) 标识符和保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则是保留字,否则是一般标识符。

三.状态转换图

程序代码:

#include<iostream>
#include <string.h>
using namespace std;
char prog[1000], token[8], ch;    //prog为存放文件字符的数组、token为统一内部表示数组,ch为文件字符提取器
int p = 0, sym = 0, n;            //sym为状态标识
char filename[30];               //存储文件路径
FILE* fpin;                        //声明函数指针
const char* keyword[23] = { "if","else","while","do","main","int","float","double","return","const","void","continue","break","char","unsigned","enum","long","switch","case","unsigned","auto","static" };    //存储关键字
void GetToken();              //词法分析函数
int main()
{
	p = 0;
	cout << "请输入源文件名:";
	for (;;)
	{
		cin >> filename;                                 //输入文件名
		errno_t err = fopen_s(&fpin, filename, "r");     //errno_t是一个数据类型,实际上是整形,如果打开文件成功,fopen_s函数返回0
		if (err==0)  //用*fpin指针以只读方式打开文件 
			break;
		else cout << "文件路径错误!请输入源文件名:";
	}
	do
	{
		ch = fgetc(fpin);   //从fpin指针所指的文件中读取一个字符赋给字符变量ch
		prog[p++] = ch;
	} while (ch != EOF);    //ch与文件结束字符是否相等
	p = 0;
	do
	{
		GetToken();
		switch (sym)
		{
		case -1:                       //处理注释
		case -2: break;                 //词法分析出错
		default:cout << "(" << sym << "," << token << ")" << endl; break;    //输出词法分析结果
		}
	} while (ch != EOF);
}

//扫描器
void GetToken()
{
	for (n = 0; n < 8; n++)
	{
		token[n] = '\0';       //每次要置空 
	}
	n = 0;
	ch = prog[p++];
	/**预处理空格、换行、制表符**/
	while (ch == ' ' || ch == '\n' || ch == '\t')
	{
		ch = prog[p++];                    //接着往下读
	}
	/**处理标识符**/
	if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')
	{
		sym = 1;
		do {
			token[n++] = ch;
			ch = prog[p++];
		} while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_');
		sym = 2;
		/**处理关键字、保留字**/
		for (n = 0; n < 22; n++)
		{
			if (strcmp(token, keyword[n]) == 0)    //字符串比较 
			{
				sym = n + 3;    //对应状态转换标识
			}
		}
		p--;   //回退,之前p多加1 
	}
	/**处理注释及除法 **/
	else if (ch == '/')
	{
		ch = prog[p++];          //还不能确定是什么,所以要检测下一个
		/**下一个还是“ / ”或“*”确定是注释**/
		if (ch == '/')              
		{
			do {
				ch = prog[p++];
			} while (ch == '\n');
		}
		if (ch == '*')
		{
			do {
				ch = prog[p++];
			} while (ch == '/');
		}
		sym = -1;
		if (ch != '*' && ch != '/')   //是除法运算符
		{
			ch = prog[p - 2];         //回退到字符“/”
			p--;     
			sym = 31; token[0] = ch; 
		}
		return;
	}

	/**处理数字 **/
	else if (ch >= '0' && ch <= '9')      //整数 
	{
		do
		{
			token[n++] = ch;
			ch = prog[p++];
		} while (ch >= '0' && ch <= '9');
		if (ch != '.' && !isalnum(ch))       //isalnum用来判断该字符变量存的是不是数字和字母
		{
			sym = 25;
		}
		else if (ch == '.')            //浮点数 
		{
			do
			{
				token[n++] = ch;
				ch = prog[p++];
			} while (ch >= '0' && ch <= '9');
			sym = 26;
		}
		else if ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) //16进制 
		{
			do
			{
				token[n++] = ch;
				ch = prog[p++];
			} while (ch != 'H' && ch != 'h');      //遇到“H”和“h”结束
			sym = 27;
		}
		p--;     
		return;
	}
	else
	{
		switch (ch)
		{
		case '+':sym = 28; token[0] = ch; break;
		case '-':sym = 29; token[0] = ch; break;
		case '*':sym = 30; token[0] = ch; break;
		case '=':
			token[0] = ch;
			ch = prog[p++];      //探测下一个
			if (ch == '=')               //是“==”
			{
				sym = 37; token[1] = ch; break;
			}
			else { sym = 32; p--; break; }    //如果就是“=”,回退一个
		case '<':
			token[0] = ch;
			ch = prog[p++];
			if (ch == '=')
			{
				sym = 36; token[1] = ch; break;
			}
			else { sym = 33; p--; break; }
		case '>':
			token[0] = ch;
			ch = prog[p++];
			if (ch == '=')
			{
				sym = 35; token[1] = ch; break;
			}
			else { sym = 34; p--; break; }
		case '!':
			token[0] = ch;
			ch = prog[p++];
			if (ch == '=')
			{
				sym = 38; token[1] = ch; break;
			}
			else
			{
				sym = -2;
				p--;
			}
		case '{':sym = 39; token[0] = ch; break;
		case '}':sym = 40; token[0] = ch; break;
		case ';':sym = 41; token[0] = ch; break;
		case '(':sym = 42; token[0] = ch; break;
		case ')':sym = 43; token[0] = ch; break;
		case '"':sym = 44; token[0] = ch; break;
		case '&':
			token[0] = ch;
			ch = prog[p++];
			if (ch == '&')
			{
				sym = 45; token[1] = ch; break;
			}
			else
			{
				sym = -2;       //单个“&”字符,不识别
				p--;
			}
		case '|':
			token[0] = ch;
			ch = prog[p++];
			if (ch == '|')
			{
				sym = 46; token[1] = ch; break;
			}
			else
			{
				sym = -2;
				p--;
			}
		default: sym = -2; cout << "词法分析出错,请检查是否输入非法字符!" << endl; break;
		}
	}
}









参考文献:

《编译原理及实现》 姜淑娟、张辰、刘兵

猜你喜欢

转载自blog.csdn.net/qq_53162179/article/details/127455422
今日推荐