Redy词法识别--运算符的识别

本文介绍了Redy中运算符识别的过程,从状态矩阵的缺点出发,提出了一种新的识别方法——状态链。详细阐述了状态链的概念、构造方法以及运行结果,并指出状态链在处理状态与输入类型较多但状态转移限制较窄的情况下的优势。在Redy中,词法识别采用了状态链方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

返回文档首页


(一)简介

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


这一章的内容有:
  1. 运算符号的识别
  2. 状态矩阵的缺点
  3. 新的识别方法--状态链

(二)运算符号的识别

(1)简介

在Redy中,总其有这么一些运算符号:

	'(' ')' '[' ']' '.' ','
	'+'  '-'  '~'
	'*' '/' '%'
	'<<' '>>'
	'<' '>' '<=' '>='
	'==' '!='
	'&' '^' '|'
	'and' 'or' 'not'
	'=' '+=' '-=' '*=' '/=' '%='
	'&=' '^=' '|=' '>>=' '<<='

其中运算符号 'and','or','not' 的属于通过变量识别的内容。

(2)状态机



其中蓝色状态表示终态,总共有34个。开始状态为OperatorBegin。
状态图中的状态总共有36个


(3)状态矩阵

对于运算符来说,输入类型有这么19种,加上除以下字符的算作一种类型,那么总其有20种
'('    ')'    '['    ']'    '.'    ','
'+'    '-'    '~'    '*'    '/'    '%'
'<'    '>'    '='    '!'
'&'    '^'    '|'


  如果用状态矩阵来处理的话,就需要维护一个36*20的数组,处理起来是一件很吓人的事情,如果不小心输错数据,要找出错误很难。
  在语言识别中,状态矩阵是一种非常常用的方法,前面的字符串,整数,浮点数,变量都是用状态矩阵的方法来识别,但对于运算符,相比前面几个,状态与输入类型的数量都要大许多,虽然运算符也可以用状态矩阵来处理,带来的一定的难度。

  下面我介绍一种新的识别方法。

(4)新的识别方法--状态链

  从上面的状态图,可以看出,虽然输入类型有20种,但是除开始状态OperatorBegin能在多种输入类型下,发生状态转移,其于的状态只能在一个或两个的输入类型下发生状态转移,例如,对于状态图中的状态LessThan来说,他只能在字符'<'与字符'='下发生状态转移,其于的字符都不能让LessThan发生状态转换

  对于状态LessThan来说,输入类型只有3种,后继状态有两个。在以前使用状态矩阵方法的时候,是把状态的信息,分开存储。而新的方法是把与状态所有相关的信息存储在一起。

  对于一个状态来说,与之相关的信息有:输入类型的数量,后继状态的数量,以及在特定的输入下到达的后继状态。

     用一个结构来表于为:

typedef int (*input_map)(char); /*输入类型映射函数*/

struct state
{
	char* s_name;				/*状态名称*/
	int s_token;				/*词文类型,用于后面的语法识别*/
	int s_inputs_num;			/*输入类型的数量*/

	char* s_input_map;          /*输入类型的映射数组,如果该值为0,则用s_input_func函数指针来判断输入类型*/
	input_map s_input_func;     /*输入类型的函数指针*/

	struct state** s_targets;   /*后继状态数组*/
	int s_final;                /*该状态是否终态*/
};


  其中结构体的两个成员s_input_map,与s_input_func的都是用于判断字符的输入类型,只有在s_input_map为空时,在使用s_input_func。当在输入类型只有一种或者两种时,使用s_input_func,在输入类型很多的时候,我们还是使用状态射数组。

  成员s_targets保存所有的后继状态,例如输入类型为1的字符的后继状态为s_tartgets[1],输入类型为n的字符的后继状态为s_targets[n]。

  我们用一个函数state_next来帮助我们字符的后继状态

