This commit is contained in:
Jacob Dufault 2017-02-17 01:57:44 -08:00
parent 5dbde940b6
commit 0dc27bd3ac
28 changed files with 1675 additions and 211 deletions

View File

@ -7,7 +7,7 @@ clang::CodeCompleteResults::CodeCompleteResults(CXTranslationUnit &cx_tu,
const std::string &buffer,
unsigned line_num, unsigned column) {
CXUnsavedFile files[1];
auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu));
auto file_path=ToString(clang_getTranslationUnitSpelling(cx_tu));
files[0].Filename = file_path.c_str();
files[0].Contents = buffer.c_str();
files[0].Length = buffer.size();
@ -41,5 +41,5 @@ clang::CompletionString clang::CodeCompleteResults::get(unsigned i) const {
}
std::string clang::CodeCompleteResults::get_usr() const {
return to_string(clang_codeCompleteGetContainerUSR(cx_results));
return ToString(clang_codeCompleteGetContainerUSR(cx_results));
}

View File

@ -6,7 +6,7 @@ std::string clang::CompileCommand::get_command() {
std::string res;
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
for (unsigned i = 0; i < N; i++) {
res += to_string(clang_CompileCommand_getArg(cx_command, i));
res += ToString(clang_CompileCommand_getArg(cx_command, i));
}
return res;
}
@ -15,7 +15,7 @@ std::vector<std::string> clang::CompileCommand::get_command_as_args() {
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
std::vector<std::string> res(N);
for (unsigned i = 0; i < N; i++) {
res[i] = to_string(clang_CompileCommand_getArg(cx_command, i));
res[i] = ToString(clang_CompileCommand_getArg(cx_command, i));
}
return res;
}

View File

