返回文档首页
(一)简介
代码下载: git clone git://git.code.sf.net/p/redy/code redy-code
这一章的内容有:
- 运算符号的识别
- 状态矩阵的缺点
- 新的识别方法--状态链
(二)运算符号的识别
(1)简介
在Redy中,总其有这么一些运算符号:
'(' ')' '[' ']' '.' ',' '+' '-' '~' '*' '/' '%' '<<' '>>' '<' '>' '<=' '>=' '==' '!=' '&' '^' '|' 'and' 'or' 'not' '=' '+=' '-=' '*=' '/=' '%=' '&=' '^=' '|=' '>>=' '<<='
其中运算符号 'and','or','not' 的属于通过变量识别的内容。
(2)状态机
其中蓝色状态表示终态,总共有34个。开始状态为OperatorBegin。状态图中的状态总共有36个
(3)状态矩阵
对于运算符来说,输入类型有这么19种,加上除以下字符的算作一种类型,那么总其有20种
'(' ')' '[' ']' '.' ',' '+' '-' '~' '*' '/' '%' '<' '>' '=' '!' '&' '^' '|'
如果用状态矩阵来处理的话,就需要维护一个36*20的数组,处理起来是一件很吓人的事情,如果不小心输错数据,要找出错误很难。在语言识别中,状态矩阵是一种非常常用的方法,前面的字符串,整数,浮点数,变量都是用状态矩阵的方法来识别,但对于运算符,相比前面几个,状态与输入类型的数量都要大许多,虽然运算符也可以用状态矩阵来处理,带来的一定的难度。
下面我介绍一种新的识别方法。
(4)新的识别方法--状态链
用一个结构来表于为:从上面的状态图,可以看出,虽然输入类型有20种,但是除开始状态OperatorBegin能在多种输入类型下,发生状态转移,其于的状态只能在一个或两个的输入类型下发生状态转移,例如,对于状态图中的状态LessThan来说,他只能在字符'<'与字符'='下发生状态转移,其于的字符都不能让LessThan发生状态转换
对于状态LessThan来说,输入类型只有3种,后继状态有两个。在以前使用状态矩阵方法的时候,是把状态的信息,分开存储。而新的方法是把与状态所有相关的信息存储在一起。
对于一个状态来说,与之相关的信息有:输入类型的数量,后继状态的数量,以及在特定的输入下到达的后继状态。
其中结构体的两个成员s_input_map,与s_input_func的都是用于判断字符的输入类型,只有在s_input_map为空时,在使用s_input_func。当在输入类型只有一种或者两种时,使用s_input_func,在输入类型很多的时候,我们还是使用状态射数组。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_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中是使用的状态链的方法进行词法识别的。
虽然,到目前为止,总共介绍了变量,字符串,注释,整数,运算符的识别方法,这几个词文识别起来都非常容易,后面的文章会给大家介绍怎么去识别整数,浮点数。