/*返回状态s在识别字符c后,转移到的状态*/
static inline struct state* state_next(struct state* s,char c)
{
	int input_type;
	if(s->s_input_map)			/*如果该状态有输入类型映射数组,则用该忽略s_input_func*/
	{
		input_type=s->s_input_map[c];   /*得到输入类型*/
	}
	else                        /*如果类型映射数组不存在,则用该函数指针获取输入类型*/
	{
		input_type=s->s_input_func(c);  /*得到输入类型*/
	}
	return s->s_targets[input_type];    /*返回后继状态*/
}


  特别要说时一下,如果函数state_next返回的状态为lex_state_err,则表明状态s,并不能在字符c下发生状态转移,这个时候,我们就需要进行错误处理。
  这时我们的驱动程序有一点小小的变化

/*返回值-1则表示识别错误,其它值表示能识别的最大位置*/
int driver(struct state* s,char* str,struct state* info)
{
	int length=strlen(str);
	int i;
	struct state* cur_st=s;     /*当前状态*/
	struct state* next_st;      /*下一状态*/
	int last_final=-1;
	for(i=0;i<length;i++)
	{
		next_st=state_next(cur_st,str[i]);    /*获得下一状态*/
		if(next_st==&lex_state_err)           /*如果返回状态为lex_state_err,则表明需要错误处理*/
		{
			return last_final;
		}
		else
		{
			cur_st=next_st;
			if(cur_st->s_final)           /*判继该状态是否为终态*/
			{
				last_final=i;                /*如果为终态,则保存识别的信息*/
				*info=*cur_st;
			}
		}
	}
	return last_final;
};


(5)构造状态链

虽然采用状态链的方法,不再用去维护一个34*20的状态矩阵,但是要维持34个状态的信息还是挺多的,不过如果仔细观察状态图,会发现,有很多状态者非常相似。
(a)状态Comma , Period , Reverse, L_RB ,  R_RB, L_SB , R_SB,都是从OperatorBegin转换过来,并且没有后继状态,我们用宏定义处理,以减少复杂度。

extern struct state lex_state_err; 
extern struct state* lex_state_err_array[];
extern char input_map_other[];

#define INIT_FINAL_STATE(name,token) {#name,token,1,input_map_other,0,lex_state_err_array,1}

#define OP_FINAL_STATE(name,token) \
	struct state name=INIT_FINAL_STATE(name,token)  


OP_FINAL_STATE(op_comma,TOKEN_COMMA);
OP_FINAL_STATE(op_period,TOKEN_PERIOD);
OP_FINAL_STATE(op_l_rb,TOKEN_L_RB);
OP_FINAL_STATE(op_r_rb,TOKEN_R_RB);
OP_FINAL_STATE(op_l_sb,TOKEN_L_SB);
OP_FINAL_STATE(op_r_sb,TOKEN_R_SB);
OP_FINAL_STATE(op_reverse,TOKEN_REVERSE);



(b)状态(NotEqualBegin,NotEqual), (BitsAnd,BitsAndAssign),(BitsOr,BitsOrAssign),(BitsXor,BitsXorAssign), (multiply,multiplyAssign),(Mod,ModAssign), (Plus,PlusAssign), (Divide,DivideAssign), (Assign,Equal) 这几9组状态基本上一样,所以也用宏定义。
static char op_input_map_equal[ASCII_NUM]=
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

};


#define __INIT_OPERATOR_ASSIGN_TRANSLATE(name,token,target_array,final) \
	struct state name= \
{ \
#name, \
	token,2,op_input_map_equal,0,target_array,final,\
}\

