我只是为了好玩而翻译。首先,我试图评估表达式。评估返回一个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 ++中使用用户定义的运算符) )但我无法想到快速,优雅和易于扩展的设计。有什么想法吗?
答案 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) //...
//...
。