2012-06-21 4 views
15

기존 libclang API를 사용하여 clang과 함께 불완전한 선언을 가진 C++을 구문 분석 할 수 있습니까? 나는. 모든 머리글을 포함하지 않고 .cpp 파일을 구문 분석하여 즉시 선언을 추론 할 수 있습니다. 그래서. 다음 텍스트 :퍼지 구문 분석을위한 Clang

A B::Foo(){return stuff();} 

이 알 수없는 기호 A를 감지 A가 내 마법 휴리스틱을 사용하여 클래스 공제 내 콜백을 호출 할 것인가는 다음이 B와 푸와 물건과 같은 방법으로 콜백 호출합니다. 결국 나는 A 클래스를 리턴 한 멤버 인 Foo가 A를 반환하는 것을 보았을 것이라고 추론 할 수 있기를 원합니다. 그리고 물건은 함수입니다. 컨텍스트 : 나는 현명한 구문 강조와 모든 헤더를 매우 빠르게 구문 분석하지 않고도 코드 분석을 할 수 있는지보고 싶습니다.

[편집] 명확히하기 위해 제한된 C++ 구문 분석을 사용하고 일부 제한 사항을 해제하기위한 휴리스틱을 사용합니다.

문법은 문맥 의존성으로 가득합니다. Foo()는 함수 호출이거나 Foo 클래스의 임시 구조입니까? Foo입니까 < 막대기 > 물건; 템플릿 Foo < 바 > 변수의 인스턴스화 및 선언, 또는 과부하가 걸린 연산자 < 및 연산자> 2가 이상하게 보입니까? 컨텍스트 내에서만 말할 수 있으며, 컨텍스트는 종종 헤더를 파싱 할 때 발생합니다.

내가 찾고있는 것은 내 사용자 규칙 규칙을 연결하는 방법입니다. 예 : Win32 심볼이 오버로드되지 않는다는 것을 알고 있으므로, CreateFile은 항상 이고, 함수는이고, 서명을 알고 있습니다. 또한 모든 수업은 대문자로 시작하고 명사이며 함수는 대개 동사이므로 Foo와 Bar가 클래스 이름이라고 합리적으로 추측 할 수 있습니다. 더 복잡한 시나리오에서, 나는 < b> c와 같은 부작용없는 표현을 작성하지 않는다는 것을 알고 있습니다. 그래서 나는 항상 템플릿 인스턴스화라고 가정 할 수 있습니다. 등등.

그래서 Clang API를 사용하여 알 수없는 기호를 만날 때마다 콜백 할 수 있는지, 그리고 내 자신의 비 C++ 추론을 사용하여 대답을 줄 수 있는지 여부는 질문입니다. 내 경험에 실패하면 구문 분석이 실패합니다. 그리고 나는 부스트 라이브러리를 파싱하는 것에 대해 이야기하지 않고있다. 나는 매우 단순한 C++에 대해서 말하고있다. 아마도 템플릿이 없으며, clang이이 경우에 처리 할 수있는 최소한으로 제한된다.

+0

언제든지 CLang을 직접 수정할 수 있습니다. 조회가 합법적으로 아무 것도 발견되지 않을 때가 여러 번 있기 때문에 (예 : 종속 컨텍스트, ADL) 얼마나 쉬운 일인지 확신 할 수 없습니다. –

+0

분명히 clang이 필요합니까? 그렇지 않다면 아마도 다른 솔루션을 시도해 보는 것이 합리적일까요? 그들이 더 잘 수행 할 수도 있습니다. –

+0

그래, 난 antlr 보았다, 그리고 그것이 더 어렵고 덜 performant있을 용의자지만, 가능합니다 .. 사실 난 제한된 일부 C + +를 구문 분석하는 antlr 사용하고 있으므로 나에게 친숙한 것입니다. 다른 대안이 있습니까? –

답변

3

좀 더 퍼지 분석보다 영업 이익에 맞게 생각하는 또 다른 솔루션입니다.

