design-patterns - 访问者模式和编译器代码生成,如何获取子属性?

我想修改编译器的代码生成器以使用访问者模式,因为当前的方法必须在生成相应代码之前使用多个条件语句来检查子项的真实类型。但是,我在访问 child 属性后遇到问题。例如,在二进制表达式中我使用这个:

LHSCode := GenerateExpressionCode(LHSNode);
RHSCode := GenerateExpressionCode(RHSNode);
CreateBinaryExpression(Self,LHS,RHS);

在访问者模式中,访问方法通常是空的,所以我无法从左轴和右轴获取表达式代码。保留共享全局变量不是一种选择,因为表达式代码生成是递归的,因此可能会删除保存在变量中的先前值。

我将只显示二进制表达式,因为这是最复杂的部分(目前):

function TLLVMCodeGenerator.GenerateExpressionCode(
  Expr: TASTExpression): TLLVMValue;
var
  BinExpr: TASTBinaryExpression;
  UnExpr: TASTUnaryExpression;
  LHSCode, RHSCode, ExprCode: TLLVMValue;
  VarExpr: TASTVariableExpression;
begin
  if Expr is TASTBinaryExpression then begin
    BinExpr := Expr as TASTBinaryExpression;
    LHSCode := GenerateExpressionCode(BinExpr.LHS);
    RHSCode := GenerateExpressionCode(BinExpr.RHS);
    case BinExpr.Op of
      '<': Result := FBuilder.CreateICmp(ccSLT, LHSCode, RHSCode);
      '<=': Result := FBuilder.CreateICmp(ccSLE, LHSCode, RHSCode);
      '>': Result := FBuilder.CreateICmp(ccSGT, LHSCode, RHSCode);
      '>=': Result := FBuilder.CreateICmp(ccSGE, LHSCode, RHSCode);
      '==': Result := FBuilder.CreateICmp(ccEQ, LHSCode, RHSCode);
      '<>': Result := FBuilder.CreateICmp(ccNE, LHSCode, RHSCode);
      '/\': Result := FBuilder.CreateAnd(LHSCode, RHSCode);
      '\/': Result := FBuilder.CreateOr(LHSCode, RHSCode);
      '+': Result := FBuilder.CreateAdd(LHSCode, RHSCode);
      '-': Result := FBuilder.CreateSub(LHSCode, RHSCode);
      '*': Result := FBuilder.CreateMul(LHSCode, RHSCode);
      '/': Result := FBuilder.CreateSDiv(LHSCode, RHSCode);
    end;
  end else if Expr is TASTPrimaryExpression then
    if Expr is TASTBooleanConstant then
      with Expr as TASTBooleanConstant do
        Result := FBuilder.CreateConstant(Ord(Value), ltI1)
    else if Expr is TASTIntegerConstant then
      with Expr as TASTIntegerConstant do
        Result := FBuilder.CreateConstant(Value, ltI32)
    else if Expr is TASTUnaryExpression then begin
      UnExpr := Expr as TASTUnaryExpression;
      ExprCode := GenerateExpressionCode(UnExpr.Expr);
      case UnExpr.Op of
        '~': Result := FBuilder.CreateXor(
            FBuilder.CreateConstant(1, ltI1), ExprCode);
        '-': Result := FBuilder.CreateSub(
            FBuilder.CreateConstant(0, ltI32), ExprCode);
      end;
    end else if Expr is TASTVariableExpression then begin
      VarExpr := Expr as TASTVariableExpression;
      with VarExpr.VarDecl do
        Result := FBuilder.CreateVar(Ident, BaseTypeLLVMTypeMap[BaseType]);
    end;
end;

希望你明白:)

最佳答案

In visitor pattern the visit method is usually void, so I can't get the expression code from LHS and RHS. Keeping shared global variables isn't an option since expression code generation is recursive thus could erase previous values kept in the variables.

您需要在访问子属性时获取它们,保留您需要的任何属性,并确保在需要时仍然拥有它们。这可能会使您的访问者的内部结构更复杂一些,但这肯定是可行的。代码生成绝对是访问者模式的常见用途。

通常您不必保留属性,但您确实需要保留中间结果并在访问其他对象时将它们组合成其他中间结果。我认为这里就是这种情况,但是交互非常复杂,有点令人困惑。

我不是 Object Pascal 方面的专家,因此我不会尝试编写实际代码,而是描述我将如何处理它。

在这种情况下,我可能会使用一个堆栈来保存中间结果。

遍历顺序可以在节点的接受方法或访问者的访问方法或外部迭代器中驱动。为简单起见,我假设它在接受方法中。

在简单对象的接受方法中,您只需执行标准的visitor.visit(this)(无论您在 Object Pascal 中如何说)。

在像 TASTBooleanConstant 这样更简单的对象的访问方法中,您将调用适当的方法,在本例中是 FBuilder.CreateConstant,其中包含您从对象中提取并推送的值该方法的结果进入访问者的堆栈。

在像您的TASTBinaryExpression 这样更复杂的对象的接受方法中,您将首先调用子对象的接受方法,然后执行标准的visitor.visit(this),确保首先访问 child 。

然后,由于首先访问了子对象,因此当调用复杂对象的访问方法时,它们的结果应该在堆栈上。在该访问方法中,您会将适当的结果从堆栈中弹出到局部变量中,根据您拥有的运算符调用适当的 FBuilder.CreateXxx 方法,将这些值作为参数传递,并将结果放入堆栈。

对于 TASTUnaryExpression 对象,情况类似,但在 accept 方法中只有一个 child 需要担心,并且只有一个中间结果要从堆栈中弹出并在 visit 方法中使用。

在您的客户端代码中,您创建访问者并调用顶级节点的接受方法,将访问者作为参数传递。完成所有递归后,堆栈应仅包含最终结果,访问者类应提供一个 getResult 方法以允许客户端检索它。

抱歉,这太啰嗦了 - 它在代码中可能更清晰,但希望这能让您了解如何处理这个问题。

Joshua Kerievsky 的书 Refactoring to Patterns 是学习如何重构以像这样在现有代码中引入模式的好资源。 .

https://stackoverflow.com/questions/4690728/

相关文章:

jquery-ui - 如何设置jQueryUI Dialog for v1.8x的背景

date - VB6 儒略日期字符串到日期对象

visual-studio-2010 - 微软内部版本 : Access compiler sett

svn - 我可以 svn merge 而不总是指定第一个修订版吗?

.net - 了解 httpGetEnabled 和 Mex 绑定(bind)

wpf - 链接内容不适用于资源词典

internet-explorer - 是否可以检测用户何时切换标签?

oop - 单例类 : static properties or non-static proper

ruby-on-rails - 有没有一种方法可以自定义页面上的 will_paginate 输出?

jsp - 纯文本 JSP 响应