在用C ++编写的解释器中处理内置函数的好方法是什么?

时间:2011-12-08 20:36:04

标签: c++ interpreter

我正在用C ++编写一个解释器,用于我简洁设计的类似lisp的语言。这是为了娱乐和学习,所以我不会追求绝对的效率。但我想尝试一个非常干净的C ++代码。我目前想知道如何实现内置函数。

基本上,我所做的就是:

我有一个抽象基类DataObject,它只提供类型信息(目前是double,int,bool),并由特定的数据容器继承,如:

class DataObject
{
public:
    virtual const Type *type() = 0;
};

template<class T, const Type * myType>
class DataObjectValue : public DataObject
{
T value;
public:
    const Type *type(){return myType;}
};

但是,当我想要表演时,我必须做以下事情:

DataObject * sum(DataObject *a, DataObject *b)
{
    if(a->type() == &Integer and b->type == &Integer)
    {
        DataObjectValue<int>* ia = dynamic_cast< DataObjectValue<int>* >(a);
        DataObjectValue<int>* ib = dynamic_cast< DataObjectValue<int>* >(b);
        return new DataObjectValue<int>(ia->value+ib->value);
    }
    else if(a->type() == &Real and b->type == &Real)
    {
        DataObjectValue<double>* ra = dynamic_cast< DataObjectValue<double>* >(a);
        DataObjectValue<double>* rb = dynamic_cast< DataObjectValue<double>* >(b);
        return new DataObjectValue<double>(ra->value+rb->value);
    }
    else...
}

很快就会很烦人(对于 - * /&lt;&lt; =&lt; =&gt; =&lt;&gt; ....)以及其他几种类型。这很难维护。当然,通过在各处引入大量模板,我已经简化了尽可能多的过程,但是,我仍然不禁想到必须有一种更清洁的方式。你是a)看看我的问题是什么(我怀疑我的解释,而不是你的能力)b)有任何建议吗?

3 个答案:

答案 0 :(得分:3)

你基本上执行类型擦除的当前实现正在存储的确切类型中,并且你是以一种稍微不同寻常的方式执行它(为什么不使用枚举而不是指向唯一对象的指针?)< / p>

我首先将 promotion 操作提供给基类​​,然后在每个级别实现它们:

enum DataType {
   type_bool,
   type_int,
   type_double
};

struct DataObject {
   virtual ~DataObject() {}    // remember to provide a virtual destructor if you
                               // intend on deleting through base pointers!!!
   virtual DataType type() const = 0;
   virtual bool   asBool() const = 0;
   virtual int    asInt() const = 0;
   virtual double asDouble() const = 0;
};

然后,您可以在一个简单的仿函数中实现操作:

template <typename T>
T sum_impl( T lhs, T rhs ) {
   return lhs + rhs;
}

并提供简单的调度功能:

DataType promoteTypes( DataType lhs, DataType rhs ) {
   if ( lhs == type_double || rhs == type_double ) {
      return type_double;
   } else if ( lhs == type_int || rhs == type_int ) {
      return type_int;
   } else {
      return type_bool;
   }
}
template <template <typename T> T operation (T,T)>
DataObject* perform_operation( DataObject* lhs, DataObject* rhs, operation op ) const {
   DataType result_type = promoteTypes( lhs->type(), rhs->type() );
   switch ( result_type ) {
   case type_double: 
      return new DataObjectValue<double>( op( lhs->asDouble(), rhs->asDouble() );
   case type_int:
      return new DataObjectValue<int>( op( lhs->asInt(), rhs->asInt() );
   case type_bool:
      return new DataObjectValue<bool>( op( lhs->asBool(), rhs->asBool() );
   default:
      abort();
   }
}

有了所有部分,您可以通过为特定操作提供功能模板(如上面的sum),然后使用其余的地方来实现操作:

// sum_impl as above
DataObject* sum( DataObject* lhs, DataObject* rhs ) {
   return perform_operation( lhs, rhs, sum_impl );
}

现在这只是我将使用的模式,但我会做一些更改,我更喜欢使用尽可能少的指针,这意味着我不会通过指针而是通过引用传递参数。此外,我会进行正确的类型擦除(看看boost any)并使Object成为包含DataObject元素的完整类型,然后对该类型执行操作(而不是层次结构)。这将使您能够提供也按值返回的函数(并在内部隐藏动态内存分配,这也意味着可以在Object内控制资源管理,而不是用户代码的责任)。通过这些更改,您可以重复使用并简化上述结构,并提供更清晰的解决方案。

答案 1 :(得分:1)

您可以使用宏程序:

(为了简洁,我离开了\

#define IMPLEMENT_OPERATOR(name, operator)
DataObject * name(DataObject *a, DataObject *b)
{
    if(a->type() == &Integer and b->type == &Integer)
    {
        DataObjectValue<int>* ia = dynamic_cast< DataObjectValue<int>* >(a);
        DataObjectValue<int>* ib = dynamic_cast< DataObjectValue<int>* >(b);
        return new DataObjectValue<int>(ia->value operator ib->value);
    }
    else if(a->type() == &Real and b->type == &Real)
    {
        DataObjectValue<double>* ra = dynamic_cast< DataObjectValue<double>* >(a);
        DataObjectValue<double>* rb = dynamic_cast< DataObjectValue<double>* >(b);
        return new DataObjectValue<double>(ra->value operator rb->value);
    }
    else...
}

现在,在此之后你可以这样做:

IMPLEMENT_OPERATOR(sum, +);
IMPLEMENT_OPERATOR(multiply, *);
IMPLEMENT_OPERATOR(division, /);
...

这将是快速和可维护的,但对于特殊操作员可能会有问题。

编辑: Shahbaz提到您可以在其他宏中使用宏。这可以通过以下方式应用。

(再次,为了简洁而遗漏了\

#define IMPLEMENT_OPERATOR_TYPE(typeobject, internal_type, operator)
if(a->type() == &typeobject and b->type == &typeobject)
{
    DataObjectValue<internal_type>* ia = dynamic_cast< DataObjectValue<internal_type>* >(a);
    DataObjectValue<internal_type>* ib = dynamic_cast< DataObjectValue<internal_type>* >(b);
    return new DataObjectValue<internal_type>(ia->value operator ib->value);
}

现在可以在IMPLEMENT_OPERATOR宏中使用:

#define IMPLEMENT_OPERATOR(name, operator)
DataObject * name(DataObject *a, DataObject *b)
{
    IMPLEMENT_OPERATOR_TYPE(&Integer, int, operator);
    IMPLEMENT_OPERATOR_TYPE(&Real, double, operator);
    ...
}

答案 2 :(得分:1)

如果两个操作数必须是同一类型,那么你可以试试这个:

class DataObject
{
public:
    virtual const Type *type() = 0;
    virtual DataObject *operator+(DataObject&) = 0;
    virtual DataObject *operator-(DataObject&) = 0;
    virtual DataObject *operator*(DataObject&) = 0;
    virtual DataObject *operator/(DataObject&) = 0;
};

template<class T, const Type * myType>
class DataObjectValue : public DataObject
{
    typedef DataObjectValue<T, myType> selfType;
    T value;
public:
    const Type *type(){return myType;}
    DataObject *operator+(DataObject& other) {
        if (other.type() != myType)
            return null;
        selfType &otherValue = static_cast<selfType&>(other);
        return new selfType(value + otherValue.value);
    }
    // etc.
};

当你开始允许int + double时,事情变得更加激烈。在某些情况下,你可以通过检查你知道的所有类型(即已经被声明)来处理它,否则将自己传递给另一个对象:

if (other.type() != myType)
    return other + *this; // assume *other is a <double> that knows how to add an <int>