您的位置:首页 > 其它

最初步的正则表达式引擎:增加了字符集表示和?运算符和+运算符

2013-06-23 15:33 399 查看
关于正则的语法方面,就做到这吧。下一步是生成nfa,进而dfa,以及dfa的最简化。等下一个版本出来的话,估计要好久了。

#include <stdio.h>
#include <malloc.h>
#include <string.h>
//这个版本在上一个版本的基础上,添加了?\+这两个操作符,这两个操作符都是单目操作符,跟*操作符同一级别
//因此碰到这两个操作符的时候,跟*操作符一样的处理
//除了新加的操作符外,还添加了字符集,模式为a-z这种,但是字符集只能通过假名来引用
//因此如果想使用字符集,则必须先通过假名来定义这个字符集,然后在后续的正则表达式中通过假名
//来使用这个字符集
//因此这里需要修改reg_opera_type,以及输入处理函数。
//注意这里的字符集定义的识别是通过判断正则的第二个字符是否是-来作用的,所以如果在其他类型的正则
//表达式中想使用-符号的话,千万不要把它放在第二个字符里面,因为这样会产生错误
//虽然可以去判断正则的长度来消除这种错误,不过我就懒得管了
//想改的话,读者自己去改吧
int token[100];
int token_pointer;
char reg_operator[100];
int reg_operator_pointer=0;
int name_number=0;
//注意这里name_number是全局的,而其他的几个栈及变量都是每个子表达式都复用的
//其实那些变量及栈可以每次申请一个,为了节省空间,我就懒得管了,直接复用。
int input_pointer=0;
char reg_input[40];//由于命名表达式的存在,考虑加长字符,其实随便多大都可以处理。
enum reg_opera_type
{
parenthesis=1,//对应括号
closure,//对应闭包运算符
one_or_more,//对应+运算符
maybe_exist,//对应?运算符
cat,//对应连接运算符
or,//对应选择运算符
alias,//对应命名子表达式
set_of_char,//对应字符集
literal_char//单字符
};//正则节点类型
typedef struct _reg_pattern
{
enum reg_opera_type type;
union
{
struct//对应二元操作符
{
int left;
int right;
};
struct//对应假名
{
int origin_number;
int hash_table_number;
};
struct//对应字符集
{
char begin;
char end;
};
char value;//对应字符值
int sub;//对应闭包运算符和括号运算符以及问号运算符及+运算符
};
}reg_pattern;//差不多当作抽象语法树吧
reg_pattern reg_pattern_table[100];
//当前的hash只使用加法hash加上一个模97,因为考虑到97是质数
typedef struct _hash_table//hash表类型
{
enum _state
{
empty=0,
in_use,
deleted
}state;
char* name_of_alias;
int reg_pattern_number;
}hash_table;
hash_table simple_hash_table[100];
int look_up_hash_table(char* source)//查询一个名字是否在hash表中
{
int string_length;
int counter;
int index;
int result;
string_length=strlen(source);
result=0;
for(index=0;index<string_length;index++)
{
result+=*(source+index);
}
result=result%97;
counter=0;
while(counter<97)//顶多查询97次,如果查询达到了97次,则说明当前hash表中不存在这个字符串
{
if(simple_hash_table[result].state==empty)//如果当前位置为空,说明不存在这个节点,直接返回-1
{
return -1;
}
else
{
if(simple_hash_table[result].state==deleted)//如果为删除状态,则继续向下寻找,注意索引溢出的处理
{
result=(result+1)%97;
counter++;
}
else
{
if(strcmp(source,simple_hash_table[result].name_of_alias)==0)//如果出于使用状态,则开始比较
{
return result;//名字相同则返回索引
}
else//不同则继续向下寻找
{
result+=(result+1)%97;
counter++;
}
}
}
}
return -1;//如果找了达到了97次,则返回-1
}