#define INIT_OPERATOR_ASSIGN_TRANSLATE(first,token1,final,last,token2) \
	static struct state* operator_private_name_state_array_##first[]=\
	{&lex_state_err,&last}; \
	__INIT_OPERATOR_ASSIGN_TRANSLATE(first,token1,operator_private_name_state_array_##first,final); \
	OP_FINAL_STATE(last,token2) 



INIT_OPERATOR_ASSIGN_TRANSLATE(op_not_equal_begin,TOKEN_UNKOWN,0,op_not_equal,TOKEN_NOT_EQUAL);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_bits_and,TOKEN_BITS_AND,1,op_assign_bits_and,TOKEN_A_BITS_AND);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_bits_or,TOKEN_BITS_OR,1,op_assign_bits_or,TOKEN_A_BITS_OR);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_bits_xor,TOKEN_BITS_XOR,1,op_assign_bits_xor,TOKEN_A_BITS_XOR);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_multiply,TOKEN_MUL,1,op_assign_multiply,TOKEN_A_MUL);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_mod,TOKEN_MOD,1,op_assign_mod,TOKEN_A_MOD);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_minus,TOKEN_MINUS,1,op_assign_minus,TOKEN_A_MINUS);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_plus,TOKEN_PLUS,1,op_assign_plus,TOKEN_A_PLUS);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_divide,TOKEN_DIVIDE,1,op_assign_divide,TOKEN_A_DIVIDE);
INIT_OPERATOR_ASSIGN_TRANSLATE(op_assign,TOKEN_ASSIGN,1,op_equal,TOKEN_EQUAL);



(c)状态(LessThan,LeftShift,LessEqual,LeftShiftAssign), (GreaterThan,GreaterEqual,RightShift,RightShiftAssign),这两组状态也相似,下面我们再看这8个状态的程序
INIT_OPERATOR_ASSIGN_TRANSLATE(op_right_shift,TOKEN_RS,1,op_assign_right_shift,TOKEN_A_RS);
OP_FINAL_STATE(op_greater_equal,TOKEN_GE);
static struct state* op_greater_than_targets[]=
{
	&lex_state_err,
	&op_greater_equal,
	&op_right_shift,
};


int op_input_map_greater_than(char c)
{
	if(c=='=') return 1;
	else if(c=='>') return 2;
	else return 0;
}


struct state op_greater_than=
{
	"op_greater_than",
	TOKEN_GT,
	3,
	0,
	op_input_map_greater_than,
	op_greater_than_targets,
	1,
};

INIT_OPERATOR_ASSIGN_TRANSLATE(op_left_shift,TOKEN_LS,1,op_assign_left_shift,TOKEN_A_LS);
OP_FINAL_STATE(op_less_equal,TOKEN_LE);

static struct state* op_less_than_targets[]=
{
	&lex_state_err,
	&op_less_equal,
	&op_left_shift,
};
int op_input_map_less_than(char c)
{
	if(c=='=') return 1;
	else if (c=='<') return 2;
	else return 0;
}



struct  state op_less_than=
{
	"op_less_than",
	TOKEN_LT,
	3,
	0,
	op_input_map_less_than,
	op_less_than_targets,
	1,
};





(d)最后还有一个状态BeginState,由于BeginState可以接受20种不同类型的输入,所以对于BeginState,我们使用输入映射数组来获取输入的类型。
enum OP_INPUT_TYPE
{
	OP_OTHER=0,
	OP_COMMA,
	OP_PERIOD,
	OP_REVERSE,
	OP_L_RB,
	OP_R_RB,
	OP_L_SB,
	OP_R_SB,
	OP_EXCLAMATION,
	OP_AMPERSAND,
	OP_BAR,
	OP_CARET,
	OP_STAR,
	OP_PERCENT,
	OP_MINUS,
	OP_PLUS,
	OP_DIVIDE,
	OP_EQUAL,
	OP_GREATER,
	OP_LESS,
	OP_INPUT_NUM
};

char op_input_map_begin[ASCII_NUM]=
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,8,0,0,0,13,9,0,4,4,12,15,1,14,2,16,0,0,0,0,0,0,0,0,0,0,0,0,19,17,18,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,7,11,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,3,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

