最初步的正则表达式引擎:支持子表达式替换

子表达式的语法为

name:regex

在处理时,会把name加入进符号表中,方法是hash,最简单的加法

而在regex中如果碰到[name]这种模式,则尝试去找符号表,如果找不到则报错。

代码如下,欢迎大家测试。

#include <stdio.h>
#include <malloc.h>
#include <string.h>
//这个版本允许定义正则子表达式,定义的时候以名字开头,然后是冒号,然后是正则表达式主体。
//在引用子表达式的时候,需要用中括号把子表达式括起来,因此中括号也跟其他操作符一样,作为保留字符
//目前还没有处理转义字符,再过几个版本吧。
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,
    kleen,
    cat,
    or,
    alias,
    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;
        };
        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的开始符号,这样是为了处理非显示的连接符
{
    switch(reg_input[input_pointer])
    {
    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 '('://对于括号,直接入栈
            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=kleen;
            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';
    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");
    while(scanf("%s",reg_input))
    {
        tackle_particle();
        printf("please type in your short regex definition\n");
        printf("if there is no more definition, just enter a newline\n");
    }
    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);
        }
    }
}

 

posted @ 2013-06-22 20:16  huangnima  阅读(488)  评论(0编辑  收藏  举报