anchor.diff:      function TransformDouble(const Expression: Compute.ExprTrees.Expr): string;
     end;
   
  -  TGPUTransformKernelGenerator = class(TInterfacedObject, IGPUTransformKernelGenerator, IExprNodeVisitor)
  +  TGPUTransformKernelGenerator = class(TExpressionGeneratorBase, IGPUTransformKernelGenerator)
     strict private
  -    FOutput: string;
  -
  -    procedure Visit(const Node: IConstantNode); overload;
  -    procedure Visit(const Node: IVariableNode); overload;
  -    procedure Visit(const Node: IArrayElementNode); overload;
  -    procedure Visit(const Node: IUnaryOpNode); overload;
  -    procedure Visit(const Node: IBinaryOpNode); overload;
  -    procedure Visit(const Node: IFuncNode); overload;
  -    procedure Visit(const Node: ILambdaParamNode); overload;
  -
       function TransformDouble(const Expression: Compute.ExprTrees.Expr): string;
  +  protected
  +    procedure Visit(const Node: IVariableNode); override;
  +    procedure Visit(const Node: IArrayElementNode); override;
  +    procedure Visit(const Node: ILambdaParamNode); override;
     public
       constructor Create;
     end;
  
positive.code:     function GetFunctions: TArray<Expr.NaryFunc>;
    end;
  
    IUserFuncBodyGenerator = interface
      ['{A18ECAD5-7F4B-4D47-A606-2FACBA5AF3C3}']
  
      function GenerateUserFuncBody(const FuncBody: Compute.ExprTrees.Expr): string;
    end;
  
    TUserFuncBodyGenerator = class(TInterfacedObject, IUserFuncBodyGenerator, IExprNodeVisitor)
    strict private
      FOutput: string;
  
      procedure Visit(const Node: IConstantNode); overload;
      procedure Visit(const Node: IVariableNode); overload;
      procedure Visit(const Node: IArrayElementNode); overload;
      procedure Visit(const Node: IUnaryOpNode); overload;
      procedure Visit(const Node: IBinaryOpNode); overload;
      procedure Visit(const Node: IFuncNode); overload;
      procedure Visit(const Node: ILambdaParamNode); overload;
  
      function GenerateUserFuncBody(const FuncBody: Compute.ExprTrees.Expr): string;
    public
      constructor Create;
    end;
  
negative.code: 
  end;
  
  { TExpressionGeneratorBase }
  
  procedure TExpressionGeneratorBase.Clear;
  begin
    FOutput := '';
  end;
  
  constructor TExpressionGeneratorBase.Create;
  begin
    inherited Create;
  end;
  
  procedure TExpressionGeneratorBase.Emit(const s: string);
  begin
    FOutput := FOutput + s;
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IArrayElementNode);
  begin
    Emit(Node.Data.Name + '[');
    Node.Data.Index.Accept(Self);
    Emit(']');
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IVariableNode);
  begin
    Emit(Node.Data.Name);
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IConstantNode);
  begin
    Emit(FloatToStr(Node.Data.Value, OpenCLFormatSettings));
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IUnaryOpNode);
  begin
    Emit('(');
    case Node.Op of
      uoNot: Emit('!');
      uoNegate: Emit('-');
    else
      raise ENotImplemented.Create('Unknown unary operator');
    end;
    Node.ChildNode.Accept(Self);
    Emit(')');
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: ILambdaParamNode);
  begin
    Emit(Node.Data.Name);
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IFuncNode);
  var
    i: integer;
  begin
    Emit(Node.Data.Name + '(');
  
    for i := 0 to Node.Data.ParamCount-1 do
    begin
      if (i > 0) then
        Emit(', ');
  
      Node.Data.Params[i].Accept(Self);
    end;
  
    Emit(')');
  end;
  
  procedure TExpressionGeneratorBase.Visit(const Node: IBinaryOpNode);
  begin
    Emit('(');
    Node.ChildNode1.Accept(Self);
    case Node.Op of
      boAdd: Emit(' + ');
      boSub: Emit(' - ');
      boMul: Emit(' * ');
      boDiv: Emit(' / ');
      boAnd: Emit(' & ');
      boOr: Emit(' | ');
      boXor: Emit(' ^ ');
      boEq: Emit(' == ');
      boNotEq: Emit(' != ');
      boLess: Emit(' < ');
      boLessEq: Emit(' <= ');
      boGreater: Emit(' > ');
      boGreaterEq: Emit(' >= ');
    else
      raise ENotImplemented.Create('Unknown binary operator');
    end;
    Node.ChildNode2.Accept(Self);
    Emit(')');
  end;
  
  { TUserFuncBodyGenerator }
  
  constructor TUserFuncBodyGenerator.Create;
  begin
    inherited Create;
  end;
  
  function TUserFuncBodyGenerator.GenerateUserFuncBody(
    const FuncBody: Compute.ExprTrees.Expr): string;
  begin
    Clear;
  
    FuncBody.Accept(Self);
  
    result := Output;
  end;
  
  procedure TUserFuncBodyGenerator.Visit(const Node: IVariableNode);
  begin
    raise ENotImplemented.Create('Variables in functions not implemented');
  end;
  
  { TKernelGeneratorBase }
  