Шаблон посетителя и генерация кода компилятора, как получить дочерние атрибуты? - PullRequest
2 голосов
/ 14 января 2011

Я бы хотел изменить генератор кода моего компилятора для использования шаблона посетителя, так как текущий подход должен использовать несколько условных операторов для проверки реального типа потомка перед генерацией соответствующего кода.Однако у меня проблемы с получением атрибутов потомков после их посещения.Например, в двоичном выражении я использую это:

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

В шаблоне посетителя метод посещения обычно недействителен, поэтому я не могу получить код выражения из 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;

Надеюсь, вы понимаете:)

1 Ответ

3 голосов
/ 14 января 2011

В шаблоне посетителя метод посещения обычно недействителен, поэтому я не могу получить код выражения из LHS и RHS. Хранение общих глобальных переменных не вариант, так как генерация кода выражения является рекурсивной, поэтому может стереть предыдущие значения, хранящиеся в переменных.

Вам нужно получать дочерние атрибуты, когда они посещаются, удерживать любые нужные вам атрибуты и обеспечивать, чтобы они оставались у вас, когда они вам нужны. Это может сделать внутреннюю структуру вашего посетителя немного более сложной, но это, безусловно, возможно. Генерация кода определенно является распространенным использованием шаблона Visitor.

Обычно вам не нужно держаться за атрибуты, но вам нужно держаться за промежуточные результаты и объединять их в другие промежуточные результаты при посещении других объектов. Я думаю, что это так, но взаимодействие достаточно сложное, чтобы немного запутать.

Я не эксперт по Object Pascal, поэтому вместо того, чтобы пытаться писать настоящий код, я просто опишу, как бы я справился с этим.

В этом случае я бы, вероятно, использовал стек, содержащий промежуточные результаты.

Порядок обхода можно задать в методах принятия узлов, в методах посещения посетителя или во внешнем итераторе. Для простоты я буду предполагать его в методах принятия.

В методе accept простых объектов вы просто выполняете стандарт visitor.visit(this) (как бы вы это ни говорили в Object Pascal).

В методе посещения для более простых объектов, таких как TASTBooleanConstant, вы вызываете соответствующий метод, в данном случае FBuilder.CreateConstant со значениями, которые вы извлекаете из объекта и помещаете результат этого метода в стек посетителя.

В методе accept более сложного объекта, такого как TASTBinaryExpression, вы сначала вызываете методы accept для потомков, а затем выполняете стандарт visitor.visit(this), гарантируя, что потомки посещаются первыми.

Затем, поскольку дети были посещены первыми, их результаты должны быть в стеке при вызове метода посещения сложного объекта. В этом методе посещения вы должны вытолкнуть соответствующие результаты из стека в локальные переменные, вызвать соответствующий метод FBuilder.CreateXxx, основанный на том, какой у вас оператор, передав эти значения в качестве параметров, и поместить результат в стек.

Для объектов TASTUnaryExpression это будет аналогично, но в методе accept есть только один дочерний элемент, и только один промежуточный результат выскочит из стека и будет использоваться в методе посещения.

В вашем клиентском коде вы создаете посетителя и вызываете метод accept вашего верхнего узла, передавая посетителю в качестве аргумента. После того, как вся рекурсия сделана, стек должен содержать только конечный результат, а класс посетителя должен предоставить метод getResult, позволяющий клиенту получить его.

Извините, это так затянуто - в коде это может быть яснее, но, надеюсь, это даст вам представление о том, как с этим справиться.

Хорошим ресурсом для изучения того, как рефакторинг для введения шаблонов в существующий код, является книга Джошуа Кериевского Рефакторинг к шаблонам .

...