| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1550 人关注过本帖
标题:把一种语言解释成另一种语言的问题,请教字符串处理高手
只看楼主 加入收藏
寒风中的细雨
Rank: 17Rank: 17Rank: 17Rank: 17Rank: 17
等 级:贵宾
威 望:66
帖 子:1710
专家分:8645
注 册:2009-9-15
收藏
得分:0 
没看明白   不知道输入输出
2012-09-07 19:52
stophin
Rank: 7Rank: 7Rank: 7
等 级:黑侠
威 望:3
帖 子:227
专家分:618
注 册:2010-3-26
收藏
得分:0 
程序代码:
/*stophin*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <iostream>
using namespace std;

#define M    100    //最大输入字符元素个数
#define N    80    //元素表中最大元素个数

#define NL    0    //无任何元素属性
#define CR    1    //关键字属性
#define ST    2    //字符串属性
#define BR    3    //括号属性
#define ER  4    //错误的属性

//用于创建表达式元素链表
struct exp      
{  
       exp():pre(NULL),signum(0),attrib(NL),elenum(0),quonum(0),next(NULL)    //构造函数
       {
           sig[0]='\0';
       }
       struct exp *pre;        //前驱结点
       char sig[N];            //结点数据
       int signum;            //结点数据个数
       int attrib;            //结点属性标识
       int elenum;            //标记个数,可用于标记括号对数
       int quonum;            //标记个数,可用于标记引号对数
       struct exp *next;    //后继结点
       void insig(char c,int at=ST)    //向结点数据写入字符,并做一些标记
       {
           if (signum>=N)        //如果写满了,则不再写入,并标记属性错误
           {
               attrib=ER;
               return;
           }
           if (at==BR)    //如果是写入括号内的内容,则统计括号个数
           {
                if (c=='(')    
                   elenum++;
                else if (c==')')
                    elenum--;
           }
           if (at==ST)    //如果是写入字符,则标记是否有括号和引号,有则置1
           {
               if (!elenum&&c=='(')
                   elenum=1;
               else if (elenum&&c==')')
                   elenum=0;
               if (!quonum&&c=='\"')
                   quonum=1;
               else if (quonum&&c=='\"')
                   quonum=0;
           }
           sig[signum++]=c;        //写入数据
           sig[signum]='\0';
           attrib=at;            //设置写入数据的属性
           return;
       }
};
typedef struct exp E;

void freelink(E **ph)    //释放结点
{
    E * p = *ph;
    E * q ;
    while(NULL != p )
    {
        q = p->next ;
        free(p);
        p = q;
    }
    *ph=NULL;
    return;
}

void viewlink(E *ph)    //打印节点
{
    while(ph!=NULL)
    {
        cout<<ph->sig<<"";
        ph=ph->next;
    }
    cout<<endl;
    return;
}

E *creat(char *expr) //创建文字元素 
{
         E *head=NULL,*pre=NULL,*last=NULL,*old=NULL;
         int i=0;
         int l=strlen(expr);    //获取文字元素长度
         if (expr[i]!='\0')
         {
              head=new E;    //创建头结点
              if (!head)
              {
                    printf("0293 创建字串链表头结点时内存空间分配失败!\n");
                    return NULL;
              }
              old=last=head;
              while (i<l)
              {
                    if (expr[i]==' ')    //略去前面的空格
                    {
                       i++;
                       continue;
                    }
                    pre=new E;              
                    if (!pre)
                    {
                        printf("0294 为 \"%c\" 创建字串链表时内存空间分配失败!\n",expr[i]);
                        pre=old;
                    }
                    //核心--------------------------------------------------------------
                    if (expr[i]=='(')    //如果是开括号
                    {
                        do            //则一直写入,并统计括号匹配情况
                        {
                            if (expr[i]!=' ')
                                pre->insig(expr[i],BR);
                            i++;
                            //直到括号完全匹配,或者遇到关键字停止,同样关键字要满足一些条件
                            //该条件与下面的"其余情况"中的if条件相差无几,详见下面的说明
                            if (!pre->elenum||(expr[i]=='i')&&(i<=l-1&&expr[i+1]=='n')&&(i&&(expr[i-1]==' '||expr[i-1]==')'||expr[i-1]=='\"'))&&(i<=l-2&&expr[i+2]==' '||expr[i+2]=='('||expr[i+2]=='\"'))
                            {
                                break;
                            }
                        }while(expr[i]!='\0');
                        i--;
                    }
                    else if (expr[i]=='\"')        //如果是引号
                    {
                        do            //则一直写入
                        {
                            pre->insig(expr[i]);
                            i++;
                            if (expr[i]=='\"')    //直到遇到下一个引号停止
                            {
                                pre->insig(expr[i]);
                                break;
                            }
                        }while(expr[i]!='\0');
                    }
                    else if ((expr[i]=='i')&&(i<=l-1&&expr[i+1]=='n'))    //如果是关键字,则写入逗号,并标记关键字属性
                    {
                    //    pre->insig(expr[i++],CR);
                    //    pre->insig(expr[i],CR);
                        pre->insig(',',CR);
                        i++;
                    }
                    else    //其余情况
                    {
                        do    //则一直写入,并标记是否有引号和括号
                        {
                            if (expr[i]!=' '||pre->quonum)    //如果有引号则引号中的字符不能略去空格
                                pre->insig(expr[i]);
                            i++;
                            //这里容错机制比较复杂
                            //首先在引号和括号未完成时不能跳出,即使遇到关键字,因为引号或括号中的"in"应该只是字符串或者变量之类的
                            //如果上述满足,则考虑是否是关键字,关键字前后必须有空格,或者"前面是闭括号或引号"并且"后面是开括号或引号"
                            //否则,认为是函数名之类的,形如 ")in(" 是关键字,而 "fin(" 不是关键字
                            if (!pre->elenum&&!pre->quonum&&(expr[i]=='i')&&(i<=l-1&&expr[i+1]=='n')&&(i&&(expr[i-1]==' '||expr[i-1]==')'||expr[i-1]=='\"'))&&(i<=l-2&&expr[i+2]==' '||expr[i+2]=='('||expr[i+2]=='\"'))
                                break;
                        }while(expr[i]!='\0');
                        i--;
                    }
                    //--------------------------------------------------------------
                    if (pre!=old)
                    {
                        last->next=pre;
                        pre->pre=last;
                        last=pre;
                        old=last;
                        i++;
                    }
                    else
                    {
                        freelink(&head);
                        break;
                    }
              }
         }
         else
         {
             return NULL;
         }
         return head;
}

void getres(char *res,E * exp)
{
    E *p=exp->next;
    int i=0;
    strcpy(res,"is_in(");
    //要生成is_in(is_in(A,B),is_in(C,D))
    //则已经生成了 (A  ,  B)  ,  (C  ,  D)
    //只要加上is_in(就行了
    //先将(A  ,  B)以及(C  ,   D)联合,再将联合后的加个外壳is_in(进行最后联合
    while(p!=NULL)
    {
        i=0;
        p->elenum=0;
        //这里有一个假设,即如果一个节点中的字符串括号是匹配的,
        //则不需要加is_in(,比如(a) in b生成is_in((a),b),而不是is_in(is_in(a),b)
        //而如果不匹配(一般来说应该是开括号多于闭括号),则将不匹配数目
        //作为即将加上的is_in(数目,但is_in(的数目也受到连续开括号数目的限制
        //如((a)in b)in c
        //得到((a)  ,  b)  ,  c
        //a的括号不匹配,所以应该加一个is_in(,即is_in((a)
        //而b的括号也不匹配,但由于b的是闭括号,因此没有加任何东西(看下面循环里面的解释)
        //这个假设将应用于带括号的算术表达式中
        while(p->sig[i]!='\0')    //检测括号匹配,开括号为正,闭括号为负
        {
            if (p->sig[i]=='(')
                p->elenum++;
            if (p->sig[i]==')')
                p->elenum--;
            i++;
        }
        if (p->elenum<0)    //如果闭括号多,则将负值改为正,以配合下面的递减操作
            p->elenum=-p->elenum;
        i=0;
        while(p->elenum--&&p->sig[i]!='\0')    //如果括号不匹配,则进行加is_in(操作
        {
            if (p->sig[i]=='(')            //这里要求开括号必须是字符开头连续的,否则退出循环,例如 b) 一进来就进入break了
                strcat(res,"is_in(");
            else
                break;
            i++;
        }
        strcat(res,p->sig+i);    //将加完is_in(后剩下的部分添加进结果
        p=p->next;
    }
    strcat(res,")");
    return;
}

void link(char *str)
{
        char* res=new char[2*M];
        cout<<str<<"\n";
        E *expre=creat(str);
        viewlink(expre);
        getres(res,expre);
        cout<<res<<endl;
        cout<<endl;
        freelink(&expre);    
        delete    res;
        return;
}

int main()
{   
    char input1[M]="fun1(a1  ,\"in or not in\", \"(((   (((((\")    in      fun2(\"((  (\\\"(in (\'   ((\")";
    char input2[M]="(A1   +   A2) in (B1+B2)";
    char input3[M]="(((( A1 + A2 )* (A3 + A4))/A5)   in  (B1+B2))in d";
    char input4[M]="fun1llinij(\"((( A1 + A2 )* (A3 + A4))/A5)\")    in    fun2(\"if A in B\")";
    char input5[M]="\"(A1)\" in ((A1)(B2))";
    char input6[M]="A in join(B1, B2)";
    char input7[M]="(a in b))in((c in d)in(e in f))";
    char input8[M]="(r in((ain in b)in(((c in d)in(e in f))in g)))in h";
    char input9[M]="((A)in B) in (C in D)";
    link(input1);
    link(input2);
    link(input3);
    link(input4);
    link(input5);
    link(input6);
    link(input7);
    link(input8);
    link(input9);
    getch();
    return 0;
}

我做的基于链表的处理程序,静大大看看可行不?正则表达式我不是太懂,所以只能硬做。我利用以前做的一个字符计算器的字符处理函数,加上一些自己写的处理函数做成的,应该还有很多BUG,但是这里9个例子都是没有问题的。做的时候就是边做边调,有问题就添条件,添语句,修修补补做出来的,所以说不出核心思想来,详细说明在注释里面,看看结果吧>>>
图片附件: 游客没有浏览图片的权限,请 登录注册
2012-09-08 18:07
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:0 
回复 22楼 stophin
膜拜达人。
2012-09-08 22:05
静夜思
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:济南的冬天
等 级:管理员
威 望:11
帖 子:8902
专家分:2567
注 册:2004-3-25
收藏
得分:0 
回复 22楼 stophin
高手,这种方式运行速度比正则表达式更快,正则只是写起来方便。真正的解释器应该就是用的这种方式。

畅所欲言
2012-09-08 22:37
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
程序代码:
#include <stdio.h>
#include <string.h>
#include <ctype.h>

/* 表示一个字符串的开始和结束的索引 */
typedef struct {
    int start; /* 包括   */
    int end;   /* 不包括 */
} Region;

char* strrcat(char* buffer, const char* expr, Region* region)
{
    char* p = buffer;
    int i = region->start, j = region->end;
    for (; *p != '\0'; ++p) ;
    for (; i < j; *p++ = expr[i++]) ;
    *p = '\0';
    return buffer;
}

char* resolve(const char* expr, char* buffer)
{
    int length = strlen(expr);
    int i, j;
    /* 查找in关键字的索引 */
    for (i = 0; i < length - 4; ++i)
        if (isspace(expr[i]) && expr[i+1] == 'i' && expr[i+2] == 'n' && isspace(expr[i+3]))
            break;
    /* if (A in B)
     *      ^  ^
     *      i  j
     */
    j = i + 3;
    Region A = {0, i}, B = {j + 1, length};
    for (; expr[A.start] != '('; ++A.start) ;
    ++A.start;
    for (; expr[B.end]   != ')'; --B.end  ) ;
    strcpy(buffer, "if(is_in("); /* A,B)) */
    strrcat(buffer, expr, &A);
    strcat(buffer, ",");
    strrcat(buffer, expr, &B);
    strcat(buffer, "))");
    return buffer;
}

void test(char** expr, int size)
{
    int i; char buffer[1024];
    for (i = 0; i < size; ++i)
        puts(resolve(expr[i], buffer));
}

int main(void)
{
    char* expr[] = {
        "if((A1+A2) in (B1+B2))",
        "if(\"(A1)\" in \"(A1)(B2)\")",
        "if(A in join(B1, B2))",
        "if((A1   +   A2) in (B1+B2))",
        "if(((( A1 + A2 )* (A3 + A4))/A5)   in  (B1+B2))",
        "if(fun1(\"((( A1 + A2 )* (A3 + A4))/A5)\")    in    fun2(\"if A in B\"))"
    };
    test(expr, sizeof expr / sizeof *expr);
    return 0;
}
输出:
程序代码:
if(is_in((A1+A2),(B1+B2)))
if(is_in("(A1)","(A1)(B2)"))
if(is_in(A,join(B1, B2)))
if(is_in((A1   +   A2),(B1+B2)))
if(is_in(((( A1 + A2 )* (A3 + A4))/A5)  , (B1+B2)))
if(is_in(fun1("((( A1 + A2 )* (A3 + A4))/A5)")   ,   fun2("if A in B")))

我觉得没这么复杂吧,只要找到关键字in的位置,然后分别取它左右两个A和B的部分就可以了。

My life is brilliant
2012-09-09 01:54
zhu224039
Rank: 8Rank: 8
等 级:贵宾
威 望:17
帖 子:862
专家分:792
注 册:2012-7-29
收藏
得分:0 
啥时候我也能成为一个角啊

我要成为嘿嘿的黑客,替天行道
2012-09-10 04:27
快速回复:把一种语言解释成另一种语言的问题,请教字符串处理高手
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.046095 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved