解释器中的运算符
Operators in an interpreter
我做翻译只是为了好玩。首先,我正在尝试评估表达式。评估 returns 一个 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) {}
};
我不知道这是否是一个不错的设计(可能不是),但目前可以使用。但是当我定义新的类型和运算符时,评估方法变得庞大。例如,在运算符“==”中,左侧和右侧可以是字符串、整数、浮点数等等……所以我想我需要为值结构定义运算符而不是在 eval 方法中检查它们(甚至可能允许像 c++ 中那样的用户定义运算符),但我想不出一个快速、优雅且易于扩展的设计。有什么想法吗?
在我的 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 版本的原子符号,表示为小整数。所以这里我只需要区分3种数字类型,所以我为它们做了一个enum
。
enum { IMM = 1, FIX, FLO, NTYPES };
通常 enum
值从 0 开始分配,但对于数学技巧,我希望这些值从 1 开始。然后我可以将这些值视为 数字系统 以 NTYPES
作为基数或基数。使用这样定义的符号,我可以将 2-digit 类型计算为单个数值。这是一个常量值,所以如果通过宏计算,可以作为switch case。
#define TYPEPAIR(a,b) ((a)*NTYPES+(b))
它也可以组成一个更大的类型模式数字表示。
TYPEPAIR(TYPEPAIR(IMM,FIX),FLO)
扩展为
((((IMM)*NTYPES+(FIX)))*NTYPES+(FLO))
这种东西让我可以用更少的代码匹配更大的事物模式。而不是像
if (TYPE(x)==IMM && TYPE(y)==FIX) //...
//...
无法表示为 switch
。
我做翻译只是为了好玩。首先,我正在尝试评估表达式。评估 returns 一个 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) {}
};
我不知道这是否是一个不错的设计(可能不是),但目前可以使用。但是当我定义新的类型和运算符时,评估方法变得庞大。例如,在运算符“==”中,左侧和右侧可以是字符串、整数、浮点数等等……所以我想我需要为值结构定义运算符而不是在 eval 方法中检查它们(甚至可能允许像 c++ 中那样的用户定义运算符),但我想不出一个快速、优雅且易于扩展的设计。有什么想法吗?
在我的 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 版本的原子符号,表示为小整数。所以这里我只需要区分3种数字类型,所以我为它们做了一个enum
。
enum { IMM = 1, FIX, FLO, NTYPES };
通常 enum
值从 0 开始分配,但对于数学技巧,我希望这些值从 1 开始。然后我可以将这些值视为 数字系统 以 NTYPES
作为基数或基数。使用这样定义的符号,我可以将 2-digit 类型计算为单个数值。这是一个常量值,所以如果通过宏计算,可以作为switch case。
#define TYPEPAIR(a,b) ((a)*NTYPES+(b))
它也可以组成一个更大的类型模式数字表示。
TYPEPAIR(TYPEPAIR(IMM,FIX),FLO)
扩展为
((((IMM)*NTYPES+(FIX)))*NTYPES+(FLO))
这种东西让我可以用更少的代码匹配更大的事物模式。而不是像
if (TYPE(x)==IMM && TYPE(y)==FIX) //...
//...
无法表示为 switch
。