struct state* op_begin_targets[]=
{
	&lex_state_err,
	&op_comma,
	&op_period,
	&op_reverse,
	&op_l_rb,
	&op_r_rb,
	&op_l_sb,
	&op_r_sb,
	&op_not_equal_begin,
	&op_bits_and,
	&op_bits_or,
	&op_bits_xor,
	&op_multiply,
	&op_mod,
	&op_minus,
	&op_plus,
	&op_divide,
	&op_assign,
	&op_greater_than,
	&op_less_than,
};
struct state op_begin=
{
	"oper_begin",
	TOKEN_UNKOWN,
	OP_INPUT_NUM,
	op_input_map_begin,
	0,
	op_begin_targets,
	0
};




(6)运行结果

            

关于运算符识别,可以在tutorial/lexical/ssl_operator文件夹中找到.

(7)结尾语

  到现在为上,总共介绍了两种识别方法,一种为状态矩阵,一种为状态链,状态矩阵适合于状态与输入类型都较少的状态机。状态链适合于状态状态数量与输入类型较多,但是大部分状态都只在较少的输入状态下发生状态转移的状态机。在Redy中是使用的状态链的方法进行词法识别的。

  虽然,到目前为止,总共介绍了变量,字符串,注释,整数,运算符的识别方法,这几个词文识别起来都非常容易,后面的文章会给大家介绍怎么去识别整数,浮点数。



返回文档首页






























private void pictureBox1_Paint(object sender, PaintEventArgs e) { if (currentTransform != null) { e.Graphics.Transform = currentTransform; // 直接应用预计算矩阵 } e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // 计算实际绘制区域 var clientRect = pictureBox1.ClientRectangle; // 自动计算数据边界(需要预先获取points的实际范围) float dataMinX = points.Min(p => p.fiveX); float dataMaxX = points.Max(p => p.fiveX); float dataMinY = points.Min(p => p.sexY); float dataMaxY = points.Max(p => p.sexY); // 计算缩放比例 float scaleX = clientRect.Width / (dataMaxX - dataMinX); float scaleY = clientRect.Height / (dataMaxY - dataMinY); float zoomLevel = Math.Min(scaleX, scaleY) * 0.9f; // 保留10%边距 // 计算偏移量 float offsetX = -dataMinX * zoomLevel + (clientRect.Width - (dataMaxX - dataMinX) * zoomLevel) / 2; float offsetY = -dataMinY * zoomLevel + (clientRect.Height - (dataMaxY - dataMinY) * zoomLevel) / 2; e.Graphics.TranslateTransform(offsetX, offsetY); e.Graphics.ScaleTransform(zoomLevel, zoomLevel); currentTransform = e.Graphics.Transform; foreach (var point in points) { // 限制缩放级别最小值 float safeZoom = Math.Max(zoomLevel, 0.001f); float pointSize = Math.Min(4f / safeZoom, 100f); // 限制点尺寸最大值 // 计算坐标(确保用 float 运算) float blueX = point.fiveX - pointSize / 2; float blueY = point.sexY - pointSize / 2; // 检查坐标有效性 if (float.IsInfinity(blueX) || float.IsInfinity(blueY) || float.IsNaN(blueX) || float.IsNaN(blueY)) { continue; } // 绘制蓝色点 e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); // 同理处理红色点 float redX = point.eightX - pointSize / 2; float redY = point.nineY - pointSize / 2; if (float.IsInfinity(redX) || float.IsInfinity(redY) || float.IsNaN(redX) || float.IsNaN(redY)) { continue; } e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize); } if (currentTransform != null) { // 应用相同的变换矩阵 e.Graphics.Transform = currentTransform; // 在此绘制内容,坐标会自动适配 e.Graphics.DrawLine(Pens.Black, dataMinX, dataMinY, dataMaxX, dataMaxY); } }// 绘制蓝色点 e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize);OverflowException 怎么解决
最新发布
03-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值