C+模糊解析的Clang+; 是否有可能用CLAN和现有的LILCLANAPI API解析不完整的C++?也就是说,解析.cpp文件,而不包括所有的头,动态推导声明。因此,例如以下文本: A B::Foo(){return stuff();}

C+模糊解析的Clang+; 是否有可能用CLAN和现有的LILCLANAPI API解析不完整的C++?也就是说,解析.cpp文件,而不包括所有的头,动态推导声明。因此,例如以下文本: A B::Foo(){return stuff();},c++,parsing,syntax,clang,fuzzy,C++,Parsing,Syntax,Clang,Fuzzy,将检测未知符号A,调用我的回调函数,该回调函数使用我的魔术启发式算法推断A是一个类,然后用与B和Foo等类似的方法调用该回调函数。最后,我希望能够推断,我看到B类的一个成员Foo返回a,而stuff是一个函数。。或者类似的东西。 上下文:我想看看我是否可以在不快速解析所有标题的情况下进行合理的语法突出显示和动态代码分析 (编辑)澄清,我正在寻找非常严格限制的C++解析,可能有一些启发式来提升一些限制。 C++语法充满了上下文依赖。Foo()是函数调用还是Foo类的临时构造?是福的东西;一个模板F

将检测未知符号A,调用我的回调函数,该回调函数使用我的魔术启发式算法推断A是一个类,然后用与B和Foo等类似的方法调用该回调函数。最后,我希望能够推断,我看到B类的一个成员Foo返回a,而stuff是一个函数。。或者类似的东西。 上下文:我想看看我是否可以在不快速解析所有标题的情况下进行合理的语法突出显示和动态代码分析

(编辑)澄清,我正在寻找非常严格限制的C++解析,可能有一些启发式来提升一些限制。 C++语法充满了上下文依赖。Foo()是函数调用还是Foo类的临时构造?是福的东西;一个模板Foo实例化和声明变量stuff,还是调用重载运算符<和运算符>?只有在上下文中才能判断,而上下文通常来自对标题的解析

我正在寻找的是一种插入自定义约定规则的方法。例如,我知道我不会重载Win32符号,所以我可以安全地假设CreateFile始终是一个函数,甚至我知道它的签名。我还知道我所有的类都以大写字母开头,都是名词,函数通常是动词,所以我可以合理地猜测Foo和Bar是类名。在更复杂的场景中,我知道我不会编写像ac这样没有副作用的表达式;所以我可以假设a始终是一个模板实例化。等等


所以,问题是是否有可能在每次遇到未知符号时使用ClangAPI回调,并使用我自己的非C++启发式方法给出答案。如果我的启发式失败,那么解析显然会失败。我不是说解析升压库:我说的是非常简单的C++,可能没有模板,限制在这种情况下CLAN可以处理的最小值。P> > P>除非你严格限制人们允许编写的代码,否则不可能解析C++(因此除了关键字/正则表达式之外的语法高亮),而不需要解析所有的头。预处理器特别擅长为您把事情搞糟


这里有一些关于模糊解析困难的想法(在visual studio的上下文中)可能会引起人们的兴趣:

我知道这个问题已经很老了,但请看一下:

LibFuzzy是一个基于Clang的启发式解析C++库 莱克瑟。模糊语法分析器是容错的,在不知道语法的情况下工作 生成系统和源文件不完整。作为解析器 如果必须进行猜测,则生成的语法树可能部分 错

它是clang highlight的一个子项目,这是一个似乎不再开发的(实验性的?)工具

我只对模糊解析部分感兴趣,并把它放在我修复了几个小问题并使工具自治的地方(它可以在clang的源代码树之外编译)。不要试图用C++14(G++6的默认模式)编译它,因为与
make_unique
会有冲突

据介绍,clang格式有自己的模糊语法分析器(并且正在积极开发),但语法分析器(是否?)与该工具的耦合更为紧密。

OP不需要“模糊语法分析器”。他想要的是完全无上下文的C++源代码解析,而不需要名称和类型解析。 他计划根据解析结果对类型进行有根据的猜测

clangproperty会使解析和名称/类型解析变得复杂,这意味着它在解析时必须具有所有可用的背景类型信息。其他答案建议使用LibFuzzy生成不正确的解析树,以及一些我一无所知的用于clang格式的模糊解析器。如果一个人坚持要生成一个经典的AST,那么这些解决方案中没有一个能够在遇到模棱两可的解析时生成“正确”的树

<>我们的DMS软件重构工具包,其C++前端可以解析C++源,生成准确的“AST”;这些实际上是抽象语法DAG,其中树中的叉表示根据语言精确语法(歧义(sub)解析)对源代码的不同可能解释

Clang试图通过在解析时使用类型信息来避免产生这些多个子解析。DMS所做的是产生歧义解析,并在(可选)后解析(属性语法评估)过程中,收集符号表信息并消除与类型不一致的子解析;对于格式良好的程序,这将生成一个没有歧义的普通AST

如果OP想对类型信息进行启发式猜测,他需要知道这些可能的解释。如果他们提前被淘汰,他无法直接猜测可能需要什么类型。一个有趣的可能性是修改属性语法(在源形式中提供的DMS的C++前端的一部分),它已经知道所有C++类型规则,用部分信息来完成这一点。这将比从头开始构建启发式分析器有巨大的优势,因为它必须从标准中了解大约600页的神秘名称和类型解析规则


我认为另一种解决方案比模糊解析更适合OP

解析时,clang通过分析器的Sema部分维护语义信息。遇到未知符号时,将返回以获取有关此符号的一些信息。通过这个,你可以实现你想要的

下面是一个如何设置它的快速示例。它不完全是功能性的(我不做
// 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;
}