구문 분석시 clang은 분석기의 Sema 부분을 통해 시맨틱 정보를 유지 관리합니다. 알 수없는 기호가 발견되면 SemaExternalSemaSource으로 폴백하여이 기호에 대한 정보를 얻습니다. 이를 통해 원하는 것을 구현할 수 있습니다.

다음은 빠른 설정 예입니다. 전적으로 기능적이지는 않습니다. (LookupUnqualified 메소드에서 아무 것도하지 않습니다.) 추가 조사가 필요할 수 있으며 이는 좋은 출발이라고 생각합니다.

// Declares clang::SyntaxOnlyAction. 
#include <clang/Frontend/FrontendActions.h> 
#include <clang/Tooling/CommonOptionsParser.h> 
#include <clang/Tooling/Tooling.h> 
#include <llvm/Support/CommandLine.h> 
#include <clang/AST/AST.h> 
#include <clang/AST/ASTConsumer.h> 
#include <clang/AST/RecursiveASTVisitor.h> 
#include <clang/Frontend/ASTConsumers.h> 
#include <clang/Frontend/FrontendActions.h> 
#include <clang/Frontend/CompilerInstance.h> 
#include <clang/Tooling/CommonOptionsParser.h> 
#include <clang/Tooling/Tooling.h> 
#include <clang/Rewrite/Core/Rewriter.h> 
#include <llvm/Support/raw_ostream.h> 
#include <clang/Sema/ExternalSemaSource.h> 
#include <clang/Sema/Sema.h> 
#include "clang/Basic/DiagnosticOptions.h" 
#include "clang/Frontend/TextDiagnosticPrinter.h" 
#include "clang/Frontend/CompilerInstance.h" 
#include "clang/Basic/TargetOptions.h" 
#include "clang/Basic/TargetInfo.h" 
#include "clang/Basic/FileManager.h" 
#include "clang/Basic/SourceManager.h" 
#include "clang/Lex/Preprocessor.h" 
#include "clang/Basic/Diagnostic.h" 
#include "clang/AST/ASTContext.h" 
#include "clang/AST/ASTConsumer.h" 
#include "clang/Parse/Parser.h" 
#include "clang/Parse/ParseAST.h" 
#include <clang/Sema/Lookup.h> 

#include <iostream> 
using namespace clang; 
using namespace clang::tooling; 
using namespace llvm; 

class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> { 
private: 
    ASTContext *astContext; 

public: 
    explicit ExampleVisitor(CompilerInstance *CI, StringRef file) 
     : astContext(&(CI->getASTContext())) {} 

    virtual bool VisitVarDecl(VarDecl *d) { 
    std::cout << d->getNameAsString() << "@\n"; 
    return true; 
    } 
}; 

class ExampleASTConsumer : public ASTConsumer { 
private: 
    ExampleVisitor visitor; 

public: 
    explicit ExampleASTConsumer(CompilerInstance *CI, StringRef file) 
     : visitor(CI, file) {} 
    virtual void HandleTranslationUnit(ASTContext &Context) { 
    // de cette façon, on applique le visiteur sur l'ensemble de la translation 
    // unit 
    visitor.TraverseDecl(Context.getTranslationUnitDecl()); 
    } 
}; 

class DynamicIDHandler : public clang::ExternalSemaSource { 
public: 
    DynamicIDHandler(clang::Sema *Sema) 
     : m_Sema(Sema), m_Context(Sema->getASTContext()) {} 
    ~DynamicIDHandler() = default; 

    /// \brief Provides last resort lookup for failed unqualified lookups 
    /// 
    /// If there is failed lookup, tell sema to create an artificial declaration 
    /// which is of dependent type. So the lookup result is marked as dependent 
    /// and the diagnostics are suppressed. After that is's an interpreter's 
    /// responsibility to fix all these fake declarations and lookups. 
    /// It is done by the DynamicExprTransformer. 
    /// 
    /// @param[out] R The recovered symbol. 
    /// @param[in] S The scope in which the lookup failed. 
    virtual bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) { 
    DeclarationName Name = R.getLookupName(); 
    std::cout << Name.getAsString() << "\n"; 
    // IdentifierInfo *II = Name.getAsIdentifierInfo(); 
    // SourceLocation Loc = R.getNameLoc(); 
    // VarDecl *Result = 
    //  // VarDecl::Create(m_Context, R.getSema().getFunctionLevelDeclContext(), 
    //  //     Loc, Loc, II, m_Context.DependentTy, 
    //  //     /*TypeSourceInfo*/ 0, SC_None, SC_None); 
    // if (Result) { 
    // R.addDecl(Result); 
    // // Say that we can handle the situation. Clang should try to recover 
    // return true; 
    // } else{ 
    // return false; 
    // } 
    return false; 
    } 

