关于正则的语法方面,就做到这吧。下一步是生成nfa,进而dfa,以及dfa的最简化。等下一个版本出来的话,估计要好久了。
1 #include <stdio.h> 2 #include <malloc.h> 3 #include <string.h> 4 //这个版本在上一个版本的基础上,添加了?+这两个操作符,这两个操作符都是单目操作符,跟*操作符同一级别 5 //因此碰到这两个操作符的时候,跟*操作符一样的处理 6 //除了新加的操作符外,还添加了字符集,模式为a-z这种,但是字符集只能通过假名来引用 7 //因此如果想使用字符集,则必须先通过假名来定义这个字符集,然后在后续的正则表达式中通过假名 8 //来使用这个字符集 9 //因此这里需要修改reg_opera_type,以及输入处理函数。 10 //注意这里的字符集定义的识别是通过判断正则的第二个字符是否是-来作用的,所以如果在其他类型的正则 11 //表达式中想使用-符号的话,千万不要把它放在第二个字符里面,因为这样会产生错误 12 //虽然可以去判断正则的长度来消除这种错误,不过我就懒得管了 13 //想改的话,读者自己去改吧 14 int token[100]; 15 int token_pointer; 16 char reg_operator[100]; 17 int reg_operator_pointer=0; 18 int name_number=0; 19 //注意这里name_number是全局的,而其他的几个栈及变量都是每个子表达式都复用的 20 //其实那些变量及栈可以每次申请一个,为了节省空间,我就懒得管了,直接复用。 21 int input_pointer=0; 22 char reg_input[40];//由于命名表达式的存在,考虑加长字符,其实随便多大都可以处理。 23 enum reg_opera_type 24 { 25 parenthesis=1,//对应括号 26 closure,//对应闭包运算符 27 one_or_more,//对应+运算符 28 maybe_exist,//对应?运算符 29 cat,//对应连接运算符 30 or,//对应选择运算符 31 alias,//对应命名子表达式 32 set_of_char,//对应字符集 33 literal_char//单字符 34 };//正则节点类型 35 typedef struct _reg_pattern 36 { 37 enum reg_opera_type type; 38 union 39 { 40 struct//对应二元操作符 41 { 42 int left; 43 int right; 44 }; 45 struct//对应假名 46 { 47 int origin_number; 48 int hash_table_number; 49 }; 50 struct//对应字符集 51 { 52 char begin; 53 char end; 54 }; 55 char value;//对应字符值 56 int sub;//对应闭包运算符和括号运算符以及问号运算符及+运算符 57 }; 58 }reg_pattern;//差不多当作抽象语法树吧 59 reg_pattern reg_pattern_table[100]; 60 //当前的hash只使用加法hash加上一个模97,因为考虑到97是质数 61 typedef struct _hash_table//hash表类型 62 { 63 enum _state 64 { 65 empty=0, 66 in_use, 67 deleted 68 }state; 69 char* name_of_alias; 70 int reg_pattern_number; 71 }hash_table; 72 hash_table simple_hash_table[100]; 73 int look_up_hash_table(char* source)//查询一个名字是否在hash表中 74 { 75 int string_length; 76 int counter; 77 int index; 78 int result; 79 string_length=strlen(source); 80 result=0; 81 for(index=0;index<string_length;index++) 82 { 83 result+=*(source+index); 84 } 85 result=result%97; 86 counter=0; 87 while(counter<97)//顶多查询97次,如果查询达到了97次,则说明当前hash表中不存在这个字符串 88 { 89 if(simple_hash_table[result].state==empty)//如果当前位置为空,说明不存在这个节点,直接返回-1 90 { 91 return -1; 92 } 93 else 94 { 95 if(simple_hash_table[result].state==deleted)//如果为删除状态,则继续向下寻找,注意索引溢出的处理 96 { 97 result=(result+1)%97; 98 counter++; 99 } 100 else 101 { 102 if(strcmp(source,simple_hash_table[result].name_of_alias)==0)//如果出于使用状态,则开始比较 103 { 104 return result;//名字相同则返回索引 105 } 106 else//不同则继续向下寻找 107 { 108 result+=(result+1)%97; 109 counter++; 110 } 111 } 112 } 113 } 114 return -1;//如果找了达到了97次,则返回-1 115 } 116 117 118 119 120 int insert_hash_table(char* source,int index_of_reg_pattern) 121 { 122 int string_length; 123 int counter; 124 int index; 125 int result; 126 string_length=strlen(source); 127 result=0; 128 for(index=0;index<string_length;index++) 129 { 130 result+=*(source+index); 131 } 132 result=result%97; 133 counter=0; 134 while(counter<97) 135 { 136 if(simple_hash_table[result].state==in_use)//如果在使用中,则继续下次寻找 137 { 138 result=(result+1)%97; 139 counter++; 140 } 141 else//如果可用,则使用 142 { 143 simple_hash_table[result].state=in_use; 144 simple_hash_table[result].name_of_alias=source; 145 simple_hash_table[result].reg_pattern_number=index_of_reg_pattern; 146 return result; 147 } 148 } 149 return -1;//已经满了 ,插入失败 150 } 151 152 int is_begin_of_token() 153 //判断输入字符是否可以当作一个token的开始符号,这样是为了处理非显示的连接符 154 //由于转义字符也是可以当作token的开始字符,所以也是返回1 155 { 156 switch(reg_input[input_pointer]) 157 { 158 case '*': 159 case '?': 160 case '+': 161 case '|': 162 case '.': 163 case ']': 164 case ')': 165 case '