@ -15,13 +15,13 @@ unsigned clang::CompletionString::get_num_chunks() {
std::vector<clang::CompletionChunk> clang::CompletionString::get_chunks() {
std::vector<CompletionChunk> res;
for (unsigned i = 0; i < get_num_chunks(); i++) {
res.emplace_back(to_string(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (clang_getCompletionChunkKind(cx_completion_sting, i)));
res.emplace_back(ToString(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (clang_getCompletionChunkKind(cx_completion_sting, i)));
}
return res;
}
std::string clang::CompletionString::get_brief_comments() {
return to_string(clang_getCompletionBriefComment(cx_completion_sting));
return ToString(clang_getCompletionBriefComment(cx_completion_sting));
}
clang::CompletionChunk::CompletionChunk(std::string chunk, CompletionChunkKind kind) :

View File

@ -4,28 +4,38 @@
namespace clang {
std::string Cursor::Type::get_spelling() const {
return to_string(clang_getTypeSpelling(cx_type));
static_assert(sizeof(Cursor) == sizeof(CXCursor), "Cursor must be the same size as CXCursor");
std::string Type::get_spelling() const {
return ToString(clang_getTypeSpelling(cx_type));
}
Cursor::Type Cursor::Type::get_result() const {
return Type(clang_getResultType(cx_type));
Type Cursor::get_type() const {
return Type(clang_getCursorType(cx_cursor));
}
Cursor::Cursor() = default;
Cursor::Cursor(const CXCursor& other) : cx_cursor(cx_cursor) {}
bool Cursor::Type::operator==(const Cursor::Type& rhs) const {
bool Type::operator==(const Type& rhs) const {
return clang_equalTypes(cx_type, rhs.cx_type);
}
CXCursorKind Cursor::get_kind() const {
return clang_getCursorKind(cx_cursor);
Type Type::get_result() const {
return Type(clang_getResultType(cx_type));
}
Cursor::Type Cursor::get_type() const {
return Type(clang_getCursorType(cx_cursor));
Cursor::Cursor() : cx_cursor(clang_getNullCursor()) {}
Cursor::Cursor(const CXCursor& other) : cx_cursor(other) {}
Cursor::operator bool() const {
return !clang_Cursor_isNull(cx_cursor);
}
bool Cursor::operator==(const Cursor& rhs) const {
return clang_equalCursors(cx_cursor, rhs.cx_cursor);
}
CXCursorKind Cursor::get_kind() const {
return cx_cursor.kind;
}
SourceLocation Cursor::get_source_location() const {
@ -37,15 +47,19 @@ SourceRange Cursor::get_source_range() const {
}
std::string Cursor::get_spelling() const {
return to_string(clang_getCursorSpelling(cx_cursor));
return ToString(clang_getCursorSpelling(cx_cursor));
}
std::string Cursor::get_display_name() const {
return to_string(clang_getCursorDisplayName(cx_cursor));
return ToString(clang_getCursorDisplayName(cx_cursor));
}
std::string Cursor::get_usr() const {
return to_string(clang_getCursorUSR(cx_cursor));
return ToString(clang_getCursorUSR(cx_cursor));
}
bool Cursor::is_definition() const {
return clang_isCursorDefinition(cx_cursor);
}
Cursor Cursor::get_referenced() const {
@ -72,14 +86,6 @@ std::vector<Cursor> Cursor::get_arguments() const {
return cursors;
}
Cursor::operator bool() const {
return !clang_Cursor_isNull(cx_cursor);
}
bool Cursor::operator==(const Cursor& rhs) const {
return clang_equalCursors(cx_cursor, rhs.cx_cursor);
}
bool Cursor::is_valid_kind() const {
CXCursor referenced = clang_getCursorReferenced(cx_cursor);
if (clang_Cursor_isNull(referenced))
@ -96,13 +102,13 @@ std::string Cursor::get_type_description() const {
auto referenced = clang_getCursorReferenced(cx_cursor);
if (!clang_Cursor_isNull(referenced)) {
auto type = clang_getCursorType(referenced);
spelling = to_string(clang_getTypeSpelling(type));
spelling = ToString(clang_getTypeSpelling(type));
#if CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32
const std::string auto_str = "auto";
if (spelling.size() >= 4 && std::equal(auto_str.begin(), auto_str.end(), spelling.begin())) {
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type));
auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type));
if (spelling.size() > 5 && spelling[4] == ' ' && spelling[5] == '&' && spelling != canonical_spelling)
return canonical_spelling + " &";
else
@ -112,7 +118,7 @@ std::string Cursor::get_type_description() const {
const std::string const_auto_str = "const auto";
if (spelling.size() >= 10 && std::equal(const_auto_str.begin(), const_auto_str.end(), spelling.begin())) {
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type));
auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type));
if (spelling.size() > 11 && spelling[10] == ' ' && spelling[11] == '&' && spelling != canonical_spelling)
return canonical_spelling + " &";
else
@ -127,10 +133,10 @@ std::string Cursor::get_type_description() const {
return spelling;
}
std::string Cursor::get_brief_comments() const {
std::string Cursor::get_comments() const {
Cursor referenced = get_referenced();
if (referenced)
return to_string(clang_Cursor_getBriefCommentText(referenced.cx_cursor));
return ToString(clang_Cursor_getRawCommentText(referenced.cx_cursor));
return "";
}

View File

@ -3,49 +3,76 @@
#include <string>
#include <vector>
#include <clang-c/Index.h>
#include <type_traits>
#include <clang-c/Index.h>
#include "SourceLocation.h"
#include "SourceRange.h"
namespace clang {
class Cursor {
public:
class Type {
public:
Type(const CXType &cx_type) : cx_type(cx_type) {}
std::string get_spelling() const;
Type get_result() const;
bool operator==(const Cursor::Type& rhs) const;
CXType cx_type;
};
Cursor();
explicit Cursor(const CXCursor& cx_cursor);
class Type {
public:
Type(const CXType &cx_type) : cx_type(cx_type) {}
CXCursorKind get_kind() const;
Type get_type() const;
SourceLocation get_source_location() const;
SourceRange get_source_range() const;
std::string get_spelling() const;
std::string get_display_name() const;
std::string get_usr() const;
Cursor get_referenced() const;
Cursor get_canonical() const;
Cursor get_definition() const;
Cursor get_semantic_parent() const;
std::vector<Cursor> get_arguments() const;
operator bool() const;
bool operator==(const Cursor& rhs) const;
bool is_valid_kind() const;
std::string get_type_description() const;
std::string get_brief_comments() const;
CXCursor cx_cursor = clang_getNullCursor();
bool operator==(const Type& rhs) const;
std::string get_spelling() const;
Type get_result() const;
CXType cx_type;
};
enum class VisiterResult {
Break,
Continue,
Recurse
};
class Cursor {
public:
Cursor();
explicit Cursor(const CXCursor& other);
operator bool() const;
bool operator==(const Cursor& rhs) const;
CXCursorKind get_kind() const;
Type get_type() const;
SourceLocation get_source_location() const;
SourceRange get_source_range() const;
std::string get_spelling() const;
std::string get_display_name() const;
std::string get_usr() const;
bool is_definition() const;
Cursor get_referenced() const;
Cursor get_canonical() const;
Cursor get_definition() const;
Cursor get_semantic_parent() const;
std::vector<Cursor> get_arguments() const;
bool is_valid_kind() const;
std::string get_type_description() const;
std::string get_comments() const;
template<typename TClientData>
using Visitor = VisiterResult(*)(Cursor cursor, Cursor parent, TClientData* client_data);
enum class VisitResult {
Completed, EndedEarly
};
template<typename TClientData>
VisitResult VisitChildren(Visitor<TClientData> visitor, TClientData* client_data) const {
if (clang_visitChildren(cx_cursor, reinterpret_cast<CXCursorVisitor>(visitor), client_data) == 0)
return VisitResult::Completed;
return VisitResult::EndedEarly;
}
CXCursor cx_cursor;
};
} // namespace clang
#endif // CURSOR_H_

View File

@ -6,7 +6,7 @@
clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic) {
severity=clang_getDiagnosticSeverity(cx_diagnostic);
severity_spelling=get_severity_spelling(severity);
spelling=to_string(clang_getDiagnosticSpelling(cx_diagnostic));
spelling=ToString(clang_getDiagnosticSpelling(cx_diagnostic));
SourceLocation start_location(clang_getDiagnosticLocation(cx_diagnostic));
path=start_location.get_path();
@ -18,7 +18,7 @@ clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnos
unsigned num_fix_its=clang_getDiagnosticNumFixIts(cx_diagnostic);
for(unsigned c=0;c<num_fix_its;c++) {
CXSourceRange fix_it_range;
auto source=to_string(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
auto source=ToString(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
fix_its.emplace_back(source, SourceRange(fix_it_range).get_offsets());
}
}

View File

@ -6,27 +6,28 @@
#include "SourceRange.h"
namespace clang {
class Diagnostic {
friend class TranslationUnit;
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
class Diagnostic {
friend class TranslationUnit;
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
public:
class FixIt {
public:
class FixIt {
public:
FixIt(const std::string &source, const std::pair<clang::Offset, clang::Offset> &offsets):
source(source), offsets(offsets) {}
std::string source;
std::pair<clang::Offset, clang::Offset> offsets;
};
static const std::string get_severity_spelling(unsigned severity);
unsigned severity;
std::string severity_spelling;
std::string spelling;
std::string path;
FixIt(const std::string &source, const std::pair<clang::Offset, clang::Offset> &offsets) :
source(source), offsets(offsets) {}
std::string source;
std::pair<clang::Offset, clang::Offset> offsets;
std::vector<FixIt> fix_its;
};
}
static const std::string get_severity_spelling(unsigned severity);
unsigned severity;
std::string severity_spelling;
std::string spelling;
std::string path;
std::pair<clang::Offset, clang::Offset> offsets;
std::vector<FixIt> fix_its;
};
}
#endif // DIAGNOSTIC_H_

View File

@ -31,7 +31,7 @@ void SourceLocation::get_data(std::string* path, unsigned *line, unsigned *colum
CXFile file;
clang_getExpansionLocation(cx_location, &file, line, column, offset);
if (file != nullptr) {
*path = to_string(clang_getFileName(file));
*path = ToString(clang_getFileName(file));
}
}
}

View File

@ -17,7 +17,7 @@ clang::SourceRange clang::Token::get_source_range() const {
}
// returns a string description of this tokens kind
std::string clang::Token::get_spelling() const {
return to_string(clang_getTokenSpelling(cx_tu, cx_token));
return ToString(clang_getTokenSpelling(cx_tu, cx_token));
}
clang::Token::Kind clang::Token::get_kind() const {

View File

@ -5,45 +5,44 @@
#include <fstream>
#include <sstream>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
namespace clang {
clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
const std::string &buffer, unsigned flags) {
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
const std::string &buffer, unsigned flags) {
std::vector<const char*> args;
for(auto &a: command_line_args) {
for (auto &a : command_line_args) {
args.push_back(a.c_str());
}
CXUnsavedFile files[1];
files[0].Filename=file_path.c_str();
files[0].Contents=buffer.c_str();
files[0].Length=buffer.size();
files[0].Filename = file_path.c_str();
files[0].Contents = buffer.c_str();
files[0].Length = buffer.size();
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), files, 1, flags);
args.size(), files, 1, flags);
}
clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
unsigned flags) {
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
unsigned flags) {
std::vector<const char*> args;
for(auto &a: command_line_args) {
for (auto &a : command_line_args) {
args.push_back(a.c_str());
}
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), nullptr, 0, flags);
args.size(), nullptr, 0, flags);
}
clang::TranslationUnit::~TranslationUnit() {
TranslationUnit::~TranslationUnit() {
clang_disposeTranslationUnit(cx_tu);
}
void clang::TranslationUnit::parse(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
const std::map<std::string, std::string> &buffers, unsigned flags) {
void TranslationUnit::parse(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
const std::map<std::string, std::string> &buffers, unsigned flags) {
std::vector<CXUnsavedFile> files;
for (auto &buffer : buffers) {
CXUnsavedFile file;
@ -53,76 +52,81 @@ void clang::TranslationUnit::parse(Index &index, const std::string &file_path,
files.push_back(file);
}
std::vector<const char*> args;
for(auto &a: command_line_args) {
for (auto &a : command_line_args) {
args.push_back(a.c_str());
}
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), files.data(), files.size(), flags);
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), files.data(), files.size(), flags);
}
int clang::TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) {
int TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) {
CXUnsavedFile files[1];
auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu));
files[0].Filename=file_path.c_str();
files[0].Contents=buffer.c_str();
files[0].Length=buffer.size();
auto file_path = ToString(clang_getTranslationUnitSpelling(cx_tu));
files[0].Filename = file_path.c_str();
files[0].Contents = buffer.c_str();
files[0].Length = buffer.size();
return clang_reparseTranslationUnit(cx_tu, 1, files, flags);
}
unsigned clang::TranslationUnit::DefaultFlags() {
auto flags=
unsigned TranslationUnit::DefaultFlags() {
auto flags =
CXTranslationUnit_CacheCompletionResults |
CXTranslationUnit_PrecompiledPreamble |
CXTranslationUnit_Incomplete |
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
#if CINDEX_VERSION_MAJOR>0 || (CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR>=35)
flags|=CXTranslationUnit_KeepGoing;
flags |= CXTranslationUnit_KeepGoing;
#endif
return flags;
}
clang::CodeCompleteResults clang::TranslationUnit::get_code_completions(const std::string &buffer,
unsigned line_number, unsigned column) {
CodeCompleteResults TranslationUnit::get_code_completions(const std::string &buffer,
unsigned line_number, unsigned column) {
CodeCompleteResults results(cx_tu, buffer, line_number, column);
return results;
}
std::vector<clang::Diagnostic> clang::TranslationUnit::get_diagnostics() {
std::vector<Diagnostic> TranslationUnit::get_diagnostics() {
std::vector<Diagnostic> diagnostics;
for(unsigned c=0;c<clang_getNumDiagnostics(cx_tu);c++) {
CXDiagnostic clang_diagnostic=clang_getDiagnostic(cx_tu, c);
for (unsigned c = 0; c < clang_getNumDiagnostics(cx_tu); c++) {
CXDiagnostic clang_diagnostic = clang_getDiagnostic(cx_tu, c);
diagnostics.emplace_back(Diagnostic(cx_tu, clang_diagnostic));
clang_disposeDiagnostic(clang_diagnostic);
}
return diagnostics;
}
std::unique_ptr<clang::Tokens> clang::TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) {
auto path=clang::to_string(clang_getTranslationUnitSpelling(cx_tu));
std::unique_ptr<Tokens> TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) {
auto path = ToString(clang_getTranslationUnitSpelling(cx_tu));
SourceLocation start_location(cx_tu, path, start_offset);
SourceLocation end_location(cx_tu, path, end_offset);
SourceRange range(start_location, end_location);
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
}
std::unique_ptr<clang::Tokens> clang::TranslationUnit::get_tokens(unsigned start_line, unsigned start_column,
unsigned end_line, unsigned end_column) {
auto path=to_string(clang_getTranslationUnitSpelling(cx_tu));
std::unique_ptr<Tokens> TranslationUnit::get_tokens(unsigned start_line, unsigned start_column, unsigned end_line, unsigned end_column) {
auto path = ToString(clang_getTranslationUnitSpelling(cx_tu));
SourceLocation start_location(cx_tu, path, start_line, start_column);
SourceLocation end_location(cx_tu, path, end_line, end_column);
SourceRange range(start_location, end_location);
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
}
clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned offset) {
Cursor TranslationUnit::document_cursor() const {
return Cursor(clang_getTranslationUnitCursor(cx_tu));
}
Cursor TranslationUnit::get_cursor(std::string path, unsigned offset) {
SourceLocation location(cx_tu, path, offset);
return Cursor(clang_getCursor(cx_tu, location.cx_location));
}
clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) {
Cursor TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) {
SourceLocation location(cx_tu, path, line, column);
return Cursor(clang_getCursor(cx_tu, location.cx_location));
}
}

View File

@ -44,6 +44,8 @@ namespace clang {
std::unique_ptr<Tokens> get_tokens(unsigned start_line, unsigned start_column,
unsigned end_line, unsigned end_column);
Cursor document_cursor() const;
clang::Cursor get_cursor(std::string path, unsigned offset);
clang::Cursor get_cursor(std::string path, unsigned line, unsigned column);

View File

@ -1,6 +1,6 @@
#include "Utility.h"
std::string clang::to_string(CXString cx_string) {
std::string clang::ToString(CXString cx_string) {
std::string string;
if(cx_string.data!=nullptr) {
string=clang_getCString(cx_string);
@ -9,7 +9,7 @@ std::string clang::to_string(CXString cx_string) {
return string;
}
std::string clang::to_string(CXCursorKind kind) {
std::string clang::ToString(CXCursorKind kind) {
switch (kind) {
case CXCursor_UnexposedDecl: return "UnexposedDecl";
case CXCursor_StructDecl: return "StructDecl";
@ -177,5 +177,5 @@ std::string clang::to_string(CXCursorKind kind) {
//case CXCursor_FirstExtraDecl: return "FirstExtraDecl";
case CXCursor_LastExtraDecl: return "LastExtraDecl";
}
return "";
return "<unknown kind>";
}

View File

@ -1,11 +1,11 @@
#ifndef UTILITY_H_
#define UTILITY_H_
#pragma once
#include <clang-c/Index.h>
#include <string>
namespace clang {
std::string to_string(CXString cx_string);
std::string to_string(CXCursorKind cursor_kind);
}
#endif // UTILITY_H_
std::string ToString(CXString cx_string);
std::string ToString(CXCursorKind cursor_kind);
} // namespace clang

548
main.cpp
View File

@ -1,85 +1,513 @@
#include <optional>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <fstream>
#include "libclangmm\clangmm.h"
#include "libclangmm\Utility.h"
#include "utils.h"
//#include <clang-c\Index.h>
CXChildVisitResult visitPrint(CXCursor cursor0, CXCursor parent, CXClientData param) {
int* level = static_cast<int*>(param);
clang::Cursor cursor(cursor0);
// While indexing, we should refer to symbols by USR. When joining into the db, we can have optimized access.
for (int i = 0; i < *level; ++i)
std::cout << " ";
std::cout << cursor.get_spelling() << " " << clang::to_string(cursor.get_kind()) << std::endl;
struct TypeDef;
struct FuncDef;
struct VarDef;
*level += 1;
clang_visitChildren(cursor0, &visitPrint, level);
*level -= 1;
return CXChildVisitResult::CXChildVisit_Continue;
/*
echo " ".repeat(param.level), cursor.kind, " ", getSpelling(cursor)
template<typename T>
struct Id {
uint64_t file_id;
uint64_t local_id;
var visitParam : VisitPrintParam
visitParam.level = param.level + 1
visitChildren(cursor, visitPrint, addr visitParam)
Id() : file_id(0), local_id(0) {} // Needed for containers. Do not use directly.
Id(uint64_t file_id, uint64_t local_id)
: file_id(file_id), local_id(local_id) {}
};
using TypeId = Id<TypeDef>;
using FuncId = Id<FuncDef>;
using VarId = Id<VarDef>;
return VisitResult.Continu
*/
template<typename T>
struct Ref {
Id<T> id;
clang::SourceLocation loc;
};
using TypeRef = Ref<TypeDef>;
using FuncRef = Ref<FuncDef>;
using VarRef = Ref<VarDef>;
struct TypeDef {
TypeDef(TypeId id);
// General metadata.
TypeId id;
std::string usr;
std::string shortName;
std::string qualifiedName;
std::optional<clang::SourceLocation> definition;
// Immediate parent and immediate derived types.
std::vector<TypeId> parents;
std::vector<TypeId> derived;
// Types, functions, and variables defined in this type.
std::vector<TypeId> types;
std::vector<FuncId> funcs;
std::vector<VarId> vars;
// Usages.
std::vector<clang::SourceLocation> uses;
};
TypeDef::TypeDef(TypeId id) : id(id) {}
struct FuncDef {
FuncDef(FuncId id);
// General metadata.
FuncId id;
std::string usr;
std::string shortName;
std::string qualifiedName;
std::optional<clang::SourceLocation> declaration;
std::optional<clang::SourceLocation> definition;
// Type which declares this one (ie, it is a method)
std::optional<TypeId> declaringType;
// Method this method overrides.
std::optional<FuncId> baseFunc;
// Methods which directly override this one.
std::vector<FuncId> derived;
// Local variables defined in this function.
std::vector<VarId> locals;
// Functions which call this one.
std::vector<FuncRef> callers;
// Functions that this function calls.
std::vector<FuncRef> callees;
// Usages.
std::vector<clang::SourceLocation> uses;
};
FuncDef::FuncDef(FuncId id) : id(id) {}
struct VarDef {
VarDef(VarId id);
// General metadata.
VarId id;
std::string usr;
std::string shortName;
std::string qualifiedName;
std::optional<clang::SourceLocation> declaration;
std::vector<clang::SourceLocation> initializations;
// Type of the variable.
std::optional<TypeId> variableType;
// Type which declares this one (ie, it is a method)
std::optional<TypeId> declaringType;
// Usages.
std::vector<clang::SourceLocation> uses;
};
VarDef::VarDef(VarId id) : id(id) {}
struct ParsingDatabase {
// NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs
// to get fixed up when inserting into the real db.
std::unordered_map<std::string, TypeId> usrToTypeId;
std::unordered_map<std::string, FuncId> usrToFuncId;
std::unordered_map<std::string, VarId> usrToVarId;
std::vector<TypeDef> types;
std::vector<FuncDef> funcs;
std::vector<VarDef> vars;
TypeId ToTypeId(const std::string& usr);
FuncId ToFuncId(const std::string& usr);
VarId ToVarId(const std::string& usr);
TypeDef* Resolve(TypeId id);
FuncDef* Resolve(FuncId id);
VarDef* Resolve(VarId id);
std::vector<std::string> ToString();
};
TypeId ParsingDatabase::ToTypeId(const std::string& usr) {
auto it = usrToTypeId.find(usr);
if (it != usrToTypeId.end())
return it->second;
TypeId id(0, types.size());
types.push_back(TypeDef(id));
usrToTypeId[usr] = id;
return id;
}
FuncId ParsingDatabase::ToFuncId(const std::string& usr) {
auto it = usrToFuncId.find(usr);
if (it != usrToFuncId.end())
return it->second;
FuncId id(0, funcs.size());
funcs.push_back(FuncDef(id));
usrToFuncId[usr] = id;
return id;
}
VarId ParsingDatabase::ToVarId(const std::string& usr) {
auto it = usrToVarId.find(usr);
if (it != usrToVarId.end())
return it->second;
VarId id(0, vars.size());
vars.push_back(VarDef(id));
usrToVarId[usr] = id;
return id;
}
int main(int argc, char** argv) {
TypeDef* ParsingDatabase::Resolve(TypeId id) {
return &types[id.local_id];
}
FuncDef* ParsingDatabase::Resolve(FuncId id) {
return &funcs[id.local_id];
}
VarDef* ParsingDatabase::Resolve(VarId id) {
return &vars[id.local_id];
}
std::vector<std::string> ParsingDatabase::ToString() {
std::vector<std::string> result;
result.push_back("Types:");
for (TypeDef& def : types) {
result.push_back(" " + def.qualifiedName);
}
result.push_back("Funcs:");
for (FuncDef& def : funcs) {
result.push_back(" " + def.qualifiedName);
}
result.push_back("Vars:");
for (VarDef& def : vars) {
result.push_back(" " + def.qualifiedName);
}
return result;
}
struct FileDef {
uint64_t id;
std::string path;
std::vector<TypeDef> types;
std::vector<FuncDef> funcs;
std::vector<VarDef> vars;
};
struct Database {
std::unordered_map<std::string, TypeId> usrToTypeId;
std::unordered_map<std::string, FuncId> usrToFuncId;
std::unordered_map<std::string, VarId> usrToVarId;
std::vector<FileDef> files;
TypeId ToTypeId(const std::string& usr);
FuncId ToFuncId(const std::string& usr);
VarId ToVarId(const std::string& usr);
};
TypeId Database::ToTypeId(const std::string& usr) {
auto it = usrToTypeId.find(usr);
assert(it != usrToTypeId.end() && "Usr is not registered");
return it->second;
}
FuncId Database::ToFuncId(const std::string& usr) {
auto it = usrToFuncId.find(usr);
assert(it != usrToFuncId.end() && "Usr is not registered");
return it->second;
}
VarId Database::ToVarId(const std::string& usr) {
auto it = usrToVarId.find(usr);
assert(it != usrToVarId.end() && "Usr is not registered");
return it->second;
}
TypeDef* Resolve(FileDef* file, TypeId id) {
assert(file->id == id.file_id);
return &file->types[id.local_id];
}
FuncDef* Resolve(FileDef* file, FuncId id) {
assert(file->id == id.file_id);
return &file->funcs[id.local_id];
}
VarDef* Resolve(FileDef* file, VarId id) {
assert(file->id == id.file_id);
return &file->vars[id.local_id];
}
TypeDef* Resolve(Database* db, TypeId id) {
return Resolve(&db->files[id.file_id], id);
}
FuncDef* Resolve(Database* db, FuncId id) {
return Resolve(&db->files[id.file_id], id);
}
VarDef* Resolve(Database* db, VarId id) {
return Resolve(&db->files[id.file_id], id);
}
struct NamespaceStack {
std::vector<std::string> stack;
void Push(const std::string& ns);
void Pop();
std::string ComputeQualifiedPrefix();
};
void NamespaceStack::Push(const std::string& ns) {
stack.push_back(ns);
}
void NamespaceStack::Pop() {
stack.pop_back();
}
std::string NamespaceStack::ComputeQualifiedPrefix() {
std::string result;
for (const std::string& ns : stack)
result += ns + "::";
return result;
}
struct FuncDefinitionParam {};
clang::VisiterResult VisitFuncDefinition(clang::Cursor cursor, clang::Cursor parent, FuncDefinitionParam* param) {
/*
echo "Parsing ", filename
let index : libclang.CXIndex = libclang.createIndex(#[excludeDeclsFromPCH]# 0, #[displayDiagnostics]# 0)
switch (cursor.get_kind()) {
let translationUnit : libclang.CXTranslationUnit =
libclang.parseTranslationUnit(index, filename.cstring, nil, 0, nil, 0, 0)
if translationUnit == nil :
echo "Error: cannot create translation unit for '", filename, "'"
return
let cursor = libclang.getTranslationUnitCursor(translationUnit)
var param : VisitFileParam
param.db = db
echo "=== START AST ==="
# printChildren(cursor, 0)
echo "=== DONE AST ==="
var file : File
file.path = filename
file.vars = newList[VarRef]()
file.funcs = newList[FuncRef]()
file.types = newList[TypeRef]()
visitChildren(cursor, visitFile, addr param)
libclang.disposeTranslationUnit(translationUnit)
libclang.disposeIndex(index)
default:
std::cerr << "Unhandled VisitFuncDefinition kind " << clang::ToString(cursor.get_kind()) << std::endl;
break;
}
*/
return clang::VisiterResult::Break;
}
void HandleFunc(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor func, std::optional<TypeId> declaringType) {
// What this method must process:
// - function declaration
// - function definition
// - method declaration
// - method inline definition
// - method definition
// TODO: Make sure we only process once for declared/defined types.
// TODO: method_definition_in_namespace.cc is failing because we process decl with correct declaringType, but
// processing method definition fails to resolve correct declaringType.
//if (func.is_definition()) // RM after addressed above
// return; // RM after addressed above
FuncId id = db->ToFuncId(func.get_usr());
db->Resolve(id)->shortName = func.get_spelling();
std::string typeName;
if (declaringType)
typeName = db->Resolve(declaringType.value())->shortName + "::";
db->Resolve(id)->qualifiedName = ns->ComputeQualifiedPrefix() + typeName + func.get_spelling();
std::cout << func.get_usr() << ": Set qualified name to " << db->Resolve(id)->qualifiedName << std::endl;
//std::cout << "!! HandleFunc " << func.get_type_description() << std::endl;
//std::cout << " comment: " << func.get_comments() << std::endl;
//std::cout << " spelling: " << func.get_spelling() << std::endl;
//for (clang::Cursor argument : func.get_arguments())
// std::cout << " arg: " << clang::ToString(argument.get_kind()) << " " << argument.get_spelling() << std::endl;
}
struct ClassDeclParam {
ParsingDatabase* db;
NamespaceStack* ns;
TypeId active_type;
ClassDeclParam(ParsingDatabase* db, NamespaceStack* ns, TypeId active_type)
: db(db), ns(ns), active_type(active_type) {}
};
clang::VisiterResult VisitClassDecl(clang::Cursor cursor, clang::Cursor parent, ClassDeclParam* param) {
ParsingDatabase* db = param->db;
switch (cursor.get_kind()) {
case CXCursor_CXXMethod:
HandleFunc(param->db, param->ns, cursor, param->active_type);
break;
default:
std::cerr << "Unhandled VisitClassDecl kind " << clang::ToString(cursor.get_kind()) << std::endl;
break;
}
return clang::VisiterResult::Continue;
}
void HandleClassDecl(clang::Cursor cursor, ParsingDatabase* db, NamespaceStack* ns) {
TypeId active_type = db->ToTypeId(cursor.get_usr());
db->Resolve(active_type)->shortName = cursor.get_spelling();
db->Resolve(active_type)->qualifiedName = ns->ComputeQualifiedPrefix() + cursor.get_spelling();
ClassDeclParam classDeclParam(db, ns, active_type);
cursor.VisitChildren(&VisitClassDecl, &classDeclParam);
}
struct FileParam {
ParsingDatabase* db;
NamespaceStack* ns;
FileParam(ParsingDatabase* db, NamespaceStack* ns) : db(db), ns(ns) {}
};
clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileParam* param) {
switch (cursor.get_kind()) {
case CXCursor_Namespace:
// For a namespace, visit the children of the namespace, but this time with
// a pushed namespace stack.
param->ns->Push(cursor.get_display_name());
cursor.VisitChildren(&VisitFile, param);
param->ns->Pop();
break;
case CXCursor_ClassDecl:
// TODO: Cleanup Handle* param order.
HandleClassDecl(cursor, param->db, param->ns);
break;
case CXCursor_CXXMethod:
case CXCursor_FunctionDecl:
HandleFunc(param->db, param->ns, cursor, std::nullopt);
break;
default:
std::cerr << "Unhandled VisitFile kind " << clang::ToString(cursor.get_kind()) << std::endl;
break;
}
return clang::VisiterResult::Continue;
}
clang::VisiterResult DumpVisitor(clang::Cursor cursor, clang::Cursor parent, int* level) {
for (int i = 0; i < *level; ++i)
std::cout << " ";
std::cout << cursor.get_spelling() << " " << clang::ToString(cursor.get_kind()) << std::endl;
*level += 1;
cursor.VisitChildren(&DumpVisitor, level);
*level -= 1;
return clang::VisiterResult::Continue;
}
void Dump(clang::Cursor cursor) {
int level = 0;
cursor.VisitChildren(&DumpVisitor, &level);
}
ParsingDatabase Parse(std::string filename) {
std::vector<std::string> args;
clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
clang::TranslationUnit translationUnit(index, "test.cc", args);
clang::TranslationUnit tu(index, filename, args);
int level = 0;
auto cursor = clang_getTranslationUnitCursor(translationUnit.cx_tu);
clang_visitChildren(cursor, &visitPrint, &level);
/*
TranslationUnit(Index &index,
const std::string &file_path,
const std::vector<std::string> &command_line_args,
const std::string &buffer,
unsigned flags = DefaultFlags());
TranslationUnit(Index &index,
const std::string &file_path,
const std::vector<std::string> &command_line_args,
unsigned flags = DefaultFlags());
*/
//std::cout << "Start document dump" << std::endl;
//Dump(tu.document_cursor());
//std::cout << "Done document dump" << std::endl << std::endl;
ParsingDatabase db;
NamespaceStack ns;
FileParam fileParam(&db, &ns);
tu.document_cursor().VisitChildren(&VisitFile, &fileParam);
return db;
}
template<typename T>
bool AreEqual(const std::vector<T>& a, const std::vector<T>& b) {
if (a.size() != b.size())
return false;
for (int i = 0; i < a.size(); ++i) {
if (a[i] != b[i])
return false;
}
return true;
}
void Write(const std::vector<std::string>& strs) {
for (const std::string& str : strs) {
std::cout << str << std::endl;
}
}
int main(int argc, char** argv) {
for (std::string path : GetFilesInFolder("tests")) {
// TODO: Fix all existing tests.
if (path != "tests/method_definition_in_namespace.cc") continue;
std::vector<std::string> expected_output;
ParseTestExpectation(path, &expected_output);
std::cout << "[START] " << path << std::endl;
ParsingDatabase db = Parse(path);
std::vector<std::string> actual_output = db.ToString();
if (AreEqual(expected_output, actual_output)) {
std::cout << "[PASSED] " << path << std::endl;
}
else {
std::cout << "[FAILED] " << path << std::endl;
std::cout << "Expected output for " << path << ":" << std::endl;
Write(expected_output);
std::cout << "Actual output for " << path << ":" << std::endl;
Write(actual_output);
break;
}
}
std::cin.get();
return 0;
}

View File

@ -1,2 +1,2 @@
void bar() {}
void bar(int a, int b) {}
void foo() {}

View File

@ -0,0 +1,9 @@
void foo(int a, int b);
/*
OUTPUT:
Types:
Funcs:
foo
Vars:
*/

View File

@ -0,0 +1,11 @@
namespace hello {
void foo(int a, int b);
}
/*
OUTPUT:
Types:
Funcs:
hello::foo
Vars:
*/

View File

@ -0,0 +1,9 @@
void foo(int a, int b) {}
/*
OUTPUT:
Types:
Funcs:
foo
Vars:
*/

View File

@ -0,0 +1,11 @@
namespace hello {
void foo(int a, int b) {}
}
/*
OUTPUT:
Types:
Funcs:
hello::foo
Vars:
*/

View File

@ -0,0 +1,11 @@
class Foo {
void foo();
};
/*
OUTPUT:
Types:
Funcs:
foo
Vars:
*/

View File

@ -0,0 +1,13 @@
namespace hello {
class Foo {
void foo();
};
}
/*
OUTPUT:
Types:
Funcs:
hello::Foo::foo
Vars:
*/

View File

@ -0,0 +1,13 @@
class Foo {
void foo();
};
void Foo::foo() {}
/*
OUTPUT:
Types:
Funcs:
foo
Vars:
*/

View File

@ -0,0 +1,15 @@
namespace hello {
class Foo {
void foo();
};
void Foo::foo() {}
}
/*
OUTPUT:
Types:
Funcs:
hello::Foo::foo
Vars:
*/

View File

@ -0,0 +1,11 @@
class Foo {
void foo() {}
};
/*
OUTPUT:
Types:
Funcs:
foo
Vars:
*/

View File

@ -0,0 +1,13 @@
namespace hello {
class Foo {
void foo() {}
};
}
/*
OUTPUT:
Types:
Funcs:
hello::Foo::foo
Vars:
*/

804
tinydir.h Normal file
View File

@ -0,0 +1,804 @@
/*
Copyright (c) 2013-2016, tinydir authors:
- Cong Xu
- Lautis Sun
- Baudouin Feildel
- Andargor <andargor@yahoo.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TINYDIR_H
#define TINYDIR_H
#ifdef __cplusplus
extern "C" {
#endif
#if ((defined _UNICODE) && !(defined UNICODE))
#define UNICODE
#endif
#if ((defined UNICODE) && !(defined _UNICODE))
#define _UNICODE
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <tchar.h>
# pragma warning(push)
# pragma warning (disable : 4996)
#else
# include <dirent.h>
# include <libgen.h>
# include <sys/stat.h>
# include <stddef.h>
#endif
#ifdef __MINGW32__
# include <tchar.h>
#endif
/* types */
/* Windows UNICODE wide character support */
#if defined _MSC_VER || defined __MINGW32__
#define _tinydir_char_t TCHAR
#define TINYDIR_STRING(s) _TEXT(s)
#define _tinydir_strlen _tcslen
#define _tinydir_strcpy _tcscpy
#define _tinydir_strcat _tcscat
#define _tinydir_strcmp _tcscmp
#define _tinydir_strrchr _tcsrchr
#define _tinydir_strncmp _tcsncmp
#else
#define _tinydir_char_t char
#define TINYDIR_STRING(s) s
#define _tinydir_strlen strlen
#define _tinydir_strcpy strcpy
#define _tinydir_strcat strcat
#define _tinydir_strcmp strcmp
#define _tinydir_strrchr strrchr
#define _tinydir_strncmp strncmp
#endif
#if (defined _MSC_VER || defined __MINGW32__)
#include <windows.h>
#define _TINYDIR_PATH_MAX MAX_PATH
#elif defined __linux__
#include <linux/limits.h>
#define _TINYDIR_PATH_MAX PATH_MAX
#else
#define _TINYDIR_PATH_MAX 4096
#endif
#ifdef _MSC_VER
/* extra chars for the "\\*" mask */
# define _TINYDIR_PATH_EXTRA 2
#else
# define _TINYDIR_PATH_EXTRA 0
#endif
#define _TINYDIR_FILENAME_MAX 256
#if (defined _MSC_VER || defined __MINGW32__)
#define _TINYDIR_DRIVE_MAX 3
#endif
#ifdef _MSC_VER
# define _TINYDIR_FUNC static __inline
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
# define _TINYDIR_FUNC static __inline__
#else
# define _TINYDIR_FUNC static inline
#endif
/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
#ifdef TINYDIR_USE_READDIR_R
/* readdir_r is a POSIX-only function, and may not be available under various
* environments/settings, e.g. MinGW. Use readdir fallback */
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
_POSIX_SOURCE
# define _TINYDIR_HAS_READDIR_R
#endif
#if _POSIX_C_SOURCE >= 200112L
# define _TINYDIR_HAS_FPATHCONF
# include <unistd.h>
#endif
#if _BSD_SOURCE || _SVID_SOURCE || \
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
# define _TINYDIR_HAS_DIRFD
# include <sys/types.h>
#endif
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
defined _PC_NAME_MAX
# define _TINYDIR_USE_FPATHCONF
#endif
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
# define _TINYDIR_USE_READDIR
#endif
/* Use readdir by default */
#else
# define _TINYDIR_USE_READDIR
#endif
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
#ifndef _MSC_VER
#if (defined __MINGW32__) && (defined _UNICODE)
#define _TINYDIR_DIR _WDIR
#define _tinydir_dirent _wdirent
#define _tinydir_opendir _wopendir
#define _tinydir_readdir _wreaddir
#define _tinydir_closedir _wclosedir
#else
#define _TINYDIR_DIR DIR
#define _tinydir_dirent dirent
#define _tinydir_opendir opendir
#define _tinydir_readdir readdir
#define _tinydir_closedir closedir
#endif
#endif
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
#else
#error "Either define both alloc and free or none of them!"
#endif
#if !defined(_TINYDIR_MALLOC)
#define _TINYDIR_MALLOC(_size) malloc(_size)
#define _TINYDIR_FREE(_ptr) free(_ptr)
#endif /* !defined(_TINYDIR_MALLOC) */
typedef struct tinydir_file
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
_tinydir_char_t *extension;
int is_dir;
int is_reg;
#ifndef _MSC_VER
#ifdef __MINGW32__
struct _stat _s;
#else
struct stat _s;
#endif
#endif
} tinydir_file;
typedef struct tinydir_dir
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
int has_next;
size_t n_files;
tinydir_file *_files;
#ifdef _MSC_VER
HANDLE _h;
WIN32_FIND_DATA _f;
#else
_TINYDIR_DIR *_d;
struct _tinydir_dirent *_e;
#ifndef _TINYDIR_USE_READDIR
struct _tinydir_dirent *_ep;
#endif
#endif
} tinydir_dir;
/* declarations */
_TINYDIR_FUNC
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
_TINYDIR_FUNC
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
_TINYDIR_FUNC
void tinydir_close(tinydir_dir *dir);
_TINYDIR_FUNC
int tinydir_next(tinydir_dir *dir);
_TINYDIR_FUNC
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
_TINYDIR_FUNC
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
_TINYDIR_FUNC
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
_TINYDIR_FUNC
void _tinydir_get_ext(tinydir_file *file);
_TINYDIR_FUNC
int _tinydir_file_cmp(const void *a, const void *b);
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
_TINYDIR_FUNC
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
#endif
#endif
/* definitions*/
_TINYDIR_FUNC
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
{
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
int error;
int size; /* using int size */
#endif
#else
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
#endif
_tinydir_char_t *pathp;
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
/* initialise dir */
dir->_files = NULL;
#ifdef _MSC_VER
dir->_h = INVALID_HANDLE_VALUE;
#else
dir->_d = NULL;
#ifndef _TINYDIR_USE_READDIR
dir->_ep = NULL;
#endif
#endif
tinydir_close(dir);
_tinydir_strcpy(dir->path, path);
/* Remove trailing slashes */
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
{
*pathp = TINYDIR_STRING('\0');
pathp++;
}
#ifdef _MSC_VER
_tinydir_strcpy(path_buf, dir->path);
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
dir->_h = FindFirstFile(path_buf, &dir->_f);
if (dir->_h == INVALID_HANDLE_VALUE)
{
errno = ENOENT;
#else
dir->_d = _tinydir_opendir(path);
if (dir->_d == NULL)
{
#endif
goto bail;
}
/* read first file */
dir->has_next = 1;
#ifndef _MSC_VER
#ifdef _TINYDIR_USE_READDIR
dir->_e = _tinydir_readdir(dir->_d);
#else
/* allocate dirent buffer for readdir_r */
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
if (size == -1) return -1;
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
if (dir->_ep == NULL) return -1;
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
if (error != 0) return -1;
#endif
if (dir->_e == NULL)
{
dir->has_next = 0;
}
#endif
return 0;
bail:
tinydir_close(dir);
return -1;
}
_TINYDIR_FUNC
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
{
/* Count the number of files first, to pre-allocate the files array */
size_t n_files = 0;
if (tinydir_open(dir, path) == -1)
{
return -1;
}
while (dir->has_next)
{
n_files++;
if (tinydir_next(dir) == -1)
{
goto bail;
}
}
tinydir_close(dir);
if (tinydir_open(dir, path) == -1)
{
return -1;
}
dir->n_files = 0;
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
if (dir->_files == NULL)
{
goto bail;
}
while (dir->has_next)
{
tinydir_file *p_file;
dir->n_files++;
p_file = &dir->_files[dir->n_files - 1];
if (tinydir_readfile(dir, p_file) == -1)
{
goto bail;
}
if (tinydir_next(dir) == -1)
{
goto bail;
}
/* Just in case the number of files has changed between the first and
second reads, terminate without writing into unallocated memory */
if (dir->n_files == n_files)
{
break;
}
}
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
return 0;
bail:
tinydir_close(dir);
return -1;
}
_TINYDIR_FUNC
void tinydir_close(tinydir_dir *dir)
{
if (dir == NULL)
{
return;
}
memset(dir->path, 0, sizeof(dir->path));
dir->has_next = 0;
dir->n_files = 0;
_TINYDIR_FREE(dir->_files);
dir->_files = NULL;
#ifdef _MSC_VER
if (dir->_h != INVALID_HANDLE_VALUE)
{
FindClose(dir->_h);
}
dir->_h = INVALID_HANDLE_VALUE;
#else
if (dir->_d)
{
_tinydir_closedir(dir->_d);
}
dir->_d = NULL;
dir->_e = NULL;
#ifndef _TINYDIR_USE_READDIR
_TINYDIR_FREE(dir->_ep);
dir->_ep = NULL;
#endif
#endif
}
_TINYDIR_FUNC
int tinydir_next(tinydir_dir *dir)
{
if (dir == NULL)
{
errno = EINVAL;
return -1;
}
if (!dir->has_next)
{
errno = ENOENT;
return -1;
}
#ifdef _MSC_VER
if (FindNextFile(dir->_h, &dir->_f) == 0)
#else
#ifdef _TINYDIR_USE_READDIR
dir->_e = _tinydir_readdir(dir->_d);
#else
if (dir->_ep == NULL)
{
return -1;
}
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
{
return -1;
}
#endif
if (dir->_e == NULL)
#endif
{
dir->has_next = 0;
#ifdef _MSC_VER
if (GetLastError() != ERROR_SUCCESS &&
GetLastError() != ERROR_NO_MORE_FILES)
{
tinydir_close(dir);
errno = EIO;
return -1;
}
#endif
}
return 0;
}
_TINYDIR_FUNC
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
{
if (dir == NULL || file == NULL)
{
errno = EINVAL;
return -1;
}
#ifdef _MSC_VER
if (dir->_h == INVALID_HANDLE_VALUE)
#else
if (dir->_e == NULL)
#endif
{
errno = ENOENT;
return -1;
}
if (_tinydir_strlen(dir->path) +
_tinydir_strlen(
#ifdef _MSC_VER
dir->_f.cFileName
#else
dir->_e->d_name
#endif
) + 1 + _TINYDIR_PATH_EXTRA >=
_TINYDIR_PATH_MAX)
{
/* the path for the file will be too long */
errno = ENAMETOOLONG;
return -1;
}
if (_tinydir_strlen(
#ifdef _MSC_VER
dir->_f.cFileName
#else
dir->_e->d_name
#endif
) >= _TINYDIR_FILENAME_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
_tinydir_strcpy(file->path, dir->path);
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
_tinydir_strcpy(file->name,
#ifdef _MSC_VER
dir->_f.cFileName
#else
dir->_e->d_name
#endif
);
_tinydir_strcat(file->path, file->name);
#ifndef _MSC_VER
#ifdef __MINGW32__
if (_tstat(
#else
if (stat(
#endif
file->path, &file->_s) == -1)
{
return -1;
}
#endif
_tinydir_get_ext(file);
file->is_dir =
#ifdef _MSC_VER
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
#else
S_ISDIR(file->_s.st_mode);
#endif
file->is_reg =
#ifdef _MSC_VER
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
(
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
#endif
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
#endif
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
#else
S_ISREG(file->_s.st_mode);
#endif
return 0;
}
_TINYDIR_FUNC
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
{
if (dir == NULL || file == NULL)
{
errno = EINVAL;
return -1;
}
if (i >= dir->n_files)
{
errno = ENOENT;
return -1;
}
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
_tinydir_get_ext(file);
return 0;
}
_TINYDIR_FUNC
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
if (dir == NULL)
{
errno = EINVAL;
return -1;
}
if (i >= dir->n_files || !dir->_files[i].is_dir)
{
errno = ENOENT;
return -1;
}
_tinydir_strcpy(path, dir->_files[i].path);
tinydir_close(dir);
if (tinydir_open_sorted(dir, path) == -1)
{
return -1;
}
return 0;
}
/* Open a single file given its path */
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
{
tinydir_dir dir;
int result = 0;
int found = 0;
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
_tinydir_char_t *dir_name;
_tinydir_char_t *base_name;
#if (defined _MSC_VER || defined __MINGW32__)
_tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX];
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
#endif
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
/* Get the parent path */
#if (defined _MSC_VER || defined __MINGW32__)
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
_tsplitpath_s(
path,
drive_buf, _TINYDIR_DRIVE_MAX,
dir_name_buf, _TINYDIR_FILENAME_MAX,
file_name_buf, _TINYDIR_FILENAME_MAX,
ext_buf, _TINYDIR_FILENAME_MAX);
#else
_tsplitpath(
path,
drive_buf,
dir_name_buf,
file_name_buf,
ext_buf);
#endif
/* _splitpath_s not work fine with only filename and widechar support */
#ifdef _UNICODE
if (drive_buf[0] == L'\xFEFE')
drive_buf[0] = '\0';
if (dir_name_buf[0] == L'\xFEFE')
dir_name_buf[0] = '\0';
#endif
if (errno)
{
errno = EINVAL;
return -1;
}
/* Emulate the behavior of dirname by returning "." for dir name if it's
empty */
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
{
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
}
/* Concatenate the drive letter and dir name to form full dir name */
_tinydir_strcat(drive_buf, dir_name_buf);
dir_name = drive_buf;
/* Concatenate the file name and extension to form base name */
_tinydir_strcat(file_name_buf, ext_buf);
base_name = file_name_buf;
#else
_tinydir_strcpy(dir_name_buf, path);
dir_name = dirname(dir_name_buf);
_tinydir_strcpy(file_name_buf, path);
base_name =basename(file_name_buf);
#endif
/* Open the parent directory */
if (tinydir_open(&dir, dir_name) == -1)
{
return -1;
}
/* Read through the parent directory and look for the file */
while (dir.has_next)
{
if (tinydir_readfile(&dir, file) == -1)
{
result = -1;
goto bail;
}
if (_tinydir_strcmp(file->name, base_name) == 0)
{
/* File found */
found = 1;
break;
}
tinydir_next(&dir);
}
if (!found)
{
result = -1;
errno = ENOENT;
}
bail:
tinydir_close(&dir);
return result;
}
_TINYDIR_FUNC
void _tinydir_get_ext(tinydir_file *file)
{
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
if (period == NULL)
{
file->extension = &(file->name[_tinydir_strlen(file->name)]);
}
else
{
file->extension = period + 1;
}
}
_TINYDIR_FUNC
int _tinydir_file_cmp(const void *a, const void *b)
{
const tinydir_file *fa = (const tinydir_file *)a;
const tinydir_file *fb = (const tinydir_file *)b;
if (fa->is_dir != fb->is_dir)
{
return -(fa->is_dir - fb->is_dir);
}
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
}
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
/*
The following authored by Ben Hutchings <ben@decadent.org.uk>
from https://womble.decadent.org.uk/readdir_r-advisory.html
*/
/* Calculate the required buffer size (in bytes) for directory *
* entries read from the given directory handle. Return -1 if this *
* this cannot be done. *
* *
* This code does not trust values of NAME_MAX that are less than *
* 255, since some systems (including at least HP-UX) incorrectly *
* define it to be a smaller value. */
_TINYDIR_FUNC
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
{
long name_max;
size_t name_end;
/* parameter may be unused */
(void)dirp;
#if defined _TINYDIR_USE_FPATHCONF
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if (name_max == -1)
#if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
#else
return (size_t)(-1);
#endif
#elif defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
#else
#error "buffer size for readdir_r cannot be determined"
#endif
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
return (name_end > sizeof(struct _tinydir_dirent) ?
name_end : sizeof(struct _tinydir_dirent));
}
#endif
#endif
#ifdef __cplusplus
}
#endif
# if defined (_MSC_VER)
# pragma warning(pop)
# endif
#endif

68
utils.cc Normal file
View File

@ -0,0 +1,68 @@
#include "utils.h"
#include <fstream>
#include "tinydir.h"
std::vector<std::string> GetFilesInFolder(std::string folder) {
std::vector<std::string> result;
tinydir_dir dir;
if (tinydir_open(&dir, folder.c_str()) == -1) {
perror("Error opening file");
goto bail;
}
while (dir.has_next) {
tinydir_file file;
if (tinydir_readfile(&dir, &file) == -1) {
perror("Error getting file");
goto bail;
}
std::string full_path = folder + "/" + file.name;
if (file.is_dir) {
if (strcmp(file.name, ".") != 0 && strcmp(file.name, "..") != 0) {
for (std::string nested_file : GetFilesInFolder(full_path))
result.push_back(nested_file);
}
}
else {
result.push_back(full_path);
}
if (tinydir_next(&dir) == -1) {
perror("Error getting next file");
goto bail;
}
}
bail:
tinydir_close(&dir);
return result;
}
std::vector<std::string> ReadLines(std::string filename) {
std::vector<std::string> result;
std::ifstream input(filename);
for (std::string line; getline(input, line); )
result.push_back(line);
return result;
}
void ParseTestExpectation(std::string filename, std::vector<std::string>* expected_output) {
bool in_output = false;
for (std::string line : ReadLines(filename)) {
if (line == "*/")
break;
if (in_output)
expected_output->push_back(line);
if (line == "OUTPUT:")
in_output = true;
}
}

8
utils.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
#include <vector>
std::vector<std::string> GetFilesInFolder(std::string folder);
std::vector<std::string> ReadLines(std::string filename);
void ParseTestExpectation(std::string filename, std::vector<std::string>* expected_output);