2016-08-10 4 views
2

Clang 컴파일러 (cindex)의 Python 바인딩을 사용하여 C++ 코드를 리팩터링합니다. 이를 사용하여 AST를 분석하고 변경 사항을 준비합니다. 나는 다음과 유사한 작업 목록으로 끝낼 :파이썬으로 소스 파일에 변경 사항을 적용하려면 어떻게해야합니까?

DELETE line 10 
INSERT line 5 column 32: << "tofu" 
REPLACE from line 31 colum 6 to line 33 column 82 with: std::cout << "Thanks SO" 
... 

내 질문은 실제 파일 변경으로 다음을 설정하는 방법입니다.

파이썬으로 직접하는 것은 지루한 것처럼 보입니다. 패치를 올바른 순서로 적용하고 일관성을 검사해야합니다. 그것은 꽤 열심히하고 오류가 발생하기 쉽습니다.

또한 좋은 라이브러리를 찾을 수 없습니다. (clang에는 Rewriter라고하는 것이 있지만 Python으로 래핑되지 않습니다. 가능한 경우 리팩토링을 위해 C++을 피하고 싶습니다.)

아마도 패치를 생성하고 git을 적용하는 것이 좋습니다. 그러나 그것조차 약간 지루하게 보인다.

아이디어가 있으십니까?

+1

전시 한 것은 일련의 패치입니다. 왜 그냥 순서대로 적용 할 수 없습니까? [어쩌면 당신은 AST에 여러 개의 중첩 패치를 작성하고 있습니까?] 각 변경 사항에 대해 AST를 수정 한 다음 수정 된 AST를 내뱉는 것이 더 유용할까요? 그런 다음 순서가 잘못된 패치 문제를 가질 수 없습니다. –

+0

변경 사항으로 인해 줄 번호가 무효화 될 수 있으므로 주문에주의해야합니다. 나는 AST를 수정하는 것이 최선의 선택이라고 동의한다. 불행히도 cindex는 그것을 지원하지 않는다. –

+1

가지고있는 도구로 작업을 잘 수행하지 못하면 다른 도구를 선택할 수 있습니다. C++을 구문 분석하고 C++ AST를 작성 및 수정할 수있는 툴 (DMS)을 My bio에서 확인한 다음 (변경 사항을 작성할 때 정확하게 패치하지 않는 문제를 방지 한) 올바른 소스 코드를 다시 생성합니다. 하지만 파이썬에서는 아닙니다. C++에서도 마찬가지입니다. DMS는 협업하여 변환을 지정할 수있게 해주는 DSL 배열입니다. –

답변

0

나는 내 자신을 굴려 냈다. 이 코드는 거의 확실하게 버그가 있지만별로 좋지 않습니다. 그러나 더 나은 해결책이 발견 될 때까지 누군가를 도울 수 있기를 희망합니다.

class PatchRecord(object): 
    """ Record patches, validate them, order them, and apply them """ 

    def __init__(self): 
     # output of readlines for each patched file 
     self.lines = {} 
     # list of patches for each patched file 
     self.patches = {} 

    class Patch(object): 
     """ Abstract base class for editing operations """ 

     def __init__(self, filename, start, end): 
      self.filename = filename 
      self.start = start 
      self.end = end 

     def __repr__(self): 
      return "{op}: {filename} {start}/{end} {what}".format(
       op=self.__class__.__name__.upper(), 
       filename=self.filename, 
       start=format_place(self.start), 
       end=format_place(self.end), 
       what=getattr(self, "what", "")) 

     def apply(self, lines): 
      print "Warning: applying no-op patch" 

    class Delete(Patch): 

     def __init__(self, filename, extent): 
      super(PatchRecord.Delete, self).__init__(
       filename, extent.start, extent.end) 
      print "DELETE: {file} {extent}".format(file=self.filename, 
                extent=format_extent(extent)) 

     def apply(self, lines): 
      lines[self.start.line - 1:self.end.line] = [ 
       lines[self.start.line - 1][:self.start.column - 1] + 
       lines[self.end.line - 1][self.end.column:]] 

    class Insert(Patch): 

     def __init__(self, filename, start, what): 
      super(PatchRecord.Insert, self).__init__(filename, start, start) 
      self.what = what 
      print "INSERT {where} {what}".format(what=what, where=format_place(self.start)) 

     def apply(self, lines): 
      line = lines[self.start.line - 1] 
      lines[self.start.line - 1] = "%s%s%s" % (
       line[:self.start.column], 
       self.what, 
       line[self.start.column:]) 

    class Replace(Patch): 

     def __init__(self, filename, extent, what): 
      super(PatchRecord.Replace, self).__init__(
       filename, extent.start, extent.end) 
      self.what = what 
      print "REPLACE: {where} {what}".format(what=what, 
                where=format_extent(extent)) 

     def apply(self, lines): 
      lines[self.start.line - 1:self.end.line] = [ 
       lines[self.start.line - 1][:self.start.column - 1] + 
       self.what + 
       lines[self.end.line - 1][self.end.column - 1:]] 

    # Convenience functions for creating patches 
    def delete(self, filename, extent): 
     self.patches[filename] = self.patches.get(
      filename, []) + [self.Delete(filename, extent)] 

    def insert(self, filename, where, what): 
     self.patches[filename] = self.patches.get(
      filename, []) + [self.Insert(filename, where, what)] 

    def replace(self, filename, extent, what): 
     self.patches[filename] = self.patches.get(
      filename, []) + [self.Replace(filename, extent, what)] 

    def _pos_to_tuple(self, position): 
     """ Convert a source location to a tuple for use as a sorting key """ 
     return (position.line, position.column) 

    def sort(self, filename): 
     """ Sort patches by extent start """ 
     self.patches[filename].sort(key=lambda p: self._pos_to_tuple(p.start)) 

    def validate(self, filename): 
     """Try to insure patches are consistent""" 
     print "Checking patches for %s" % filename 
     self.sort(filename) 
     previous = self.patches[filename][0] 
     for p in self.patches[filename][1:]: 
      assert(self._pos_to_tuple(p.start) > 
        self._pos_to_tuple(previous.start)) 

    def _apply(self, filename): 
     self.sort(filename) 
     lines = self._getlines(filename) 
     for p in reversed(self.patches[filename]): 
      print p 
      p.apply(lines) 

    def _getlines(self, filename): 
     """ Get source file lines for editing """ 
     if not filename in self.lines: 
      with open(filename) as f: 
       self.lines[filename] = f.readlines() 
     return self.lines[filename] 

    def apply(self): 
     for filename in self.patches: 
      self.validate(filename) 
      self._apply(filename) 
      # with open(filename+".patched","w") as output: 
      with open(filename, "w") as output: 
       output.write("".join(self._getlines(filename))) 

그냥하는 PatchRecord 객체를 생성 create, replacedelete 방법을 사용하여 변경 내용을 추가하고, 준비가되면 apply으로 적용 할 수 있습니다.