编译原理——设计一个词法分析程序,实现对C程序设计语言的源程序(自定)的词法分析

源码在最下面


何云辉 CSDN

内容: 设计并实现一个词法分析器,实现对自己输入的类C语言源程序中的所有单词进行分类,指出其所属类型,实现简单的词法分析操作。

一.运行环境: win10        Microsoft Visual Studio 2010

二.原理:
       大多数程序语言的单词符号可以用正规文法来描述,由正规文法可以构造出相应的状态转换图,由此状态转换图就可以方便地识别单词符号,因此由状态转换图就不难构造出相应单词词法分析程序。
       依次读入源程序符号,对源程序进行单词切分和识别,直到源程序结束。

三.词法分析程序的输出:

  1. PL/0语言的单词符号分类:
    保留字、标识符、无符号整数、运算符、界符
  2. 构造状态转换图
    对于上述基本符号用以下正规文法规则进行描述

<无符号整数> ::= <数字>|<数字>
<标识符> ::= ··字母|<标识符>数字|<标识符>字母
<运算符> ::= +|-|*|/|=
<界符> ::= ,|;|{|}|(|)

       从中可以看出,以上每一条规则所构成文法是一个左线性文法,由此可以画出各自的状态转换图。
在这里插入图片描述
       因为词法分析程序是一个子程序,用来识别和分析各类单词符号,并且在识别出一个单词之后,将返回调用程序。故可以将各自状态转换图合并成一张状态转换图。

  1. 词法分析程序的构造
           有了状态转换图很容易写出词法分析程序,我们可以把词法分析程序作为一个子程序来构造,当语法分析程序需要一个单词符号时就调用这个子程序。每一次调用,词法分析程序就从输入串中识别出一个单词符号。

  2. 定义单词种类与词法规则
    ①标识符:首字符为字母或下划线,其后由字母、数字或下划线组成
    注:标识符长度不能超过20个字符。

         ②无符号整数:由十进制数字组成的一个序列。首位数字不能为0;
         注:无符号整数不带正负号。

         ③ 保留字:procedure、call、begin、end、and、or、if、else、int、float、const、for、new、public、while、long、static、do、read、char、return、using、void、break、include、switch、continue、try、private、var、odd、then、write
         注:保留字不区分大小写。

         ④ 运算符:+、-、 *、 /、 =、 <、> 、 <=、 >=、 !=、<>、 ==、#
         ⑤ 界符: ( ) , ;{ }

四.将词法分析工作分离的考虑:
    1.使整个编译程序的结构更简洁、清晰和条理化。
    2.编译程序的效率会改进。
    3.增强编译程序的可移植性。
       词法分析程序的主要功能是从字符流的源程序中识别单词,它要从左至右逐个字符地扫描源程序,因此它还可完成其他一些任务。比如,滤掉源程序中的注释和空白(由空格、制表符或回车换行字符引起的空白);又如,为了使编译程序能将发现的错误信息与源程序的出错位置联系起来,词法分析程序负责记录新读入的字符行的行号,以便行号与出错信息相关联;再如,在支持宏处理功能的源语言中,可以由词法分析程序完成其预处理等。

例如下面为一段C语言源程序:

#include <stdio.h>
void main()
{
int sum,n;
sum=0;
n=1
while(n<=100)
{
sum=sum+n;
n++;
}
printf("sum=",sum);
}

输入上述源程序后以“@”结束。运行输出:
在这里插入图片描述
五.说明:
    1.程序运行后可以自己输入C语言源程序,以@符号结束
    2. 程序运行过程中,会依次读入源程序符号,对源程序进行单词切分和识别,直到源程序结束。
    3.cout是iostream类的一个对象,它重载了<<运算符,支持内在的各种类型,使用起来更加方便灵活,所以在这次的项目中使用cout输出,没有用printf。

六.程序源码(用Microsoft Visual Studio 2010测试,真实有效,能正常运行)

#include <string.h>
#include <iostream>
#include <fstream> 
#include <cstring>
#include <conio.h>

using namespace std;

