Redy词法识别--扫描器的设计与实现

返回文档首页


(一)简介

代码下载: git clone git://git.code.sf.net/p/redy/code redy-code


这一章的内容有:

扫描器的设计与实现


(二)扫描器的设计与实现

在上一章里面,我给介绍的怎么去实现一个高效的输入缓冲区,也对扫描器作了一个简单的介绍。扫描器对语言的源码进行扫描,识别出由这些字符序列组成的词文,用于在后面的语法分析。

在Redy中基本的词文由浮点数,整数,长整数,运算符,变量,字符串,关键字组成,扫描器的任务是从输入字符序列中识别出一个接一个的词文。
第一步,用一个结构体来表示扫描器
#define SCANNER_DEFALUT_LITERIAL_SIZE 128
struct scanner
{
	struct lex_file* s_lf;
	int s_cur_token;
	int s_line;
	char* s_cur_literal;
	int s_literial_size;

};


扫描器struct scanner总共有5个成员,其中

  1. s_lf指向扫描文件的输入缓冲区
  2. s_cur_token表示扫描器当前识别到的词文
  3. s_line表示扫描器当前描扫到源文件的每几行,用于描扫到错误词文时,能准确的给出错误的位置
  4. s_cur_literal表于当前识别到的词文的内容
  5. s_literial_size表于s_cur_literal的空间的大小
第二步,实现扫描器的创建与销毁

扫描器的创建有两种方式,第一种为给定一个文件名来创建扫描器,第二种是从已经打开的文件来创建扫描器,这两个函数都调用sc_init来初始化扫描器

static void sc_init(struct scanner* sc,struct lex_file* lf)
{
	sc->s_lf=lf;
	sc->s_cur_token=TOKEN_UNKOWN;
	sc->s_line=1;
	sc->s_cur_literal=(char*)malloc(SCANNER_DEFALUT_LITERIAL_SIZE);
	sc->s_literial_size=SCANNER_DEFALUT_LITERIAL_SIZE;
}


struct scanner* sc_create(char* filename)
{
	struct lex_file* lf=lf_create(filename);
	if(lf==NULL)
	{
		WARN("Open file[%s] Failed",filename);
		return NULL;
	}
	struct scanner* sc=(struct scanner*)malloc(sizeof(*sc));
	sc_init(sc,lf);
	return sc;

}
struct scanner* sc_stream_create(FILE* file)
{
	struct lex_file* lf=lf_stream_create(file);
	if(lf==NULL)
	{
		WARN("Create Scanner Failed");
		return NULL;
	}
	struct scanner* sc=(struct scanner*)malloc(sizeof(*sc));
	sc_init(sc,lf);
	return sc;
}

扫描器的销毁

void sc_destory(struct scanner* sc)
{
	lf_destory(sc->s_lf);
	free(sc->s_cur_literal);
	free(sc);

}

第三步,实现扫描器对源文件的扫描,并且返回扫描到的一个词文

扫描器对采用我们前面的状态机字符序列进行识别,状态机的开始状态为me_begin,扫描器从输入缓冲区得到一个字符,然后调用函数state_next得到当前状态的后继状态,后继状态有这么三种情况

  1. 终态,说明扫描器已经扫描到了一个词文,但是扫描器是采用最大识别的方法,所以扫描器还有往后扫描,看能不能扫描到长度更大的词文,如果不能,则需要回到该位置,所以需要调用函数lf_mark对当前位置进行标记,以便回到该位置。
  2. 错误状态(lex_state_err),说明扫描器扫描的字符序列已经不能构成一个词文了,所以这时需要停止扫描,然后在进行判断看以前时否到达过终态,如果没有则说明我们源程序中出现了错误的字符序例。如果到达过,则返回以前识别到的词文。
  3. 普通状态,继续往下扫描
在最后,扫描器把扫描的词文通过函数sc_set_cur_literial复制到成员s_cur_literal里面。


static void sc_set_cur_literial(struct scanner* sc,char* buf,int length)
{
	if(sc->s_literial_size<length+1)
	{
		char* new_space=(char*)malloc(length+1);
		free(sc->s_cur_literal);
		sc->s_cur_literal=new_space;
	}
	memcpy(sc->s_cur_literal,buf,length);
	sc->s_cur_literal[length]='\0';
}