private: 
    clang::Sema *m_Sema; 
    clang::ASTContext &m_Context; 
}; 

// *****************************************************************************/ 

LangOptions getFormattingLangOpts(bool Cpp03 = false) { 
    LangOptions LangOpts; 
    LangOpts.CPlusPlus = 1; 
    LangOpts.CPlusPlus11 = Cpp03 ? 0 : 1; 
    LangOpts.CPlusPlus14 = Cpp03 ? 0 : 1; 
    LangOpts.LineComment = 1; 
    LangOpts.Bool = 1; 
    LangOpts.ObjC1 = 1; 
    LangOpts.ObjC2 = 1; 
    return LangOpts; 
} 

int main() { 
    using clang::CompilerInstance; 
    using clang::TargetOptions; 
    using clang::TargetInfo; 
    using clang::FileEntry; 
    using clang::Token; 
    using clang::ASTContext; 
    using clang::ASTConsumer; 
    using clang::Parser; 
    using clang::DiagnosticOptions; 
    using clang::TextDiagnosticPrinter; 

    CompilerInstance ci; 
    ci.getLangOpts() = getFormattingLangOpts(false); 
    DiagnosticOptions diagnosticOptions; 
    ci.createDiagnostics(); 

    std::shared_ptr<clang::TargetOptions> pto = std::make_shared<clang::TargetOptions>(); 
    pto->Triple = llvm::sys::getDefaultTargetTriple(); 

    TargetInfo *pti = TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pto); 

    ci.setTarget(pti); 
    ci.createFileManager(); 
    ci.createSourceManager(ci.getFileManager()); 
    ci.createPreprocessor(clang::TU_Complete); 
    ci.getPreprocessorOpts().UsePredefines = false; 
    ci.createASTContext(); 

    ci.setASTConsumer(
     llvm::make_unique<ExampleASTConsumer>(&ci, "../src/test.cpp")); 

    ci.createSema(TU_Complete, nullptr); 
    auto &sema = ci.getSema(); 
    sema.Initialize(); 
    DynamicIDHandler handler(&sema); 
    sema.addExternalSource(&handler); 

    const FileEntry *pFile = ci.getFileManager().getFile("../src/test.cpp"); 
    ci.getSourceManager().setMainFileID(ci.getSourceManager().createFileID(
     pFile, clang::SourceLocation(), clang::SrcMgr::C_User)); 
    ci.getDiagnosticClient().BeginSourceFile(ci.getLangOpts(), 
              &ci.getPreprocessor()); 
    clang::ParseAST(sema,true,false); 
    ci.getDiagnosticClient().EndSourceFile(); 

    return 0; 
} 

아이디어와 DynamicIDHandler 클래스는 알 수없는 기호 (따라서 의견과 코드) 변수이다 cling 프로젝트입니다.

5

사람들이 작성할 수있는 코드를 크게 제한하지 않는 한 모든 헤더를 구문 분석하지 않고 C++ (그리고 키워드/정규 표현식 이외의 구문 강조 표시)를 구문 분석하는 것은 기본적으로 불가능합니다. 전처리 기는 특히 당신을 위해 일을 망칠 때 유용합니다.

관심이있을 수 있습니다 여기 (비주얼 스튜디오의 맥락에서) 퍼지 분석의 어려움에 대한 몇 가지 생각이 있습니다 : http://blogs.msdn.com/b/vcblog/archive/2011/03/03/10136696.aspx

+0

