페이지

레이블이 cdt인 게시물을 표시합니다. 모든 게시물 표시
레이블이 cdt인 게시물을 표시합니다. 모든 게시물 표시

2012년 12월 13일 목요일

Manipulating Texts in Eclipse

Manipulating texts in Eclipse

History

revisionchangesauthor
0.1initial version 2012-12-05 수 Darren Ha
0.2textBuffer.commit 2012-12-13 목 Darren Ha

Introduction

manipulating texts in Eclipse way can be hard for initial Eclipse plugin developers. I couldn't find any structured tutorial or articles. So this articles is born. any comments about the contets are welcomed! I hope it helps you too.

Getting IDocument form a IPath

A FileBuffer represents a file that is being modified over time. Fille buffers for text files provide IDocument(a content model) and IAnnotationModel(a marker model). so IDdocument is essential for manipulating texts.
ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
IPath path = new Path("/sample/test.h");
try{
    bufferManager.connect(path, LocationKind.IFILE, monitor);
    ITextFileBuffer textBuffer = bufferManager.getTextFileBuffer(path, LocationKind.IFILE);
    IDocument document = textBuffer.getDocument();
}finally{
    bufferManager.disconnect(path, LocationKind.IFILE, monitor);
}  

Creating and applying TextEdit

We can modify IDocument using TextEdit subclasses: e.g. ReplaceEdit, InsertEdit, DeleteEdit to apply those changed multiple times in a file, you should use MultiTextEdit which is tree container of TextEdits. MultiTextEdit::apply can produce an error if the changes made at the same location at multiple times.
MultiTextEdit::apply doesn't change file contetns immediately. to change file content you must call ITextFileBuffer::commit.
ITextFileBuffer textBuffer = ...;
IDocument document = ..;

FindReplaceDocumentAdapter finder = new FindReplaceDocumentAdapter(document);
IRegion regionReplace = finder.find(0, "textTobeReplaced", true, true, false, false);
IRegion regionInsert = finder.find(0, "//TODO", true, true, false, false);

MultiTextEdit multiEdit = new MultiTextEdit();
multiEdit.addChild( new ReplaceEdit(regionReplace.getOffset(), regionReplace.getLength(), "ReplacedText"));
multiEdit.addChild( new InsertEdit(regionInsert.getOffset(), regionInsert.getLength()));
multiEdit.apply(document);
textBuffer.commit(monitor, true);

using CDT refactoring mechanism

You can use CDT built-in refactoring framework to change texts more elegant way. the following codes demonstrate invoking rename refactoring of CDT programtically. You can invoke this by clicking Alt+Shift+R in eclipse source editor. It's cool!
If the indexer has unresolved symbols , when build is broken, the refactoring operation can fail.
IFile file = new File("/sample/test.h");
IRegion region = ...;
CRefactoringArgument arg = new CRefactoringArgument(file, region.getOffset(), region.getLength());
CRenameProcessor proc = new CRenameProcessor(CRefactory.getInstance(), arg);
proc.setReplacementText("ReplacedText");
proc.setSelectedOptions(-1);
proc.setScope(TextSearchWrapper.SCOPE_SINGLE_PROJECT);
CRenameRefactoring refactor = new CRenameRefactoring(proc);

((CRenameProcessor)refactor.getProcessor()) .lockIndex();
try{
    RefactoringStatus rs = refactor.checkInitialConditions(monitor);
    if (rs.hasFatalError()) {
        throw new Exception("checkInitialcondtion fail");
    }
    rs = refactor.checkFinalConditions(monitor);
    if (rs.hasError()) {
        throw new Exception("checkFinalcondtion fail");
    }
    Change change = refactor.createChange(monitor);
    change.perform(monitor);
}finally{
    ((CRenameProcessor)refactor.getProcessor()).unlockIndex();
}

2012년 11월 10일 토요일

how to use DOM AST in CDT


update 2012/11/10:
 I created separate plugin to provide DOM AST viewer called ASTViewer. You can install it using the following update site :
https://raw.github.com/nberserk/ASTViewer/master/org.eclipse.cdt.ui.astviewer.update 

for more details information see here : https://github.com/nberserk/ASTViewer


I can't find any useful resources yet. so I share this information on my blog.
DOM AST viewer shows Abstract Syntax Tree of C/C++ program in tree manner. very useful for AST related work. I don't know Why this viewer is included in test plugin. that's why we can't use it in official CDT.

