diff --git a/.clang-format b/.clang-format index d384dc18..b899d756 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ # compatible with clang-format 18 UseTab: Never -ColumnLimit: 100 +ColumnLimit: 120 # Indent IndentWidth: 4 diff --git a/.vscode/launch.json b/.vscode/launch.json index 661d13a3..d121d769 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,48 +16,12 @@ "type": "lldb", "request": "launch", "name": "ASTVisitor", - "program": "${workspaceFolder}/build/ASTVisitor", + "program": "${workspaceFolder}/build/bin/clice", "args": [ "/home/ykiko/C++/clice/test.cpp", ], "cwd": "${workspaceFolder}" }, - { - "type": "lldb", - "request": "launch", - "name": "Preprocessor", - "program": "${workspaceFolder}/build/Preprocessor", - "args": [ - "/home/ykiko/C++/clice/main.cpp", - ], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "ASTVisitor", - "program": "${workspaceFolder}/build/ASTVisitor", - "args": [ - "/home/ykiko/C++/clice/test.cpp", - ], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Module", - "program": "${workspaceFolder}/build/Module", - "args": [ - "/home/ykiko/C++/clice/main.cpp", - ], - "cwd": "${workspaceFolder}" - }, - //{ - // "type": "lldb", - // "request": "attach", - // "name": "Attach", - // "program": "${workspaceFolder}/build/clice" - //}, ], "compounds": [ { diff --git a/CMakeLists.txt b/CMakeLists.txt index b68fdd7c..d514ca9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(CLICE) set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -w") set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/deps/llvm/build-install") @@ -10,31 +11,31 @@ find_package(LLVM REQUIRED CONFIG) find_package(Clang REQUIRED CONFIG) message(STATUS "Found LLVM: ${LLVM_DIR}, Version: ${LLVM_PACKAGE_VERSION}") -add_definitions(${LLVM_DEFINITIONS}) - add_subdirectory(${CMAKE_SOURCE_DIR}/deps/libuv) add_subdirectory(${CMAKE_SOURCE_DIR}/deps/spdlog) add_executable(clice) -target_sources(clice PRIVATE - src/main.cpp -) +file(GLOB_RECURSE SRC_FILES "${CMAKE_SOURCE_DIR}/src/*.cpp") +target_sources(clice PRIVATE ${SRC_FILES}) target_include_directories(clice PRIVATE + ${CMAKE_SOURCE_DIR}/include ${LLVM_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/deps/libuv/include ${CMAKE_SOURCE_DIR}/deps/spdlog/include ) set(LLVM_LIBS - LLVMCore + #LLVMCore LLVMSupport + #LLVMTargetParser ) set(CLANG_LIBS clangAST + clangASTMatchers clangBasic clangDependencyScanning clangDriver diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..0be54a41 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,2 @@ +# arch + diff --git a/include/AST/Diagnostic.h b/include/AST/Diagnostic.h new file mode 100644 index 00000000..9a2f261c --- /dev/null +++ b/include/AST/Diagnostic.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/include/AST/ParsedAST.h b/include/AST/ParsedAST.h new file mode 100644 index 00000000..d7e44a8b --- /dev/null +++ b/include/AST/ParsedAST.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Preamble.h" + +namespace clice { + +struct ParsedAST { + std::unique_ptr action; + std::unique_ptr instance; + + static std::unique_ptr + build(llvm::StringRef filename, llvm::StringRef content, std::vector& args, Preamble* preamble); + + auto& ASTContext() const { return instance->getASTContext(); } + + auto& SourceManager() const { return instance->getSourceManager(); } + + auto TranslationUnitDecl() const { return ASTContext().getTranslationUnitDecl(); } +}; + +} // namespace clice diff --git a/include/AST/Preamble.h b/include/AST/Preamble.h new file mode 100644 index 00000000..e2820b58 --- /dev/null +++ b/include/AST/Preamble.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Diagnostic.h" + +namespace clice { + +/// Represents the preamble of a translation unit. +/// We build preamble for the translation after first edit. +/// The preamble is used to speed up the reparse of the translation unit. +struct Preamble { + clang::PrecompiledPreamble data; + + static std::unique_ptr + build(llvm::StringRef filename, llvm::StringRef content, std::vector& args); +}; + +} // namespace clice diff --git a/src/AST/ParsedAST.cpp b/src/AST/ParsedAST.cpp new file mode 100644 index 00000000..830e08a5 --- /dev/null +++ b/src/AST/ParsedAST.cpp @@ -0,0 +1,71 @@ +#include "AST/ParsedAST.h" +#include "clang/Lex/PreprocessorOptions.h" + +namespace clice { + +std::unique_ptr ParsedAST::build(llvm::StringRef filename, + llvm::StringRef content, + std::vector& args, + Preamble* preamble) { + auto vfs = llvm::vfs::getRealFileSystem(); + + clang::CreateInvocationOptions options; + // options.VFS = vfs; + + auto invocation = std::make_shared(); + invocation = clang::createInvocation(args, options); + + auto buffer = llvm::MemoryBuffer::getMemBuffer(content, filename); + if(preamble) { + + auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false); + + // check if the preamble can be reused + if(preamble->data.CanReuse(*invocation, *buffer, bounds, *vfs)) { + llvm::outs() << "Resued preamble\n"; + // reuse preamble + preamble->data.AddImplicitPreamble(*invocation, vfs, buffer.release()); + } + } else { + invocation->getPreprocessorOpts().addRemappedFile(invocation->getFrontendOpts().Inputs[0].getFile(), + buffer.release()); + } + + auto instance = std::make_unique(); + instance->setInvocation(std::move(invocation)); + + clang::DiagnosticIDs* ids = new clang::DiagnosticIDs(); + clang::DiagnosticOptions* diag_opts = new clang::DiagnosticOptions(); + clang::DiagnosticConsumer* consumer = new clang::TextDiagnosticPrinter(llvm::errs(), diag_opts); + clang::DiagnosticsEngine* engine = new clang::DiagnosticsEngine(ids, diag_opts, consumer); + instance->setDiagnostics(engine); + + // if(auto remappingVSF = + // createVFSFromCompilerInvocation(instance->getInvocation(), instance->getDiagnostics(), vfs)) { + // vfs = remappingVSF; + // } + // instance->createFileManager(vfs); + + if(!instance->createTarget()) { + llvm::errs() << "Failed to create target\n"; + std::terminate(); + } + + auto action = std::make_unique(); + + if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { + llvm::errs() << "Failed to begin source file\n"; + std::terminate(); + } + + if(auto error = action->Execute()) { + llvm::errs() << "Failed to execute action: " << error << "\n"; + std::terminate(); + } + + return std::unique_ptr{ + new ParsedAST{std::move(action), std::move(instance)} + }; +} + +} // namespace clice diff --git a/src/AST/Preamble.cpp b/src/AST/Preamble.cpp new file mode 100644 index 00000000..c0437f2d --- /dev/null +++ b/src/AST/Preamble.cpp @@ -0,0 +1,45 @@ +#include + +namespace clice { + +std::unique_ptr + Preamble::build(llvm::StringRef filename, llvm::StringRef content, std::vector& args) { + auto invocation = clang::createInvocation(args, {}); + auto buffer = llvm::MemoryBuffer::getMemBuffer(content, filename); + + // compute preamble bounds, i.e. the range of the preamble. + // if MaxLines set to false(0), i.e. limit the number of lines. + auto bounds = clang::ComputePreambleBounds(invocation->getLangOpts(), *buffer, false); + + // create diagnostic engine + clang::DiagnosticsEngine* engine = + new clang::DiagnosticsEngine(new clang::DiagnosticIDs(), + new clang::DiagnosticOptions(), + new clang::TextDiagnosticPrinter(llvm::errs(), new clang::DiagnosticOptions())); + + // if store the preamble in memory, if not, store it in a file(storagePath) + bool storeInMemory = false; + + // use to collect information in the process of building preamble, such as include files and macros + // TODO: inherit from clang::PreambleCallbacks and collect the information + clang::PreambleCallbacks callbacks = {}; + + auto preamble = clang::PrecompiledPreamble::Build(*invocation, + buffer.get(), + bounds, + *engine, + llvm::vfs::getRealFileSystem(), + std::make_shared(), + storeInMemory, + "", + callbacks); + + if(auto error = preamble.getError()) { + // TODO: report error + return nullptr; + } + + return std::unique_ptr{new Preamble{std::move(preamble.get())}}; +} + +} // namespace clice diff --git a/src/main.cpp b/src/main.cpp index 14640acb..df1cd57e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,16 @@ #include +#include +#include +#include +const char* source = R"( int main(){ + return 0; +} +)"; -} \ No newline at end of file +int main() { + auto args = std::vector{"clang++", "main.cpp"}; + auto AST = clice::ParsedAST::build("main.cpp", source, args, nullptr); + AST->TranslationUnitDecl()->dump(); +}