在评估表达式树时停止并继续

时间:2009-11-17 19:31:49

标签: design-patterns parsing compiler-construction dsl

在办公室,我们已经将简单的域特定语言(DSL)应用于我们遇到的几个问题域。

基本上,我们将自定义脚本解析(lex / yacc)到表达式树中。表达式树中的每个节点都有一个.evaluate()方法,可以递归调用,直到程序完成。很好,而且很简单。这是一件好事,因为我对解析技术,编译器构造和诸如此类的东西几乎一无所知(我的同事知道一件事或两件事是件好事。)

现在是挑战:我们目前正在开展的实施需要能力

  • 停在树中的任何位置
  • 坚持所有州
  • 并在将来的任何给定时间恢复州/位置。

坚持实际状态不应该太难,但树中的位置(或“callstack”)会让我感到困惑。如何实施这样的系统?使用某种ID为每个节点存储树中的位置?或者正在评估树本身是错误的方法,我们应该以某种方式将其转换为线性的东西吗?

我猜这是一个相当普遍的问题,但我不知道该寻找什么。任何关于正确术语的帮助,正确方向的推动,搜索的关键词,要看的设计模式等都是最受欢迎的!

(在Win32 / Dephi中这样做,但希望我们可以保持这种语言不可知)

1 个答案:

答案 0 :(得分:1)

可以使用子编号列表指定树中的位置。您的评估看起来像这样(快速编写;未编译/测试):

public class Context {
    private Context parent; // set in constructor
    private int child;  // with get/set
}

public ReturnType evaluate(Context context) 
    context.setChild(context.getChild() + 1);
    context = new Context(context); // push a new context for calls
    // do evaluation - when calling kids, pass context            
}

以上内容应将上下文作为一个数字列表进行跟踪,以确定您当前处理的是哪个孩子。

我建议不要在每个节点类型中实现它,而是建议使用装饰器(或使用模板方法的超类)为您完成,例如:

// decorator
public class ContextTrackerEvaluator<T> implements Evaluator<T> {
    private Evaluator realEvaluator;
    public ContextTrackerEvaluator(Evaluator realEvaluator) {
        this.realEvaluator = realEvaluator;
    }
    public T evaluate(Context context) {
        context.setChild(context.getChild() + 1);
        context = new Context(context); // push a new context for calls
        realEvaluator.evaluate(context);
    }
}

// OR superclass w/ template method
public class EvaluatorBase<T> {
    public final T evaluate(Context context) {
        context.setChild(context.getChild() + 1);
        context = new Context(context); // push a new context for calls
        doEvaluate(context);
    }
    // subclasses override doEvaluate to do their real work
    protected abstract T doEvaluate(Context context);
}

如果您使用装饰器,请确保装饰每个节点......

这使您可以访问上下文列表。

然后您可以添加一个可以比较的“停止上下文”参数(这样您就可以传入两个上下文,并且可以比较它们以查看它们是否匹配。

希望这有帮助! - 斯科特