anyway, here is how to use DOM AST :
  • first get CDT source from CDT repository: http://git.eclipse.org/c/cdt/org.eclipse.cdt.git
    you can get source code for each released version here. e.g. > CDT_8_0_2.zip
  • copy CDT_8_0_2.zip/CDT_8_0_2/core/org.eclipse.cdt.ui.tests to your hard drive
  • import org.eclipse.cdt.ui.tests
  • remove build error ( i removed all source files except org.eclipse.cdt.ui.tests.DOMAST)
  • run
  • Window->Show View -> C/C++ -> DOM AST
  • done





2012년 10월 25일 목요일

Using AST in CDT

CDT AST(abstract syntax tree)

History

revisionchangesauthor
0.1initial version 2012-10-25 목 Darren Ha
0.2getting simplified ast tree using index 2012-10-31 수 Darren Ha

Introduction

AST는 코드에서 refactoring 등의 기능을 지원하기 위해서 만든 data structure이며 트리 구조를 가지고 있다. 이것을 이용해서 JDT 나 CDT 등에서 아주 강력하고 개발자에게 많은 도움을 주는 툴들을 만들 수 있다. refactoring 기능이나 static analyzer등의 기능들. 그렇다면 CDT의 index 와는 뭐가 틀릴까? index가 전체 프로젝트의 단위에서 동작하고 각 심볼단위(function or variable)로만 동작했다면, AST는 translation unit(header file or source file)단위로 정보를 get 할 수 있고, 각 function안의 statement 한 줄 한 줄에 대한 정보를 모두 가지고 있다. detail면에서는 ast훨씬 더 정교한 데이터를 가지고 있는 셈이다. <br/>

Creating With AST

// getting ast from CDE CoreModel
Path path = new Path("hello/src/hello.cpp");
ITranslationUnit tu = (ITranslationUnit) CoreModel.getDefault().create(path);
IASTTranslationUnit ast = tu.getAST(); // it's full ast
// excludes header from ast, much simplified ast.
IASTTranslationUnit simpleast = tu.getAST(index, ITranslationUnit.AST_SKIP_ALL_HEADERS); 
tu.getAST()의 경우는 header를 포함한 모든 ast node를 리턴하지만, tu.getAST(index, ..)의 경우는 헤더를 포함할지 하지 않을지 선택할 수 있다. 헤더를 포함하고 싶지 않을 경우에 사용하면 된다. C++의 헤더 디펜던시를 생각하면, performance gain이 크다.

Visiting AST

// traversing AST
ast.accept(new ASTVisitor() {
        {
            shouldVisitDeclarations = true;
        }
        @Override
        public int visit(IASTDeclaration declaration) {
            System.out.println(declaration);
            return PROCESS_CONTINUE;
        } // visit
    });  
ast tree는 visitor 패턴을 통해서 traverse할 수 있고, ASTVisitor의 생성자에서 visit하고 싶은 대상을 정할 수 있다. ASTVisitor::shouldVisitXXXXX 의 변수를 true로 해주면 매칭되는 node를 visit하게 된다.
예로 아래의 c++ function을 ast tree로 나타내 보자. 그럼 이해가 어느정도 될 것이다.
Application*
BasicApp::CreateInstance(void)
{
    int a;
    return new BasicApp();
}
위의 소스를 ast tree로 보면 아래처럼 표현된다. 소스에 비해서 아주 자세하게 표현되고 있다.
  • ICPPASTFunctionDefinition: BasicApp::CreateInstance
    • ICPPASTNamedTypeSepcifier: Application
    • ICPPFunctionDeclarator: Application*
      • IASTPointer
      • ICPPASTQualifiedName: BasicApp::CreateInstance
      • ICPPASTParameterDeclaration
        • ICPPASTSimpleDeclSpecifier: void
    • ICPPASTCompoundStatement: {}
      • IASTDeclarationStatement
        • ICPPASTSimpleDeclaration: a
          • ICPPASTSimpleDeclSpecifier: int
          • ICPPASTDeclarator, IASTImplicitNameOwner
            • IASTName: a
      • IASTReturnStatement: return new BasicApp();
        • IASTNewExpression
          • ICPPASTTypeId
            • ICPPASTNamedTypeSpecifier:BasicApp
              • IASTName: BasicApp
            • ICPPASTConstructorInitializer

Application