int insert_hash_table(char* source,int index_of_reg_pattern)
{
int string_length;
int counter;
int index;
int result;
string_length=strlen(source);
result=0;
for(index=0;index<string_length;index++)
{
result+=*(source+index);
}
result=result%97;
counter=0;
while(counter<97)
{
if(simple_hash_table[result].state==in_use)//如果在使用中,则继续下次寻找
{
result=(result+1)%97;
counter++;
}
else//如果可用,则使用
{
simple_hash_table[result].state=in_use;
simple_hash_table[result].name_of_alias=source;
simple_hash_table[result].reg_pattern_number=index_of_reg_pattern;
return result;
}
}
return -1;//已经满了 ,插入失败
}

int is_begin_of_token()
//判断输入字符是否可以当作一个token的开始符号,这样是为了处理非显示的连接符
//由于转义字符也是可以当作token的开始字符,所以也是返回1
{
switch(reg_input[input_pointer])
{
case '*':
case '?':
case '+':
case '|':
case '.':
case ']':
case ')':
case '\0':
return 0;
default:
return 1;
}
//这里两个闭括号的存在都是为了处理非显示的连接符
//而把开括号移动到外部的switch之中
//主要是为了防止括号嵌套引起的错误
}
tackle_invisible_cat()//如果后面跟的是开括号(包括普通括号和假名括号)或者字符,则这时候要去处理隐式连接符
{
if(is_begin_of_token())
{
tackle_cat();
}

}

