访客模式:避免为简单表达式计算器进行投射

时间:2015-09-03 14:27:55

标签: c# generics interpreter visitor-pattern

我正在尝试为简单的语言解释器设计一个abstract syntax tree。为了避免在评估期间进行投射,我尝试使用泛型(这种技术被更全面地描述here)。

我的基本类型是Exp<T>,我使用IExpression作为分组界面(见下文 - 评估将返回)。

public interface IExpression
{
}

public abstract class Exp<T> : IExpression
{ 
    public abstract U Accept<U>(IVisitor<U> visitor);

    public IExpression Eval()
    {
        return this.Accept<IExpression>(new EvaluationVisitor());
    }
}

public class Lit : Exp<int>
{
    public int value;

    public Lit(int value) 
    { 
        this.value = value; 
    }

    public override U Accept<U>(IVisitor<U> visitor)
    {
        return visitor.Visit(this);
    }
}

public class Plus : Exp<int>
{
    public Exp<int> e1, e2;

    public Plus(Exp<int> e1, Exp<int> e2)
    { 
        this.e1 = e1; 
        this.e2 = e2; 
    }

    public override U Accept<U>(IVisitor<U> visitor)
    {
        return visitor.Visit(this);
    }
}

我曾希望我现在可以使用visitor design pattern来评估树。然而,对Plus表达式的评估会导致丑陋的演员表演。有什么方法可以避免这种情况吗?

public interface IVisitor<T>
{
    T Visit(Lit exp);
    T Visit(Plus exp);
}

public class EvaluationVisitor : IVisitor<IExpression>
{
    public IExpression Visit(Lit exp)
    {
        return exp;
    }

    public IExpression Visit(Plus exp)
    {
        var v1 = (Lit)exp.e1.Accept<IExpression>(this);
        var v2 = (Lit)exp.e2.Accept<IExpression>(this);
        return new Lit(v1.value + v2.value);
    }
}

1 个答案:

答案 0 :(得分:1)

不要试图使用始终需要演员的Visit的返回值,而是考虑给你的访问者一些状态:

public class EvaluationVisitor : IVisitor<IExpression>
{
    public IExpression Visit(Lit exp)
    {
        mValueStack.Push( exp.value );
        return exp;
    }

    public IExpression Visit(Plus exp)
    {
        exp.e1.Accept<IExpression>( this );
        exp.e2.Accept<IExpression>( this );
        int v2 = mValueStack.Pop();
        int v1 = mValueStack.Pop();
        mValueStack.Push( v1 + v2 );
        return new Lit( v1 + v2 );
    }

    public int Value
    {
        get
        {
            if( mValueStack.Count != 1 )
            {
                // Malformed expression, could throw an exception or something
            }
            return mValueStack.Peek();
        }
    }

    private readonly Stack<int> mValueStack = new Stack<int>();
}

您也可以让EvaluationVisitorVisit方法返回实际值:

public class EvaluationVisitor : IVisitor<int>
{
    public int Visit(Lit exp)
    {
        return exp.value;
    }

    public int Visit(Plus exp)
    {
        int v1 = exp.e1.Accept<int>( this );
        int v2 = exp.e2.Accept<int>( this );
        return v1 + v2;
    }
}