ast로 이런것을 할 수 있다. 다음은 c++에서 흔히 저지르는 실수인 if (a == b) 대신에 if (a=b) 로 assignment가 되는 실수를 detect하는 visitor를 만든 예이다. 이 예제는 reference 1에서 가져온 예임을 밝힙니다.
class CheckCodeVisitor extends ASTVisitor {
    CheckCodeVisitor() {
        shouldVisitExpressions= true;
    }
    public int visit(IASTExpression expression) {
        if (isAssignmentExpression(expression) && isUsedAsCondition(expression)) {
            System.out.println("warning ..." + expression.getFileLocation());
        }
        return PROCESS_CONTINUE;
    }
    private boolean isAssignmentExpression(IASTExpression e) {
        if (e instanceof IASTBinaryExpression) {
            IASTBinaryExpression binExpr= (IASTBinaryExpression) e;
            return binExpr.getOperator() == IASTBinaryExpression.op_assign;
        }
        return false;
    }
    private boolean isUsedAsCondition(IASTExpression expression) {
        ASTNodeProperty prop = expression.getPropertyInParent();
        if (prop == IASTForStatement.CONDITION || prop == IASTIfStatement.CONDITION)
            return true;
        return false;
    }
}

DOM AST(AST viewer)

CDT에는 DOM AST라는 AST트리를 보여주는 아주 훌륭한 viewer가 있었는데, 언젠가부터 org.eclipse.cdt.ui.tests 패키지에 포함되어 버려서 현재는 소스를 빌드하지 않으면 볼 수가없다. AST 관련 작업을 해야 한다면 필수 유틸 되겠다.

2012년 9월 20일 목요일

using cdt index

using CDT index

intro

CDT는 내부적으로 우리가 자주 사용하는 code assist, Open Declaration(F3), Open call Hierarchy(Ctrl + Alt + H) 등에 사용하기 위해서 내부적으로 c/c++ 코드를 parsing 해서 그 정보를 가지고 있는데 이것을 Index라고 한다. Indexer를 사용해서 특정 변수/함수 의 definition , declaration, reference 등의 정보를 가지고 올 수 있다.

getting IIndex

ICProject project = CoreModel.getDefault().getCModel().getCProject("hello");
CCorePlugin.getIndexManager().joinIndexer(IIndexManaer.FOREVER, null); // wait until indexing completes
IIndex index = null;
try{
    index = CCorePlugin.getIndexManager().getIndex(project);
    index.acquireReadLock(); // lock index

    IIndexBinding[] bindings = index.findBindings(Pattern.compile("main"), false, IndexFilter.All, null);
    for (IIndexBinding b : bindings) {
        System.out.print("name: " + b.getName()+" " );
        String[] qnames = b.getQualifiedName();
    }
}catch(Exception e){
    e.printStackTrace();
}finally{
    try{
        index.releaseReadLock();
    }catch (Exception e2){
        e.printStackTrace();
    }
}

Indexing 작업은 보통 main thread와 다른 thread에서 수행이 되기 때문에 joinIndexer로 인덱싱이 끝날때까지 기다린후 index에 lock을 걸고 findBinding 으로 쿼리를 수행할 수 있다.

querying

findBinding으로 IIndexBinding 을 얻고 여기서 얻은 IIndexBinding으로 아래 함수들을 불러 원하는 IIndexName을 얻을 수 있다. IIndex::findDeclarations : 함수/변수의 선언부와 구현부 IIndex::findDefinitions : 함수의 구현부 IIndex::findReferences : 합수나 변수가 사용된(use) 부분 그렇다면 함수의 선언부만 쿼리하고싶으면 어떻게 할까? IIndex::findNames(binding, IIndex.FINDDECLARATIONS)를 부르면 된다. 실제로 IIndex::findDeclarations은 IIndex::findNames(binding, IINdex.FINDDECLARATIONSDEFINITIONS)와 같다.
// finding declaration of BasicApp::CreateInstance
Pattern[] p = new Pattern[2];
p[0] = Pattern.compile("BasicApp");
p[1] = Pattern.compile("CreateInstance");
IIndexBinding[] bindings = index.findBindings(p , true, IndexFilter.CPP_DECLARED_OR_IMPLICIT, monitor);
for (IIndexBinding binding : bindings) {                
    IIndexName[] names = index.findNames(binding, IIndex.FIND_DECLARATIONS);
}  

getting index info

위 쿼리 함수들로 얻은 IIndexName으로 파일 위치나 기타 정보등을 얻을 수 있다.
for(IIndexName name: names){
    // get full path
    String path = name.getFile().getLocation().getFullPath();
    // get offset & length
    int offset = name.getNodeOffset();
    int length = name.getNodeLength();  
}