void tackle_or()//处理并操作符,这个处理完之后,输入指针也会后移
{
if(reg_operator_pointer!=0)//如果操作符栈中已经有操作符了
{
if(reg_operator[reg_operator_pointer-1]!='(')//括号另外说
{
name_number++;
if(reg_operator[reg_operator_pointer-1]=='.')
//如果前面的优先级比当前的高,则处理前面的优先级
{
printf("name%d is concat of name%d and name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
}
else
//这里处理的是相同优先级的情况,其实这里可以与前面的合并的,只不过打印信息不同
{
printf("name%d is  name%d or name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
}
token[token_pointer-2]=name_number;
token_pointer--;
reg_operator[reg_operator_pointer-1]='|';
input_pointer++;
}
else//对于括号,则直接入栈
{
reg_operator[reg_operator_pointer++]='|';
input_pointer++;
}
}
else//对于空操作符栈,也是直接入栈
{
reg_operator[reg_operator_pointer++]='|';
input_pointer++;
}
}
int tackle_cat()//处理连接符,处理完之后输入指针不会移动,因为指针本来就指向了后面的字符
{
if(reg_operator_pointer!=0)//如果操作符栈不为空
{
if(reg_operator[reg_operator_pointer-1]=='(')//如果前面有括号,则直接入栈
{
reg_operator[reg_operator_pointer++]='.';
}
else//对于前面不是括号的情况下
{
if(reg_operator[reg_operator_pointer-1]=='.')//优先级相同则输出前面的那个
{
name_number++;
printf("name%d is the concat of name%d and name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
reg_pattern_table[name_number].type=cat;
reg_pattern_table[name_number].left=token[token_pointer-2];
reg_pattern_table[name_number].right=token[token_pointer-1];
token[token_pointer-1]=0;
token[token_pointer-2]=name_number;
token_pointer--;
}
else//否则的话,前面的优先级比当前优先级低,操作符入栈
{
reg_operator[reg_operator_pointer++]='.';
}
}
}
else//如果操作符栈为空,则入栈
{
reg_operator[reg_operator_pointer++]='.';
}
}
void tackle_parenthesis(void)//处理闭括号模式,这里有点复杂,这里匹配完之后,指针会向后移一位
{
if(reg_operator[reg_operator_pointer-1]=='(')//如果前面那个操作符为开括号,则匹配输出,并把输入指针向后移
{
name_number++;
printf("name%d is (name%d)\n",name_number,token[token_pointer-1]);
reg_pattern_table[name_number].type=parenthesis;
reg_pattern_table[name_number].sub=token[token_pointer-1];
token[token_pointer-1]=name_number;
input_pointer++;
reg_operator[--reg_operator_pointer]='\0';
//这时候需要考虑后面的是否少了显示的连接符,如果判断缺少连接符,则需要加上去
tackle_invisible_cat();
}
else//如果闭括号前面还有运算符,那么根据他们的优先级输出,这个时候输入指针是不变的,注意
{
name_number++;
if(reg_operator[reg_operator_pointer-1]=='.')
{
printf("name%d is the concat of name%d and name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
reg_pattern_table[name_number].type=cat;
reg_pattern_table[name_number].left=token[token_pointer-2];
reg_pattern_table[name_number].right=token[token_pointer-1];
}
else
{
printf("name%d is  name%d or name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
reg_pattern_table[name_number].type=or;
reg_pattern_table[name_number].left=token[token_pointer-2];
reg_pattern_table[name_number].right=token[token_pointer-1];
}
token[token_pointer-1]=0;
token[token_pointer-2]=name_number;
token_pointer--;
reg_operator_pointer--;
}
}
int tackle_alias(void)//注意这里处理完中括号匹配之后,还需要处理可能存在的非显示连接符
{
int length,index;
char* new_alias;
index=length=0;
while(reg_input[input_pointer+length]!=']')
{
length++;
}
new_alias=malloc(sizeof(char)*(length));//总的长度为length-1,另外加上一个\0。
for(index=0;index<length-1;index++)
{
new_alias[index]=reg_input[input_pointer+index+1];
}
new_alias[index]='\0';
input_pointer+=length+1;
index=look_up_hash_table(new_alias);//寻找hash表
if(index!=-1)
{
length=simple_hash_table[index].reg_pattern_number;//找到别名所代表的正则模式
token[token_pointer++]=length;//别名的正则模式的名字入栈
printf("match a alias,%s is the alias of name%d\n",new_alias,length);
tackle_invisible_cat();
return 1;
}
else//否则,返回错误信息
{
printf("the alias can't be matched\n");
return -1;
}
}

int tackle_inter_reg(void)
{
token_pointer=0;
while(reg_input[input_pointer]!='\0')//还没处理到输入的末尾
{
switch(reg_input[input_pointer])
{
case '\\'://注意这里因为在c语言中这个字符也是转义字符,所以要双斜杠才能写出来
input_pointer++;
name_number++;
printf("transition literal is encounted\n");
printf("the literal is %c\n",reg_input[input_pointer]);
printf("the literal's token name is name%d\n",name_number);
token[token_pointer++]=name_number;
reg_pattern_table[name_number].type=literal_char;
reg_pattern_table[name_number].value=reg_input[input_pointer];
input_pointer++;
tackle_invisible_cat();
break;
case '('://对于括号,直接入栈
reg_operator[reg_operator_pointer++]='(';
input_pointer++;
break;
case ')':
tackle_parenthesis();
break;
case '*'://由于这个运算符优先级很高,不需要入栈了,直接拿顶上的token输出就行
name_number++;
printf("name%d is multiple of name%d\n",name_number,token[token_pointer-1]);
reg_pattern_table[name_number].type=closure;
reg_pattern_table[name_number].sub=token[token_pointer-1];
token[token_pointer-1]=name_number;
input_pointer++;
tackle_invisible_cat();
break;
case '+'://同上
name_number++;
printf("name%d is one or more instance of name%d\n",name_number,token[token_pointer-1]);
reg_pattern_table[name_number].type=one_or_more;
reg_pattern_table[name_number].sub=token[token_pointer-1];
token[token_pointer-1]=name_number;
input_pointer++;
tackle_invisible_cat();
break;
case '?'://同上
name_number++;
printf("name%d is one or zero instance of name%d\n",name_number,token[token_pointer-1]);
reg_pattern_table[name_number].type=maybe_exist;
reg_pattern_table[name_number].sub=token[token_pointer-1];
token[token_pointer-1]=name_number;
input_pointer++;
tackle_invisible_cat();
break;
case '['://对于开的中括号,因为中括号中只能允许假名存在,所以直接调用函数处理,
//处理完之后指针位于闭中括号的后面一个字符,所以switch中不需要考虑闭中括号了
//注意处理时后面可能接上一个非显示的连接符
//这个时候需要特殊处理
tackle_alias();
break;
case '|'://对于这个符号,
tackle_or();
break;
default:
name_number++;
printf("name%d is %c\n",name_number,reg_input[input_pointer]);
token[token_pointer++]=name_number;
reg_pattern_table[name_number].type=literal_char;
reg_pattern_table[name_number].value=reg_input[input_pointer];
input_pointer++;
tackle_invisible_cat();
break;
}
}
//处理完了输入,这个时候可能还有剩余的内容在栈中
while(reg_operator_pointer>=1)//如果全部的输入都弄完了,可是 操作符栈中还有数据,则输出
{
name_number++;
if(reg_operator[reg_operator_pointer-1]=='.')
{
printf("name%d is concat of name%d and name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
reg_pattern_table[name_number].type=cat;
reg_pattern_table[name_number].left=token[token_pointer-2];
reg_pattern_table[name_number].right=token[token_pointer-1];

}
else
{
printf("name%d is name%d or name%d\n",name_number,token[token_pointer-2],token[token_pointer-1]);
reg_pattern_table[name_number].type=or;
reg_pattern_table[name_number].left=token[token_pointer-2];
reg_pattern_table[name_number].right=token[token_pointer-1];

}
token[token_pointer-2]=name_number;
token_pointer--;
reg_operator_pointer--;
}
return name_number;//返回最后一个生成的token,作为假名的正则代号
}
int tackle_particle()//这里处理一个正则表达式名字的定义,每次处理完之后复用输入数组
{
//首先处理假名部分
int index;
char* name;
input_pointer=0;
while(reg_input[input_pointer++]!=':')//注意这里在停止迭代之后,指针指的是冒号后面的那个字符,
{
//一直向后找,直到找到冒号分隔符
}
//字符串长度比指针的值小1
name=malloc(sizeof(char)*(input_pointer));//考虑到字符串的结尾\0,于是不需要减1了
for(index=0;index<input_pointer-1;index++)//把名字复制过去
{
name[index]=reg_input[index];
}
name[index]='\0';
if(reg_input[input_pointer+1]=='-')//如果后面接的是字符集模式,则直接处理,因为这个比较简单
{
name_number++;//新建一个字符集的名字
printf("new set of literal is created\n");
printf("the set's name is name%d\n",name_number);
printf("the set begins from %c, and end with %c\n",reg_input[input_pointer],reg_input[input_pointer+2]);
index=name_number;
}
else
{
index=tackle_inter_reg();//处理当前的小正则表达式,并得到这个表达式所生成的名字
}
insert_hash_table(name,index);
printf("creat a alias: %s is name%d\n",name,index);//输出,建立了一个新的假名
}

int main(void)
{
int index;
printf("please type in your short regex definition\n");
printf("if there is no more definition, just enter a newline\n");
scanf("%s",reg_input);
while(strlen(reg_input)>=3)//当输入的字符数小于三个的时候,认为结束
{
tackle_particle();
printf("please type in your short regex definition\n");
printf("if there is no more definition, just enter a newline\n");
scanf("%s",reg_input);
}
for(index=0;index<97;index++)//这里将所有的假名输出,并释放空间,其实无所谓,反正系统会回收
{
if(simple_hash_table[index].state==in_use)
{
printf("there is a alias whose name is %s\n",simple_hash_table[index].name_of_alias);
free(simple_hash_table[index].name_of_alias);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: