翻译中的操作员

时间:2015-04-12 08:44:23

标签: operators interpreter

我只是为了好玩而翻译。首先,我试图评估表达式。评估返回一个Value对象,每个类型都有自己的Value结构。例如:

struct Value  // This is the abstract base class for every value type
{
     int type;
};

struct IntegerValue : public Value
{
     int value;

     IntegerValue(int value) : value(value), type(VALUE_INTEGER) {}
};

我不知道这是一个不错的设计(可能不是),但到目前为止还有效。但是当我定义新类型和操作符时,评估方法变得非常庞大。例如,在运营商' =='左侧和右侧可以是字符串,整数,浮点数......所以我想我需要为Value结构定义运算符而不是在eval方法中检查它们(甚至可能允许在c ++中使用用户定义的运算符) )但我无法想到快速,优雅和易于扩展的设计。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

在我的postscript解释器(用C编写)中,我将我的类型对象定义为联合,因此我可以仔细安排成员覆盖相同的内存。

union {
    word tag;
    struct { word tag; word pad0; int val; } _int;
    struct { word tag; word pad0; float val; } _real;
    //...
} object;

您可能不需要在项目中注重记忆,但我从这个结构中获得了很多里程。

至于处理可能发生的类型组合的爆炸。我最近在我的APL解释器中实现了几种数字类型,并且在宏的帮助下(隐藏了更多代码),3种可能的类型成为9个独立的案例:

/* apply binary math op to nums, yielding num
TODO: additional numeric types.
    configurable overflow promotion handling.
 */
#define BIN_MATH_FUNC(func,z,x,y,overflow,domainI,domainD) \
     switch(NUMERIC_TYPES(x,y)){ \
     case TYPEPAIR(IMM,IMM): DOM(domainI,z,numimm(x),numimm(y)) \
                             if (overflow(numimm(x),numimm(y))) \
                                 z=flo((D)numimm(x) func (D)numimm(y)); \
                             else z=num(numimm(x) func numimm(y)); break; \
     case TYPEPAIR(IMM,FIX): DOM(domainI,z,numimm(x),numint(y)) \
                             if (overflow(numimm(x),numint(y))) \
                                 z=flo((D)numimm(x) func (D)numint(y)); \
                             else z=num(numimm(x) func numint(y)); break; \
     case TYPEPAIR(IMM,FLO): DOM(domainD,z,numimm(x),numdbl(y)) \
                             z=flo(numimm(x) func numdbl(y)); break; \
     case TYPEPAIR(FIX,IMM): DOM(domainI,z,numint(x),numimm(y)) \
                             if (overflow(numint(x),numimm(y))) \
                                 z=flo((D)numint(x) func (D)numimm(y)); \
                             else z=num(numint(x) func numimm(y)); break; \
     case TYPEPAIR(FIX,FIX): DOM(domainI,z,numint(x),numint(y)) \
                             if (overflow(numint(x),numint(y))) \
                                 z=flo((D)numint(x) func (D)numint(y)); \
                             else z=num(numint(x) func numint(y)); break; \
     case TYPEPAIR(FIX,FLO): DOM(domainD,z,numint(x),numdbl(y)) \
                             z=flo(numint(x) func numdbl(y)); break; \
     case TYPEPAIR(FLO,IMM): DOM(domainD,z,numdbl(x),numimm(y)) \
                             z=flo(numdbl(x) func numimm(y)); break; \
     case TYPEPAIR(FLO,FIX): DOM(domainD,z,numdbl(x),numint(y)) \
                             z=flo(numdbl(x) func numint(y)); break; \
     case TYPEPAIR(FLO,FLO): DOM(domainD,z,numdbl(x),numdbl(y)) \
                             z=flo(numdbl(x) func numdbl(y)); break; \
     }    

宏的func参数是C数学运算符,如+*%。所以我只需要在需要浮点数学或整数的地方出现浮点数,我想要整数数学。 domain?函数仅用于检测除零。

TYPEPAIR辅助宏非常有用,也许并不明显它应该如何工作。参数是enum值,C&C的原子符号版本,表示为小整数。所以在这里我只需要区分3种数字类型,所以我为它们制作了enum

enum { IMM = 1, FIX, FLO, NTYPES };

正常enum值从0开始分配,但对于数学技巧,我希望这些值从1开始。然后我可以将这些值视为数字系统 {{ 1}}作为基数或基数。使用这样定义的符号,我可以将2 数字类型计算为单个数值。这是一个常数值,因此如果它由宏计算,它可以用作切换案例。

NTYPES

它还可以组成一个更大的类型 - 模式数字表示。

#define TYPEPAIR(a,b)  ((a)*NTYPES+(b))

扩展为

TYPEPAIR(TYPEPAIR(IMM,FIX),FLO)

这种东西可以让我用更少的代码匹配更大的模式。而不是像

那样的东西
((((IMM)*NTYPES+(FIX)))*NTYPES+(FLO))

不能表示为if (TYPE(x)==IMM && TYPE(y)==FIX) //... //...