我正在尝试为简单的语言解释器设计一个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);
}
}
答案 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>();
}
您也可以让EvaluationVisitor
从Visit
方法返回实际值:
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;
}
}