그래, 일부 제한을 해제하기 위해 경험적으로 매우 무겁게 제한된 C++ 구문 분석을 찾고 있습니다. C++ 문법은 문맥 의존성으로 가득차 있습니다. 가능하다면 궁금해하니 –

+0

... 내 질문에 설명을 추가했습니다. 답변 해 주셔서 감사합니다. –

5

나는 질문은 꽤 오래 알고 있지만,보고를 here 있습니다

LibFuzzy는 Clang의 Lexer를 기반으로 C++를 경험적으로 파싱하기위한 라이브러리입니다. 퍼지 파서는 내결함성이 있으며, 빌드 시스템 및 불완전한 소스 파일에 대한 지식없이 작동합니다. 파서 이 반드시 추측을하므로 결과 구문 트리의 일부가 부분적으로 일 수 있습니다.

더 이상 개발되지 않은 것으로 보이는 실험적 도구 인 clang-highlight의 하위 프로젝트입니다.

저는 퍼지 구문 분석 부분에만 관심이 있으며 my github page에 갈래서 몇 가지 사소한 문제를 해결하고 툴을 자율적으로 만들었습니다 (clang의 소스 트리 외부에서 컴파일 할 수 있음). make_unique과의 충돌이 있기 때문에 C++ 14 (G ++ 6의 기본 모드)로 컴파일하려고하지 마십시오.

this page에 따르면 clang 형식에는 고유 한 퍼지 구문 분석기가 있고 (적극적으로 개발되었지만) 파서는 도구에 더 밀접하게 결합되어 있습니다.

0

OP는 "퍼지 구문 분석"을 원하지 않습니다. 그가 원하는 것은 완전한 문맥이다. 은 이름과 타입 해석에 대한 요구없이 C++ 소스 코드를 파싱한다. 그는 구문 분석의 결과를 기반으로 유형에 대해 교육적인 추측을 할 계획입니다.

구문 분석 및 이름/형식 확인을 적절히 처리하면 구문 분석시 사용할 수있는 모든 배경 형식 정보가 있어야합니다. 다른 답변은 부정확 한 구문 분석 트리를 생성하는 LibFuzzy와 내가 알지 못하는 clang 형식의 퍼지 구문 분석기를 제안합니다. 고전적인 AST를 고집한다면,이 솔루션들 중 어느 것도 모호한 구문 분석에 직면하여 "올바른"트리를 생성하지 못합니다.

우리 DMS 소프트웨어 재 설계 툴킷은 C++ 프론트 엔드 해석 C++ 소스 without the type information 및 생산 정확한 "하는 AST '와; 이들은 실제로 추상 구문 dags입니다. 여기서 나무에있는 분기는 정확한 언어 문법 (모호한 (012) 파문)에 따라 소스 코드의 다양한 해석을 나타냅니다.

Clang이 분석하려고하는 형식 정보를 사용하여 이러한 여러 개의 하위 구문을 생성하지 않아야합니다. DMS가하는 일은 모호한 구문 분석을 생성하고, (선택적) 사후 구문 분석 (특성 - 문법 평가) 단계에서는 기호 테이블 정보를 수집하고 형식과 일치하지 않는 하위 구문을 제거합니다. 잘 형성된 프로그램의 경우 모호성이없는 일반 AST가 생성됩니다.

OP가 유형 정보에 대한 경험적 추측을 원할 경우 그는 이러한 가능한 해석을 알아야합니다. 사전에 제거되면 필요한 유형을 직설적으로 추측 할 수 없습니다. 흥미로운 가능성은 속성 정보 문법 (DMS의 C++ 프론트 엔드의 일부로 소스 형식으로 제공됨)을 수정하는 아이디어입니다.이 문법은 이미 C++ 유형 규칙을 모두 알고 있으므로 부분 정보로이를 수행합니다. 이것은 표준에서부터 약 600 페이지에 달하는 아케인 이름과 타입 분해능 규칙을 알아야한다는 점에서 휴리스틱 애널라이저를 처음부터 빌드하는 것보다 더할 나위없는 일입니다.

You can see examples of the (dag) produced by DMS's parser.

관련 문제