//看是否为数字
bool NUMBER(char A)
{
	if (A >= '0'&&A <= '9')
		return true;
	return false;
}
//看是否为字母
bool LETTER(char A)
{
	if ((A >= 'A'&&A <= 'Z') || A >= 'a'&&A <= 'z')
		return true;
	return false;
}

bool RESERVEDWORD(char *a)
{
	
	if (strcmp(a, "if") == 0||strcmp(a, "main") == 0||strcmp(a, "else") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "int") == 0||strcmp(a, "float") == 0||strcmp(a, "const") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "for") == 0||strcmp(a, "new") == 0||strcmp(a, "public") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "while") == 0||strcmp(a, "long") == 0||strcmp(a, "static") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "do") == 0||strcmp(a, "read") == 0||strcmp(a, "char") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "return") == 0||strcmp(a, "using") == 0||strcmp(a, "void") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "break") == 0||strcmp(a, "include") == 0||strcmp(a, "switch") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "continue") == 0||strcmp(a, "try") == 0||strcmp(a, "private") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "procedure") == 0||strcmp(a, "call") == 0||strcmp(a, "begin") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "end") == 0||strcmp(a, "and") == 0||strcmp(a, "or") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "var") == 0||strcmp(a, "odd") == 0||strcmp(a, "then") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	if (strcmp(a, "write") == 0)
	{
		cout << "保留字, '" << a << "'" << endl; 
		return true;
	}
	return false;
}
 
int main()
{
	//保留字;标识符;无符号整数;运算符;界符。
	char a;
	FILE *f;
	char CHAR;
	char TOKEN[120];
	f = fopen("write.txt", "w");   //创建文本文件code,保存输入的程序代码
	if (f == NULL)
	{
		cout << "con't create file" << endl;
		exit(0);
	}
	cout << "\n请输入源程序  以“@”结束" << endl;
	while ((a = getchar()) != '@')   //getchar() 是从控制台接收字符,注意只会接收一个字符
		fputc(a, f);
	fclose(f);
	cout << "over\n" << endl;
	f = fopen("write.txt", "r");
	while ((CHAR = fgetc(f)) != EOF)
	{
		
		while (CHAR != ' ')
		{
			if (LETTER(CHAR)) //如果以字母开头
			{
				int k = 0;
				do {
					TOKEN[k] = CHAR;
					k++;
				} 
				while ((CHAR = fgetc(f)) != EOF && CHAR != ' '&&LETTER(CHAR));
				if (NUMBER(CHAR))//如果以数字开头
				{
					do {
						TOKEN[k] = CHAR;
						k++;
					} 
					while ((CHAR = fgetc(f)) != EOF && CHAR != ' '&&NUMBER(CHAR));
				}
				TOKEN[k] = '\0';
				if (!(RESERVEDWORD(TOKEN)))		//判断标识符是否为保留字
					cout << "标识符, '" << TOKEN << "'" << endl;
 
			}
			if (NUMBER(CHAR))              //整数情况
			{
				int i = 0;
				do {
					TOKEN[i] = CHAR;
					i++;
				} 
				while ((CHAR = fgetc(f)) != EOF && CHAR != ' '&&NUMBER(CHAR));
				TOKEN[i] = '\0';
				if (!RESERVEDWORD(TOKEN))
					cout << "无符号整数, '" << TOKEN << "'" << endl;
			}
			
			if (CHAR == '+' || CHAR == '-' || CHAR == '*' || CHAR == '/' || CHAR == '>' || CHAR == '<' || CHAR == '#'|| CHAR == '=' || CHAR == '>=' || CHAR == '<=' || CHAR == '!='|| CHAR == '==')  //运算符情况
			{
				cout << "运算符, '" << CHAR << "'" << endl;
			}
			if (CHAR == ',' || CHAR == ';' || CHAR == '(' || CHAR == ')' || CHAR == '{' || CHAR == '}')   //界符情况
			{
				printf("界符: %c\n",CHAR);
			}
			break;
		}
	}
	fclose(f);
	printf("按任意键退出...");
	getch();
	return 0;
}

源码下载地址:https://download.csdn.net/download/hyh17808770899/12247590


何云辉 CSDN
发布了38 篇原创文章 · 获赞 15 · 访问量 1690

猜你喜欢

转载自blog.csdn.net/hyh17808770899/article/details/104849585