int sc_next_token(struct scanner* sc)
{
	struct lex_file* lf=sc->s_lf;

	char cur;
	char next=lf_next_char(lf);
	struct state* cur_state=&me_begin;
	struct state* next_state;
	struct state* finnal_state=NULL;
	while(1)
	{
		cur=next;


		if(cur==EOF)
		{
			sc->s_cur_token=TOKEN_EOF;
			break;
		}
		if(cur=='\n')
		{
			sc->s_line++;
		}
		next_state=state_next(cur_state,cur);
		if(next_state==&lex_state_err)
		{
			if(finnal_state==NULL)
			{
				sc->s_cur_token=TOKEN_ERR;
			}
			else
			{
				sc->s_cur_token=finnal_state->s_token;
			}
			break;
		}


		if(state_final(next_state))
		{
			finnal_state=next_state;
			lf_mark(lf);
		}
		next=lf_next_char(lf);
		cur_state=next_state;
	}

	sc_set_cur_literial(sc,lf->l_buf+lf->l_begin,lf->l_mark-lf->l_begin);
	lf_reset_to_mark(lf);

	return sc->s_cur_token;
}


第四步,写一个小程序来测试的扫描器

int main(int argc,char** argv)
{
	if(argc<2)
	{
		printf("usage %s [filename]\n",argv[0]);
		exit(0);
	}
	struct scanner* sc=sc_create(argv[1]);

	int token;
	int i=0;
	while(1)
	{
		i++;
		if(i%5==0)
		{
			printf("\n");
		}
		token=sc_next_token(sc);
		if(token==TOKEN_EOF)
		{
			break;
		}
		if(token==TOKEN_ERR)
		{
			goto err;
		}
		if(token==TOKEN_ID)
		{
			if(symbol_type(sc_token_string(sc))==TOKEN_ID)
			{
				printf("{variable,%s}  ",sc_token_string(sc));
			}
			else
			{
				printf("{keyword,%s}  ",sc_token_string(sc));
			}
			continue;
		}
		if(token==TOKEN_ANNO)
		{
			continue;

		}
		if(token==TOKEN_WS)
		{
			continue;
		}
		if(token==TOKEN_NEWLINE)
		{
			printf("{newline}  ");
			continue;
		}
		printf("{%s,%s}  ",token_name(token),sc_token_string(sc));
	};

	return 0;
err:
	printf("err token at line %d\n",sc->s_line);
	return -1;
}



运行结果:

现在我们用扫描器来扫描Redy的一段小程序,来看看效果怎么样。

Redy程序:

a=random()
b=random()
if a+b/2==557
	a.inc()
	if a/2
		a.dec()
	else
		a.inc()
	end
elif a+b/3==6
	a.dec()
else 
	b=a/2
end
print a
print b


运行结果



以上程序大家可以在文件夹tutorial/lexical/scanner下面找到,对程序编译后,可以在bin目录下找到可执行文件,测试数据放在debug_data文件夹下面














【本科毕业设计项目 源码+说明书+PPT】 随着当今时代新型网络技术的迅速兴起,以及第三产业的蓬勃发展,互联网早已成为社会生活当中不可或缺的一部分,而网络安全问题也越来越不容忽视,越来越多的黑客通过利用系统漏洞实施安全攻击、偷盗、诈骗等违法行为,这就需要我们利用先进的技术手段去尽早、准确地检测和解决这些安全漏洞和安全隐患。但是上述这些扫描器一般都属于C/S架构,变更不够灵活,维护与管理的难度较大。当然当前也有很多款网页版扫描器,但大多系统功能较为单一,且缺少对用户的管理和扫描结果的保存和处理,仅支持一次性在线扫描。 针对这一背景,开发了本系统。本系统的特色主要有以下几个方面。 (1)适用于Windows操作系统环境下,采用了B/S架构,用户只需按照要求注册登录即可使用,方便快捷,分布性强,便于维护和管理,可扩展性高。 (2)界面较简单、直观、易操作,对系统用户的专业水平要求不高,便于专业知识储备较少的普通用户接受。并且本系统中增设了仿命令行功能,既可以帮助用户实现对本系统功能的快速了解以及快速使用,也可以调动喜爱命令行的使用者的兴趣。 (3)系统区分了管理员用户和普通用户,管理员用户拥有较多的特权,实现了较简单的基于角色的访问控制,增强了系统安全性和可管理性。 (4)支持用户上传及管理漏洞扫描所用的第三方插件,可以提高漏洞扫描的效率和性能,提高了系统功能的可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值